pax_global_header00006660000000000000000000000064131567101420014512gustar00rootroot0000000000000052 comment=e2d0f885080c57ad7b6180aac70c6b0222ebca46 metrics-3.2.5/000077500000000000000000000000001315671014200131675ustar00rootroot00000000000000metrics-3.2.5/.gitignore000066400000000000000000000001671315671014200151630ustar00rootroot00000000000000.idea .settings target .classpath .project bin *.iml *.ipr *.iws .metadata jcstress.* metrics-jcstress/results/ TODO.mdmetrics-3.2.5/.travis.yml000066400000000000000000000003201315671014200152730ustar00rootroot00000000000000language: java install: echo "I trust Maven." # don't just run the tests, also run Findbugs and friends script: mvn verify jdk: - oraclejdk8 notifications: email: recipients: - ryan@10e.us metrics-3.2.5/LICENSE000066400000000000000000000261421315671014200142010ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2010-2012 Coda Hale and Yammer, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. metrics-3.2.5/NOTICE000066400000000000000000000007261315671014200141000ustar00rootroot00000000000000Metrics Copyright 2010-2013 Coda Hale and Yammer, Inc. This product includes software developed by Coda Hale and Yammer, Inc. This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64, LongAdder), which was released with the following comments: Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ metrics-3.2.5/README.md000066400000000000000000000011561315671014200144510ustar00rootroot00000000000000Metrics [![Build Status](https://secure.travis-ci.org/dropwizard/metrics.png)](http://travis-ci.org/dropwizard/metrics) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.dropwizard.metrics/metrics-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.dropwizard.metrics/metrics-core/) ======= *Capturing JVM- and application-level metrics. So you know what's going on.* For more information, please see [the documentation](http://dropwizard.github.io/metrics/). License ------- Copyright (c) 2010-2013 Coda Hale, Yammer.com Published under Apache Software License 2.0, see LICENSE metrics-3.2.5/docs/000077500000000000000000000000001315671014200141175ustar00rootroot00000000000000metrics-3.2.5/docs/Makefile000066400000000000000000000133051315671014200155610ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = target # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: less $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: less $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Metrics.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Metrics.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Metrics" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Metrics" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." less: lessc --compress source/_themes/metrics/less/metrics.less > source/_themes/metrics/static/metrics.css upload: clean dirhtml rsync -avz --delete --exclude=maven $(BUILDDIR)/dirhtml/ codahale.com:/home/codahale/metrics.codahale.com/ metrics-3.2.5/docs/list_contributors.rb000077500000000000000000000010111315671014200202300ustar00rootroot00000000000000#!/usr/bin/env ruby require 'octokit' Octokit.configure do |c| # Provide an Access Token to prevent running into the hourly rate-limit # see https://help.github.com/articles/creating-an-access-token-for-command-line-use c.access_token = ENV['GITHUB_TOKEN'] || '' c.auto_paginate = true end contributors = Octokit.contributors('dropwizard/metrics') contributors.each do |c| user = Octokit.user(c.login) name = if user.name.nil? then user.login else user.name end puts "* `#{name} <#{user.html_url}>`_" end metrics-3.2.5/docs/metrics-hat-full.png000066400000000000000000001507211315671014200200130ustar00rootroot00000000000000PNG  IHDRWtEXtSoftwareAdobe ImageReadyqe<sIDATx Wun=k.iV/m ocC؍eZ6H³Ix̒?e0 ,G`I6kmY:5gtWUW=3>ꮮ]Uw="s\~O>y(-u/-A޾}Syy($BZ풖mC=b_IO1 U!/B| ?Pb@IEnI< A1P!A G%<j@޺A@mxv%f[/1P*|;)sb* [ `4/Xb \ Ǩ< ۷ooᅦJW,7eI_<^*  c)KRdE; cH}bS8u^X2V-wJy~Q0i')^R!Hwb &?F.-}vMJ*qUCߵbRw1u+f aX@6jPd*Q?(|n.heAvBF yyTPv*p7:oD¥= P5S۷oV=;ECUC3U1%UjfvĀ:D@3hEI`=fPKbM\rMռ @MP b`Ӄ&1 }hb`a41@" ۷o?1`hӇ&P*ZXM#>An߾V<{ Jn4O>Ä4RRaux`[rvm߾Sh؍- (RŀdF M[1 -R?P͞]hֲ"2wK14RPҘ@@TBPvo߾A4j DV61 M A` KH04A3k1ЋSYhRRYZќU۷?fPijT]`N@/jQ@@hʪbTL *h ^4 fx 4jIi"bP11Џg`}?Z rykaN}1۷ESG5v2_xnC0"@ ;PD;![< hN4gu}"|<bL~q!~  j?x w RTs)Ρv/ zj(b؆`1>P \m<&}πkO :) FV۷?Uȁ^rzz,a[{z=vhfHC X { ;I'`8Ѯ b]h9[!Y^mme6`Z{N|e+b ;p`no7)y-^@CHz R#@ dy"A71Os uN܊t1;Ѽzb?L%@JSMC x/!~ SvCr3x ] <61pw?c@Qhɢo<:IK< 37+6 jQ%^$d(`&2vHW iށq"<zp"ZpEi*@JBWz@ Yb,CTCZ-i 0`!@ @ц%ʋ!P"`NwFNmEb=b蓖R[-b{{Ĥ%iR YL@Yz'~.Ҳ0o\Hb#WOc!@ ؋1P g*vf/՛~G+GҢ{[LB AjzgiHW YHL0?݊=H˧ !+2f2 Ç~?'T/ZȓO>B;`Ͽ_K_3=\k OH$5-1;H{)&mJE:ffX }˧%fU={KX{)C\B /!ִF'K<)xS ^qn6F oUm_HK@qJwjdW/`?D=d‹iQi"]} 5SDA@Ԫ`<_A@+ ֋~K2c6sԐqX@&0xl {SB€U(?)(]!O;(8Eg9MrK$q'S_hcYOɖogS@r^MT#i!2;bg/ѕ+t Ef)9D ҺoT@"LMbXjCc}J ):2]8{Ɔ 鐲 ~. :o2`W?|p j?䓧6*/d@"Nt0?]^$j#xz `nqՔ+.~sSu8L|G @42I4T 9ў|h @QڬP,U1Pq Nӣ}VDAiy(!H ]1@ V3fFLk#9LS9oHh&L۾[ B*Y;X'OL?{7zݴhq ѢQtOpp܀65Sa%juExs y~^.uJVE}"0Iq+2  UlYiUGiʍ>sN}H,]6ݸHB0!P44c0ӲN8]#y'r>I ܅*c!Q}*SP~\W(_U,]w~Yۣ`lbq'70>>76Fu6K'[vه? a1 }#@9gv_*8p=Do{}dGzL);.'_{߿ b8A b_ VF+SE~֮r䎙9ُݲOCfݪt+Nd9U AZ{2}Á1P 8f"J+O0(wBvt֑X{I(a&/vR1ݔAPW^9Z@%< zv=:UG(Y 8CT6 ԵU ||S RR: zY<[>*lAPȖ&@q[﹉, n؍iH@%KZݔFJ7r"XC`xIPV,.O 4A)oER+]#-d^|j"20 Q \ PbhnA y5yRQ|lALZ6lXEH)^]k&8wg* MP>A NKP1\Я ~T}ɒ?Ѿ}hiC4==k8f%tu鵯~Y00`AטI5 fYAMq 泡O*g]pr@\ :yvm:ZA45de-Εs *# T@qU4O7tO2 3C/}~SБi@BB @ ;A (ArQ~q:R[=d}/wJ{$ qiN/l8z>,`' {|(̈;sh|?KͽmX/0Be$E3#́d_ O ]d:=TB!H|>C͙31w$Kp0rF/Ny-2j`8s 3`O~O|XtsQ9GJĜ%SWZzoC%ATp8)-]]mt݆5٪mPed$H_):4ڥ^mZBZOHQ,ooę Yl fd ɘ]}w,ݘo|iebTcJxt_'E^h"'S TJ5G6Ysy8#b`!fUY;RxK[&]!qZD "K|`+ڷYHjRA=,B:Y|WoQKӢ~CS{bB DLCGiB٤Bz4[|Ե)vrҙ(="NQA[qL,y~쉰쎭2e{sg$ zefTEn+OlmfLɄxaG_hҟմbqZzbHmEBN/K+|nFVGr (yܷۧAgx͆:p3zqEy_1@4{m`&c!֏XBdApn̵y\Fzk!PKĀ`^bKhd, f#\!D1W0d}k>.{;- Ѩ3AW _mo@ ۚ#O=3>{e+%^a4E, }*0p*$(W4@@K5wփQR=5ރ yH o툙sj, wsǽ!%1WJ 2Ő^i2Ҝ\HW@}n(c΁9LkWWg$1L}[V Ie;o{g?yzkw#*`0m8%H1> iɋ?}_~xHgMe#)1fr R1 |Z U,WIЮy3h.1Y= ٸKYlg̢d&<@y@Ve-J\+^cBӗ'5K$l]SpC&wA @ D<"bzyAJ3 ǰׯ~ND~6J6Ы:qC `‚d qnvm;g2 oS~Zz W?s&i-fkyy1 iV^D@kc(6dpp{0CBRGzPb0ŸB X L_u!d3&gG(V/b! @x}C yY*Pl:e-1WQhD":Ko@V|Ex v q}Z2 hIÜBCN?WQDJ☚؜x(fp(YTPa0FKe!L G3t%˗؟\N:QngY9CCđkF/sw>DFm 1zi:sl "p:86-j3D ~v1lPag`_D|'s r8}of^:|} MLL0B{r O vM  ᜋbI0ZPf732H0:MW i|RY>lh|S 9n&rpPy3 BS3*ծ1q3pd6x- &Cb P<ZhiSiC# X0~sΰyuZW<NNj_A0]N3WQ\F,C-" Af4A(&kDoL>,?~8 #vRL^B =sFGFOOM|^LOD>蟘 EoU 6@Y1wPZA; ; "QWtXq8N 'OК sZsSJy^TD;z^tz4u/^S)YZ3hVpHnlHl(DgL2~x…  }11*9~DnWߴ *H m eN M8X^3xe "Y5W_- OѪ JQJ9:qToj(V'}&P{1###{%Խ"Hz:KiB@Ӓ1oc}߷%aGGG⅋9/1*ЕсhUԅ(Ut(P:< db3 Hp9xH_=i:ViqnzF:}zV ) k^4%)5M G 5a199)oO!"]xQ2*I / 8-/\niig Q*|Ñ0C Yg=Y҄'bG^4R CI1`Ū ŰcC&`+~1(Ka]}]MI[ogЊeɛg7I7믻?tHed"9P}=D"BL`1K tpLEI@L10tECx \039&6iuL=\0z^&n;@L=dPӑ \,܍3bIۿΜ=KW[q 7/H/_30{YV=!1a0X /W D$H9(mx@ g!M0pJB2N-d:DGvڴUt&Wi& ˴"|43;K'Ni)Vvi8!7 9D?=0%TCYpB 꽴G2ƻdY{4Qr@ $TŁ> 鱝Zl $;]S/;%% ˒%J}(3 $g.;>"~`XdXӕ/ %32) 47sF.u p藷HF120 FIbK> beH@/Ck,n%ZQK@x"*&O}n[;w[^wu;/VyB~MQT!<U`.i]-苄Pr L )f_ -Pz8ב;%A㑣J@ % C}CCNNV_jz%oB$ʅ6(TB!9*@5s~=erОix@?dStG}!W*.::$ H/Z\l TĈ8uuxp|-QSC[G1 uҏ_b1~/mc1,qF%=F.Mq6/K~8<[OCvhK/ɽh$:aЅ2=UY .' 2]Z^y˃r"8 \ox%#VH(HP&Y$D3|;:1 Nf5yB6ZXL쫸S^ B@d S:^P-3~xbWfc!"CŚw>kx <ѡƝ7p''l_Fz{"b$/wzxdZUcڶxTP7DKB PZn9EE"W"`XH~$ iFR ƕh$b(naiT{\M0sZo͆\ok'_!q%/56:J#ùI1뮨K>d2uzV=2$ 26N; EQ63 ia^ruS D뭠qE19-j[aNgmisȳV7Vqy0\Nkh'g6ٰ+md.ΙKhvf6hMPUZ (ꅊGeBhUwn\,k=Ki WWAL96bBҾl^uzO\ "/hnmIagC"@Es{ bz ,(`p(B\d'!oJ&<wC.IK{V ٙ:s,dW6<*Lщv3 DUCs`;>u=19Yo 4~ W(0o1nmw< --D]L@ m YV2vdCZy팂`Е/dDC# 嵱^1ӘP4f^TG-mrb3bXe/13 c; :@P f-` u19y_/g%7Ą,GE2թ(㌂Tjd I41m f @ɿ[rQ{GCOhvrRc|hqP>HPȓK&!g 9ܫNT5P$L1j'DiJsޒyס (hff&zuu'#d ⊇Hu F'XdO3<fO@8 ,s=Έ1?}n^2AQ45K@̨KLvww3vDE^Q@&$d*5G䰋\]p:]."`vzFIE()>Ihr&SʄUM@:`0'J]" $I_ K`'H)μ *( 5@D2.GNi'> ذ -_~V^tlFZ~ȏÑM\^M%/={"x$UCVҼzFK?VY$ r!0eNy/jK!jH4\i>6@KK/'@nKLA}≖T B/\ghKg^PZ@'x*_[Abfs2(fvCiAHa@D*p9J"(0W/f"}!BPZ@$"B8cH7t%!jJ\ Y. l#OJ4x QJS3MԸ,DnٙYx @{z3(b.)怑1 yP|U炃ٳįRZ_!.-(ME]di48ECHsWڱ&f4Pb #1[j$4π>m17*9 >>r#+p|eЦA('DԿހ̾EoGfҏPYбxP( cAŢ31jN6t' "J2pt_m:/1@H_O rb l5Ds/th)իۗ2zI4pZ{Y̾6 t.D񈌃 ff)Vޒ `}#\u Rrj`PȌ=r }xcߑIԢ Wt]< ]:BB;}ʫ͊i"~π[(GR͉lbY͉4@|{s[/H{e-"Ԋ)ҿrV]kLӕ/P,xJW}d-ユQOsn'Zmw3gpuC  ؾF9&C]ILPy!s1Yss|B8u45;C_]I+|Kؔu0M,>MzDscӕ3?K @-&9 ݌PkڤMiƺ$DPOBOKFrg6Ƽcl㮈I^ ` G"o_6$Nr̃O*bA`(ӌDi&Cnw [C9 _(?aFAI(_l):V/%F^G^RU\ f/VEkL7~Hc6D!fuX>bƂEhx c^cZ'Q˴&~8PSoXZuYc&1.,oFTY<]o5  @xg@g 13 keOәSS8@) (Z1j3П>o v2>:<`.`,Q?~@*WAgiy  ;0 @9X(`IﰙQZЂ/޿EO2͸fYy^!`gaƟ׈01j2̼-2*`&,gr0L/,.rw'd]y4P)b>:n$oG؅4<`9qF]o4ƙu+LZ0HS-( @ R{X0ݝoD'rRćy@wcag!1iBWHT9.k^;'-5yzN}-k#0蒭_ZBPwv πyZYg1d,A"BzRM.BPw~w1>zx Ρ8i%SV)m\Qc%}1΁x<(#ŀ9k{]Jaw'cl8c cvu[c -61Zb/ y =S|r?bQ1Ȑ%,ӐJco6²Bdq{Zlߜ|^~  @vC20fJ6d<, @Z *be3*gLn\Ф;ӃI8(0Lr e@R *O~"6 ,{6IzA檫hɒҵv/?>fY7Y)@G=tStnp:EB( 4M9 OM(b.w/1JF0;`ѻ3 ܍3 2HV 1| ҹDӣ hz;U^6& KnݥU& 4`yn$ ]ތ6t ox? $9X1'ƅ$b>"~AHfb%A"mټ>_UЋd 6ۤLjuDoF7^Id=vhdі7\g4鹣Ai=EW0e@ {4C.&84ZSOK8`E:6IIyv;@q齽IOnц%I^txlb K"@|J0eUCI)/{h,Dۼ4VFw#c,n lƘg1_^!`"؃NOtߖu}tq3俞%і7t8g%ETuЭW5ʋYY<bʀ \l6iȕM rQ*iy\Y:}w/ 6xângڒ:^>beɨw@1NjwtOS޳>FKctEZxqƄs!瘻 ͘g9‘mDo}NU,=gȡCz*Z>NCDWD^sjIH4}.skMiI B_6wֶdקtR Kwvm<~<0 O{-'[/m?ͯ&38IȨIfv<}K_E=|>2vnxNz\78ӎ<_j lk9|^r9y 3OY7/΀dנ8rlP^F-hkuɋכ_ԥYY<,6ߔlڊz>33杜Ly"BV<=P$D:ρ.=k,"-BHi3eD^{Yy0}y M%XRx/nkXXMifvo cd8hӆ"@ .$ !`l̟D"FЌK#~6颸c%=ni'(<@@ ڦ VБQsKЌwW^cf!`)tzTϞR<"!NLO^Mz:yTb˜s+]i# !` T"/ *2Z@"4* ucnD<4L]'ZՒ@\tDlL*d bҍRыFVtpy@qda0qÒ(}MP&''! .p6C%t=R7}eEnZ )p \ 'ωiFvxT"+ ?c( HM`\SĀ~RNֶ+bPAb@s.`zAS3tu 7iɥ#ÃDaj;ٿOђJz$E:xAO;)b,\4C&C*bm,ݭ ^ےD0 , b,Fry MuqrgCb! Q39Y]^DLh 7W7`|l Ib-›VzjMuqHn T@rhn]^lU 9 tx^>a( ۬)@*3!S Y5K)74.)Q9@V 9M3P}%!POyS{'fǩQg@qltzq?鹳2 ; ffgV5J Ȑ`f(![T(eo#/\~%^q&ȏoK7(dj:Wy~Ff٘LVv!FEG:1$040yYD|ݺ6^E ^8X -I'_E#˿$9[6c5[Z1tBUn6ԤGIu2k,/ME#P 06F'M`!Mu^ndd#఩jm< ,{"PEL<M tB[!`3({Q~BN֢d#zn}L{ܿ WSn%|ě虗) ^>#=!gAI˝P1\kb#.q>Bh`"79P.>poi3du0P@$D,[d&:nNOT1聕PォVv8}Ò=yvV/KB-(*Jx;TqyAsx7EtA:yiHDڛ%#ߘUq9@ BTu DˉǦb3p^ ,-ANBd *9Z0Ӭs,,(8-=tz/́p$AGNi5-jtVj WxLR:69 -WMKMҩC}7:seu6sxW.`E-wHP v%kn.]&HwH !iwI$q-ˠED3w4LpqxxyIkIK9Dbh&81.{G>995Im+Hki=s='<: )$-m]WEk^u7<4tillj$C2`]#[}e`?PTg4DcttEG$Ju91ȼ/4rTB IYD͛aiG`G[hQO;'O:OkW/3zH y_Bu"Z0K1jo ЊCOЉ_=/LXsrt7Jx@mҗJF&NDcG$ " zSMIdڴ"i߫3C\49:DpTD(4rq2%j| VBXSƄBIMo/aJ"`rJ~7{sO\;7 '`G}F۶+,]4+-]I:U : Ѳ.U$(p^c+kkoz ƕzYnR!@ Ôk܀Ndݻ/< CMBd/*i}YcכF{$z=3֞rfch@r_Bb aLKIw_RsWn9t4H-Z_C/ H :mSSF^L!\% *^ex:Zֽ(%{uorA~zƫSֽG+)'#<;+M er{40y24 hc%WD@Qq 3ߪD_B "' ,9 ŝ˓J[(5Ph1kޚ?Pm^M[AG&"LBv 1j/^sbg܆2{j"{#f{Swh #֞MApyMáAg2ZB^^\*e||~,ؙwʕoۻ/c>MQ C+t9 <ةKX,Q'0N!\jUMe*j3R55߾XcW[KXQmVJ:<04CfiM=9 mL/?! CVV^{CS '|)4-{ߚhHw 1jS<.[\6n % XiYBRڽi.}f"`tt3l~sr'b?sr(W̄7>@]qBX814v|}J1jU|,/ABX쌂Tycڮ?@qKgL6txcjUE3PQYN\y%|*Yܽ7Jy-9:n$Dz[[Z'[*]ܦ2MZݓ[`p(|酣lN.>HXL$|̹߯KT_E$b*:w 1(Z_R* yU  咢:m~c&24{"w,ۥikzUDB'/]1G@MY{ι;c^)!sedmMȭaNy}.efBKrI="H#K7lZZ4tk`n5"/"ӆLČi?U ѷޛk~ǥoP -Op!]bԮ x\}2 87޴m,Lф"R<mXIzwKmQn4}tJ}M3zS 8g&p~O;=b11攳1Ϡ" 53Y!gg ޖ6u2$\%/ƒ ]! "zКUi֬u&mx'"^ m(l}SC {={Uܕk7}bZ lPVI) HvƍKBYsy @#c]ۄ1 4ᤱHȧRrSoմ]$W(1~ӗ"W* ds'b𘊥tsP2S1g' ),oH|{ɒ^2 9OPH%i\G=.dSSS?4:"O)%hez]:^].\^W׿}z]wPSG=KQȳ4y 81pA,q a{f=iz֊G'.#b GŝP^q!DNK1[DJc6!q~OI7e@<ĢEx[;<4</@?u/jy&[M)dֽFX3vKGOҭ7]O7|?A2>o)`)uToOevICN+^SO"5W]ExͽO;,*u)y3S-6IVܹA:~zwY=mx}^I /0Fy }ϦW-)4T*.̸nڰ~\J˃ib"=RrQdd|j{zn0;$/|"\u@`@H-xvaZ*{bZ'|7PCy:lj +ʣs:jj[D#7Vوb+~j:ZV &mɯ+tL`:N.̆f UgOxOl&-?/b`~BQ+;mvQg$6ؐh5k8KDG(+QOgڲ8[f@Uڦ(TƹF\B1N u}'9گN}fd\Q]WKKSSuKP"OdM+Wo l3PF $Dz|! +[ GoG^KqYhd]i{7z6a yjdNqΊ8?vm\XN~&M 0$YxL? !AcD)}a/CAbgF[:KE#pC`p{T*Yjdf 39vYƩ35WI(8u'AUMg&g91X䭷{i49CPh-3KUbBf 0cƘry}U14C)D%S)Serr~Vkuqdo`<g^^G` WCV@nS1c@a*I\ިi5Cw\ףWA@< l%ZUp"X5z/ W6M?~*(B!;w(k 8#p7^1c`Bg@3/UPB36'jJ L$VEX߃ e 5k a8:߂w_BȐ@ ԏ|zuYX8+^_ c U}}mg^ԱѶ^ٰ(b}飮 ,|*HАjB̻sHrsbB9qRo'l 蟊 Lx pˁ1e6.pr0|K%p&P0 { 5Sk_>@p3 9 :zqꎎW]q&C 0Jr6ui)3PEam?e8slS ^Rt.Pjxe@Ї QЄ N1̘Y(HLɣcF"<3`-9_ZWaYX]ڛt)@c6^`p33fj%2>G,mWhjaSfb0ѻ'e PCpI"F+*O2h\zt? _7se&cz1&`ƘJgN٬QW8)'4X$*\-^x`⍂$G_L}Z,6$`-$Ƈ>V3/ ȟ'!DC™Vt[e$Ԅtq'>A2x˿7i610نi\W @ި05TqZء&v2K>!P\(JsǞL2O}gИ҂n]iH/ @`s#@8ȁOW&!3Sߦ6jMT\;=rɊ8F1?j50y444@}] nꍷ׾ǩ36 F@mjdW!44( 0`k!mCM!T07if_H%W`5-e|[nձZ ȃ=Tr f g 1ضjkj(LNMe=4oSL/DnvVؑxR2}En4WсIN y? C dM׋ dvd5O9k@b4zP/:90z5}l`̑CŠYAcC# (fi5ܮPX0<O>#2{y~\zN}I@s=g o~AZv9NJ07 ȾN!nme0D0P7[a-4T0`tbs ЖWexKZY:I4h V&!=SU!t|a~>Kx׭@3fUgiPE^ oiufǹ~ͩ*(^礧)  N0>%:_[9tU: |]Z_wv8=3y,3V`= 04Rvl|֖ Q 0`%3tI$J^+dYeţ$p3P0\.w*b++)x%#@{e}5_] 狇I>H@>eUF o{Y Oፁf̪Ҷl SdFjkkn!~PXXe ,.- H Mw`딭wTPpb>ܵ+ c5ČN> `K[3vSǴ|ې\h],|&Ek*V/| {Mk р"(67bq33fέ˃43ytxpL=D]019 SNG]]]Μ/# Ӽ/hÌJ2[q*ճ]Ric =EBe.@ q!tUQ|t]6SP}˰2g ؁<(1g'`udslx[;cf j"5sqV?r =:r7Q>hmm=wCKK epl\&z( j9aҙNy PFk^y k 6˂`0Am֏ͤǿ^ϓA#iX\.%y)M0C% ɳzXf6F6f l8 /Rp0bEػg GaX^^7ȣ37ueŚ]~K'"l 1>>(̠ lk ^+`|V`FN|MypBvl^HbBo|9Ͻ}CV@003f⋒`݀;wҚh b_/؃sEa{w7ؾZ`zf @Q3$9_m0()3GTjՖ .Oo@B$(,C<8?&h`>WK1c@;`y% oܤ Mtd̘9?`U1Kv VVVc쫡:gxyHf|pi:Pf BDX*v`ԗe NB<(:7 {OnSx9X< ,ٽY; k_΍'O\uut 0c Y]]TyƸ 9ɠ 7?drj0˗|QdG Q:[8lt``3;Z[S L^}M$bd$ahDbfIQ$;&?™yr Y+ p[} KYxRE&;ؾxQz-+ -o ( - ÇᎁflbҖ^MW˴;2 %?>.G [$ћzXTV;"+D P ΈXJ|Z=Gfasp/"WN8bLJ,mzKyce̘R}VCwuG}܅8)ȹl<'50ݰ5OK*umt~XPvړsM;xogS_d 0旳4>@ PbпP>I T$_zIϾdJV@g9`U  9]7\{ݗoz&2d`cPw]ON?&;2̄ (vAþf_n#,t3fkc-O<}GFgXM4H[&lxPXO`eZP8η0 Θ/ҙt+Ȏ*l:} (->4dЃ*T oMח8^([|rkb?*VV(Us[cY~i%'^#T;X֨tzsK9h8 g`9n/|uǍ>g `;ۛ#(ҕ,dt4tn ή)7~ 1XI WӦOJ#=;m7/oN.BuC8!ȼ0_d`$& "(^ tmۖ| 0X`uqmnnAAVtJ8h-gs*~ꌼ0kkk R]XXT2 h>[C Z}LVߠhp.7h,sUP.Bx7\v9.6,|vM-Ҿg2ߛ@,~U@@r?92@DB~]8$^͇w A߿^OA9d&lx}g es~3"dXwg (XY%ںJ-llhV]J :)xmcYB֭ 1! LEL1  #CvW# T`GBeaڈ+4hdV|-te9@W.^4.|_ $h.G0˂c!A/hJ lN\>Z`6 "%r#`Svsw=ʪ2f`hL`Wcwځ8o&rU7O>}f3YڨiÞJ7>CC2;`K'.7^Xħ; u$\JAW[nڛx8K#+z'_ErS@<*Iw"~XLN-) 0x `| R5C:8c;^wQd 0B0c@AF$McEQ|(Q`7]٫nSPfϻV)mH8`LRp;|U1oIa%`GknwJ{iF2ԩ=MpUQГ0]a(1<>"#x^|O^,9>> vڥ$8'//~2b4vMA-# 0`*0#;vxc ,v U=qb6qژgnZDȮv-2BON`-yفM}ڤ{7ea~6%g)c_/| ~bH(4BtzO $Н NW~8C9Z2j,u l 贠83ExҢwK\hԦ/I:`r` RpMq'wi `lAA ޹f[Pe' !k0.SҪG?o}yAmcL_ޛuBeSCgAPpP1pHaaEGB\oA(`|=GF ACObf2{yP{P{`v0l @/>O9=Pnm4xMkP/Cv;V ަ(uȦy]X̘Pv*DJP661.7*J.k[9sux@wCp03|b#>_Ɍ7b_stlKPF]~Krq!L7ŒdT?ڶbBCp43J&fym{EـN' Z CA%zݬvlbY%pEp2#B?j P L&mc8a*屽#/fvp"D`R+&#Mi,{<ۮH_F+&LC83 K]rv_>h-iC!iǾ3L$}K$e0&26+ggH쁋)xˁzا`b6CCZhk C(dN!Gs~8ZoB{Y!01N)ĐBZj2 ^R,Wml+mjO/NM T̴y]ИzVmSCQ?.oΰSm;--c<8x ,76ȉ 14L@,hڷglȰX6ќKF6@ 7KF0@g\Y `͎`DxtdƧV4>?JـJcaq% omT~ιw -pSHe'BBޖ\+P@e*z bZg8sf5= =O +r`/*݊)t{s%d_^ySZ[k5Dm΅Ӭs yGR]m~^=Qbsn֭-틴tq69Áus t*VǾJ:r03$ִY~Hyx/SWG>_{INŨX3eׯp=Y-D@#?c1c3!ańVay_vZWY<],ܛUU?AC8K Ԅ9<hií u(֯ྂ>"|+A׌lo9а9EB"6=:p/"l]8B2Ɓ g4؜befG SA[!}$tw6CT;_xN []i$CŴ%0[@l(0l 9&-" 9p쫯vU,F?K4%7FK%`H$h6BP< *at l͜9&]hxyfiXŴPT $s2ơcK;@%wLtuNrbN}/R[ = Xʸ8 +zf)38:<`p_t*i5?DjhFB 풥s"ux!hJ ibysǓ7/_ BޘA*px߸0A~hLDj``2_~1 gǝZ,knf(DyJl{a7X,Gc"E=\sNJU20R@Jr'PK/пqk1^yy8!(QАH0S z418m?b`_.qn5#Ka87[KC)_D9/׮ c- 14PW` }TAБcw4 :^u嗫00@$ST_0=3 Up8\1@ Tw[wi~?<@2߁/:,%% _fV#0LgK2P_} c#@$c`r'~_!Vd{#H*l`. jj "JY8gY,cǍXÀY|Hu}:J)YȇkahSDp˞\탃\i__K8=P BRøEbDt{ٛ$DO[>q"nȯ |~"3 `-`WgQhBfmu }>S>x 0:c؏/ R$~]| ?jihڤ:Pvl8ǀ@AʐKו]0vrbg]Rwzo*;|Ia 7G 8] |V\KzƏAGD]c(mgߴQ6@)W9B3|~yPLڽ >z|7`<] GEimͰMu my9qEBv;5)Pc ~@keXImCST°V~R!`(y% ^ ţ"ˬy8п/2L%+ P_ FB~V͗w;y49FCﴞ1s$ ֫3($¡ H@^{U8xA yCd#U ```c8yx,-(NH@ZHT2ϤazjJ$0mr3l `bNV `b9a T/+뙁 pe!fN{mA+_kzz&G!>P_u~ p/yx,!RέDXW/r1^.mkkv,TD]r c]jܲɎɬ{̼~r >Zf >E#lp!i|C<UlhSs󉶶ӐW`]qayhuWS"pBdn_{?y[s/ \ǥ^#AOg˃8 8vȫDdj P",I= lW>~;@[0o[Df=>B77 Q c\Z Exx}`ς8g׵=cm#?t¸Kkq__[@*ƿ*tv)GI] f`uƗ_~ :9d@lu;c~Q$x"Oa9-Ж4ُ:CSB^R9f'#WzqNeR1[f ~kz`]$`e>[8㪢FDsS@vg5K`(#CpKW=R!c]c#g`kI)qj#M8.]]2{ p]"k 1k=? Je2{DAj:M>+GgTlyO,BkIZLȻ$DPU!Ko~a@V;@;a <6@ӧÇ߇ş;@*G@j؂`8մc@pzaeyFG)0ńUatz Pf{;-vD׋@ '@){ Ba:Xd(i̖W~xͣ' C+ߛɖS^ao #-wȰdM(xv-%lW<(gg`Y|gV0@0sd,J0\0z  jb'n㎮-[BPQ`@K7`jj+ 7q9>r:8׉!".F ,;iU/DY⹒f܅CXw >:|.u*[30Yu>a-p'wPr9HAN{ doxlF 8L Bbfv:sE&Cna'ׅLӘd<{i8kiA.Hgr=Ep!D*(sβxqbۄBbNƋ+v)Y3a!}ofI]0́~8{fw٥N=rX@*HC$ A1N" BܑĕX1aWGonOԆUCV]hJw8QL)g|= c*67j%XNO\?@}SVJ.}N3 9ޠNkY:6N$#}hh.޾b1EQЅ߇8w,WWӴRkג-AN @1Q9gU:o!+%^3ⵠC xe4Xx e9 \8BT(?&[mޣ'O>mߞSdd j 'f9`^[&(C 秳vS`eoʱcLy,.)Z* T 8h&e7f`EAqh|qp EzK`pp.\@"҇ gfG:0hf8bP5!PAbP4 s`x̪ː,ր@3T9q(`e$nj$ ;?YU`{~CҋCJÎPА_˰8l ;~\;20Pg?Id͆{|&2*XM%;u=[ tK#:T]~Qc@n۷)$!=a  `"8q^Y%-A?!YitLo3Lu~#R07tk6$ ޷ySsc 0sew:`@$P Fl2ԙHAKc.Άȩ[" #زCilZ' lprSw5۷a Ŋ7͡љcc#MWĪhXV|31t @S=xB7;O=/jW9t #q"Y0H? e4@}D} UaXZʻ/6ʏG#l@{:pV l KFû3mL_SLi/9B byb\j^}8`axQ@QZ7m]ss3]'e$aVBtE"Bj\ x\1S Djȑ6 -R ^kYH/-}-ӰsNaGe 00PkQ[jŃ8 B&sl~^Ez ߇u:₴ 3,-D7o]və_Ix81>Ng4x}EE*0<倍k**Q  Ո ya l.03si-@h1lJ%p""0P T]ܮ;zeޥ(xca%cJg0K-6@f$`q# # 0@@ #SrٍC@= ±ghKP"V zom o;>@hP5``: C*03{m F 5LboGÒ73b`JlḮT):[ś :-o*:t16sņ:CAC޹:@iYT/ 9%di(uR*WmʞZ`?^_;zޠ60ym!"EB^&(ÊJp 00Joe@aՂ% a"޻/<6Ubo^8shⵈ(FWΔ.vnp`QPwDhX$#rw=CcD`Qak3j jhcƀ)23!D030Pu(Pq+e,/-ꩍ Y?xْ .v2E6l_JH"͜\Lm&e",MF KaL&`4B;MMMjQvF()W)6*z1m3I*!j!0auZ %fs 8.knQzy;?(Ybt1{jZęx`vS h_j`g gN0f!(d\"s-Мۦ&xy wP54LTQ"u#=pJ囥mU acΆ}~Ub,.l״mO30̑]4c;< "fNݬ/5e ʬ p,3 ?)Ld&R-2hNHf^4, -fk6#t λ_vc"Cv3m68<zSPm$(؂%zKkLBi/xPGhwk|Y!,ij6?ʯfR{ѽ ,Z pu6=}%aZ@71 xM[l [`, c Cѡ7 x݈;oEliZ5 U(? sh k9ᠴXy@!|f3PVF+8l0#"y=<9;@ۥYEYl)[Xp`?^tŋ=nh`];DHFUҿvVoM P ^ 鄃: od!T JѽPkxԄG3 ӫSfg`V(z{Ww?y *a;u0S@a5۷k@پ[`W@b } ˕fsY:I4`ڑ` 5uR~Mɐ7hqr)eMN4+U ~J) Aܕ (w؀)޵X }O00q$O{xR!o͜:x(:K":XV[,VtXGmM[-iQlZC@5E ']i^EPWP~EcTBЋ@M̪>-E"Pffk6}k OY#ʨ96ԡ T 5ep8$&uk6{Ѵe,Sgic6&(8ٙg 4!sw5۷vV~Cپ©EQ0鉘 :±Qڂ:PVlgVCx}wC y7z7P (4N@wMȖ00PVB:uH$8~ϻw:20QYH=-4LXjV8DMJqācD@%\ &Y  J4=8`@]MZ]-geSS;1l`}Rς䷿τ 0Pp xe_!nW&y!:t7/PP([&IAWw7h)&W-#  z*hx AYb%HPq7͉Zp*Vxu3ت;C2q0 ge  Egd<<Yz -AV`Fp8vS Xx-:XVM,VtE8h"[!&BRLOMFI `@/;zl^EV fń@% ,]}Q*!a*̪:;;!j!EcNBQ+ ]= ԑ{Rz+tKdѽ')AYyA©7]Xz0 ^th,qưV,‚B@0<4D #aQ}zNP5 Oq*JO ,LP]uVhmmU9J̲F#.G fĪ `M`sG$h}QlhF6lO?IfzPpy`,'2&+!o,܊͎mw G`Ȥ8vuuQ6@Bt|lvEf>N0\0*>4PXja5ΈqkH#YDjS,EG)j ,@[h&j<|fg[6 ]:y$T3+Pp14 TDsmgC3m+9{ (Xjx$:p22AveyYB@j-x >y&"! iymS% "gAoX2\Sg Ta/5 MH46fɽ&b{+CCZ6۶ҊǾSE'f` Hdm(2Lq{@_3ѡSXCACޙ>%Bc`L2fGd(F"Aބ5}hP  BqR-m@1alڼY􊡈jF$`ey<(S ]flYNvm8zD5L*N`,nd,j *e!#d Ή$IJ7>u D$0TBѠa` [|www띬g!<׮gAG i{+ iFˌ+mjraȤ3B VWSgzճ ɐfg (x =yqYC;`hy$I^4LjkXgc.9(dqI`4d =ꄺv ׷,֖!6 j>Q2``rmΝn;:l"'2_lab bc… hj% m* uÃL~ +R!V`V((#][ @h 0aO ߭lŒ-pAs;nxR/./PP 6zM45xm#]5#^PF׃12t618PQ@P h$8LML/B!F}ǵ0_&C% ;z[:d972\v7f^ܤ797q 0i΍ h~ `b6;o s`llLsZ d\p92s:.=vAW`W{ud%.H;CQ}{ZNocIX\Z,1WO^ \5ǁaúLM\P\ >(ǽ~`L9<ݵm# e|D7Y,Rt}$l_NnDNkhkt>P*jglJ&i#)ΦoQYc@Wȸyݠ̊Ua){vS0i53.&lz+ɕXL5A 445Bcs~,-EjPs;7,JD3AA,' ()(PpP`': .DV8aq])& 3ѡ3,yy)ryiR*m[i A%s N̬@KK 86@ 2+3f]M8-mm4C@iXYX` 秾䓌`^Aέ[:hnĻk#:D^5LnE 0NGfc>TzmbP6?eo```Δ ط?MYu3/`BL!}IV0sAmr_LONœ"$&%3岆il^"o<30lA:gA$9uG=QäRZ<8>ql!3-ںI.sAAY^8!3|Wa*F4}20> >Ћ 3 w 2P м#3S8/g3|tXC MĩJX7.l@o|b~+& dVv[>ذLg98Pg +:D:&l(ȯ 2UU>,0԰Z/K$н<6x* ,f VǦ(? .:p ,ˀJjFOM`Z/ ,Bp=wMy 0TPp/B%S7vZ':x;#x,t YP:CCCt*2 *J@fd ؁7tp0d% E[MfFc,j vp0Lp{@QfkntQY;۷X8to, ű9388KÔT)q*g.nR yMFA.c``Aڼev_,krdl,M,S@AM b/SI 7 + 0P *;.(/ک,ϻ0y˗eP ]IuNYh6k y̻`Yw3bAEj5%0E lY̬7$T$/-.¢*eWO%O~[} 0JPp\d ^:b0s@;۷vnh$`xe l(5)ͯCU"qn41̔wI' J0/T:;:d(ZzuR-;^u= ~U-(+@AS/KtX0(0(-@0048HK+P1L5!,k/XBAS@VG kntNr~z10-Gn 8sƳ};PSwF(6TCA$ږ/ủ"m 0%oS&TMƃS@ KP۞3Hu60]GhLaO|S  HqظhK;r, S^`VMl\hoߪ#a7-׋PD}d,%sXȈoni1{6ï^SCWPȶljԾ-!VW'HKuYO%Vɿ;?O{YBf4(&):,XZtX0( 0,NNNɠ@- k ^@oDZORG=ְ݉A8a2  POq X+V1 0: c+QW_ -hFe[P>`.N)|%vq7t%0T安١!Si\[o# n@kmmPh@jhL({3ڨް>ƐĄ}TR7] 5]Ąb#U3([k"4<(*xxml:QsGcQ/36|KIW_;HcGh `pPť%~ Li2'|a@E}^z 0c0 zcN< #x\p=8ő# @lȂ%8P F{,Gs,qjpW36^؀l6TB?mB-42 nF8rMwsf܃ T7<7ѡ7B[!:|t K a/s+nb0ҋ-"̺h wÎ;{)ڗ30lNB™k`x4Lr,ZtPP@y,~2 0=סFq6~zd(?Ԝ?} k07 #WT 476Y e Dk;bB1@A@dr2+-W$aRY:1h$ Aߏ^:Sn'k>4f^샅 J6k0D"оy}2lɇp13fCC)&緶w=S.J0+r= ,+)|f N( ϧV-〧Ql\iABi8axFYJ`Nן3t ٶ(RBW+IjL '׈|;p( 扯}̘y Αa耸#cԝ:bbze܍hsjȩ;z ,8(_p0_M@0UPWWs'С8SPa/w=9500/Ձ׍19pv]]#;v =)1+(#nHAm A݂ kThTG`r,'_d s*܋ c\PJ`Q[P(Pyp8,LFX+Ғ: 3#03;+΃5 wNԿ<'xf N!p| KO~,VP#ono(x`#MJ6⠜AP05cccDeP,l%  yEp6himÇb+_Jʫf<3f F(A %j߱e)gTz>с9Z~GmK":;'3&wb$|&~ L ]9@Y՚meC#7>EgK90f` >8y̴ #X N lYllm{!= NTQBf׳TF~!a{;u۶ێ(Øf*) ;DU ϼM𽋆IWjc B,7L2`d0aQBU;e l[-cA.Øf*)8 [RtqX l0@x/|t4<@@*o!%`;Y}sg100Yۍ]0q.]Y>DP|p:q"Fz66샼( +=lpoNc,a`+u朋]s~jX lN/t;xlf455?Gn+`'4}g;3fkDPp̹k&:ST` HCLAMm  {NG~P9Ǎ+3fN1J;w=[ ,.гvRci5A8fa 68x5[oy˭o9]` 3fEYWbebj1څ,0t -PV2D0? %ha^}b I>D=N  `3`pTB8f=f Jtqp.a ,XTWWGA @U w??޵ 0cpuBt /wCf ;%*55b1*8dawSAϡSGne*@f<Dg yEAyEP8DYtJMA9l` .ܱ6h}׻ߍ@Z% 0cV2`%" ka! Z<! 4>(`aðASS3ؽǎ= ̘1CK&' (x V It2Xp_2łŒ}~?(SOjqX#f1m.lt 1[}g3c0-LCS///PkBDi/5{&jS`6܇A{|>ߋ:߇i;Vսc`POzKJ  `Ƭ@A͂R ^)(EQWÑ%aa/#P?gaf̘/_5OҨlFսQG+(|av. ]0EBG*'cNЇr,,/,e,5=JӐd(uAA"lж[n}˱t303q8XmAh;ggx7SXkKO@ pfz`qAPH$   `o@y8N1xt@)0eCoH${ot_303OY?*OCYC0;DJt(X#@a!Z.UZBDy@!Р&-K]_Xw@f̘yLJ hZ`b0훲 X3BFc f3DcXZ\K$RC{@rP~?aP8J ,u>|C>8a& dƌYIllOPѡ':ܶ P7BcAl5 8Umiw܁@z_3R;XFΨt~pW@9ʼnڀEw:_3f ܆UI*4GPHvZ|W5H2v,y%eJ0Vd \.*^&iVlA4VGقhVɱ6%O{sÃ'Ȁ3f%F8&(wS0 (2f466A!!cdvj` @,wqÁJu Pҡ`PIFb4Lr dMl"EU*H/pCt-d f*t*AO&n&ub\0V0D]ACc#yq 3/X6@̱~ctq/[ 1+04Mѫ0B/9n465ŌiC6pZn8{䦛*~A (`Ƭ|8.klx1/-Pn !̄C|@`U`HtS}zOd1Vvd 0@Yـ$Lvލ}ٳs=?++ 0@Yq>f 0(0(1A$C>kW8B C䎝;xg>s%n-?y'zvߝfncbE&:F'BVf$HsĹPJ%!L:nؼy tvnᑾ~A&>Os~p0G PG{3@lKZ e9u NݛHqHJ!KGϑ9@`TX(%(6l,6 VVV`hp.]z;qli@E HE l1Tcͻp21Ӣ8bҢ$Y+Dʒ2] 4}-w;K9Ӣ1 Ѩ7 x*e+#x#SdПOed.?|~e s|\0qڞxܿm 2_- ~R׉btc~+WO|MLP"R"0[VvYAQroaw3D0;~1vR9|stKf q~\riK)h(VLF)#14)#hs8Xelo[JWj3E L?Έ&I :ޞC4dڒGjfHk.=#=g2P\dL`Ɂ9 DtB3<dT2g\lI$A<_4räقpe lAךrϻ.>#Ȁn|rlZs%V6a'M 0b7zG)L&-KApʂ#72 ^)hmku|j/tw??/߾:Hy2&S$@Ijw I`$ [nD {̵L tD1"@vaŒHyf, 3*@!@e,@ `5e IENDB`metrics-3.2.5/docs/metrics-hat.ai000066400000000000000000003004511315671014200166550ustar00rootroot00000000000000%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R 70 0 R 135 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream application/pdf Web Adobe Illustrator CS4 2012-02-18T22:58:30-08:00 2012-02-18T23:01:06-08:00 2012-02-18T23:01:06-08:00 256 192 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAwAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYqh7/ULSwtmubuQRxLQV3JJPQADcnFWtP1Ky1C3FxaSCWImhO4II6gg7jFUTirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVS7XtFh1iw +qyOYirCSOQCtGAI3G1dmOKsPvdSk8rodI02T1Livq3V06j7TKOKom4Hw061xVW8u+d7+S+itdRK yxzsEWUKFZWY0WvGgpX2xVkV75gkttetdLFqzpcAH169K16Cm4Wm++KpzirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSTzjqNzYaK8ls/pyyOsYcGjAGp JX32xV5hLNLNIZJXaSRvtO5LMabbk4q1FI0UqSJ9pGDLXfcGoxV6n5V1e51TShcXKhZVdoywFA1A DyA+mmKpxirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVYT+ZJlpYDf0f3lfDl8P8MVYRirsVZZ+Xt5crqctoCTbyRtIydgykAN+NMVZ3bXlvc+p6T8jExR x3BH9e2VY80Z3wnkabJ45Rq+qtlrW7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYqhNU0u01O0a1ulqh3VhsysOjKcVeceZPLM+jSIQ5ntpBtNx40av2W3OKpbb 2hae1FyGhtrhwvrEUHHkAzKTsaVxVnV5HovkvQ7i9ZnaWTjEr05SySPsiRqKd96ZRqcnDAkc27Bj 4pgMKk886joetRahLpVzb6dKqrcxyGMuY/5uCsaMvUV+WaPTZPCzWDz6O4zYvExVW46vWrO7t7y0 hu7ZxJb3CLLDIKgMjjkp333BzogbFuiIo0VXCh2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku xV2KuxV2KuxV2KuxV2KuxV2KuxVxAIoRUHqDiqE1LS7HUbY293GHj6qejKfFT2xVjfmzynqF/wCW JLGDUmSe1/e2F26cpIiEZCxIIDFUcldhvTMXWQ4sZPdu5Olnwz9+zzXXfKk2jWwC6xqGpwyJ8a6l KLlw3ikhCsvy6ZzufIDMbO8wx9JZ7pv5heWtO0iysrWOaRbeCONdlUfCoHUmv4Z1GIARFdzz2Uky N97T/mtb/sWO3i0v8AuWMFFvzUf9m1jHzYn+mKtf8rUl/wCWWL/gmxV3/K1Jf+WWL/gmxVUX81kH 27JT8pKf8anFUXB+aejsf39vLH7oVf8AWUxQnWn+dPLV8wSK9RJW6Ry1jNfCrfCfoOKp3irsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVUrqN5LWaOOnN0ZU5dKkECuQnHiiR3 hlCVEF5L5g0BX0YPPzk1CNuMtw7EyVoeQBrRRX9lds5OczEb99PSY4gnbuYadIu4yg+sBo6UcgFW G37P2h9+dXhvgF9weeyn1n3oa5truKMtHJ6jhR+76VcdaEnYH36ZY1qBi1wAH0Nj0+OP/mrFVjNq 69Yqf7NP+asVUJL69jNJOKnwMif81YqhpPMBjFWbr4EH9WKoSfzgsaMV5M4Hwim1e2KUrm87ayqS SrGiox/dF67ew6FsUPsHyzZXVj5d0yzu3Ml1Bawx3Dt1MioA5/4KuKEyxV2KuxV2KuxV2KuxV2Ku xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KsJ162BW9Sm3rFvoY5yuuhXH/Weh0k/p/qvPbxODsn8 pI+7Oj0suLFE/wBEfc6XURrJIeZSq475e0qhb9yn+qP1YqgLhuuKsd1f++/2I/WcVQOmahb6dqsF 7cWiX8MJYtaSmiPVSors32SeXTtleWBlEgGmeOQjIEi068+TaRqXlXTdbstLh0x7i4liMcIXom25 VY69K9M12lE455QMjIV+pzc/DLCJAAb/AK2E+XoWv/MukWLDl9ZvbaE9yQ8qrT8c2rgPuTFi7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqxnWoC1xdooqWCsAPoOaDXQuUx 7nb6SdRiXnPmHTZFSSWGRkcmpoFP6wcxMXaeSERjAFDq5c9BCRMze6QzBG0eG94enMJvRn+IkPU8 QQpOxqR02zYaLU5ZZakbgXD1enxxhY2kp8v3K/LN06pA3DdcVSDVd5Qf8n+JxVPPKH5efpqIX+pS PBYNX0Y46CSSmxapB4rX23zV6ztEYzwx3k5+m0RmOI8mVav5D0i80aDR4DKlhaM8kXAjkHc1ZjI3 Ou/+SB75rMesyHLxRHFI9HPlpoeHwyPDFg2j+QLnR/P/AJflFwHt4tTtJH5rwdVWdWB60I2pXNzh 1ZJAnEwJdbl0oAuEuIPq7M5wnYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FUFfaVBdvzZij0oSO4zEz6SOQ3yLkYdTKAp5f5umntNTutPULwjICuRVqMoYV3p3zDPY2I7ky+ z9TlDtPIOgefNpdx9eNzc3PqohrDCqcADvud2rSv8czcGkhi5c3HzaqWTY8kXy/dD6f15lOMgZ26 4qlUlr9c1OysuXD65PFb8/5fVkCcvo5VyGSXDEnuDKEbkA9vls4YILe0gjWJCVhj9NmHFEUmgG/7 K0G+cocVmyHoROhQWC4tY9Ugsb2L04JzSyZgPSeWM/3Zr0cAckB2I6VpnS6bAMUaHPqXWZMcs8ZZ Ab4Oceoj/O918+7bvX+fNAsbny3cXiqIprRDK0iKK8EFSPuG3+ZE9RASgbcXBMxmGa+Wrue88uaV eXDcp7mzt5pW8XkiVmP3nJ4zcQfJryCpEeaZZNg7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq8q/MiIJ5idv9+RRt+BX/jXFLAr24RGKBlEv7KueIb5HFUDDcyEmKTY7kKw o30U+Fh7jFVKduuKpTPdfVdQsb2vEWtzDMzeARwScqzxJhIDnRbMRAmCeVvWtB16W91aGOZlYGOT 06CnxbNXb/JU5zODIeL1O+zYxw7K/mu+tLfy36c0SSSuqmOIrxHqIw4lQK0PqUC07+ObvX6jgjwj 6pfYO91/ZokJ8YNV+nakr1Lz7DN5DlWdh9ZbnbXCvy+xGSJGJ3/ZFN++Qw6niw0d5cvejJp+HLY2 jzZv+Vnn7yj5k8u2Nno17zurC2hhuLKYCO4T0o1SpSpqNvtKSPfNjCNAB18zZJZvkmLsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqXa95i0XQLH69q90tpbFhGrsGYsx3CqqBmY0 FdhiqS2P5q/l7ePxj1y3iPQG552oJ8A1wsQOKsK/NnWIY9cs5IeE9vNZo6yRsCCPVkFQwqDilgE+ saTcKY5mCg9UlXb79xiqHiWDraXVY6/3YYSJ9G9R9+KrLhuuKpTfAMKEVBqCMVWad5n1HRpIXR1d LdgYmc8WUD9mvcU/DMDNoIzNjYubi1koijuGS6l+av5e6nGtzqs80dwgXlZLyljLIQVp6da0I25U zWZtBqL2o+fk5+HWYQN9nl/m78w7G/D2miWRtbJzykZwFeQjxVSaD6c2Oj0Rx7zNycLVasT2iKCS abf31jdwahp9w9rewESQzxMUdT4gjNi4D6k/Jj89ovM7RaB5iKweYKcbe4ACx3VB4dFk9uh7eGKk PZcUOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV5N+f/lLzHrOn6dqOkQteRaZ6 31qzi3lpLwIkRer04UIG/h3xSHzxFqbKxDfCwNCp2IPhilFRXFuSWWsTt1eMlCfnxpy+nFVSQXTr 8EqzDwkHBv8AglHH6OP04q6zu1gtngnBglaQlVfYNUAfCwqpO3Y4qlWp3bjlxYj5HFWI6pe3PqLx mcda0YjwxVLGZmNWJJ8TvirWKuxVN7RiIomI7D7umKEeomt5o7m3cxzRsHilQ0KsNwQRil9mfk/5 5k84eTYL2621O1b6rqA6VkQAh6f5akHFizfFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYqwzzp+UnkvzYXnvbT6rqTf9LG0pFMT/l7FJP9mpPgcVeH+bfyB88aFzuNJK67Yrv+4HC5 AHjASeX+wZifDFNvOvrtxbzPBcI8M8Z4yRSAq6kdQymhBxSjYtRR1KPRlbYqdwR8jiqhdaXZXKH0 XMDdlHxJ/wACen0HFWK6v5c1eFwRCZ0JoGhq+59hv+GKoUeWfMZNP0Xdj5wSD9a4qi7fyR5mmoTZ mJe7Ssq0+ivL8MVTmx8grARJfyiZh/uqOoT6WNCfwxVHahpEU0XDiEKikbAdKdvlihBWKvFCyOtJ YyULj7QpuKE+xxV7z/zi0syxeY619EvbEeHIiSv00xUveMUOxV2KuxV2KuxV2KuxV2KuxV2KuxV2 KuxV2KuxV2KuxV2KuxV2KuxVIPNfkLyn5qtzFrenx3EgHGO6A4Tp4cJVo4+VaeIxV4b5w/5xp16x Mlz5UvRqVsN1sborFcgeCyfDE/08MU28o1K01vQ7s2esWU9hcj/dVwjRkgd1qPiHuMUr4NU98VTr TvMMsACV5w/77Y7Af5PhiqfQXdnerWJqP3jPXFCjcWvXbFUtuLXrtiqXtp0kswjhjMk0rBERQSzM TRQAOpOKX1H+VHkk+UvKkVpOANRu2+s31N6OwAEdf8hQB864sWZYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUJqmj6Tq1o1pqlnDfWrdYbiNZEr40YHf3xV5f5j/5x r8j6iWl0iW40Sc7hYm9eCp8Y5Ty/4FwMU2871j/nHD8wNPZm0y4tNWhH2VVzbzH5rLSMf8jMVtjV x+X35k6c/wC/8vX1VO7QRNOBTvyh9QYqibSHzqgCy6LfTL/l2s9fvCg/firI9K8k+cdWYBPLt3CD 1kuGFsoHj++RT92KvWfIf5U2Hl+ZdRv/AE7rVF/uuNWjhJ7qzBSzf5XEfLvihn+KuxV2KuxV2Kux V2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV 2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2 KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kux V2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV//9k= proof:pdf uuid:65E6390686CF11DBA6E2D887CEACB407 xmp.did:0180117407206811B560E3B795EA8D68 uuid:51d254eb-6124-d547-ad58-231b74486676 uuid:f15aad41-f89d-7941-9cbf-cef2eb721bee xmp.did:FD7F1174072068118A6DAF53DA354BFE uuid:65E6390686CF11DBA6E2D887CEACB407 proof:pdf saved xmp.iid:FD7F1174072068118A6DAF53DA354BFE 2012-02-17T16:00:28-08:00 Adobe Illustrator CS5 / saved xmp.iid:0180117407206811B560E3B795EA8D68 2012-02-18T22:58:31-08:00 Adobe Illustrator CS4 / Web Document 1 True False 515.000000 515.000000 Pixels Cyan Magenta Yellow Black Default Swatch Group 0 Adobe PDF library 9.90 endstream endobj 3 0 obj <> endobj 137 0 obj <>/Resources<>/ExtGState<>/Properties<>/Shading<>/XObject<>>>/Thumb 163 0 R/TrimBox[0.0 0.0 515.0 515.0]/Type/Page>> endobj 138 0 obj <>stream H͎?"hqǁ#@>)ݒ k13;?ޯ>?~]~_bվmX]_[\oVӚkBk~^.RjtňS^mV/y}_>.\w|ZnVk Vjſ΋î č%/O>| 맏_~]Y~*m7L{^2079> cxZm O)l%ˊ1".>.NZdӭ5Ŋ_~l}->~O/=^jٚcFd@%͐bB=zڂWg>6_hukח>k/˿`˶D]2Fr.׼&Yj]c[+rܗ!Շ$i͏uȘ\%$VPIP2qs_> ٻMʔ9rɇdhwZۺ\Fپr -U6Vc{f-lVcN6kU ڶc-%ɄNoֶDzh_h/D١%O::i 6I}F]ok5ujTkO]ӋJJ6QOEB2rCfP*<0s0DBTq#/,,zmXkР $`Zc bKrXa{/s6 [ZLY/*DkBv:gY3ҿDRQ2l&b[AU'{l$vj {kΨ*#!$4]؀S9W%W7CuD UHR H9"6t ikM/kd0J\E;e,m9H@ao<$BVYďWzr\ D٨2`m ș@ܢRPo*C/v*'t\$w)9gD\G" ۖSu%n7ԋL0c搜4AJ.)ɯ ` dFU7>rpPe(hֱNMDgk"e4,FwXV^ ?"j}47Y(uu N*4B,GvM@vg{,HA/7^?Q@?0IxUp,w# }j Iy+yϽI~yl*6eXJ}ܧDѣwTIwuxTxߦf) M͡y i H&Y= OG}Jh guFA2H-0iNJyWPSc|B`0uNgwBw5ZJV7ֳ+5P{ ~ܧ@ 4+LH o=zY2D3@[(s~7JQԃ}NBљH0Fi<=&C^2'ڕjFA> tLm4SC*Z$"U'i"7ʜ%f 5>St5b%E3AWj%YB#\n0~WO?<Ozڜiɧ&xN)EF☗0br{xf c݃aeD C`P'H0 c2SOXS0#@ dfB@U XxY.dy}WziXf{pP>x0`ZyqXdylj\_nu+Ert~tI{VXwJѹ,S=X2҅PlCk:pw.*O $Fd#\g60kk/h%vj@ Н߾;e9lEʥf n#{3gLrY )xb:P[{ըEu;3cT 3]=O[bRIw>$~,Ձ0S-퉿8wa8)`?Lj|KTPȒ.Ի"|eki 9I] {>5ZN )KxMfM|A'JICΞ{~ɾ&P9G^FwQ@R u.ɧh1H n uՔ+K :UUxp:'}>S)`%3iBIpҖvL^=ҁh($H-̤d@U /gTګ5OT'7>WTgf2^mߦA0Tҥ`ֲYc(#P^oP];PAF)YDsHR$(TMZ9c-^ ɑ$ci\t8jӡIZ"7S/ٮ]X{xDΧyİ(M0,tIoVz>t?P5l& N7RH G:hP8ܚDDD@`%+C:-P 2%c [UGjqOөC6, %1HnJLD^ /rTx;-kJhh bTZcGl,zmvsyovR.PG5vt~A0 =m)ffydu1aFEMŪhgYSTi ")sRW{_%РiLQ5 0ޕ`ƼO%Utjh I%B7~`W/{˳ƯAD<,(;=ް9GAgݔ͂qĩj,i/` dq=)_.hDN1x$*ocvkAw RE CxCi!qLG2R(9ݶ|M`\rg#os`l2gn|_H[6aU_<0 OZLX=kFJ#Ƞ&ճ?kn*[I vjR2F): Mt:9 eV[UMֲ'h@<@`U^^/7$4#6v-¿8-TB PW^6r|~v[<;69  zwό^_;DE%f"Znl=U|j $eR%O{P:&]Vqģ4>Mvr”&)*uUeJ|Cm7_Rԅ4Jthl '-)$~dlN\Ր]^h0#^p7R'Je'y]ϖ(29zW8_AEks<_%vK_[[t H)h 5)8Lb#śk(&ń3Sd7q  bA_#.+jE.l,#CQi8ۭ_\}!gX#h qȨuVay}uAI!32* A5/$< kfOˋGi*wm dH=BEd"I>V8s1S0۾؈~ L}걏DkdHS3glo0,i/w:#\fz^ P,vPÁ=Ϟ~TW¦[wA=@LS"OtSuM:u"B[nZ-Tmp #3+o S],JWdP%> 9 f jy|>RPjfPNe;.3E~n`m?]XFI6TP\\"h;Dթ͟W\joP;J;%Uͬ*BRBk:s Y"Ke^`IX"8w.|.jiw{6p)=;xy]~g݉ou)^g5wZ7aD5|IFjէu9 ܜ'b꽝ۅa`6R&<Wm׮kﵷU:shT3 W=g t\ꪮ>= I9B|SqEԷ;}ӘA* lUFng+8kL27j8˷LHu=5^ U^%sּv\TTTqdwP nB0zʋRdl\2ܵ0Pn ,8:*>G DCM//qR,C؀~ AvJټeU"n% gt5X'6 VMky`T@v Y~ٮջH.ULZy~53Su NY%2&M~KTJ",:Q* HzLv0,W3G>^=T'0iнuN^1Fu 1hq u2dc]Z̶^l:Z=ۡj =5ֆ 8KJubp˫ˉ^\NOR$M;tXNns#|qTSZB8o.CGm0v9[k.PDI hDbq=ǹ*2I%yE UVsS虀;eIVJVrm;z˃*Xd57$\|C(ɮ!M=4tFjȵ<re3!sV|ƓN1i|ۿ{ğZʴޢQi}G-GVIzb'=+@#xf~]t!.噱3z2- Ud(CGY#u(U}fuP)r?HED/LWv\^J껦Pm9F.'0$41ŶG#pr:8Zf>CG "$/Pؒ[ %";!m V+G§ hTJlPӬ3ӇG Vf:Ŋ$Q@#sRz:_1r~y3[Z;t+bPp$Ws]IFx:-lgrV@NyS'!qӕy HAv%g[஛p# ubO6d;?9bAt͂ iVdꅁh\ '7JFXS΂DZf#5@x@PyFf4C9X;R¦=5! 4k ;T͙G&,%/ I.H-yLM7*ͬPgӔ#6s /!&KbEaԃ[-NDF(ubVR / bz n(o%z7ГqI豣%Ҳ8e?,R>7酽d)u{7{%TwH.-&X aCޫ,!١E {aɀiux, {7qEMj\XOڭ?s ]kvxm\ڕ׶_^+Vm^MA%"fq* aEJ~}qD&j`oO|=ڃS]oYdU#[UH0X4ۼKsU6V^@粑 y{ue F g-$)`FT!"mP48hD'%]9eP-Z]/}ne/%ǪareݱkVԀ*сԮƥL`cKlrӸ4= ӼʪЦqoGaoEC?qab? 檆 *2u(#oru{y7TCV~JL#V<˽DWTĐg6ʠT\ D7ǯiYnǽnFZ32ܐwc' k*Pqؑjw& x f[^1TV8qE*N(Ę!: ~?&Uzmݍ0C9n*L3߫Ql?KP432 Q>u׃ǂb$3>؞*ҙOU~tFb/W$mBr V\d(b whͺ侭:.Jtxt6҂pY %sz3ir{J@+JGD4˹m}W !Hz<@1j:E.%OP1faNt_5zJ<JSPX_;y;lSHF6E3om%g$^5[옪UOhLRy0N[Ss73x2RU4]< FbOOϿ|O_o?ϙ Yvq?O({)/=QAPӂ@/RRLMTEV`R>c'# Poj5'QߝoYfIy7"FyFWDs/;R}|{rA?|ճƳƳƳ(?]J endstream endobj 139 0 obj <> endobj 163 0 obj <>stream 8;X-C4*op0&GBO;"][QRE"hYkdO[o@"1U\O`jG%^R#4bIjuPSV5VFW!8]/U3I#7mD 4Fh[+30ZH,2p**a5e%0u!("Bt?73G#U!Gmk6/V-_X()kuC0^Ys#Q7$Cn('G_:^*`m HP2_3"!#d>K&(6NiW&tchX/,Umafs0/)EdEn"K,69>6%oKO"[E4'cdE-Pu@@r,utd -SW&Tiuhpn9oD6>mK*RCs3XB&Nu\.U%;ZCH$j#YNZr@+A,ZY_n0ZLDCdPkU)f&2-\ -3;kP]s`!o#Ro+5nM=CbkJR#Y'7u\2!7!>s_KXp[<#sJ@5/Yf,5sGH!IHR>RX4h7h <1B&!.\M,>ZWAmYXq`KlIddVgl:A\o#PW%cqObf< RB4ksFNdGO)*$j8"9297GF/B%J_LV\;b1k?_&=r0q;Ve-mV.fVKj.BWeJIm#_%)EK (R:*8qgfYN2Ph,*f[bKBS>_AYQ-:2_r009.d/N-%$ZM M'7",?dV-h4[)(Uf"WnjiCT"RT+Yh?2$b]m/m15&5%["j?q;lcfFReo,s/@f46LL4 iURg+#hDNb@Ad7e<=*lLZsl_+-l^lZ0!03Aj&\a&NJYk?Jbc<#2W;fi>910gK=7n+[H$-bOYtel&-DGEL2Wd)B]A=B'g@Xh9=l3OCbX :Z#?U#>4Lq*Z89HYZ^[^1K7rqT1hpALnm2Z6J12RM;cFmFI;&tReqT"9e&Fm!_M34 V0rEuLO@@!>'MIRrLt5nS]9`a#kueGc78?/opLY9$'tMh'HS+no[:5?$TU,\(Q!fU [_cMghuA]+IfN!6S3-~> endstream endobj 165 0 obj [/Indexed/DeviceRGB 255 166 0 R] endobj 166 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 152 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 239.868 136.009 m 260.449 121.285 283.533 111.325 306.368 108.685 c 306.368 108.685 l 299.63 308.734 l 294.091 308.627 288.466 308.566 282.75 308.566 c 282.75 308.566 l 263.4 308.566 244.966 309.197 228.165 310.334 c 228.165 310.334 l h W n q 0 g /GS0 gs 257.5613708 -245.5507202 245.5507202 257.5613708 166.25 305.5 cm BX /Sh0 sh EX Q Q endstream endobj 153 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 316.604 196.366 29.375 26.479 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 345.9775 217.7852 cm 0 0 m -5.692 1.147 -18.187 3.529 -26.504 5.061 c -28.354 -1.127 -29.378 -7.684 -29.374 -14.34 c -29.373 -15.308 -29.344 -16.261 -29.301 -17.207 c -20.761 -18.576 -9.204 -20.394 -3.579 -21.419 c -3.558 -14.029 -2.275 -6.766 0 0 c f Q Q endstream endobj 154 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 206.292 240.352 10.127 8.099 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 206.2915 240.3516 cm 0 0 m 9.823 1.048 l 10.184 3.323 10.232 5.711 9.928 8.099 c 0.208 7.062 l 0.476 4.665 0.397 2.271 0 0 c f Q Q endstream endobj 155 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 321.252 222.141 28.704 11.113 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 321.252 228.0508 cm 0 0 m 9.231 -2.066 18.862 -4.223 26.356 -5.91 c 27.074 -4.172 27.856 -2.474 28.704 -0.826 c 20.578 1.031 10.905 3.237 2.273 5.203 c 1.449 3.514 0.689 1.777 0 0 c f Q Q endstream endobj 156 0 obj <>/ExtGState<>/Shading<>>>/Subtype/Form>>stream q 237.712 145.282 m 239.868 91.163 l 260.449 87.269 283.533 84.634 306.368 83.936 c 306.368 83.936 l 306.368 83.936 l 304.111 140.603 l 301.255 140.535 298.384 140.5 295.5 140.5 c 295.5 140.5 l 275.467 140.5 256.09 142.175 237.712 145.283 c 237.712 145.283 l h W n q 0 g /GS0 gs 107.9995117 -62.6660156 62.6660156 107.9995117 201.25 155.78125 cm BX /Sh0 sh EX Q Q endstream endobj 157 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 138.038 239.701 24.004 90.261 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 160.5 243 cm 0 0 m -6.667 -0.333 -6.75 3.503 y -11.984 86.941 l -13.066 86.988 -14.235 86.958 -15.5 86.833 c -18.39 86.547 -20.667 85.877 -22.462 85.006 c -18.752 61.025 -12.302 10.747 -11.75 2.364 c -11 -9.022 6.789 0.34 0 0 c f Q Q endstream endobj 158 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 128.527 330.579 15.559 43.924 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 128.5269 369.9028 cm 0 0 m 1.647 -11.566 3.52 -26.098 5.116 -39.324 c 6.725 -38.128 8.633 -37.116 10.815 -36.372 c 12.369 -35.841 13.963 -35.494 15.56 -35.286 c 11.828 4.6 l 9.965 4.417 8.099 4.026 6.287 3.408 c 3.832 2.571 1.724 1.396 0 0 c f Q Q endstream endobj 159 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 127.459 378.049 12.291 32.201 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 132.2188 380.1689 cm 0 0 m 2.463 0.838 5.001 1.339 7.531 1.554 c 3.531 30.081 l -4.76 -2.12 l -3.308 -1.302 -1.721 -0.585 0 0 c f Q Q endstream endobj 160 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 362.5 257.029 3 5.443 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 362.5 258.6748 cm 0 0 m 0.701 -0.72 1.757 -1.285 3 -1.646 c 3 2.104 l 1.747 2.477 0.688 3.058 0 3.797 c h f Q Q endstream endobj 161 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 364.216 261.754 5.612 32.613 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 365.9902 262.1313 cm 0 0 m 0.795 -0.243 1.812 -0.401 3.125 -0.375 c 3.377 -0.369 3.609 -0.354 3.838 -0.337 c 3.359 8.498 2.838 26.898 2.938 29.951 c 3.078 34.102 -3.703 31.352 -1.231 31.23 c 1.195 31.111 1.088 29.718 y h f Q Q endstream endobj 162 0 obj <>/ExtGState<>>>/Subtype/Form>>stream q 345.004 292.5 4.13 4 re W n /CS0 cs 1 1 1 scn /GS0 gs q 1 0 0 1 349.1338 296.5 cm 0 0 m -3.662 0 l -4.075 -1.23 -4.229 -2.618 -4.066 -4 c -0.421 -4 l -0.577 -2.616 -0.419 -1.229 0 0 c f Q Q endstream endobj 177 0 obj <> endobj 142 0 obj <> endobj 141 0 obj [/ICCBased 178 0 R] endobj 178 0 obj <>stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 176 0 obj <> endobj 175 0 obj <> endobj 174 0 obj <> endobj 173 0 obj <> endobj 172 0 obj <> endobj 171 0 obj <> endobj 146 0 obj <> endobj 179 0 obj <> endobj 180 0 obj <> endobj 170 0 obj <> endobj 169 0 obj <> endobj 168 0 obj <> endobj 167 0 obj <> endobj 147 0 obj <> endobj 148 0 obj <> endobj 149 0 obj <> endobj 150 0 obj <> endobj 151 0 obj <> endobj 185 0 obj <> endobj 186 0 obj <> endobj 187 0 obj <> endobj 188 0 obj <> endobj 189 0 obj <> endobj 184 0 obj <> endobj 190 0 obj <> endobj 191 0 obj <> endobj 183 0 obj <> endobj 182 0 obj <> endobj 192 0 obj <> endobj 181 0 obj <> endobj 135 0 obj <> endobj 193 0 obj [/View/Design] endobj 194 0 obj <>>> endobj 143 0 obj <> endobj 144 0 obj <> endobj 145 0 obj <> endobj 140 0 obj <> endobj 195 0 obj <> endobj 196 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 14.0 %%AI8_CreatorVersion: 14.0.0 %%For: (Coda Hale) () %%Title: (metrics-hat.ai) %%CreationDate: 2/18/12 11:01 PM %%Canvassize: 16383 %%BoundingBox: 0 -3 1024 765 %%HiResBoundingBox: 0 -3 1024 765 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 10.0 %AI12_BuildNumber: 367 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Registration]) %AI3_Cropmarks: 222.5 141.5 737.5 656.5 %AI3_TemplateBox: 512 384 512 384 %AI3_TileBox: 102 111 836 687 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -284.5 843.5 1 1595 907 18 0 0 43 129 0 0 0 1 1 0 1 1 0 %AI5_OpenViewLayers: 7 %%PageOrigin:112 84 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 197 0 obj <>stream %%BoundingBox: 0 -3 1024 765 %%HiResBoundingBox: 0 -3 1024 765 %AI7_Thumbnail: 128 96 8 %%BeginData: 5104 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDBAFFA87D7D %FD7DFFA87DA8A8FD7CFFA8A87DFFA8A87DA8A8FFFFFFA8FD76FFA8A87DA8 %7DA8FFFFA8A8A8FD75FFA87DA87D7D7DA8FFA87DFD75FFA87DA87DA87DA8 %A8FD76FFA8A87D7D7DA87D7D7DA8A8FD76FF7DA87DA87DA87DA87DA8A8FD %74FFA87D7DA87D7D7DA87D7D7DFD75FFA87DA87DA87DA87DA87DA8A8FD74 %FF7D7D7DA87D7D7DA8FD047DA8FD73FFA87DA87DA87DA87DA87DA87DA8FD %73FFFD057DA87D7D7DA87D7DA8FFC9C9FD71FF7DA87DA87DA87DA87DA87D %FFFFC9C1FD72FF7D7D7DA87D7D7DA8A8FFFFCFC9C29FFD72FFA87D7DA87D %7DA8FFFFFFA0A075757CFD72FFA87D7DA87DA8FFFFA775A0C899757DFD76 %FF7DFFFFA7997C99C275A7FD77FF7DFFA875517C759FA7FD07FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FD66FFA7CFC9C299CAFD04A87DA87DA87DA87DA87D %A87DA87DA87DA87DA87DA8A2A8A8FD5FFFCFC9C99F9F767D52FD177D52FD %047DA8FD5BFFCFC9A0A0757CFD1F7DA8FD5AFFCA9FC9A09F757D527D527D %527D527D527D527D527D527D527D527D527D527D527DFD04527DFD5AFFCA %C8C9C99FA1FD067D527D5277527D5277527D527D527D5252527652522752 %275227A8FD5BFFC9C9C8C17C7EFD0A7D527D527D527DFD04524B52272E27 %2727522727A8FD5BFFCFC9C9C1A77DA27DA1FD0C7DFD06527CA77C5227FD %0452FD5CFFC9C9C9C27C7EFD0A7D527D527D527DFD0552C9C9C875282752 %2752FD5CFFCFC9CEC2A77DA8FD0E7DFD0852A07C275227277DFD5CFFCAC9 %C9C8A0FD057D527D7D7D767D767D527DFD0652275227C875282727F87DFD %5DFFC9CFC8C97D847DA17CFD0B7D527DFD04522852C8A0275227277DFD5C %FFCFC9C9C9A0A17CA099C876FD077D527DFD065251A0A0C8997C4B27F87D %FD5DFFC9CFC8C8C2C299A0C1C252FD077D527DFD0552C9C8C9C8C8C1C299 %517DFD5CFFCFC9C9C9C2C8C19F75A0A076527D7D7D527D527D5276525251 %C8C8C8A0C8C2A77DA075A1FD5DFFCAC9C9C9C8C8C1C2997652FD077D527D %525252A7C8C8C2C9C9A8A8FFA8A175FD5EFFC9CAA7A7A0A07552527D527D %7D7D527D527D527D5252C2C9C8C8A7A8A8CFA8FFA875A1FD60FF7D7E7D7D %59FD077D527D527D52527CC9C8C9C9A89BCAAEFFFFFF7CA7FD60FFFD057D %527D527D527D527D527D525227A0C9C9A0C9A8A894CAA8FFA97C7CFD60FF %7DA8FD0A7D527D527D52527CCEC9C9C9A8A8CA9ACAAFFF7CA7FD60FFA8FD %047D527D527D527D527D527D5252277CC1A0A0CAA8A8A8A09FFFA87CA7FD %60FFA8FD057D527D527D777D767D527D525251C29FC2A7A8A8C97599A7A8 %75A1FD60FFA8FD047D527D527D527D527D5276FD0452759F99C9A8A89F75 %6EA07C51F852A8FD5EFFA87D7DA17D7D527D527D7D7D777D527D52765276 %9FA0A0CFA09F75A075A0FD04277DFD5DFFA8FD067D527D527D527D527652 %765252275275A0C2C89FC8C1A0FD062752FD5CFFA87D52FD097D527D527D %527652522E525176A0C89F75272720FD0627FD54FF7DFD04527DA8A8527D %FD07527D527D527DFD065227522727F827F827F82721FD0627FD53FFFD0C %52765252527DFD0652274BFD0627F827272720FD092752FD52FF27522752 %27FD05522752275227FD0552274C2727F827F827F827F827F827F827F827 %20FD07277DFD51FF7D27524B5227FD04527752522752527DFD0452274B27 %27F8272727F8272727F8272727F8FD042752FD0427A8FD51FF7D27272752 %27522752275252524B5227522752FD0427F827F827F827F827F827F827F8 %27F8FD04274B2727F87DFD53FF7D522752275227FD09522752FD04272027 %27272027272720FD042752522727522752275252FD54FFA87D2727274B27 %52275227FD05522727F827F827F827F827F827F827F82727524B52272727 %5227522752A8FD56FF7D5227272752275227FD04527652522727F827F827 %F827F8272752527DFD0552272752275227FD59FFA852272027274C275227 %52275252522727275227524C52527DFD06522752272727522752A8FD5AFF %A87D27272752275227FD04527652FD057D527D527D527DFD065227525252 %7DFD5EFF5227F827274B2752275227FD04527D767D527DFD0A52275227A8 %FD5FFFA87D2727274B2752275227FD04527D7D7D527D527D527DFD065227 %7DFD63FF7D52FD05274B275227522752527D527DFD08522777FD67FF7D52 %27272752275227FD04527D7D7D527D527D527D527DFD6AFFA87752FD0527 %52275227525276527D5276527DA8FD6EFF7D7D5252274B274B275227524B %5276A8FD73FFA8A87D7D527D527D7DA8A8FDFCFFFDFCFFFDFCFFFDFCFFFD %FCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFD54FFFF %%EndData endstream endobj 198 0 obj <>stream %AI12_CompressedDatax~H?E%11j4h\2s;E5Wr?\~&Tu-iT\{a8YGZLf +>jbynV2"5wټ?')|RDW{2dㅣb؁bo̓=i^#4d,LRVLRB o|N ?Y;?OJX$epәm!LZQg&| 'yšC[K;pJmaF7@ ˓T_فq  Lv,T; "yxm7Pgtycكp^ d:fPN_f/eBQ]3AxQ'WZ`Y HhĔ=a;٢*"!|f9nAJvgW׋C _AMwpS9+RfPvƵcE@ c%5NV2YD+I#BJᬀʳ{ AvFnou C(/'wƽXtJv2%-Rfd?GF0Ns \ʬ?F4-WLQ.Tn6YN}6ZZn~tZ S[u_tM޽`XU> _-tSod!:"ֺ4&]JcifW2Z^;ATM&=az2-=km2nZQW㣨߃G_0.erRlsx9ɿ34gҴo19$a1jNhmO#i跆EgF?n!⠳d4E3ړLsq`QA cMdͤv* 4괭!erʷ-OS !CDÇ#x"CH0R$ 3rdH'Nr$Of#"EP$ڴPQX*Nq)R"MM:J34 yGt,-F#H4DH,EH&"D1JZTFG3Q!*2C2C3aX& #0YFdD`I!FYeg9V`Q8t'Ec!'ǸX>"FSq:O4l8>,|ĸȁ8"tpsx |EOD !lIu)ki45[T X{4k dv\P/h6S]Y䖆ct|KˤeCqf"JDYg$*Y mG T4nQTt֏лEQvwUɚӽwJZojx].+U5R֤KYtSd4Kɕ.}"Z'Xu䬢SՕUz,-ւ`-w[êU-zVڢQZXኻ/;.!ARVZcѕMj"E5KݩC6udmQDdB8[hls$! l-g Yh'ǃ `:4 a` k,u^>.@)T|x+|gA,DiIȊlVf<8 6͂cdidD!+BF!-`Q!"ȈlFd2<x&̀D2tٖE> |Ab?zRGb ^89¦@D,?J@18̀Z" m'Q m 34(Q""*<:gAQ94 dA:cāVXQD%,w}`mtI!fYMErҢDhPUx,FR)/RG#hmZ~'5ڴ|#em.#Vg8!B0Bb/lШOfhH[I�!V%$A U9͐4iEW(ctW wTt¿pD&*4Q MThMThB3 =4Q  0q M\hBAąL,a.M\hÅ I/f ~w!-G(&BQZRmc<+S&ueNKWUf 1AF'̬E[W Df9l=ۗǎGR\@҈ki@\Q%ƃH 0&*T5żeTfk_ܢ[PA }8PFqRQ!ʰÈex;$bŘq7b츪ȑVjr=@q8GRTe2 H> d,)(hRFD2O2*d)p\K *{dT?DD Y8ɬA)#Z~(#ɮ"+x,ʟ~I}iQ>]O!j.s~,yJ)t/DoE.N?լ0\眖I\<:c nTa<#O PdqMt?Ǣ{O Q|p%|;⏀#nG]^ _`'bd!p`qpFUWPqF:6 (h38p"882uίU,ƅ_XJNCoi 8m4ʴ3ESAX&Z$W0q6ehǣI+=)+X bDeȂDbؒl<݄g?H{E LPd"E&(2A LPd"o.(E&(2A LPd"E&(2A(ꂢ LPd"E&(2A LPd"o"F1&(2A LPd"E&(2A LPX]P"PAj,XT#(F`Qf>4Lde"+YDV&2LdCG"?E/b&3Lg?&3Lg⿸Lg?&3Lg?߅t߳EoF7ߌn|3ftV;7uryioF|3fz3:EB¼+h4 ?yWм+h]AyWм+h4 } 4Lg?&3Lg?P&3Lg?&3L/.Mg?&3Lg?2&3Lg?&3 Su[|}m[jnQu("L(TI4$!7Jx7wҚ砚PՄ&T5 UMjBUPՄO?̛?tE LPd"E&(2A LPd"o"V+##,{,($n31$BEc1 $xDk$zBHC/>,X',X"[wfB?;֟52ADL`]_~YgNԠy1I8Gck2 ~N7>"&7/)N9L<$xdWp&! YAi\m#knh\]QJJxѬW5 }:e  eCqfB) ȥS*RIUQqmnQT]]q~W-+kzNuªjAKYkMzP_# DUpIJxVjuUjvU`"ɷjfSêU-zVڢQZXኻOJ$ VihcQRwꐍrYd[-z-0y&+xds$! .3wT9dA}ra0V.Pn,axCj=(@Ep+ 9|2{{o7( YuyeU7oA7[A! l8ӥA`}_Ho*҅bV{kr[0KmZuOGI6-_HF=_PchfhA*OT-@T >:"^~?kPGDMk u1Q MThBADL$a.MThŸBR2d.N5SũTsq8\j.N.N57 'td>GɄD&$2! LHdB"ȄD&$2!$}k4k͵As!usv\;_of\;h1=T3Q MThBADL$a.MThŸBq=T7Q MThBADL$a.MTh¯d\Njr5Egn.vpa<}XϓJk _d,j4v&AKp zg&y,r09!@h,A2sМT?.O/ Go,zH0@kb Cx U i jTŢcAL+ͣ;y狙O/QClTZ +z*9rvF#OU5'CDSĕ r(H :A-$ ioӞC^:g#iM3a I!i؟ˇH|:YȇhHpl*r1HEg#`GG0:4[4'ҬmLfpb#mud(-:а3n6'd&˩20JQJyoKlU֛X(|ƌ;MNض"$GyBGPϧҬ3n=跭b- ` }Tfyg[ZgaͶ +Ӯv˩(ߗ{ZLW']0EgܙY*oѶn]w}9W3i2.BB-3@:5_)0 ?X }V[B :M`^[l;~̎vK Uu|׈P\ ;c[:;Ci,T~鴋NZY"ihYfMܬ#q~S s$;aU?dHV`BQ+cW^2@ʩp(EV]VƿI7G\L5rU |7@Z?ƌ5 '4p`*u4RKMFd B&?W5*eK"pjS+ƛtݍȲq%rҴ_ancQB,:9/3wR\ҮrOr^}lm;ӗB"8\9Oݥlb,U9HAsCx'kȗ> Ly [d6S].b4ҩfg wXDg}-> pIq嫋?89CweS wO\-3fmxIėtbs)zrʼn%5KRgazxE>xs ->=we:yu\B W :rxl+z")]~*Ogܷb.d% з?-d$Y-lC;8?/bM'/{$^ܸJ0;z>lxޥY+O rE%\@lig,y?d+#yEg:?uԳxtz?1OO xdSc' (H%OL#9 *y.䋣$Sh?xTphݟOolAn ddbӽ'T6@ g iq50+Q 4/WxYwݠF>BZ##j‡_ YIrLBhZ*'Wh>Ǿ<8c|lf:X C Ua[Ͷ<ɺ!D6HӳY6(ϱb~~rW$+8G$O\F'Ge:)9I'k ;W\,wEIUlX gmiyԛ17&q:YTΒX $<$XsTʯ|It/cYhL{􀃧r9i3#Qsm^tBnSahOv{ݮތՖDyȕ2yA2TzTN5O"*./Uh8,rF{U.p# &Z^{OdWf+Jt1{2m >]* CI̮'w㙛N}v?wB\:85!\=HXW9SS%t9p# 1w6*G3jLI6Pk'C\5g6 5 EPE\b9 ?glrI` -n[ȏbسyv(Ω *nk%3?>rFɮF͑j 6ٺr{#E/^\~ yҳ[ 7/wfJWdxC+ڏ#]cۗNA^ϫ~ gFηi B!S &#{, Fݑ[ 2IM{:\GJ[I%+MȂ)==\ө7HL Sg EWr"0Ǵ:\`SځEtTRr1d7MGN!jŷF*..,EPHiRd˪l߮?qZ(o;+{ Fhˁ50+V|}9xF9㣘I)ƶu% cy])䮯wm'>q`٬Bs5qzm *h Ծ]lUZ^uf[XD{4$$rvӜLJеNTt`سm.s\r6ҷ8gIuu c+9~ AJ@66d3u4E2*:'WAbHizs\EŐ5{Ď^Z[Qp.Tz#{Eoe#O=v)5d勍'%\.M;N\} VSTR N?'}!|Ltw½\\N8S"h,yS0m$WA7t53)1'X:Q't^Ԓ#MاJK8}=&δ\(F%j%={7,`;?6Э ]3\4=e[w+]R*ԸZ10'= Ɨ\&ƺIo, SA|+66S*9%ҟՒ5!.C>V; IܻulԚj~E] {<2D!ݡ)^% &v1(G0&H$?9b!V&X?qֺJM-kyVSbΙ\*swe~aO'!ߺ:Qq-z.ϋgq$C`h&Y8»%& C}ߚng{l >6Ï[Y: ?g_?-\ܕgnm>ř('Vs6<"[TTY |60 ^ EL! ZtU3{po7~ &h"Tǁ*=V;OS:̕1!%y^=GGTI6xBψ #;:aQ1& c+PYpazVj Qnr(4}}g6;؎ˡ[GWP(T, Ʈ*_ 3g"4*䥡s' +^ !g Zn:i`YyĢ:7E/@E) -rUķW'|j9̽^KMEQ|Op*Qo4((⛍Nu܂c.o#Ֆ )1ii+z VVĐ6]-RcL)}1Tr=`.uTb W]RJt&G9L[z8dķ3n7 RWSp+ۜ\/Y-oG~fVmL}_݃[4~3R4t '+uFc]i,y@:57ybAC!QI BxDt4/7Fم+oS9_.!M^zmdw׀5"r O-P Jzm.ŷWjl^LԚYׄ &{uM̢8PxK5ژīB{4$ݓ8Y/Xn@ K.._>}0E`N!|w4]p^6իs4=^J3p=`r}f鶅&205WcM870vЕFuݬW0utgPޚ,2՝]6Qt Jci4h-o0BiL -OfOI+`lҔE鏎+CQ]m=y}ul/=9Bd : tjO3 eu;5ý(-:rK]@ Qi;?" =a"CQav+3tS@:,ڬ?*8tޏl%kwQ,-\kQ֯0I=zn*{m{µ6Fmv?mAMS}Ɇd¯:܏Npf9vofp^Bx2kn)t(q Ӳc@a0|Z݆\>OVuXg X{!~uֹil$HX9:Ffs|B] L|u ybG0kܻGhlu,׺ o#Yu}00"Η^~,'LwKݤfߟ[zfhfZcaḜgV(i 1WoŌf!\}԰~pnyj>O=[PYs4qlP 67GSV+8:%>L&o{M<}l6[v\ C*E E<)J_E:yeI'E/=\U?m6`vsi8cbpQu/'3Ʊ/C⼆?XOwr?:M߽|zۍfZK3t,y~ƵVl\욞>b.|󞡟53qC79o+tu6ӟ'hT%Hb3D?o`-A_$T9?X3&:Z3ݗ?Gf6k]<\.h>pϹS> dy6OOnPXүXdX4*I֣<]vGkz2KF)*Fʻ* 僚'3Îop5a pxWH;߬{bkᯫ1*D}aGz| Y5g$ځCטҚj uKT̗B-/lH*?[d`3V-^&=2ɎS!tK>vE ;z"ozSJl,ƒ fFCF͍-K\pեT7Ǒ mTo<_*U*Ol^2hs[#}a ,c_.P?튮{ui%>lXBX ='S=Nm\6%as+ oڜ,w's>|yՉWHlk;o ܽق[31\9۱юʼn^26lltȅ-qk{%[-АUgmev~wnKIO[Ѱe]XL.g[8]9*Ʈ+pz5]я;'X\ˠvAwwG^Bu&w\m+ϩsw_<ݣb^ ܩ3'ԍ{8} *zj,mb+xp~cQSB'y>}>]0 qz /OŤz_Tl'/MFtWa诗g~>w?Jaa-΀ 'O\ʭ#R7:5}\Δ?&\ $x}oRpD?B9 {S*y꡻IH*^a~I77p툆3ˋp9Ïa0h$<o<* ޅ:Ѽμhq7LU=2/%vRdLEr~9:s$; Q gᦽBuL3_'ie⌄uE.Ƚd<#sd|hڣL V~|,L|`D>\ej7zg# цkួKQ=y-r=vzq12|$J^GQNSqSy4~:'@N$D by.Mڙ`q&O I2ɉIApϓ5ᥘl/ɅDO #z|O+VT={O?ogY]IYp6ҩ@0K ͗UO5KԴ?uyjr^+gS)s4t㴥ۣ 8Zzs;Wwݧ>~)1_Z~3*22me[`Ws /'LJlؕgϛ}qپo s1ūVl-=E\{\r/V).x!s!Tf^6u1 Z!ޏS+f%uu8ZwIV׷Zrp}=#JT~Խ|5bqֹm1gTx/=A;a{IcW<Q[|"sS~}j6S޳9?>?7Keq$͗jeRqm>mnod/2crhI'tKJUZ+MҼkU3_ZY|jM y?-b=y;K"ک=a&yӥ/>[r~t;Mrx'}=N*]o8go}xꏯ}~0V}Lgk ;Li:6|X~O+%^ݞ{uV8bryA'ftJeǴRmM'~ۥ{KE?y>ѡ,z5|lnuv7WpUKN{>jVSHZq[QFIwc|XGta?}cw0ނFߨu^^}'ym xnp9.Zg7L/lMjp Ic[ej [(QDGmudNK9a9/h>F\#YX3M.0LT'ŇxƝHǎ.ɵ?"c4\y@f'hv|6Kk$'‘ ^s6m+Svjqn Ӈg)S-d灸14Q|FZs8vi72M΀铗&:sp!\hw ]yҐ%ESstl/:f;HN ¹!f iwnJ {^o.yӧb2bhp#KgzU~N}L_k/cU{#"Ѹ? &?/u/%)/ κS48΂Āɟ^hVxsVV4XJ\Ku֯wչqmqk힧|;Ah5Fsۋ[=|Kx|S>#18>i}uj=TG/⭒YA3ViCM݇\hl'}  ȅ|Yl2TӘߚs/1({bϚ^Y;͸9SJP;'r(_d<h}-ۈ٘]| 2%D)0etfuӜ-j4=g"h.Yen E4R=ъ:}avk%9Z0>Ã1SNlx&[ 4b*2ExHg$LQ>rk$]l5*\E\-G+;:[o^>nl hƼo[ W<~~2ePǂQ%\d\ ( MׇnTqAxt?e~K7 r ;':k3Wת){bYՓM0݄h^M@c=2#ۨd${uؒ%q@l@EeJ E(:!Km{V%'> zF}RcRޣmd^Rjn퍵dž*n`b:3nb;U5ӒEb˯gOo /|Kqrݟ#R:su)4L X<jUK[y,Cc_6cbF6Af6#ڗ%TC&p@zv+,R"&ՏcŹ` H^~A,|7wWsZ6 t} %ܑN2:`0|5x[3%ݮtd;%r{#*FLʫ;E0|KsUXC7omF^ÛB0pav.Y_/i\#C*7y⺴_#.,ֹQL^[^=g ASnN#L'֠8 aT2[y<u.]n;Qd_-1w*$I #J]䯗Iδ %љAM].YTgXsi̾R1׀t5T-M&6Sv*)|dZAS1?e6Az}i"L\_ĵ Wj5>uG Ȳ [/ilw&BTg"_VmNu^s꾺{ݦ%C T|ny*D,|6ފߍo*ڞ)Os0M ެc:@Lyf_yOum!$q6D~5r規=>$v(5ũWͧnь*$?g:Čosw1R B]߿Ջh_hz Lgb: [gi cV Kz֡~-|[]pĚ;C }-yP^]UfW,}00mg?#\9Hg;Ro`,*o/Tt3;{aJ{7bo\Pja;>{!* 1p.@@5 hsY,VN~AE 5{=*A:G͆B5Hpk.DKPᲳ嗖#ͅRxj 0~ Qb_@`@FyDd vC衪X)XM+|P7mwIIeK>½&δ`8-֓ '|H3Xf$cceԢ˗ bĎ'${Px[BUW\YyQ?Y<7'-qE[0l8ft;;~qt2z;;6w ;,ΟCn_C3{ë;~`vxۿN[;|;̦z5;ҟ_QZ}_J}semu`E^S+kɍU+;$]6+|GnQDټǷXsǏ/t_͜]2x K[Pu掋5[o/,}h1kgkGu7}mh{Sժ6WB~0/߭ol~i)f칯a" _$ 0kοyF:;Y~ lt+?uv c횫uxkZjq~iХ=^:e~?9 5>{bacB:Z0ޙb7%||IbƷBH^b,ǀgCmE-ZִL[~QCwF1þXƷP*Ǿb#OVO;9.^f?+V1wgTR3t(= R>]r[XcrZxuܖ:VNwq6M@aw gإh;ۗ*2{Dm۷̡Glӥgw }tTdɂ.Ȭ Td65@NEfmh_avvTd H0V9deǭH/w6'm׍,O ?'ܿb!W ?SC[99# Er[˴p҅Ƕ.npv1YDg9|ϟ'9P~_y鯂pZz~+Th?Ctj>WmRo~8"b?~8^K~x?3*)znk߀^a5;$~o#WBoewx-ެ1L{e*Y v Yns+maۿg Wo#; z؝1C7>?vҭln0c:8| T ti.F61sihZR;OkD#oհб>@ّZϟ?#܎ؑJڣ?#U#|+njTхO_ݑzcX]v=M"#D' EWRƱ +z6 [[U3G JGՅ[FMWK7H_f[jYsk5hX1f//mS/aةu,1Pb4^X:XٶvWi0NfƢ P\?!5BMtꆤ77]:Gqf:7}\QV?IqPх&Y/sןK>yVp2wc"5 >?9qB~ȊҠ]()t$Vis\2m?Z!wꙺ9OY\>Gד]djfr/Ϯh} N>?N]ha8ډiT&푪ܤqp<Ñ>7({:STz8?ozM/ 3Bt 3듒Ϭ7 Q'{vkT` (ܜw]OgDg'F6 ЮKR6:^XR,~ꨊF};n}KGԽtaQU_f\vQyi*Bw針 #bN/ M;ɊqpX977P]/E>`ѣG#_w^_Ҳ[~#8v/o/||i"Ռy~h].Z~ӨG|_E20q0TiJ6vlm~oW 4$C6F@D0ZbkcU&A[WWdUc3ONvfmt紾=[l4Ys_/zs/-DžOx_i] ѐZ^f}$W3tZS,%k5^]Grvثks({^g0AkHBb'J-lV؀[{45{zgbǼd>sjMhRoNvU㊍++}Oκ0ȏ]C' $˿\3q ٫<ݝ8$O[i8yp! ) ؏kC9\i,]|Ҟax3u`\ѵc:;$+vb}擴\17#; {e(1S5=ˬ81Q v(XP" p| ǐ]l(2RCT6 oKX\Y*Ud)G_W2` j{(D٦._-*WUQ+ͣ(N#ђ8Y)i_ol}kZr|}Z jǿ1{7P7H冚_𶭭7^+Tc5U]S9PoPukFJCߨPktg73_H9HQ.} ׯ~SU7~BݦYͧ7lD.i~42ToW5uV뫏\*M4ߘ,g"0DŦ7iH4e]Qqkt~٭druPUYuƟJޓg QWÏMUo<<5af׿UFNPo:nOo~(C?;???^+ߏ.;>vgO>?Z_L~|gjzg׳;/jwNٍ{d;|)~vr?^npܷskwbb奯V/w_`KN1-${}3kRƗ^}՝W/J@js_/ց?<>+|S޽Z7W7+->6px[_H֟~j7ޭ%_۠gB0/}r9Ga(Y~_eŮʬM 9'=;o_/+vN˸oW|~aؼԩ7??]Lӻ]*/,j:{ݟڕ~9IB `Hw,Znj_ tѮЗrXP揎6K7 ƛ5v?^]_W.ˣNdl؃֊WW.$OVLE0׈g1h|}cj׻%MZX {hwwʩ0u"8?tۭ7{!~:xlIN֏x}E.^?wb_~^x~{ڞh\xV,W=yzsy۝*^XA3],PfY/f|֕o_.lؽ T'֡ @B.vV-ղѓ4:8K7ޝ7y|V~3d Ðnp.υxra~Q2?l܏a,^/MMb%8t~88y۬+XROW e#痱9w(d:sR.ZDM=NF'dw'TdۯU,BΫ^{{}Ubc譯V!y]Y_([s{B: ;uPƃŴ|IWGۃ]/z]<w׋+g,S{.ndK'u%B?^&x׷GJ1+W\%OVQRrUOffrq:]bó`޾<=u~M3:oKo>x)G_L#[b?NxSۋJ6%34HO[K\[`H.ַ%x[y˅/ua mE-R\7.ϘTuKN\ɳ|79_3LI8).a`ޝQ ?p.*gQhA=qB2O`^^Z/Mo98~J#94gkk4 n 7YSYR\c5Ws9US9YjMOOe7.cw*Go>$.ViRT\w*ǬCy3u=ɍoYx>[>&lk]36fV 0.F|zƦM1_ wQ9cSMzƦƨ3V;=ch@OzGWMWbw*a"4}P=剨cvcG#U+R׿U+*%5*]UqMD Q~m_wU3k_R)tO<$Kunw_/Oã|jz~ ,vc"~pIW9Wrc{z)[Pvn^,l~_x˥뽟 ﯖ3^Ԁ/`7_\PyɲoO_RWw)zJ658d0ȩNW[@_ 1]Yukg~?C͆#泝¼)F6>}Z?{5OEOT;~Zذ[E~}ʹG[7]<Y.P7U^'m.jL^9ZUAߪu-,NcͫfCvGW?tŊĴc׺MuUr otb^{vO?Q**Ksw gr~Oqk߾J$ȮKJ}ڵ&?%Tp?MT<1mҷ͜f ϗP!h57N-_~Tf<_|2K3+%6 V(_Jֿ/3^N`W#}*H ܕSY$Y{G]?_YFXkܽ]~xyΫ&Ua!ߎt\n(s5'I۠*Mf5%]&ҝ!W\a{D9[ҶDӎ:\9szIIU~[v@g/_gx'Kƫۋ.a}O_vqģK ߵ / b5mB.>(oby^[-wwB^_= 01shaҳM&u6]U4ʍ8vo%-/.wEy[VVףhiK 'ybp4Vz6΀TYEb4.t0Xfe.;Xܗ7bym1>3L3ZW-c>4)PD+.֖qw{meaq.f0kk#hz"HE4NZ؉lk3*t;Rbua,efva.3ۛ}WzP8@ՋnFglܬ֛`ű]g<Ư7> 36E24*Xgaqi؉368 wm/c8\_v(rӕ |j0@vNE sSԣ6iz.@O+TL1m^=o؞v؞yk'yl_;-<=wZ:yk7{c{!N@_V-q"}U]mⱆowi"H}~W5 -,. _  %͊yˆ _;oضe[;"^z}Gwm靝7Z[Z$ kZ8::/l]/G[Ç'{Ƿ}eUY)-[wZ/JZvXO;)K|L@,|Xsa >4uIԦ,3A4tV Y>=4q?s0u/d2U+/a/a&/ЖДKhx ^B8y@wB3ykp ܳ9+g; 6imtUr uI'ɊLneg+0|9~6xyVPY6r]  _=xwrCd` y|\CSu)qu= 6_u.G!ÔCؘl5M.o+3JS{T ^&E[\ BBAb.r=lKzӹV!5r?wi<~ X,w]r ԅ4 TX6КVg ;kI d"lZ2Z!ChyI*80$#~[i fJM&'`>dڤDEd]t D@T/2}[}o7,43r2pC. NC򩃚Fb@3i qdd7easce~ԵSKƢaMzF"+܌j%jJ4+^8+|VGD'P;X(KLFCR#xܾ~E84 r@䌘\x aX|-=IS+.ݸ&(λ  Lnp>G/4B64« Dɒ`0Ï„JT8[#CUZ %YpP"[\T9sI0Jޒ+AE 07a|\w@}ind,03AWиa+VjϴUXη)Ü(bXh9hE9Zf22Z9gh m<`$*/ R OӴ cʚ_7ɫH g (|F&WH ).Fɕmrec=3!4(Oqr@.> ?#)DEVP.V9X.!Dn$OpuZJVAT? ʹL\j"ikYY">d%7\Nj`/ dX\dJRȄ>H$b,U:EK\0F[U./ٵT 1䠗؊C 2D8CHxCu؊ #& 1 n"_Z+ .P1|~W=ċ!^*  0YJ>fDE4$D_>%!/D 8;C_їC_5Tx }C; CG Eޝ~F2P@GX 9`v-19&@0RɄn9McA1eӠF$"G |!(JB'B`,n+2a2=@c3fP^bǣq(Š!GְG/3`ekmu Wg߭HІ&/ ka"7WO6xi6oso/a/h`@8SzqĀ C7`LنIADEڽ@#$t"ҷz&$Qr +A:IV4Pl. ?y°) KDo@xPE{i+M:iM hM|e @45dِ͆u6Ti:[TY:2KRuV>Ra% 0rBU| t! L]yS.!F6,CPw#rF'B|CfSwһ. 0&@Hq?Qi9%FH?*@˨s,',m½QemLӃ09,^ݘgkl||6d+Kc -^)4éj*aYNO޳%젦+W\TA. L@0i%\k"c OA [c٬?3Y;3T=4WNf4$[`v4VB/ 8#$hoFbGJJR*Z{$O`-IC)pp tYzQ|&,C5Xj-# o*T|]JJ]y--]Ml%tB>,q4oޘc"i}6Kȳ29KX>FIMVE$1D6 7k:pP m-x \k&`( [E4\|."rӂ)V,_!rRЄӖ ;_6 )µf֧[)CI?Io(H.`@b—MYFσFbBi1Ds  y| Q;=:>oDG>h帩\jDЬtE@`j/h w@ѦƤ]SXe(m`6 Gi̳= P(^H/D)\T'<%9&:F*#^5H\D Ѩ5uS`"j r5 8˪DM䪨Axa=2UR)eSU\pY|hM Ă?otT\M\tZT& [|&\*v!,5 Luvdaaa'dT.w Yp] +'dYMpӎ`c:L3"ׄǍ>F@ t=.->.rN's\V\fX {d!ˉ ŐA!u ~€ЃbąûA;,,J%и9s#DL]j0B)P@wn+P_˘]4 R]HWjnH5BF (G4a±t) MDakC=z1TYYdXĢ%`)0Oa/[I2-lj""ѧ:;õ~Cp ëV2 2=[z|@jJG-*G@o27Wh㋮jU/h tȍyGX>A ;7-)ZӃѫAVAsE5黡ѕ:]g%l+_Sy7k“D1Q %َo~(6*PE?a1Eٴ j5P$ t܀$MetYIYh&hl,mgJ,d~ q d\¶I$/pۑ4#Vܾ^`"A2SЗp $aAqVg` 2r2InB~we73v*r+v*VgѸʁtnf2Nae9Vt-<'𷬯HX >Pe')zjkp` bL0\#*p]tvDP]Qkkq쳹$Xda R2GB"' .[xAD>fU A7D,.:$i+Ma+`` :r`NG`4KsäIazMo#5(Vsر4%# Ғ+ dL0{j&G<]8Ĭ}ot1CZ\2 >0cLʎ&paciхO} S* 'EA3$."kpEF4 f#(4A3(!ɖ~k-ƶmzDsP1ʤ 1WE$YvCzH[rE "8OmʧzϐL6 hȬ 6E+"ʼGcrN -*pѐ- vETU[&R -CO!,8\5h wUd3۬ܔ_Gy'҇ Sa4)0$CFu9 @M/<@IsY ga-rhRDA0?-`F` B RW/yKl#,]=vB5Xz RץF)2GHtyϙ@%!VbD=GpA)#ANVNtPhyW52X~A2xDCx~E8hdZ(#R.*@퀸? 3.!+ lY"@v,ANVfc,uȴv=(A#Ѷ0*1G{qjM&cN|2I Rd *ҫm{X CNa1#Jx+~ Z}avROد @ 36j8?y/ѫ=S۠W&.•9C'C Llpwo:`&r A"&ڀ\LZ{Dd /s@.o vL1e9"$ R "*2" >ܖ°!;K[89Mʛg- +/k.) ?Y \\%THWl/b{NPd@70i8MO)=q.f΀3F;;I{^s8i|_3R zӠi<s2Ә $Y e00c 3`_! тp4jcg:v $[ НaTvht;i4ʣx8/Y>w@V W ﶷ.k?S+g0FނN蚁NHbU2bjK8 ڗ8 56=i0~ e.yᤐ*2:\sKfpzCl0s$zuh0B_NfnR%Bc8:4W@B%Kcn4$%x,cG~3'4 ,b)i JKO͗FnA%QN-OI 7'HB~6VZcYA0 |22aحwFs=DYM|ٸ2U;4o Ӑ#ݰ' ȫW$r:&'Dj#@+ԿB}h"ܘdx\@+EfO:s&IaP RBt瀙I iehME12Om0Q2 &ScT3^E!N!\V\m;zQ .]FCF-.O>z^b >(Jd vI+`~dHG> 6EYYԧ"|( JKGaȄ _PC"ENnIn]"pdE$yS$|+]k7g 9è(%djle+IQ#o!-0^I)X/IiI2.%j:g<1>=aAQ#OШ| ^CP*}#> H!,z%ekc!3VPjOUl;r(\d@0Z0!gt? jQ? DC3=1OhN Q> CsuKO@)LϬ ĘP)4T ,]¤G~pRȂ nSz BMȣIhTu8p<PT ցBF p$̈f:b r(xIfG͐PSaS͠ MAXG?H%)Yq/ nd+y-D<sG$'YCOZ N`J䩢F -$t'qH O00_7ڮl}A`oPEM nC-TE.zPZᑑP0A/P0Y%V-JPD[(2$j1:jg чbaM=wGu.^)6l% Q K="LTiy%d(8Um [gD2 T3Q)Qm%s[wf:ԡZR2F4irǼķ!c6!If2d.fM3Z=d; %bVl- WTi㰚%@( <$w.ۈ嚝 @r@?.(M"EHњ;p-` }3rΊדz\CfS×$M s!zTJ<A6>{Jk!JB"n흝*U"=BD~?kPd >'AAuB=)mR_7l.t:bvVwq5 NVeyB"F>>L7ЃkY+ Y -zm+lׄX9?5^ZNAPPj-C%āB2S`"UC@P9;faAH N<sHb塂x$5G@>6PUBzap@q :L%+1 hB 2jaޗ鐮8GY\A0gThFҳ&8sfk2Kw 4`zpH4^E#n- kʆ'BjPx" ^t'Z*&& KTV,7gpB,\9SӃAALsf/g $ik-5Q.DqEG= 7 % g@8T>~vQC)@\M1V5%"E Ez#Z}G( o@\6>$Gn(;64XYlKS$5JBAF`5]Y`u;`~S 19. " W_G(kCqOw 5^&dtl,g0#Lp,5>q^Z&\f>NY, U+ʩK$"Q62`1S `v4m>愁ќ%_VFJjHG)`vѧc-9(gTӕ1UA9bNz6p8I:?2m .ba aBCWHwDO``@ Wp'XD {,~ u fۀ,d+{uep `'"Rt} ` 4?<& eqRIJ>>h^-Έq4Yɡ7 A*K~G:r2de%c#d] }X i{{L#= Y|XR$MBE"֠>&]G[%Ă3gK]]ʒ`u4]K2C [r VZqp~1iM1`6Vo1m.Ý4CS#s!=ӷRM5pfm9O1ߘCbh u~X5Q w~xuJQaK~; Gi^>n ,_4y 4Ja3⌜/j]]zxؾV(U{BTAȳ4PE9n\ʞg7~9!dZjKFQK=Bk )1jk<@J5}ʯ-t0~+?q³G 0[$SD#HRcƊGuP 1D$WG)hbat4BuJM)lӓS]2Fct+aGP&RyQaYdC@쇄0K]VxռhR@tY,qI"΂ Х˚YIs>HE$hΨ( "B5*7/8Y#d ]hȡ4eWԩ;$R`L>xGxwZѤ|0a$-c˙J\VE5TzOLfDerMĔ~YSg9OC0˻fB ː+,u(FhEL%W@)L!bV"@H vRBz$>Gc} UxjfsDUA[Ar~dd[ U$L|ۍIZ XB"Z&(i4ȹBjohѬ/$OjdtnPpS%tHdrZŎ.!aJՁSPs'_Lӡs6~ձ웥51G{Dfz0 y>$ |dP V|g-i\²PmpGz8 U"pcNJj>- q\0ͱYZ@܍l'-j٩vP &SXͶy! Kx?xBOv ss"}Tޫ݃QɀLd A)N7XȄy @DsNrp:$ Ch-̈b,[xBɖaD"  D-_9B.I4Gܑ&ut̠(.׬p O^t8I=pqM!0eъ Gud5g J,?4Th9y,V [KσA )VM}d{5!BeMnp8` "#2JPYg`%_Ixs@7@̑^A;+Q,T#{rv3}Y[=Ep X8<Lpx M2_vʵ/  MH Ǚjr(B|J5aZd4Eqȉ.t]P$&̳IAX3$$> ͆<)Ǖ[%(r'fTɾ1P"\>hҊ4=W^33Z s!1:nd1Ahl <I=eZ8C$+)}q6xc4=*@fƜmB@|g mLYW+A:я*!sQNz割G]νR k?͔'\*\LuXCd3a '2-CHR>T0&>,͍>A{w,=4=7YC_4JYoJ)!W)\]qSbjHVZ 8B1J92++ӌLY3ܫփ]"c;4RJsz*f S'Qb([v稶x.s:Xbjccu̩2 Wo#%DqdtL⋺CYpK8gI8I/(T;G5&cKb5+q?dB͋UJaQ5WE&LܱW3K'h5m@ H=QwJĀ&Ü 0V4i|, v9U^֨\2欦 *RW3W'fSV]ɮPVqpPJ֙s@qo2wT6s@M#f=&L,_ҭ)FX ͉|3N+>2ߙ? ZWx.A]NbF捍d6n,52e'Ȇoco ĺ1q9"KMjr)%{FbVr칩4 XRLVt$>c 1djE67˦:Yv"YMf)Fv :GhjJj&COA6%WMNJBddWK9aG~k5"hVL` G ĆgIS8DAfwJaReo} r*!ղf*P+j0oCp,EA3'i 2ӔI:N@{" |I|ޏ mɩ!$e啫ETdoܖDߢ ?ۖ4<`ld/Tm`;(lǎ1<) < q-}TѲ֊ qISؑ_sG_YIEyǠCyD9@B2N0д}㇌UC-X8ۧT+{sU.!#8YBh)#YC2\cO߆C bhxQ2ΣHU6^s|,?Dyl$NԭJ+ȩtEؿlE2  Pst:gLދh<9 z98\pibIx&.KJt@H68 J- XV9Xtm~RbOeAaȿM%?a2xCDJP_)N,RE@ie.aD¿ =*DD9?RLA/60-B"!FY=DRdMƮr(ip=! .$:r?tZc0 Bp}x`S.AA Q ZB:7GnwV&c\{-4'?1Bǐw/ˎWu]4Z(4 wVA3^<8a t;v(U`|ǩS5|ӹ'q% ITԌV L8*jltK6,B_&I5g:H=Oum*||.:$x+ T)|;=1p:W(=D_=P[4\HX'<#>%zj]TNO*z"QN&JϢ4Bm@$}{JC|Vo@xuraӦqm4򔱊 $l@G x, BgQ.XE^jᔭծZ6eJ Duyt1i8kuV:Π)xʨG>=*$aj{Fcsª YP b7IG25lDinh밤Z*G/nkVCx^521VA`?~Rn#JE)Oqm9…6yPb,x!)u&̕x)J$? i""8Rȸs۵H+ŝ QG&P\ܓ^GQ"D2x@߬(z-'4c>M WCS イ5D̸ӎqO& >ځ]DFK`=փG3N YŰtsa=ST٧L#=EEcDSai:s$ hEb !(IH7@~aVhES},B4t׀闽24[V@i͒e|yFk@!P3@lD !a/ !x:&91 4_:PYh]WIM'wG6eaI4I`D,OFggò6~)BiY N<~jD7 $hdoڊd9x}9\{r{WtdB,:^\GI \鴛d͟|'nX=dzqCu% 7;VcqJ& ˢ ]mRAI_ԓ~H!BfF\GX[nV"JI8ꐂgCxDW$:`ߔKVt=dJ`qrS7)GQ3]. 8CTPZI9=}pGh5y8 ̠SbyJ\0T::ї0,iyl vœzF=lT'>aY "K^ĨH=NF_j+ :^wT2ׂXd5IIXSq*8oBڕ8Q5Q? o5Mȼ%σ,.HNjzWvF~ oŀwaN(3q0FY̋}̕Q aCf4d\HUcլTץ*# 3Y#&Cx;"XzUlW&"Tf"./bIqN_ ?jr$K %U.ϻ;?jB:ȢFua;=piwzva5MϽV$EHb(IgR<}!̗!@-HZ` _ "ai=Ji1IsjFL]LO8yua8a @\u]R8/i  ,y60aDx#@( OǢzp| a؇ 6YV,W2Q9MOA$EMd d3Y۲ ӔȧNE ]"V.׵>?Ls)~|LO)'yLDw䠦W-UqӔY5 )lCU=i $M1uq٨|PtD"aϐX&ȱB3p;8t`FBeil#H"ij֊;@y!mm$7̮ȜL~%~#r?j9ElLLx$= 8؁G_c&c l?Т?X_Bv-{WsZ=O>T#[AJL 4wM9DV3aLMН1*9\) };ẽ`J"gp{_w]Y&٩Y;M4Ph--P/>oq,s)ٞ!7cTr3So&1t2ξPK<֊vc.Szl ;o՞RXBXUdI,0!6T&COLiޗ1dq!`/6-(rFA5/5d xNhN,iQ7"/p^3B:gRkf.' k:g1_w_yzyNwS;XPqȭi>z&c[ψ:|M̩jTMo@Z**Rs~zA vjh 6JkO>Ɣ僲T#(A3Um!:0p ՑY) CTҲہh5#X?=w; w?:xGw_mx"^oj endstream endobj 164 0 obj [/ICCBased 178 0 R] endobj 5 0 obj <> endobj 70 0 obj <> endobj 128 0 obj [/View/Design] endobj 129 0 obj <>>> endobj 63 0 obj [/View/Design] endobj 64 0 obj <>>> endobj 136 0 obj [135 0 R] endobj 199 0 obj <> endobj xref 0 200 0000000004 65535 f 0000000016 00000 n 0000000176 00000 n 0000014536 00000 n 0000000006 00000 f 0000093877 00000 n 0000000007 00000 f 0000000008 00000 f 0000000009 00000 f 0000000010 00000 f 0000000011 00000 f 0000000012 00000 f 0000000013 00000 f 0000000014 00000 f 0000000015 00000 f 0000000016 00000 f 0000000017 00000 f 0000000018 00000 f 0000000019 00000 f 0000000020 00000 f 0000000021 00000 f 0000000022 00000 f 0000000023 00000 f 0000000024 00000 f 0000000025 00000 f 0000000026 00000 f 0000000027 00000 f 0000000028 00000 f 0000000029 00000 f 0000000030 00000 f 0000000031 00000 f 0000000032 00000 f 0000000033 00000 f 0000000034 00000 f 0000000035 00000 f 0000000036 00000 f 0000000037 00000 f 0000000038 00000 f 0000000039 00000 f 0000000040 00000 f 0000000041 00000 f 0000000042 00000 f 0000000043 00000 f 0000000044 00000 f 0000000045 00000 f 0000000046 00000 f 0000000047 00000 f 0000000048 00000 f 0000000049 00000 f 0000000050 00000 f 0000000051 00000 f 0000000052 00000 f 0000000053 00000 f 0000000054 00000 f 0000000055 00000 f 0000000056 00000 f 0000000057 00000 f 0000000058 00000 f 0000000059 00000 f 0000000060 00000 f 0000000061 00000 f 0000000062 00000 f 0000000065 00000 f 0000094138 00000 n 0000094169 00000 n 0000000066 00000 f 0000000067 00000 f 0000000068 00000 f 0000000069 00000 f 0000000072 00000 f 0000093947 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000094020 00000 n 0000094052 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000038749 00000 n 0000094254 00000 n 0000014589 00000 n 0000015239 00000 n 0000025464 00000 n 0000039313 00000 n 0000032785 00000 n 0000032671 00000 n 0000038941 00000 n 0000039065 00000 n 0000039189 00000 n 0000035855 00000 n 0000036464 00000 n 0000036610 00000 n 0000036764 00000 n 0000036910 00000 n 0000037056 00000 n 0000027179 00000 n 0000027758 00000 n 0000028305 00000 n 0000028726 00000 n 0000029205 00000 n 0000029808 00000 n 0000030333 00000 n 0000030872 00000 n 0000031295 00000 n 0000031686 00000 n 0000032203 00000 n 0000025528 00000 n 0000093840 00000 n 0000026615 00000 n 0000026665 00000 n 0000036400 00000 n 0000036336 00000 n 0000036272 00000 n 0000036208 00000 n 0000035791 00000 n 0000035727 00000 n 0000035663 00000 n 0000035599 00000 n 0000035535 00000 n 0000035471 00000 n 0000032607 00000 n 0000032822 00000 n 0000036001 00000 n 0000036098 00000 n 0000038628 00000 n 0000038422 00000 n 0000038277 00000 n 0000037865 00000 n 0000037202 00000 n 0000037373 00000 n 0000037498 00000 n 0000037623 00000 n 0000037746 00000 n 0000038035 00000 n 0000038156 00000 n 0000038519 00000 n 0000038823 00000 n 0000038855 00000 n 0000039389 00000 n 0000039567 00000 n 0000040546 00000 n 0000045828 00000 n 0000094281 00000 n trailer <]>> startxref 94449 %%EOF metrics-3.2.5/docs/pom.xml000066400000000000000000000053321315671014200154370ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 docs Metrics Documentation true true true ${basedir}/source true org.codehaus.mojo build-helper-maven-plugin 1.9.1 parse-version initialize parse-version org.apache.maven.plugins maven-resources-plugin process-resources initialize resources @ ${project.build.directory}/source false kr.motd.maven sphinx-maven-plugin ${project.build.directory}/source metrics-3.2.5/docs/source/000077500000000000000000000000001315671014200154175ustar00rootroot00000000000000metrics-3.2.5/docs/source/_static/000077500000000000000000000000001315671014200170455ustar00rootroot00000000000000metrics-3.2.5/docs/source/_static/metrics-hat.png000066400000000000000000000646761315671014200220160ustar00rootroot00000000000000PNG  IHDRXtEXtSoftwareAdobe ImageReadyqe<i`IDATx$Wu/й{zrޝfmTDB M c6|m}?>mpc@ ei%mޝgzsW}眪N3=}WWS]]}9B[nZ Vou[Rovr]w:|R<~>/wث?8:S>ϐN1ǯy;N|rz۶mXVMw>IK}Q˱ H=XH܂+XrL+ @r;V8%!5**vys|M+I8o'M=|) 0M>.%x>_za+8ց['`pއjU_Cԯ~9&v4FO9 JRz|?xM$1,% pV}~kRI7/86S6#ԿsmS+ _[n1\]Qou"^c ܨ:8mMM[/un%RRoW@XՉk}jOTW/5.>+,ZOΨ0߄ƶsmHAs 8wqv2x:p$6 ?D69|E<^EޙϞ:@Jjح:9TT4]CLkM P-PH ^(ڗ\OsXd~~ԬsXȠoq虆z$nm ѥ ]2Q dͼ`uP ^߲ƮZ\U0d vO]nyLI((Zo ;uJ#fѠRo 5|\`) 뵃4 l bS^W#$G݂Uo~z9KGN|!3ne>w6[AISꖲ:@\nsx W8BYq?TQ? O|uGa k ʏ@"QR SКwgF6sDI*Im;]t}pK5x4C39Ѧ #fC(nDKO0VFG)H3> ~Hf"k w]~aP-)o  @T/ y.8]u+ٕNwU)Ҥzު 3ЯwޕɻؙlAG.kEY>?D0#O1^ 5C"I#<(cuJo"z'ukт0cz 0_ "LNݩi۠ٙExgpnuV R]wfދuHt<<69$S!?ߧ{>~:-tT &M1:@ΰ+?M\z'Z./M7;{<=p?G8t#$]Ku"@I?}"trlGb=ƺzUٵPm}?V#GheqM-~~|İ81pb` [+NjVGO7" f~ 4:0,Έ|pGĽ yM0 uk*{.7+.J\zF8r`#* Gwh:3۾oC(@/ۇ{wc>a/: IfWIf I#ܽpv6)۩[ g[X-Q7pSe"pIAS |_GKcft hw ={% 2v< 1BU0AwhJT440aG-GQ,zZrhғ!'_-b~꿾3~r6׀#s(̞l`T/oM~.o>Dv'p8.Dsr1[n؁ȖvF |xچuư07G@yrت-l\[i}/B 6BQu\pRq~iolZnғP5}&`§ѣfhn[oȡ3q2;D8#AF E]#mb력FU )=k_t)hhAa/s}ĢA /cyt_~v+n%.b#UtFCsl7a hN1^u#x?7kZΑ_:*}wwg(p`מ͈ ̑jG/nn@{:W'”&b榑 b{Oy{չG k&? .FnӋ=z޹~!}Sox/0$=I:L_%+Uq.UZk.P;M.R8`CS˟o#L59L!Iq}:&!jYكo$r>YQ p-][s _kJmD(͌gL9y#ޞoPTRu\tѳ)m5$5vkIS2`rM|b3Ea`Hf{97ݓu6pTI+UȈu^[C8qz4ވ xF9qN9 ږal{vt ;9Z N\sD82W NH29c^;5jOJ]l,eihoTH.P=A76 @o56ZR\a4/ x;f$@ݤSPN% XVgn:$cu 6KFL=69c@j3gŸ h9KW[@k/w%V :]F`5pv~'95[r!7tG{d*S\8uR&i $ Dzbمi*$Y4 ٤[c&p^/_ߧ:@^DccT+WϹJ Gc65gzx'15<,ibB4!a'0̐I V 3 9rոl|~8Ot]A  2Hr)>;׽ G+Ԫ6C4 G7ɣG /+G6  ktz3EF]XtP<'DXT`N$YPJh8ɂT>-uZ& ~Nbw밈UU11B;K]ytn@>ԁ`z.YbJCw#M"]aڅ"Ͷm#a'G.Ǣ#Rj?G:ZTt?0..."IDXR5 )4 –qkcVnChhCt'߰ږ[$"$$Ak8 CgFӰ|-\aF@kK3@oH(\;{L7-Wʳ!'x @6söXJb人4i,$qr0|≜/֖V ޡI*W7Ϣ5PP]]&yW+5IRCߺmo\pƧŊehAW T2tn!_ KOT*~f2ر/8Mdh2 rYs]I dlx |>𴧞| ػo~IRtlX"3<4 T=iG4W$@A\"~>).MxhH@1dP#.O.V\:;0xQK@eda%% YZډ4xaaGg E7Redh߽wAʸ{NR,p[VQ90q"Ռ+\lJKAQ=щ'033MOe!u,}B+Ӕ7ax'?dťw o_#t<;m+O30>#5<jbǧgƈkdQUMT#ۻ)Cay䫺kn z{{J&gѝ]y*psYe5S~ΒyO=ppGA$1 02xeyiNs QX gg)97[8Nn{EL>í#!*PC3|[qMG+U֍\/t rƺ2FcGxx {H*WQ2Kh[k{Ƀ3U=zYTSө4sb1ai̪RƒkEǓ;D#_ɩb0v4§;gDC7{+$6-35MR>^BixyL~|E~З$Ibtwlœ`27pA(:OC5&fbMW]&ʼngn#|tΊ[>6VZYUhqSrM((ٗQ[Nټ|Cs.Kr1ǢX& \(XLD1M Z¯ ? l%ap. 4hS3/8aթͷaڹS䳩I|% .vA"@`1L6m݅UCJsWpXq#E[Ҥi潷n$ D]0:1T`ЋI%s6]ſ8&]bH'08pRV6b kFPYx/8YDԙZ li'sg9+>IM  }+46:+2(W\ªxCV-lfA;ZM8dr Gf(tJk 6F0Kz-G+rXU|Í 5,on8o2&'/i"af%F 8Up79_?ɽ $+Lz Ų_YъX:qf7>;n=w,ba /y#I`a S Gαq;+zȐEabt9"<]+~I#v$%J.zE^Dn$#J٢t4V1]|@L֙޲C7]rY>nj#`Zs{h.,ٻϕ& X.|m[.c-WJbǜK ,47@Q)]+|eR~4jin*+pfkQ ;@q8&&owWcj N2 M<}i'j.99^mצ  ȁ7 wd֬s\RR.@4a;xI6 CҰ,Aj"[`j"YΟ -pFg-@j`U) iӗ$@wNPYo"`X^MKʥS8DB]qοQK%82 T*ZU lKL *S淙ԄUU9CΣpv7@Y^Ł;cl2/Q6N 8$lb-ʵ{E#gH*@G$"^ ̼UTKMi2܃K% 5)_?+'ܥ Kvg%m~V4Ny݂_!qfHcarHflG5)+Т'})^Jҫ *qw)kUޗko2mY|tUc:Gt6:@.|I!,t9)SвH!9DYR}Q5u1ՇxX!8ɤgF"xvl=6oeA2(sj JZvg(jIĢA_|C(G1dfc`bfRd]J}b wE_`QP~ X%K ?78M f#GFzcJu$SqsmT4m;cCEoC{o6D3nՔcm^e̐N0x䀛V-/4@oLG wk82#8#C+ʬ !KheKH`Vۈ]ױ gB1Htb6Wތ$"kv,2ц&$ Q𸁓sU.WyjVwCoDjfy,E84H64!|K.wg" U#i$eU-[԰ٞ2v>өX\>s06=(:6e\i[ۧM~K-+E.w"p-|MW΂eό`n1|eOsY?c6޴}G 8vd-qH7h&{Y^֡nI8?{P!RZb TA7X & i"ٽlV'BUjj\r #${k P+Yy/<\&áqe6vdl ]_ʺ7 ]x?O=Ojc ޼z'}*օoD"U~%u$Q)q&Rĕ$:mͭktQh=$&A<`a:`b[c.5)ҔV{lU1"sΏ.%_xu3K9+R 砱"Ԅv8F~F:Fp{mF_L$096h@b [ặ}v/>h,,f7-}]k2~*.܃b>s0nI1J780=3-Lꢹ55``F"hy3xK+ UaR5ͻjII X-3oEJ*lLLMWn-@f`/f]jtNbs`hFQB2Ļ66{~2Ie׃X,[79]Wo#=ɑwb>#&;']xկDYQU ,`RKmMJw|kA֓ ԸV ҂{*3oKVکTSpG'qݖn}'rFj~QQ^@M@(LcÆv8 k 6 s$1B `ޱP|` [Ա;O>sM7 ǎ55y{կDp|CJn:[g `H娧a T7xHaq',FBH"[6 mW5ȆVK ܪ!d__{#pZou+v=Lo!qэ$OؿΕ&y6wf<8zbL }{fkK#"0vmaznpIq[o{a_g__ ? 0|X5²bX:2YE>g&oD'I:)' wܴ [dTe|ja- *SiWI@}CqVc;"KBZ~AOcilG= gfBjCNV#Z2ر_hdsU|IʙȓT i"Cxm7b7;_F8jLImQL=F 'cUޘNe"/cIulGC1a) 3=xXtytRQ2 E-[x$6ǩUYtfڻ*J#wЛZii.&"4^8)L8KpXV<~>ѐݝhjl$ipjO\uW^ ]R]k%4 yY8)hn4wG i80lS[@zڱ`j4tYhZ\n Bw~ܿcXS;k:@.vx@w\ :é[U*reSP [oz8 YRTmjkkVqWYfEUYȡd@>jn|ض%@OjNꆩ‚ܬ2ھO+d\DgϦpr,ͻ0̽6[~<gX?^-UizP9q۷c]gR 6a6C\~YOJֹp1G{gH|::I"Xu%]t#^69$BBpywS/oB^4\aI v  ꔶ.3^ICk.`H~ŀ2VwIʐzHң9=;7$spxRmb~ZqAm7 uM.$z_ 5nu\,8 #Fk,U}m*Il5IE2Wx.Up_$ [q:5@BA1LXF#zz?G'鰿(i >R'#Dݭ")q=sC#WPdH/p傎C'?Dc:\ :^,|khcH~탰Z`,!n{;mNYbءQ ߓs^ryrR9~\ kW(M}|&nE5̛-f#+j#pU]qY*, Wъ•*{}D}RUŊmMm-NHe7 AaGhUa:~>0ߴ l"l!Sz7,Ήz^G֯9((C3ÞR;+f"@MizzmmTdJj< ا_f\HOχZRYqimw )ruEwkr1)ZH=ք@SMt_ QAV0f ٹL͢fFI=><cr+(]mIOrId,GOEsT>d%m ` ii f ,A$UX2=86h4398?y(>4 W#?,n6/"Zc aTqNFυp/A8<,]g$EkKe,sTr4\TsX;q顃HZY$~H(%#C>E5 0S ĈAMd6l^,RU% ^%@25~,C*ԘR0PSgg96Ok_)pX@(& HXjgv]n4 ?#iBUHMM͈Fc'H xT9ܥfފAKIRI&bbk ضŀp(0:x 7\C~]@#n,:хg$%s|uZ^ v^87ܽ.&!~CXR4u7`%WGnK[ ]>> sY*Hdlink:r8 d ™ RH^?X,UVRQ2+ܪ%*Ro9ԄuZayw C6Z~*6 ]a߮50ؼ.,\9Ea TVAו$ֲ#O%eWDoOOq9sj z+0S!Hzh=G{A;v ==/$r#ro}zl\)ϽN$ޑ+D~1P]tnmQUߞ*K6Њ.39s/F&U8[ =Yղ4bΫ1>w(Ŷo=6B]RS4>lq{ьۗ[ȼs>C**YFX5ՀԖ~p;:eW/ bקU6ZM*˒J;0=;ȈpAJc<^ށaӽ**̉~)3BȴA@,l&4?$!:,Dq^ĺ=q/{wC9xp Q26xo8fN<ʼncm_0k{|;s('X-.GQ?v\@ثvzά$C*OchhX,K Fr[oG{;v_K''3Ocȓj9TyV&:#S_t|}r4@*X Φu)H=?f"cn~v ,.,H1w)e nDz|擟y%=EL\#K<k-[EBvwu >z'0==-iE Cbgr3oY*WwWKHT xa@b$4U7Ū% z4B‰ĕ8/cDSaSvH4 $ 5Eꛥ03qt$g01^;Ȁmrr\xyQ1JRD(^{׮v^.h}oI3v\I @*T{[cjf bRࡑ:G#*QJ9*T/ϋV"Hknbf z"k9'_-Ѓ O_}˻*qG(5HމqM׷w?Ejo;'{Gkp]w ϫĤ_䞾EIrY_M+|:uZ?N?-%|e>R5_cXb2^s62P %&K)+jT_jR xT)E<ET!ٴCs!4˙9[iy3FvBe `vG01u{g&9lYϼ [ ÆI#(E ) !7Ɗٜ+_,D<0.s~ a8yu8٧mRr $6FozL RllYCgWZ$U!)4 Oj(/x>q{kee)(Mhoo#B lŤ`#-(+|`5CҤ 83}t?Z[XM Z/oO}x2^VȢŇ&s䜰4arH'5|~&(.<'<^ t"UaVkpD#^dN?ΚOvtt7 |s {9Fu"n[QT7qMϵoeiBzxQu۶E/_YHzzzpA~~*z)sK꾰h` (0KPi]@?rhŨ[ݔ]\Pr{fժƂ g'if#BZ>$ѣEG C!C=ѱV~f߷: }, xp 5+ܤ\K̳6EٓO%P9>9],],a42PWc;'\{OK xmiiW\fWL&3G4N)zJ=J{#>B->Or̖=2<\qӜjTmnOTVQ,{z2hsv4l}JRъJXVS- \:$5w@xQ PqQK(ic+ T*#Rrɫi檿O7^bDwH$;9׋k5O\OX@m?Nv B!rUW0c5h6lJR۩.A\"17τp:~$}GH?=0p'HJIqk+Xn]V7[{}9ú3{qxsKg!G[ͫ8I5WųAr]2²*esC+s#]Y 6sX0f2o6[U&Sg-Qؚ;k!ĭ|=(i m}}}ߣ{rOXseYР}L&eё|g.P` Q4X[Ėٟak=?D?ƍWy4G K'Ӓa'd:$'ynjKJ_L7b r+'Y+M]/̊I SE5LraUZ>Ŭ-%9'=r􌀄_cN1#<9w"xXqڮT(f=9@(HխGz+w E bYkpINYu4|ýyˍeVp*|Syn&~UU,,,l#l~}[xс 6&AD<#$^vRT<O:U'c;\TTY+[x5uϷE7/U]?9J.%Z^ܨZUZW6]xey㙪{x1! я׼濵_lŢy _׼pQ} ݱtC8G +!BRmvLON0:,)~c8=B`xG$'rx*H:!_̯ .ɷz1V8]tduhJp 5x:Nt y3x``KrEX )DgH"bTT5^+ў,H)6itR 2.VVJp>Fwt7os J?^ٙQm2س~rनk P ,+Ie ʸdQ^4[3[\@|m!҃s*$H*H;TPU86%SY_JU]Շup5'bs.v>!1-\$aX&.'qT>[kdz(\\+{Ş|vMyz k%\z{KpxdKN7p@E HE$~GHܽqRdeXkr\VR*kɠ-KV8!).*ryҕ:Eb,aYGtx)Wc箝#2 P'N=ݽ=RHpKڒؽ%Euȝ1TŽ4j"PuU׼۴K nezR5 el]Y\KdGwq[-nLl5Dk :ϧuvȄU= wM$HYrJREʯJ&R-qp }4հ=J=GСCgzzב *79M"ER v*} iR'9Ԫ)"gk_m|SXO%Z}]H%M5R%nAEzo7I gAZߡl60=9sGLg'\QM|$AiU*XQ#V*ەN2s;@2 M& |L/ī*_%T US\N^RatQVjp{9[oxcGSkh\+)yȫUUhW<2a=h6!HF/dvMrEtGқ57HEeFGGLށ63 h;{Ijt$)W+s"Q(d QI(M~Iy,rb8!w; gOw,r LK`R87\5iβ\ 2>ieb9Sh(]Սvy8ab|BƆ Dy %Z~Mqw_))6shD>lk[1wW &~]U@ =&f.yzL8!Ybk#_dkph mR_K7\Uo%e|yu^RW~^ .Z XJtoww˅ ֓$.{d1";v4IQI5v Kt|bk?HMv^4 ^ﳊ{ׂ7qm"1?/>6st.綰X"sK =kl`ɓ;i& >dQr协gYEY凉+tcvJ+VX<^yލ"PuniKfSVT8;KX&Ie  }naeԮpt(<$9~0>>u$X!F[k{) : /.-0;.P ֡YmT¬F(oe >ٳzӡF-^WC%hV3@Ԫ$ɀ2 ߱rBX钟w \ֲ pih] 椥WJBRC??QAy۸Zea+% Į^w>#43[q6mw-F;@W3tܷsё{'B}nFzZf,V}R[ >e9,C16>& DYXoUȧ(Xk6@mȨt[{2ѫ3@wvgŲIv\گ W0x-4KE@d3u#XCȒ,!A4%.R94t WU ߦqrZ fZJJ*.Xĺ!O 5j/Nk8ot25@E=/@}XzE%|ক_KET ,j50UQn;?R+p,*RY 387cWf>^`JSgb.[r?s߼rEI=8L6Ѡ$ 1,֭,j@ޠup <L:Oss!Y-%)0%x!U)g*[p_ (+~ ,:0s ԽB/NvЧ5 .T17r`],Qb3K1;}*U[Okډ+\cYAeev,Cq@ٹc' ZOq;H!d.5=Ru̒FC NuzE1=4"'7mN=[ PS/YƦ^:_9w@B%sp_GcVܵ޴aQD~BBMR2z hYU^9kzY7N9o\,-5Z[},F1+#`q3SќMfcZwWE%ҍte?+R7*JSdBcqPZCR*STY&8PNk"snY L("nRғyH)HQ0wS*gϪ>\/̘Yn4%f !w9/&uЮ{h()D tHΩ`@i@sla*e`V?iG:$b=T%RwJK+IĕKEi{՝*ݩ9b͛n`xu.Fgt\*,e Wٮ\Ǿ&J}zh;kcݿOM=$i^,hVT/ÞJA!'\Y0& kɝ-^ w=OVl: ҾNǕtߟVq˺!]NR=FKTJuAfgf (3ngYkV ˂#"VZ:U=L3VЬZ͈.Hٿ;A XysGi;S}ѥOY ]=Q0M/hTKp+#SLkR̩2WާaH"=ur{ {%- if links -%} {%- if links[0][0] %}{% endif -%} {{ firstname|e }} {%- if links[0][0] %}{% endif -%} {%- for ismain, link in links[1:] -%} , {% if ismain %}{% endif -%} [{{ loop.index }}] {%- if ismain %}{% endif -%} {%- endfor %} {%- else %} {{ firstname|e }} {%- endif %} {% endmacro %} {% extends "layout.html" %} {% set title = _('Index') %} {% block body %}

{{ _('Index') }}

{% for key, dummy in genindexentries -%} {{ key }} {% if not loop.last %}| {% endif %} {%- endfor %}
{%- for key, entries in genindexentries %}

{{ key }}

{%- for column in entries|slice(2) if column %} {%- endfor %}
{%- for entryname, (links, subitems) in column %} {{ indexentries(entryname, links) }} {%- if subitems %}
{%- for subentryname, subentrylinks in subitems %} {{ indexentries(subentryname, subentrylinks) }} {%- endfor %}
{%- endif -%} {%- endfor %}
{% endfor %} {% endblock %} {% block sidebarrel %} {% if split_index %}

{{ _('Index') }}

{% for key, dummy in genindexentries -%} {{ key }} {% if not loop.last %}| {% endif %} {%- endfor %}

{{ _('Full index on one page') }}

{% endif %} {{ super() }} {% endblock %} metrics-3.2.5/docs/source/_themes/metrics/layout.html000066400000000000000000000140751315671014200227230ustar00rootroot00000000000000 {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} {%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and (sidebars != []) %} {%- set url_root = pathto('', 1) %} {# XXX necessary? #} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} {%- if not embedded and docstitle %} {%- set titlesuffix = " | "|safe + docstitle|e %} {%- else %} {%- set titlesuffix = "" %} {%- endif %} {{ title|striptags|e }}{{ titlesuffix }} {%- for cssfile in css_files %} {%- endfor %} {%- if favicon %} {%- endif %} {%- block linktags %} {%- if parents %} {%- endif %} {%- if next %} {%- endif %} {%- if prev %} {%- endif %} {%- endblock %} Fork me on GitHub
{%- block sidebar %} {%- if title != "Home" %} {%- endif %} {%- endblock %}
{% block body %} {% endblock %}
{%- if title == "Home" -%}
YourKit is kindly supporting the Metrics project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products: YourKit Java Profiler and YourKit .NET Profiler.
{%- endif -%}

{%- if show_copyright %} {%- if hasdoc('copyright') %} {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} {%- else %} {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} {%- endif %} {%- endif %} {%- if last_updated %} {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} {%- endif %} {%- if show_sphinx %} {% trans sphinx_version=sphinx_version|e %}Created using Sphinx {{ sphinx_version }}.{% endtrans %} {%- endif %}

Dropwizard Metrics v{{ release }}

metrics-3.2.5/docs/source/_themes/metrics/less/000077500000000000000000000000001315671014200214575ustar00rootroot00000000000000metrics-3.2.5/docs/source/_themes/metrics/less/accordion.less000066400000000000000000000007771315671014200243230ustar00rootroot00000000000000// ACCORDION // --------- // Parent container .accordion { margin-bottom: @baseLineHeight; } // Group == heading + body .accordion-group { margin-bottom: 2px; border: 1px solid #e5e5e5; .border-radius(4px); } .accordion-heading { border-bottom: 0; } .accordion-heading .accordion-toggle { display: block; padding: 8px 15px; } // Inner needs the styles because you can't animate properly with any styles on the element .accordion-inner { padding: 9px 15px; border-top: 1px solid #e5e5e5; } metrics-3.2.5/docs/source/_themes/metrics/less/alerts.less000066400000000000000000000023041315671014200236400ustar00rootroot00000000000000// ALERT STYLES // ------------ // Base alert styles .alert { padding: 8px 35px 8px 14px; margin-bottom: @baseLineHeight; text-shadow: 0 1px 0 rgba(255,255,255,.5); background-color: @warningBackground; border: 1px solid @warningBorder; .border-radius(4px); } .alert, .alert-heading { color: @warningText; } // Adjust close link position .alert .close { position: relative; top: -2px; right: -21px; line-height: 18px; } // Alternate styles // ---------------- .alert-success { background-color: @successBackground; border-color: @successBorder; } .alert-success, .alert-success .alert-heading { color: @successText; } .alert-danger, .alert-error { background-color: @errorBackground; border-color: @errorBorder; } .alert-danger, .alert-error, .alert-danger .alert-heading, .alert-error .alert-heading { color: @errorText; } .alert-info { background-color: @infoBackground; border-color: @infoBorder; } .alert-info, .alert-info .alert-heading { color: @infoText; } // Block alerts // ------------------------ .alert-block { padding-top: 14px; padding-bottom: 14px; } .alert-block > p, .alert-block > ul { margin-bottom: 0; } .alert-block p + p { margin-top: 5px; } metrics-3.2.5/docs/source/_themes/metrics/less/bootstrap.less000066400000000000000000000026641315671014200243740ustar00rootroot00000000000000/*! * Bootstrap v2.0.0 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ // CSS Reset @import "reset.less"; // Core variables and mixins @import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "mixins.less"; // Grid system and page structure @import "scaffolding.less"; @import "grid.less"; @import "layouts.less"; // Base CSS @import "type.less"; @import "code.less"; @import "forms.less"; @import "tables.less"; // Components: common @import "sprites.less"; @import "dropdowns.less"; @import "wells.less"; @import "component-animations.less"; @import "close.less"; // Components: Buttons & Alerts @import "buttons.less"; @import "button-groups.less"; @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less // Components: Nav @import "navs.less"; @import "navbar.less"; @import "breadcrumbs.less"; @import "pagination.less"; @import "pager.less"; // Components: Popovers @import "modals.less"; @import "tooltip.less"; @import "popovers.less"; // Components: Misc @import "thumbnails.less"; @import "labels.less"; @import "progress-bars.less"; @import "accordion.less"; @import "carousel.less"; @import "hero-unit.less"; // Utility classes @import "utilities.less"; // Has to be last to override when necessary metrics-3.2.5/docs/source/_themes/metrics/less/breadcrumbs.less000066400000000000000000000006101315671014200246350ustar00rootroot00000000000000// BREADCRUMBS // ----------- .breadcrumb { padding: 7px 14px; margin: 0 0 @baseLineHeight; #gradient > .vertical(@white, #f5f5f5); border: 1px solid #ddd; .border-radius(3px); .box-shadow(inset 0 1px 0 @white); li { display: inline; text-shadow: 0 1px 0 @white; } .divider { padding: 0 5px; color: @grayLight; } .active a { color: @grayDark; } } metrics-3.2.5/docs/source/_themes/metrics/less/button-groups.less000066400000000000000000000070361315671014200252050ustar00rootroot00000000000000// BUTTON GROUPS // ------------- // Make the div behave like a button .btn-group { position: relative; .clearfix(); // clears the floated buttons .ie7-restore-left-whitespace(); } // Space out series of button groups .btn-group + .btn-group { margin-left: 5px; } // Optional: Group multiple button groups together for a toolbar .btn-toolbar { margin-top: @baseLineHeight / 2; margin-bottom: @baseLineHeight / 2; .btn-group { display: inline-block; .ie7-inline-block(); } } // Float them, remove border radius, then re-add to first and last elements .btn-group .btn { position: relative; float: left; margin-left: -1px; .border-radius(0); } // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match .btn-group .btn:first-child { margin-left: 0; -webkit-border-top-left-radius: 4px; -moz-border-radius-topleft: 4px; border-top-left-radius: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomleft: 4px; border-bottom-left-radius: 4px; } .btn-group .btn:last-child, .btn-group .dropdown-toggle { -webkit-border-top-right-radius: 4px; -moz-border-radius-topright: 4px; border-top-right-radius: 4px; -webkit-border-bottom-right-radius: 4px; -moz-border-radius-bottomright: 4px; border-bottom-right-radius: 4px; } // Reset corners for large buttons .btn-group .btn.large:first-child { margin-left: 0; -webkit-border-top-left-radius: 6px; -moz-border-radius-topleft: 6px; border-top-left-radius: 6px; -webkit-border-bottom-left-radius: 6px; -moz-border-radius-bottomleft: 6px; border-bottom-left-radius: 6px; } .btn-group .btn.large:last-child, .btn-group .large.dropdown-toggle { -webkit-border-top-right-radius: 6px; -moz-border-radius-topright: 6px; border-top-right-radius: 6px; -webkit-border-bottom-right-radius: 6px; -moz-border-radius-bottomright: 6px; border-bottom-right-radius: 6px; } // On hover/focus/active, bring the proper btn to front .btn-group .btn:hover, .btn-group .btn:focus, .btn-group .btn:active, .btn-group .btn.active { z-index: 2; } // On active and open, don't show outline .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } // Split button dropdowns // ---------------------- // Give the line between buttons some depth .btn-group .dropdown-toggle { padding-left: 8px; padding-right: 8px; @shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); .box-shadow(@shadow); *padding-top: 5px; *padding-bottom: 5px; } .btn-group.open { // IE7's z-index only goes to the nearest positioned ancestor, which would // make the menu appear below buttons that appeared later on the page *z-index: @zindexDropdown; // Reposition menu on open and round all corners .dropdown-menu { display: block; margin-top: 1px; .border-radius(5px); } .dropdown-toggle { background-image: none; @shadow: inset 0 1px 6px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); .box-shadow(@shadow); } } // Reposition the caret .btn .caret { margin-top: 7px; margin-left: 0; } .btn:hover .caret, .open.btn-group .caret { .opacity(100); } // Account for other colors .btn-primary, .btn-danger, .btn-info, .btn-success { .caret { border-top-color: @white; .opacity(75); } } // Small button dropdowns .btn-small .caret { margin-top: 4px; } metrics-3.2.5/docs/source/_themes/metrics/less/buttons.less000066400000000000000000000066141315671014200240540ustar00rootroot00000000000000// BUTTON STYLES // ------------- // Base styles // -------------------------------------------------- // Core .btn { display: inline-block; padding: 4px 10px 4px; font-size: @baseFontSize; line-height: @baseLineHeight; color: @grayDark; text-align: center; text-shadow: 0 1px 1px rgba(255,255,255,.75); #gradient > .vertical-three-colors(@white, @white, 25%, darken(@white, 10%)); // Don't use .gradientbar() here since it does a three-color gradient border: 1px solid #ccc; border-bottom-color: #bbb; .border-radius(4px); @shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); .box-shadow(@shadow); cursor: pointer; // Give IE7 some love .ie7-restore-left-whitespace(); } // Hover state .btn:hover { color: @grayDark; text-decoration: none; background-color: darken(@white, 10%); background-position: 0 -15px; // transition is only when going to hover, otherwise the background // behind the gradient (there for IE<=9 fallback) gets mismatched .transition(background-position .1s linear); } // Focus state for keyboard and accessibility .btn:focus { .tab-focus(); } // Active state .btn.active, .btn:active { background-image: none; @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); .box-shadow(@shadow); background-color: darken(@white, 10%); background-color: darken(@white, 15%) e("\9"); color: rgba(0,0,0,.5); outline: 0; } // Disabled state .btn.disabled, .btn[disabled] { cursor: default; background-image: none; background-color: darken(@white, 10%); .opacity(65); .box-shadow(none); } // Button Sizes // -------------------------------------------------- // Large .btn-large { padding: 9px 14px; font-size: @baseFontSize + 2px; line-height: normal; .border-radius(5px); } .btn-large .icon { margin-top: 1px; } // Small .btn-small { padding: 5px 9px; font-size: @baseFontSize - 2px; line-height: @baseLineHeight - 2px; } .btn-small .icon { margin-top: -1px; } // Alternate buttons // -------------------------------------------------- // Set text color // ------------------------- .btn-primary, .btn-primary:hover, .btn-warning, .btn-warning:hover, .btn-danger, .btn-danger:hover, .btn-success, .btn-success:hover, .btn-info, .btn-info:hover { text-shadow: 0 -1px 0 rgba(0,0,0,.25); color: @white } // Provide *some* extra contrast for those who can get it .btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active { color: rgba(255,255,255,.75); } // Set the backgrounds // ------------------------- .btn-primary { .buttonBackground(@primaryButtonBackground, spin(@primaryButtonBackground, 20)); } // Warning appears are orange .btn-warning { .buttonBackground(lighten(@orange, 15%), @orange); } // Danger and error appear as red .btn-danger { .buttonBackground(#ee5f5b, #bd362f); } // Success appears as green .btn-success { .buttonBackground(#62c462, #51a351); } // Info appears as a neutral blue .btn-info { .buttonBackground(#5bc0de, #2f96b4); } // Cross-browser Jank // -------------------------------------------------- button.btn, input[type="submit"].btn { &::-moz-focus-inner { padding: 0; border: 0; } // IE7 has some default padding on button controls *padding-top: 2px; *padding-bottom: 2px; &.large { *padding-top: 7px; *padding-bottom: 7px; } &.small { *padding-top: 3px; *padding-bottom: 3px; } } metrics-3.2.5/docs/source/_themes/metrics/less/carousel.less000066400000000000000000000034121315671014200241640ustar00rootroot00000000000000// CAROUSEL // -------- .carousel { position: relative; margin-bottom: @baseLineHeight; line-height: 1; } .carousel-inner { overflow: hidden; width: 100%; position: relative; } .carousel { .item { display: none; position: relative; .transition(.6s ease-in-out left); } // Account for jankitude on images .item > img { display: block; line-height: 1; } .active, .next, .prev { display: block; } .active { left: 0; } .next, .prev { position: absolute; top: 0; width: 100%; } .next { left: 100%; } .prev { left: -100%; } .next.left, .prev.right { left: 0; } .active.left { left: -100%; } .active.right { left: 100%; } } // Left/right controls for nav // --------------------------- .carousel-control { position: absolute; top: 40%; left: 15px; width: 40px; height: 40px; margin-top: -20px; font-size: 60px; font-weight: 100; line-height: 30px; color: @white; text-align: center; background: @grayDarker; border: 3px solid @white; .border-radius(23px); .opacity(50); // we can't have this transition here // because webkit cancels the carousel // animation if you trip this while // in the middle of another animation // ;_; // .transition(opacity .2s linear); // Reposition the right one &.right { left: auto; right: 15px; } // Hover state &:hover { color: @white; text-decoration: none; .opacity(90); } } // Caption for text below images // ----------------------------- .carousel-caption { position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 15px 5px; background: @grayDark; background: rgba(0,0,0,.75); } .carousel-caption h4, .carousel-caption p { color: @white; } metrics-3.2.5/docs/source/_themes/metrics/less/close.less000066400000000000000000000004641315671014200234600ustar00rootroot00000000000000// CLOSE ICONS // ----------- .close { float: right; font-size: 20px; font-weight: bold; line-height: @baseLineHeight; color: @black; text-shadow: 0 1px 0 rgba(255,255,255,1); .opacity(20); &:hover { color: @black; text-decoration: none; .opacity(40); cursor: pointer; } } metrics-3.2.5/docs/source/_themes/metrics/less/code.less000066400000000000000000000020141315671014200232560ustar00rootroot00000000000000// Code.less // Code typography styles for the and
 elements
// --------------------------------------------------------

// Inline and block code styles
.code-and-pre,
pre {
  padding: 0 3px 2px;
  #font > #family > .monospace;
  font-size: @baseFontSize - 1;
  color: @grayDark;
  .border-radius(3px);
}
.code, code {
  .code-and-pre();
  color: #d14;
  background-color: #f7f7f9;
  border: 1px solid #e1e1e8;
}
pre {
  display: block;
  padding: (@baseLineHeight - 1) / 2;
  margin: 0 0 @baseLineHeight / 2;
  font-size: 12px;
  line-height: @baseLineHeight;
  background-color: #f5f5f5;
  border: 1px solid #ccc; // fallback for IE7-8
  border: 1px solid rgba(0,0,0,.15);
  .border-radius(4px);
  white-space: pre;
  white-space: pre-wrap;
  word-break: break-all;

  // Make prettyprint styles more spaced out for readability
  &.prettyprint {
    margin-bottom: @baseLineHeight;
  }

  // Account for some code outputs that place code tags in pre tags
  code {
    padding: 0;
    background-color: transparent;
  }
}
metrics-3.2.5/docs/source/_themes/metrics/less/component-animations.less000066400000000000000000000004101315671014200265040ustar00rootroot00000000000000// COMPONENT ANIMATIONS
// --------------------

.fade {
  .transition(opacity .15s linear);
  opacity: 0;
  &.in {
    opacity: 1;
  }
}

.collapse {
  .transition(height .35s ease);
  position:relative;
  overflow:hidden;
  height: 0;
  &.in { height: auto; }
}
metrics-3.2.5/docs/source/_themes/metrics/less/dropdowns.less000066400000000000000000000060601315671014200243700ustar00rootroot00000000000000// DROPDOWN MENUS
// --------------

// Use the .menu class on any 
  • element within the topbar or ul.tabs and you'll get some superfancy dropdowns .dropdown { position: relative; } .dropdown-toggle { // The caret makes the toggle a bit too tall in IE7 *margin-bottom: -3px; } .dropdown-toggle:active, .open .dropdown-toggle { outline: 0; } // Dropdown arrow/caret .caret { display: inline-block; width: 0; height: 0; text-indent: -99999px; // IE7 won't do the border trick if there's a text indent, but it doesn't // do the content that text-indent is hiding, either, so we're ok. *text-indent: 0; vertical-align: top; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid @black; .opacity(30); content: "\2193"; } .dropdown .caret { margin-top: 8px; margin-left: 2px; } .dropdown:hover .caret, .open.dropdown .caret { .opacity(100); } // The dropdown menu (ul) .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: @zindexDropdown; float: left; display: none; // none by default, but block on "open" of the menu min-width: 160px; max-width: 220px; _width: 160px; padding: 4px 0; margin: 0; // override default ul list-style: none; background-color: @white; border-color: #ccc; border-color: rgba(0,0,0,.2); border-style: solid; border-width: 1px; .border-radius(0 0 5px 5px); .box-shadow(0 5px 10px rgba(0,0,0,.2)); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; *border-right-width: 2px; *border-bottom-width: 2px; // Allow for dropdowns to go bottom up (aka, dropup-menu) &.bottom-up { top: auto; bottom: 100%; margin-bottom: 2px; } // Dividers (basically an hr) within the dropdown .divider { height: 1px; margin: 5px 1px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid @white; // IE7 needs a set width since we gave a height. Restricting just // to IE7 to keep the 1px left/right space in other browsers. // It is unclear where IE is getting the extra space that we need // to negative-margin away, but so it goes. *width: 100%; *margin: -5px 0 5px; } // Links within the dropdown menu a { display: block; padding: 3px 15px; clear: both; font-weight: normal; line-height: 18px; color: @gray; white-space: nowrap; } } // Hover state .dropdown-menu li > a:hover, .dropdown-menu .active > a, .dropdown-menu .active > a:hover { color: @white; text-decoration: none; background-color: @linkColor; } // Open state for the dropdown .dropdown.open { // IE7's z-index only goes to the nearest positioned ancestor, which would // make the menu appear below buttons that appeared later on the page *z-index: @zindexDropdown; .dropdown-toggle { color: @white; background: #ccc; background: rgba(0,0,0,.3); } .dropdown-menu { display: block; } } // Typeahead .typeahead { margin-top: 2px; // give it some space to breathe .border-radius(4px); } metrics-3.2.5/docs/source/_themes/metrics/less/forms.less000066400000000000000000000241201315671014200234740ustar00rootroot00000000000000// Forms.less // Base styles for various input types, form layouts, and states // ------------------------------------------------------------- // GENERAL STYLES // -------------- // Make all forms have space below them form { margin: 0 0 @baseLineHeight; } fieldset { padding: 0; margin: 0; border: 0; } // Groups of fields with labels on top (legends) legend { display: block; width: 100%; padding: 0; margin-bottom: @baseLineHeight * 1.5; font-size: @baseFontSize * 1.5; line-height: @baseLineHeight * 2; color: @grayDark; border: 0; border-bottom: 1px solid #eee; } // Set font for forms label, input, button, select, textarea { #font > .sans-serif(@baseFontSize,normal,@baseLineHeight); } // Identify controls by their labels label { display: block; margin-bottom: 5px; color: @grayDark; } // Inputs, Textareas, Selects input, textarea, select, .uneditable-input { display: inline-block; width: 210px; height: @baseLineHeight; padding: 4px; margin-bottom: 9px; font-size: @baseFontSize; line-height: @baseLineHeight; color: @gray; border: 1px solid #ccc; .border-radius(3px); } .uneditable-textarea { width: auto; height: auto; } // Inputs within a label label input, label textarea, label select { display: block; } // Mini reset for unique input types input[type="image"], input[type="checkbox"], input[type="radio"] { width: auto; height: auto; padding: 0; margin: 3px 0; *margin-top: 0; /* IE7 */ line-height: normal; border: 0; cursor: pointer; border-radius: 0 e("\0/"); // Nuke border-radius for IE9 only } // Reset the file input to browser defaults input[type="file"] { padding: initial; line-height: initial; border: initial; background-color: @white; background-color: initial; .box-shadow(none); } // Help out input buttons input[type="button"], input[type="reset"], input[type="submit"] { width: auto; height: auto; } // Set the height of select and file controls to match text inputs select, input[type="file"] { height: 28px; /* In IE7, the height of the select element cannot be changed by height, only font-size */ *margin-top: 4px; /* For IE7, add top margin to align select with labels */ line-height: 28px; } // Chrome on Linux and Mobile Safari need background-color select { width: 220px; // default input width + 10px of padding that doesn't get applied background-color: @white; } // Make multiple select elements height not fixed select[multiple], select[size] { height: auto; } // Remove shadow from image inputs input[type="image"] { .box-shadow(none); } // Make textarea height behave textarea { height: auto; } // Hidden inputs input[type="hidden"] { display: none; } // CHECKBOXES & RADIOS // ------------------- // Indent the labels to position radios/checkboxes as hanging .radio, .checkbox { padding-left: 18px; } .radio input[type="radio"], .checkbox input[type="checkbox"] { float: left; margin-left: -18px; } // Move the options list down to align with labels .controls > .radio:first-child, .controls > .checkbox:first-child { padding-top: 5px; // has to be padding because margin collaspes } // Radios and checkboxes on same line .radio.inline, .checkbox.inline { display: inline-block; margin-bottom: 0; vertical-align: middle; } .radio.inline + .radio.inline, .checkbox.inline + .checkbox.inline { margin-left: 10px; // space out consecutive inline controls } // But don't forget to remove their padding on first-child .controls > .radio.inline:first-child, .controls > .checkbox.inline:first-child { padding-top: 0; } // FOCUS STATE // ----------- input, textarea { .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); @transition: border linear .2s, box-shadow linear .2s; .transition(@transition); } input:focus, textarea:focus { border-color: rgba(82,168,236,.8); @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); .box-shadow(@shadow); outline: 0; outline: thin dotted \9; /* IE6-8 */ } input[type="file"]:focus, input[type="checkbox"]:focus, select:focus { .box-shadow(none); // override for file inputs .tab-focus(); } // INPUT SIZES // ----------- // General classes for quick sizes .input-mini { width: 60px; } .input-small { width: 90px; } .input-medium { width: 150px; } .input-large { width: 210px; } .input-xlarge { width: 270px; } .input-xxlarge { width: 530px; } // Grid style input sizes input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input { float: none; margin-left: 0; } // GRID SIZING FOR INPUTS // ---------------------- #inputGridSystem > .generate(@gridColumns, @gridColumnWidth, @gridGutterWidth); // DISABLED STATE // -------------- // Disabled and read-only inputs input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { background-color: #f5f5f5; border-color: #ddd; cursor: not-allowed; } // FORM FIELD FEEDBACK STATES // -------------------------- // Mixin for form field states .formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { // Set the text color > label, .help-block, .help-inline { color: @textColor; } // Style inputs accordingly input, select, textarea { color: @textColor; border-color: @borderColor; &:focus { border-color: darken(@borderColor, 10%); .box-shadow(0 0 6px lighten(@borderColor, 20%)); } } // Give a small background color for input-prepend/-append .input-prepend .add-on, .input-append .add-on { color: @textColor; background-color: @backgroundColor; border-color: @textColor; } } // Warning .control-group.warning { .formFieldState(@warningText, @warningText, @warningBackground); } // Error .control-group.error { .formFieldState(@errorText, @errorText, @errorBackground); } // Success .control-group.success { .formFieldState(@successText, @successText, @successBackground); } // HTML5 invalid states // Shares styles with the .control-group.error above input:focus:required:invalid, textarea:focus:required:invalid, select:focus:required:invalid { color: #b94a48; border-color: #ee5f5b; &:focus { border-color: darken(#ee5f5b, 10%); .box-shadow(0 0 6px lighten(#ee5f5b, 20%)); } } // FORM ACTIONS // ------------ .form-actions { padding: (@baseLineHeight - 1) 20px @baseLineHeight; margin-top: @baseLineHeight; margin-bottom: @baseLineHeight; background-color: #f5f5f5; border-top: 1px solid #ddd; } // For text that needs to appear as an input but should not be an input .uneditable-input { display: block; background-color: @white; border-color: #eee; .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); cursor: not-allowed; } // Placeholder text gets special styles; can't be bundled together though for some reason .placeholder(@grayLight); // HELP TEXT // --------- .help-block { margin-top: 5px; margin-bottom: 0; color: @grayLight; } .help-inline { display: inline-block; .ie7-inline-block(); margin-bottom: 9px; vertical-align: middle; padding-left: 5px; } // INPUT GROUPS // ------------ // Allow us to put symbols and text within the input field for a cleaner look .input-prepend, .input-append { margin-bottom: 5px; .clearfix(); // Clear the float to prevent wrapping input, .uneditable-input { .border-radius(0 3px 3px 0); &:focus { position: relative; z-index: 2; } } .uneditable-input { border-left-color: #ccc; } .add-on { float: left; display: block; width: auto; min-width: 16px; height: @baseLineHeight; margin-right: -1px; padding: 4px 5px; font-weight: normal; line-height: @baseLineHeight; color: @grayLight; text-align: center; text-shadow: 0 1px 0 @white; background-color: #f5f5f5; border: 1px solid #ccc; .border-radius(3px 0 0 3px); } .active { background-color: lighten(@green, 30); border-color: @green; } } .input-prepend { .add-on { *margin-top: 1px; /* IE6-7 */ } } .input-append { input, .uneditable-input { float: left; .border-radius(3px 0 0 3px); } .uneditable-input { border-right-color: #ccc; } .add-on { margin-right: 0; margin-left: -1px; .border-radius(0 3px 3px 0); } input:first-child { // In IE7, having a hasLayout container (from clearfix's zoom:1) can make the first input // inherit the sum of its ancestors' margins. *margin-left: -160px; &+.add-on { *margin-left: -21px; } } } // SEARCH FORM // ----------- .search-query { padding-left: 14px; padding-right: 14px; margin-bottom: 0; // remove the default margin on all inputs .border-radius(14px); } // HORIZONTAL & VERTICAL FORMS // --------------------------- // Common properties // ----------------- .form-search, .form-inline, .form-horizontal { input, textarea, select, .help-inline, .uneditable-input { display: inline-block; margin-bottom: 0; } } .form-search label, .form-inline label, .form-search .input-append, .form-inline .input-append, .form-search .input-prepend, .form-inline .input-prepend { display: inline-block; } // Make the prepend and append add-on vertical-align: middle; .form-search .input-append .add-on, .form-inline .input-prepend .add-on, .form-search .input-append .add-on, .form-inline .input-prepend .add-on { vertical-align: middle; } // Margin to space out fieldsets .control-group { margin-bottom: @baseLineHeight / 2; } // Horizontal-specific styles // -------------------------- .form-horizontal { // Legend collapses margin, so we're relegated to padding legend + .control-group { margin-top: @baseLineHeight; -webkit-margin-top-collapse: separate; } // Increase spacing between groups .control-group { margin-bottom: @baseLineHeight; .clearfix(); } // Float the labels left .control-group > label { float: left; width: 140px; padding-top: 5px; text-align: right; } // Move over all input controls and content .controls { margin-left: 160px; } // Move over buttons in .form-actions to align with .controls .form-actions { padding-left: 160px; } } metrics-3.2.5/docs/source/_themes/metrics/less/grid.less000066400000000000000000000003471315671014200233000ustar00rootroot00000000000000// GRID SYSTEM // ----------- // Fixed (940px) #gridSystem > .generate(@gridColumns, @gridColumnWidth, @gridGutterWidth); // Fluid (940px) #fluidGridSystem > .generate(@gridColumns, @fluidGridColumnWidth, @fluidGridGutterWidth); metrics-3.2.5/docs/source/_themes/metrics/less/hero-unit.less000066400000000000000000000005071315671014200242630ustar00rootroot00000000000000// HERO UNIT // --------- .hero-unit { padding: 60px; margin-bottom: 30px; background-color: #f5f5f5; .border-radius(6px); h1 { margin-bottom: 0; font-size: 60px; line-height: 1; letter-spacing: -1px; } p { font-size: 18px; font-weight: 200; line-height: @baseLineHeight * 1.5; } } metrics-3.2.5/docs/source/_themes/metrics/less/labels.less000066400000000000000000000006361315671014200236160ustar00rootroot00000000000000// LABELS // ------ .label { padding: 1px 3px 2px; font-size: @baseFontSize * .75; font-weight: bold; color: @white; text-transform: uppercase; background-color: @grayLight; .border-radius(3px); } .label-important { background-color: @errorText; } .label-warning { background-color: @orange; } .label-success { background-color: @successText; } .label-info { background-color: @infoText; } metrics-3.2.5/docs/source/_themes/metrics/less/layouts.less000066400000000000000000000005631315671014200240530ustar00rootroot00000000000000// // Layouts // Fixed-width and fluid (with sidebar) layouts // -------------------------------------------- // Container (centered, fixed-width layouts) .container { .container-fixed(); } // Fluid layouts (left aligned, with sidebar, min- & max-width content) .container-fluid { padding-left: @gridGutterWidth; padding-right: @gridGutterWidth; .clearfix(); }metrics-3.2.5/docs/source/_themes/metrics/less/metrics.less000066400000000000000000000104241315671014200240160ustar00rootroot00000000000000@import "reset.less"; @import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "mixins.less"; @import "scaffolding.less"; @import "grid.less"; @import "layouts.less"; @import "type.less"; @import "code.less"; @import "tables.less"; @import "buttons.less"; @import "navs.less"; @import "navbar.less"; @import "hero-unit.less"; @import "utilities.less"; // Has to be last to override when necessary #call-to-action { text-align: right; } a.headerlink { display: none; } #title { color: #ffffff; } .hero-unit h1 { padding-bottom: 20px ! important; } #top-bar small { color: #f8f8ff; text-shadow: 0px -1px 0px #5f0c17; } .admonition { padding: 14px 35px 14px 14px; margin-bottom: 18px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); background-color: #fcf8e3; border: 1px solid #fbeed5; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .admonition .admonition-title { font-size: 14pt; font-weight: bold; } .admonition.note .admonition-title, .admonition-todo .admonition-title { color: #c09853; } .admonition.tip, .admonition.hint { background-color: #dff0d8; border-color: #d6e9c6; } .admonition.tip .admonition-title, .admonition.hint .admonition-title { color: #468847; } .admonition.error, .admonition.warning, .admonition.caution, .admonition.danger, .admonition.attention { background-color: #f2dede; border-color: #eed3d7; } .admonition.error .admonition-title, .admonition.warning .admonition-title, .admonition.caution .admonition-title, .admonition.danger .admonition-title, .admonition.attention .admonition-title { color: #b94a48; } .admonition.important { background-color: #d9edf7; border-color: #bce8f1; } .admonition.important .admonition-title { color: #3a87ad; } .admonition > p, .admonition > ul { margin-bottom: 0; } .admonition p + p { margin-top: 5px; } a.internal.reference > em { font-style: normal ! important; text-decoration: none ! important; } tt { .code(); } .section > p, .section ul li, .admonition p, .section dt, .section dl { font-size: 13pt; line-height: 18pt; } .section tt { font-size: 11pt; line-height: 11pt; } .section > * { margin-bottom: 20px; } pre { font-family: 'Panic Sans', Menlo, Monaco, Consolas, Andale Mono, Courier New, monospace !important; font-size: 12pt !important; line-height: 22px !important; display: block !important; width: auto !important; height: auto !important; overflow: auto !important; white-space: pre !important; word-wrap: normal !important; } #body h1, h1 tt { font-size: 28pt; } h1 tt { background-color: transparent; font-size: 26pt !important; } #body h2 { font-size: 24pt; } h2 tt { background-color: transparent; font-size: 22pt !important; } #body h3 { font-size: 20pt; } h3 tt { background-color: transparent; font-size: 18pt !important; } #body h4 { font-size: 16pt; } h4 tt { background-color: transparent; font-size: 14pt !important; } #sidebar tt { color: #08c; background-color: transparent; } .hero-unit .toctree-wrapper { text-align: center; } .hero-unit li { display: inline; list-style-type: none; padding-right: 20px; } .hero-unit li a { .btn(); .btn-success(); padding:10px 10px 10px; font-size:16pt; &:hover { color: @grayDark; text-decoration: none; background-color: darken(@white, 10%); background-position: 0 -15px; // transition is only when going to hover, otherwise the background // behind the gradient (there for IE<=9 fallback) gets mismatched .transition(background-position .1s linear); .btn-success(); } &:focus { .tab-focus(); .btn-success(); } &:active { background-image: none; @shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); .box-shadow(@shadow); background-color: darken(@white, 10%); background-color: darken(@white, 15%) e("\9"); color: rgba(0,0,0,.5); outline: 0; .btn-success(); } } .hero-unit li a:after { content: " »"; } table.docutils { border: 1px solid #DDD; .table(); .table-striped(); } metrics-3.2.5/docs/source/_themes/metrics/less/mixins.less000066400000000000000000000507161315671014200236670ustar00rootroot00000000000000// Mixins.less // Snippets of reusable CSS to develop faster and keep code readable // ----------------------------------------------------------------- // UTILITY MIXINS // -------------------------------------------------- // Clearfix // -------- // For clearing floats like a boss h5bp.com/q .clearfix() { *zoom: 1; &:before, &:after { display: table; content: ""; } &:after { clear: both; } } // Webkit-style focus // ------------------ .tab-focus() { // Default outline: thin dotted; // Webkit outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } // Center-align a block level element // ---------------------------------- .center-block() { display: block; margin-left: auto; margin-right: auto; } // IE7 inline-block // ---------------- .ie7-inline-block() { *display: inline; /* IE7 inline-block hack */ *zoom: 1; } // IE7 likes to collapse whitespace on either side of the inline-block elements. // Ems because we're attempting to match the width of a space character. Left // version is for form buttons, which typically come after other elements, and // right version is for icons, which come before. Applying both is ok, but it will // mean that space between those elements will be .6em (~2 space characters) in IE7, // instead of the 1 space in other browsers. .ie7-restore-left-whitespace() { *margin-left: .3em; &:first-child { *margin-left: 0; } } .ie7-restore-right-whitespace() { *margin-right: .3em; &:last-child { *margin-left: 0; } } // Sizing shortcuts // ------------------------- .size(@height: 5px, @width: 5px) { width: @width; height: @height; } .square(@size: 5px) { .size(@size, @size); } // Placeholder text // ------------------------- .placeholder(@color: @placeholderText) { :-moz-placeholder { color: @color; } ::-webkit-input-placeholder { color: @color; } } // FONTS // -------------------------------------------------- #font { #family { .serif() { font-family: Georgia, "Times New Roman", Times, serif; } .sans-serif() { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } .monospace() { font-family: "Panic Sans", Menlo, Monaco, Consolas, "Courier New", monospace; } } .shorthand(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { font-size: @size; font-weight: @weight; line-height: @lineHeight; } .serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { #font > #family > .serif; #font > .shorthand(@size, @weight, @lineHeight); } .sans-serif(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { #font > #family > .sans-serif; #font > .shorthand(@size, @weight, @lineHeight); } .monospace(@size: @baseFontSize, @weight: normal, @lineHeight: @baseLineHeight) { #font > #family > .monospace; #font > .shorthand(@size, @weight, @lineHeight); } } // GRID SYSTEM // -------------------------------------------------- // Site container // ------------------------- .container-fixed() { width: @gridRowWidth; margin-left: auto; margin-right: auto; .clearfix(); } // Le grid system // ------------------------- #gridSystem { // Setup the mixins to be used .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, @columns) { width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)); } .offset(@gridColumnWidth, @gridGutterWidth, @columns) { margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1)) + (@gridGutterWidth * 2); } .gridColumn(@gridGutterWidth) { float: left; margin-left: @gridGutterWidth; } // Take these values and mixins, and make 'em do their thang .generate(@gridColumns, @gridColumnWidth, @gridGutterWidth) { // Row surrounds the columns .row { margin-left: @gridGutterWidth * -1; .clearfix(); } // Find all .span# classes within .row and give them the necessary properties for grid columns (supported by all browsers back to IE7, thanks @dhg) [class*="span"] { #gridSystem > .gridColumn(@gridGutterWidth); } // Default columns .span1 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 1); } .span2 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 2); } .span3 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 3); } .span4 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 4); } .span5 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 5); } .span6 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 6); } .span7 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 7); } .span8 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 8); } .span9 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 9); } .span10 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 10); } .span11 { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 11); } .span12, .container { #gridSystem > .columns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 12); } // Offset column options .offset1 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 1); } .offset2 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 2); } .offset3 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 3); } .offset4 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 4); } .offset5 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 5); } .offset6 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 6); } .offset7 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 7); } .offset8 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 8); } .offset9 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 9); } .offset10 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 10); } .offset11 { #gridSystem > .offset(@gridColumnWidth, @gridGutterWidth, 11); } } } // Fluid grid system // ------------------------- #fluidGridSystem { // Setup the mixins to be used .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, @columns) { width: 1% * (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)); } .gridColumn(@fluidGridGutterWidth) { float: left; margin-left: @fluidGridGutterWidth; } // Take these values and mixins, and make 'em do their thang .generate(@gridColumns, @fluidGridColumnWidth, @fluidGridGutterWidth) { // Row surrounds the columns .row-fluid { width: 100%; .clearfix(); // Find all .span# classes within .row and give them the necessary properties for grid columns (supported by all browsers back to IE7, thanks @dhg) > [class*="span"] { #fluidGridSystem > .gridColumn(@fluidGridGutterWidth); } > [class*="span"]:first-child { margin-left: 0; } // Default columns .span1 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 1); } .span2 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 2); } .span3 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 3); } .span4 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 4); } .span5 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 5); } .span6 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 6); } .span7 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 7); } .span8 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 8); } .span9 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 9); } .span10 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 10); } .span11 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 11); } .span12 { #fluidGridSystem > .columns(@fluidGridGutterWidth, @fluidGridColumnWidth, 12); } } } } // Input grid system // ------------------------- #inputGridSystem { .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, @columns) { width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 10; } .generate(@gridColumns, @gridColumnWidth, @gridGutterWidth) { input, textarea, .uneditable-input { &.span1 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 1); } &.span2 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 2); } &.span3 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 3); } &.span4 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 4); } &.span5 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 5); } &.span6 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 6); } &.span7 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 7); } &.span8 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 8); } &.span9 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 9); } &.span10 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 10); } &.span11 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 11); } &.span12 { #inputGridSystem > .inputColumns(@gridGutterWidth, @gridColumnWidth, @gridRowWidth, 12); } } } } // CSS3 PROPERTIES // -------------------------------------------------- // Border Radius .border-radius(@radius: 5px) { -webkit-border-radius: @radius; -moz-border-radius: @radius; border-radius: @radius; } // Drop shadows .box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) { -webkit-box-shadow: @shadow; -moz-box-shadow: @shadow; box-shadow: @shadow; } // Transitions .transition(@transition) { -webkit-transition: @transition; -moz-transition: @transition; -ms-transition: @transition; -o-transition: @transition; transition: @transition; } // Transformations .rotate(@degrees) { -webkit-transform: rotate(@degrees); -moz-transform: rotate(@degrees); -ms-transform: rotate(@degrees); -o-transform: rotate(@degrees); transform: rotate(@degrees); } .scale(@ratio) { -webkit-transform: scale(@ratio); -moz-transform: scale(@ratio); -ms-transform: scale(@ratio); -o-transform: scale(@ratio); transform: scale(@ratio); } .translate(@x: 0, @y: 0) { -webkit-transform: translate(@x, @y); -moz-transform: translate(@x, @y); -ms-transform: translate(@x, @y); -o-transform: translate(@x, @y); transform: translate(@x, @y); } .skew(@x: 0, @y: 0) { -webkit-transform: translate(@x, @y); -moz-transform: translate(@x, @y); -ms-transform: translate(@x, @y); -o-transform: translate(@x, @y); transform: translate(@x, @y); } .skew(@x: 0, @y: 0) { -webkit-transform: skew(@x, @y); -moz-transform: skew(@x, @y); -ms-transform: skew(@x, @y); -o-transform: skew(@x, @y); transform: skew(@x, @y); } // Background clipping // Heads up: FF 3.6 and under need "padding" instead of "padding-box" .background-clip(@clip) { -webkit-background-clip: @clip; -moz-background-clip: @clip; background-clip: @clip; } // Background sizing .background-size(@size){ -webkit-background-size: @size; -moz-background-size: @size; -o-background-size: @size; background-size: @size; } // Box sizing .box-sizing(@boxmodel) { -webkit-box-sizing: @boxmodel; -moz-box-sizing: @boxmodel; box-sizing: @boxmodel; } // User select // For selecting text on the page .user-select(@select) { -webkit-user-select: @select; -moz-user-select: @select; -o-user-select: @select; user-select: @select; } // Resize anything .resizable(@direction: both) { resize: @direction; // Options: horizontal, vertical, both overflow: auto; // Safari fix } // CSS3 Content Columns .content-columns(@columnCount, @columnGap: @gridColumnGutter) { -webkit-column-count: @columnCount; -moz-column-count: @columnCount; column-count: @columnCount; -webkit-column-gap: @columnGap; -moz-column-gap: @columnGap; column-gap: @columnGap; } // Opacity .opacity(@opacity: 100) { opacity: @opacity / 100; filter: e(%("alpha(opacity=%d)", @opacity)); } // BACKGROUNDS // -------------------------------------------------- // Add an alphatransparency value to any background or border color (via Elyse Holladay) #translucent { .background(@color: @white, @alpha: 1) { background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); } .border(@color: @white, @alpha: 1) { border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha); .background-clip(padding-box); } } // Gradient Bar Colors for buttons and alerts .gradientBar(@primaryColor, @secondaryColor) { #gradient > .vertical(@primaryColor, @secondaryColor); border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%); border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%); } // Gradients #gradient { .horizontal(@startColor: #555, @endColor: #333) { background-color: @endColor; background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+ background-image: -ms-linear-gradient(left, @startColor, @endColor); // IE10 background-image: -webkit-gradient(linear, 0 0, 100% 0, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+ background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10 background-image: linear-gradient(left, @startColor, @endColor); // Le standard background-repeat: repeat-x; filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",@startColor,@endColor)); // IE9 and down } .vertical(@startColor: #555, @endColor: #333) { background-color: mix(@startColor, @endColor, 60%); background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+ background-image: -ms-linear-gradient(top, @startColor, @endColor); // IE10 background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+ background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+ background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10 background-image: linear-gradient(top, @startColor, @endColor); // The standard background-repeat: repeat-x; filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down } .directional(@startColor: #555, @endColor: #333, @deg: 45deg) { background-color: @endColor; background-repeat: repeat-x; background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+ background-image: -ms-linear-gradient(@deg, @startColor, @endColor); // IE10 background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+ background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10 background-image: linear-gradient(@deg, @startColor, @endColor); // The standard } .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) { background-color: mix(@midColor, @endColor, 80%); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor)); background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor); background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor); background-image: -ms-linear-gradient(@startColor, @midColor @colorStop, @endColor); background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor); background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor); background-repeat: no-repeat; filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down, gets no color-stop at all for proper fallback } .radial(@innerColor: #555, @outerColor: #333) { background-color: @outerColor; background-image: -webkit-gradient(radial, center center, 0, center center, 460, from(@innerColor), to(@outerColor)); background-image: -webkit-radial-gradient(circle, @innerColor, @outerColor); background-image: -moz-radial-gradient(circle, @innerColor, @outerColor); background-image: -ms-radial-gradient(circle, @innerColor, @outerColor); background-repeat: no-repeat; // Opera cannot do radial gradients yet } .striped(@color, @angle: -45deg) { background-color: @color; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,.15)), color-stop(.75, rgba(255,255,255,.15)), color-stop(.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); background-image: -moz-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); background-image: -ms-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); background-image: linear-gradient(@angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent); } } // Reset filters for IE .reset-filter() { filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); } // Mixin for generating button backgrounds // --------------------------------------- .buttonBackground(@startColor, @endColor) { // gradientBar will set the background to a pleasing blend of these, to support IE<=9 .gradientBar(@startColor, @endColor); .reset-filter(); // in these cases the gradient won't cover the background, so we override &:hover, &:active, &.active, &.disabled, &[disabled] { background-color: @endColor; } // IE 7 + 8 can't handle box-shadow to show active, so we darken a bit ourselves &:active, &.active { background-color: darken(@endColor, 10%) e("\9"); } } // COMPONENT MIXINS // -------------------------------------------------- // POPOVER ARROWS // ------------------------- // For tipsies and popovers #popoverArrow { .top(@arrowWidth: 5px) { bottom: 0; left: 50%; margin-left: -@arrowWidth; border-left: @arrowWidth solid transparent; border-right: @arrowWidth solid transparent; border-top: @arrowWidth solid @black; } .left(@arrowWidth: 5px) { top: 50%; right: 0; margin-top: -@arrowWidth; border-top: @arrowWidth solid transparent; border-bottom: @arrowWidth solid transparent; border-left: @arrowWidth solid @black; } .bottom(@arrowWidth: 5px) { top: 0; left: 50%; margin-left: -@arrowWidth; border-left: @arrowWidth solid transparent; border-right: @arrowWidth solid transparent; border-bottom: @arrowWidth solid @black; } .right(@arrowWidth: 5px) { top: 50%; left: 0; margin-top: -@arrowWidth; border-top: @arrowWidth solid transparent; border-bottom: @arrowWidth solid transparent; border-right: @arrowWidth solid @black; } } metrics-3.2.5/docs/source/_themes/metrics/less/modals.less000066400000000000000000000030461315671014200236310ustar00rootroot00000000000000// MODALS // ------ .modal-open { .dropdown-menu { z-index: @zindexDropdown + @zindexModal; } .dropdown.open { *z-index: @zindexDropdown + @zindexModal; } .popover { z-index: @zindexPopover + @zindexModal; } .tooltip { z-index: @zindexTooltip + @zindexModal; } } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: @zindexModalBackdrop; background-color: @black; // Fade for backdrop &.fade { opacity: 0; } } .modal-backdrop, .modal-backdrop.fade.in { .opacity(80); } .modal { position: fixed; top: 50%; left: 50%; z-index: @zindexModal; max-height: 500px; overflow: auto; width: 560px; margin: -250px 0 0 -280px; background-color: @white; border: 1px solid #999; border: 1px solid rgba(0,0,0,.3); *border: 1px solid #999; /* IE6-7 */ .border-radius(6px); .box-shadow(0 3px 7px rgba(0,0,0,0.3)); .background-clip(padding-box); &.fade { .transition(e('opacity .3s linear, top .3s ease-out')); top: -25%; } &.fade.in { top: 50%; } } .modal-header { padding: 9px 15px; border-bottom: 1px solid #eee; // Close icon .close { margin-top: 2px; } } .modal-body { padding: 15px; } .modal-footer { padding: 14px 15px 15px; margin-bottom: 0; background-color: #f5f5f5; border-top: 1px solid #ddd; .border-radius(0 0 6px 6px); .box-shadow(inset 0 1px 0 @white); .clearfix(); .btn { float: right; margin-left: 5px; margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs } } metrics-3.2.5/docs/source/_themes/metrics/less/navbar.less000066400000000000000000000136521315671014200236270ustar00rootroot00000000000000// NAVBAR (FIXED AND STATIC) // ------------------------- // COMMON STYLES // ------------- .navbar { overflow: visible; margin-bottom: @baseLineHeight; } // Gradient is applied to it's own element because overflow visible is not honored by IE when filter is present .navbar-inner { padding-left: 20px; padding-right: 20px; #gradient > .vertical(@navbarBackgroundHighlight, @navbarBackground); .border-radius(4px); @shadow: 0 1px 3px rgba(0,0,0,.25), inset 0 -1px 0 rgba(0,0,0,.1); .box-shadow(@shadow); } // Navbar button for toggling navbar items in responsive layouts .btn-navbar { display: none; float: right; padding: 7px 10px; margin-left: 5px; margin-right: 5px; .buttonBackground(@navbarBackgroundHighlight, @navbarBackground); @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); .box-shadow(@shadow); } .btn-navbar .icon-bar { display: block; width: 18px; height: 2px; background-color: #f5f5f5; .border-radius(1px); .box-shadow(0 1px 0 rgba(0,0,0,.25)); } .btn-navbar .icon-bar + .icon-bar { margin-top: 3px; } // Override the default collapsed state .nav-collapse.collapse { height: auto; } // Brand, links, text, and buttons .navbar { // Hover and active states .brand:hover { text-decoration: none; } // Website or project name .brand { float: left; display: block; padding: 8px 20px 12px; margin-left: -20px; // negative indent to left-align the text down the page font-size: 20px; font-weight: 200; line-height: 1; color: @white; } // Plain text in topbar .navbar-text { margin-bottom: 0; line-height: 40px; color: @navbarText; a:hover { color: @white; background-color: transparent; } } // Buttons in navbar .btn, .btn-group { margin-top: 5px; // make buttons vertically centered in navbar } .btn-group .btn { margin-top: 0; } } // Navbar forms .navbar-form { margin-bottom: 0; // remove default bottom margin .clearfix(); input, select { display: inline-block; margin-top: 5px; margin-bottom: 0; } .radio, .checkbox { margin-top: 5px; } input[type="image"], input[type="checkbox"], input[type="radio"] { margin-top: 3px; } } // Navbar search .navbar-search { position: relative; float: left; margin-top: 6px; margin-bottom: 0; .search-query { padding: 4px 9px; #font > .sans-serif(13px, normal, 1); color: @white; color: rgba(255,255,255,.75); background: #666; background: rgba(255,255,255,.3); border: 1px solid #111; @shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0px rgba(255,255,255,.15); .box-shadow(@shadow); .transition(none); // Placeholder text gets special styles; can't be bundled together though for some reason .placeholder(@grayLighter); // Hover states &:hover { color: @white; background-color: @grayLight; background-color: rgba(255,255,255,.5); } // Focus states (we use .focused since IE8 and down doesn't support :focus) &:focus, &.focused { padding: 5px 10px; color: @grayDark; text-shadow: 0 1px 0 @white; background-color: @white; border: 0; .box-shadow(0 0 3px rgba(0,0,0,.15)); outline: 0; } } } // FIXED NAVBAR // ------------ .navbar-fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: @zindexFixedNavbar; } .navbar-fixed-top .navbar-inner { padding-left: 0; padding-right: 0; .border-radius(0); } // NAVIGATION // ---------- .navbar .nav { position: relative; left: 0; display: block; float: left; margin: 0 10px 0 0; } .navbar .nav.pull-right { float: right; // redeclare due to specificity } .navbar .nav > li { display: block; float: left; } // Links .navbar .nav > li > a { float: none; padding: 10px 10px 11px; line-height: 19px; color: @navbarLinkColor; text-decoration: none; text-shadow: 0 -1px 0 rgba(0,0,0,.25); } // Hover .navbar .nav > li > a:hover { background-color: transparent; color: @navbarLinkColorHover; text-decoration: none; } // Active nav items .navbar .nav .active > a, .navbar .nav .active > a:hover { color: @navbarLinkColorHover; text-decoration: none; background-color: @navbarBackground; background-color: rgba(0,0,0,.5); } // Dividers (basically a vertical hr) .navbar .divider-vertical { height: @navbarHeight; width: 1px; margin: 0 9px; overflow: hidden; background-color: @navbarBackground; border-right: 1px solid @navbarBackgroundHighlight; } // Secondary (floated right) nav in topbar .navbar .nav.pull-right { margin-left: 10px; margin-right: 0; } // Dropdown menus // -------------- // Menu position and menu carets .navbar .dropdown-menu { margin-top: 1px; .border-radius(4px); &:before { content: ''; display: inline-block; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 7px solid #ccc; border-bottom-color: rgba(0,0,0,.2); position: absolute; top: -7px; left: 9px; } &:after { content: ''; display: inline-block; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 6px solid @white; position: absolute; top: -6px; left: 10px; } } // Dropdown toggle caret .navbar .nav .dropdown-toggle .caret, .navbar .nav .open.dropdown .caret { border-top-color: @white; } .navbar .nav .active .caret { .opacity(100); } // Remove background color from open dropdown .navbar .nav .open > .dropdown-toggle, .navbar .nav .active > .dropdown-toggle, .navbar .nav .open.active > .dropdown-toggle { background-color: transparent; } // Dropdown link on hover .navbar .nav .active > .dropdown-toggle:hover { color: @white; } // Right aligned menus need alt position .navbar .nav.pull-right .dropdown-menu { left: auto; right: 0; &:before { left: auto; right: 12px; } &:after { left: auto; right: 13px; } }metrics-3.2.5/docs/source/_themes/metrics/less/navs.less000066400000000000000000000141461315671014200233240ustar00rootroot00000000000000// NAVIGATIONS // ----------- // BASE CLASS // ---------- .nav { margin-left: 0; margin-bottom: @baseLineHeight; list-style: none; } // Make links block level .nav > li > a { display: block; } .nav > li > a:hover { text-decoration: none; background-color: @grayLighter; } // NAV LIST // -------- .nav-list { padding-left: 14px; padding-right: 14px; margin-bottom: 0; } .nav-list > li > a, .nav-list .nav-header { display: block; padding: 3px 15px; margin-left: -15px; margin-right: -15px; text-shadow: 0 1px 0 rgba(255,255,255,.5); } .nav-list .nav-header { font-size: 11px; font-weight: bold; line-height: @baseLineHeight; color: @grayLight; text-transform: uppercase; } .nav-list > li + .nav-header { margin-top: 9px; } .nav-list .active > a { color: @white; text-shadow: 0 -1px 0 rgba(0,0,0,.2); background-color: @linkColor; } .nav-list .icon { margin-right: 2px; } // TABS AND PILLS // ------------- // Common styles .nav-tabs, .nav-pills { .clearfix(); } .nav-tabs > li, .nav-pills > li { float: left; } .nav-tabs > li > a, .nav-pills > li > a { padding-right: 12px; padding-left: 12px; margin-right: 2px; line-height: 14px; // keeps the overall height an even number } // TABS // ---- // Give the tabs something to sit on .nav-tabs { border-bottom: 1px solid #ddd; } // Make the list-items overlay the bottom border .nav-tabs > li { margin-bottom: -1px; } // Actual tabs (as links) .nav-tabs > li > a { padding-top: 9px; padding-bottom: 9px; border: 1px solid transparent; .border-radius(4px 4px 0 0); &:hover { border-color: @grayLighter @grayLighter #ddd; } } // Active state, and it's :hover to override normal :hover .nav-tabs > .active > a, .nav-tabs > .active > a:hover { color: @gray; background-color: @white; border: 1px solid #ddd; border-bottom-color: transparent; cursor: default; } // PILLS // ----- // Links rendered as pills .nav-pills > li > a { padding-top: 8px; padding-bottom: 8px; margin-top: 2px; margin-bottom: 2px; .border-radius(5px); } // Active state .nav-pills .active > a, .nav-pills .active > a:hover { color: @white; background-color: @linkColor; } // STACKED NAV // ----------- // Stacked tabs and pills .nav-stacked > li { float: none; } .nav-stacked > li > a { margin-right: 0; // no need for the gap between nav items } // Tabs .nav-tabs.nav-stacked { border-bottom: 0; } .nav-tabs.nav-stacked > li > a { border: 1px solid #ddd; .border-radius(0); } .nav-tabs.nav-stacked > li:first-child > a { .border-radius(4px 4px 0 0); } .nav-tabs.nav-stacked > li:last-child > a { .border-radius(0 0 4px 4px); } .nav-tabs.nav-stacked > li > a:hover { border-color: #ddd; z-index: 2; } // Pills .nav-pills.nav-stacked > li > a { margin-bottom: 3px; } .nav-pills.nav-stacked > li:last-child > a { margin-bottom: 1px; // decrease margin to match sizing of stacked tabs } // DROPDOWNS // --------- // Position the menu .nav-tabs .dropdown-menu, .nav-pills .dropdown-menu { margin-top: 1px; border-width: 1px; } .nav-pills .dropdown-menu { .border-radius(4px); } // Default dropdown links // ------------------------- // Make carets use linkColor to start .nav-tabs .dropdown-toggle .caret, .nav-pills .dropdown-toggle .caret { border-top-color: @linkColor; margin-top: 6px; } .nav-tabs .dropdown-toggle:hover .caret, .nav-pills .dropdown-toggle:hover .caret { border-top-color: @linkColorHover; } // Active dropdown links // ------------------------- .nav-tabs .active .dropdown-toggle .caret, .nav-pills .active .dropdown-toggle .caret { border-top-color: @grayDark; } // Active:hover dropdown links // ------------------------- .nav > .dropdown.active > a:hover { color: @black; cursor: pointer; } // Open dropdowns // ------------------------- .nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, .nav > .open.active > a:hover { color: @white; background-color: @grayLight; border-color: @grayLight; } .nav .open .caret, .nav .open.active .caret, .nav .open a:hover .caret { border-top-color: @white; .opacity(100); } // Dropdowns in stacked tabs .tabs-stacked .open > a:hover { border-color: @grayLight; } // TABBABLE // -------- // COMMON STYLES // ------------- // Clear any floats .tabbable { .clearfix(); } // Remove border on bottom, left, right .tabs-below .nav-tabs, .tabs-right .nav-tabs, .tabs-left .nav-tabs { border-bottom: 0; } // Show/hide tabbable areas .tab-content > .tab-pane, .pill-content > .pill-pane { display: none; } .tab-content > .active, .pill-content > .active { display: block; } // BOTTOM // ------ .tabs-below .nav-tabs { border-top: 1px solid #ddd; } .tabs-below .nav-tabs > li { margin-top: -1px; margin-bottom: 0; } .tabs-below .nav-tabs > li > a { .border-radius(0 0 4px 4px); &:hover { border-bottom-color: transparent; border-top-color: #ddd; } } .tabs-below .nav-tabs .active > a, .tabs-below .nav-tabs .active > a:hover { border-color: transparent #ddd #ddd #ddd; } // LEFT & RIGHT // ------------ // Common styles .tabs-left .nav-tabs > li, .tabs-right .nav-tabs > li { float: none; } .tabs-left .nav-tabs > li > a, .tabs-right .nav-tabs > li > a { min-width: 74px; margin-right: 0; margin-bottom: 3px; } // Tabs on the left .tabs-left .nav-tabs { float: left; margin-right: 19px; border-right: 1px solid #ddd; } .tabs-left .nav-tabs > li > a { margin-right: -1px; .border-radius(4px 0 0 4px); } .tabs-left .nav-tabs > li > a:hover { border-color: @grayLighter #ddd @grayLighter @grayLighter; } .tabs-left .nav-tabs .active > a, .tabs-left .nav-tabs .active > a:hover { border-color: #ddd transparent #ddd #ddd; *border-right-color: @white; } // Tabs on the right .tabs-right .nav-tabs { float: right; margin-left: 19px; border-left: 1px solid #ddd; } .tabs-right .nav-tabs > li > a { margin-left: -1px; .border-radius(0 4px 4px 0); } .tabs-right .nav-tabs > li > a:hover { border-color: @grayLighter @grayLighter @grayLighter #ddd; } .tabs-right .nav-tabs .active > a, .tabs-right .nav-tabs .active > a:hover { border-color: #ddd #ddd #ddd transparent; *border-left-color: @white; } metrics-3.2.5/docs/source/_themes/metrics/less/pager.less000066400000000000000000000007051315671014200234470ustar00rootroot00000000000000// PAGER // ----- .pager { margin-left: 0; margin-bottom: @baseLineHeight; list-style: none; text-align: center; .clearfix(); } .pager li { display: inline; } .pager a { display: inline-block; padding: 5px 14px; background-color: #fff; border: 1px solid #ddd; .border-radius(15px); } .pager a:hover { text-decoration: none; background-color: #f5f5f5; } .pager .next a { float: right; } .pager .previous a { float: left; } metrics-3.2.5/docs/source/_themes/metrics/less/pagination.less000066400000000000000000000017711315671014200245060ustar00rootroot00000000000000// PAGINATION // ---------- .pagination { height: @baseLineHeight * 2; margin: @baseLineHeight 0; } .pagination ul { display: inline-block; .ie7-inline-block(); margin-left: 0; margin-bottom: 0; .border-radius(3px); .box-shadow(0 1px 2px rgba(0,0,0,.05)); } .pagination li { display: inline; } .pagination a { float: left; padding: 0 14px; line-height: (@baseLineHeight * 2) - 2; text-decoration: none; border: 1px solid #ddd; border-left-width: 0; } .pagination a:hover, .pagination .active a { background-color: #f5f5f5; } .pagination .active a { color: @grayLight; cursor: default; } .pagination .disabled a, .pagination .disabled a:hover { color: @grayLight; background-color: transparent; cursor: default; } .pagination li:first-child a { border-left-width: 1px; .border-radius(3px 0 0 3px); } .pagination li:last-child a { .border-radius(0 3px 3px 0); } // Centered .pagination-centered { text-align: center; } .pagination-right { text-align: right; } metrics-3.2.5/docs/source/_themes/metrics/less/patterns.less000066400000000000000000000005041315671014200242060ustar00rootroot00000000000000// Patterns.less // Repeatable UI elements outside the base styles provided from the scaffolding // ---------------------------------------------------------------------------- // PAGE HEADERS // ------------ footer { padding-top: @baseLineHeight - 1; margin-top: @baseLineHeight - 1; border-top: 1px solid #eee; } metrics-3.2.5/docs/source/_themes/metrics/less/popovers.less000066400000000000000000000021151315671014200242230ustar00rootroot00000000000000// POPOVERS // -------- .popover { position: absolute; top: 0; left: 0; z-index: @zindexPopover; display: none; padding: 5px; &.top { margin-top: -5px; } &.right { margin-left: 5px; } &.bottom { margin-top: 5px; } &.left { margin-left: -5px; } &.top .arrow { #popoverArrow > .top(); } &.right .arrow { #popoverArrow > .right(); } &.bottom .arrow { #popoverArrow > .bottom(); } &.left .arrow { #popoverArrow > .left(); } .arrow { position: absolute; width: 0; height: 0; } } .popover-inner { padding: 3px; width: 280px; overflow: hidden; background: @black; // has to be full background declaration for IE fallback background: rgba(0,0,0,.8); .border-radius(6px); .box-shadow(0 3px 7px rgba(0,0,0,0.3)); } .popover-title { padding: 9px 15px; line-height: 1; background-color: #f5f5f5; border-bottom:1px solid #eee; .border-radius(3px 3px 0 0); } .popover-content { padding: 14px; background-color: @white; .border-radius(0 0 3px 3px); .background-clip(padding-box); p, ul, ol { margin-bottom: 0; } } metrics-3.2.5/docs/source/_themes/metrics/less/print.less000066400000000000000000000005451315671014200235070ustar00rootroot00000000000000/*! * Bootstrap @VERSION for Print * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. * Date: @DATE */ // HIDE UNECESSARY COMPONENTS // -------------------------- .navbar-fixed { display: none; }metrics-3.2.5/docs/source/_themes/metrics/less/progress-bars.less000066400000000000000000000035151315671014200251440ustar00rootroot00000000000000// PROGRESS BARS // ------------- // ANIMATIONS // ---------- // Webkit @-webkit-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } // Firefox @-moz-keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } // Spec @keyframes progress-bar-stripes { from { background-position: 0 0; } to { background-position: 40px 0; } } // THE BARS // -------- // Outer container .progress { overflow: hidden; height: 18px; margin-bottom: 18px; #gradient > .vertical(#f5f5f5, #f9f9f9); .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); .border-radius(4px); } // Bar of progress .progress .bar { width: 0%; height: 18px; color: @white; font-size: 12px; text-align: center; text-shadow: 0 -1px 0 rgba(0,0,0,.25); #gradient > .vertical(#149bdf, #0480be); .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); .box-sizing(border-box); .transition(width .6s ease); } // Striped bars .progress-striped .bar { #gradient > .striped(#62c462); .background-size(40px 40px); } // Call animation for the active one .progress.active .bar { -webkit-animation: progress-bar-stripes 2s linear infinite; -moz-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } // COLORS // ------ // Danger (red) .progress-danger .bar { #gradient > .vertical(#ee5f5b, #c43c35); } .progress-danger.progress-striped .bar { #gradient > .striped(#ee5f5b); } // Success (green) .progress-success .bar { #gradient > .vertical(#62c462, #57a957); } .progress-success.progress-striped .bar { #gradient > .striped(#62c462); } // Info (teal) .progress-info .bar { #gradient > .vertical(#5bc0de, #339bb9); } .progress-info.progress-striped .bar { #gradient > .striped(#5bc0de); } metrics-3.2.5/docs/source/_themes/metrics/less/reset.less000066400000000000000000000046131315671014200234750ustar00rootroot00000000000000// Reset.less // Adapted from Normalize.css http://github.com/necolas/normalize.css // ------------------------------------------------------------------------ // Display in IE6-9 and FF3 // ------------------------- article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } // Display block in IE6-9 and FF3 // ------------------------- audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } // Prevents modern browsers from displaying 'audio' without controls // ------------------------- audio:not([controls]) { display: none; } // Base settings // ------------------------- html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } // Focus states a:focus { .tab-focus(); } // Hover & Active a:hover, a:active { outline: 0; } // Prevents sub and sup affecting line-height in all browsers // ------------------------- sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } // Img border in a's and image quality // ------------------------- img { max-width: 100%; height: auto; border: 0; -ms-interpolation-mode: bicubic; } // Forms // ------------------------- // Font size in all browsers, margin changes, misc consistency button, input, select, textarea { margin: 0; font-size: 100%; vertical-align: middle; } button, input { *overflow: visible; // Inner spacing ie IE6/7 line-height: normal; // FF3/4 have !important on line-height in UA stylesheet } button::-moz-focus-inner, input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 padding: 0; border: 0; } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; // Cursors on all buttons applied consistently -webkit-appearance: button; // Style clicable inputs in iOS } input[type="search"] { // Appearance in Safari/Chrome -webkit-appearance: textfield; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 } textarea { overflow: auto; // Remove vertical scrollbar in IE6-9 vertical-align: top; // Readability and alignment cross-browser } metrics-3.2.5/docs/source/_themes/metrics/less/responsive.less000066400000000000000000000147721315671014200245570ustar00rootroot00000000000000/*! * Bootstrap Responsive v2.0.0 * * Copyright 2012 Twitter, Inc * Licensed under the Apache License v2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Designed and built with all the love in the world @twitter by @mdo and @fat. */ // Responsive.less // For phone and tablet devices // ------------------------------------------------------------- // REPEAT VARIABLES & MIXINS // ------------------------- // Required since we compile the responsive stuff separately @import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "mixins.less"; // RESPONSIVE CLASSES // ------------------ // Hide from screenreaders and browsers // Credit: HTML5 Boilerplate .hidden { display: none; visibility: hidden; } // UP TO LANDSCAPE PHONE // --------------------- @media (max-width: 480px) { // Smooth out the collapsing/expanding nav .nav-collapse { -webkit-transform: translate3d(0, 0, 0); // activate the GPU } // Block level the page header small tag for readability .page-header h1 small { display: block; line-height: @baseLineHeight; } // Make span* classes full width input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input { display: block; width: 100%; height: 28px; /* Make inputs at least the height of their button counterpart */ /* Makes inputs behave like true block-level elements */ -webkit-box-sizing: border-box; /* Older Webkit */ -moz-box-sizing: border-box; /* Older FF */ -ms-box-sizing: border-box; /* IE8 */ box-sizing: border-box; /* CSS3 spec*/ } // But don't let it screw up prepend/append inputs .input-prepend input[class*="span"], .input-append input[class*="span"] { width: auto; } // Update checkboxes for iOS input[type="checkbox"], input[type="radio"] { border: 1px solid #ccc; } // Remove the horizontal form styles .form-horizontal .control-group > label { float: none; width: auto; padding-top: 0; text-align: left; } // Move over all input controls and content .form-horizontal .controls { margin-left: 0; } // Move the options list down to align with labels .form-horizontal .control-list { padding-top: 0; // has to be padding because margin collaspes } // Move over buttons in .form-actions to align with .controls .form-horizontal .form-actions { padding-left: 10px; padding-right: 10px; } // Modals .modal { position: absolute; top: 10px; left: 10px; right: 10px; width: auto; margin: 0; &.fade.in { top: auto; } } .modal-header .close { padding: 10px; margin: -10px; } // Carousel .carousel-caption { position: static; } } // LANDSCAPE PHONE TO SMALL DESKTOP & PORTRAIT TABLET // -------------------------------------------------- @media (max-width: 768px) { // GRID & CONTAINERS // ----------------- // Remove width from containers .container { width: auto; padding: 0 20px; } // Fluid rows .row-fluid { width: 100%; } // Undo negative margin on rows .row { margin-left: 0; } // Make all columns even .row > [class*="span"], .row-fluid > [class*="span"] { float: none; display: block; width: auto; margin: 0; } } // PORTRAIT TABLET TO DEFAULT DESKTOP // ---------------------------------- @media (min-width: 768px) and (max-width: 980px) { // Fixed grid #gridSystem > .generate(12, 42px, 20px); // Fluid grid #fluidGridSystem > .generate(12, 5.801104972%, 2.762430939%); // Input grid #inputGridSystem > .generate(12, 42px, 20px); } // TABLETS AND BELOW // ----------------- @media (max-width: 980px) { // UNFIX THE TOPBAR // ---------------- // Remove any padding from the body body { padding-top: 0; } // Unfix the navbar .navbar-fixed-top { position: static; margin-bottom: @baseLineHeight; } .navbar-fixed-top .navbar-inner { padding: 5px; } .navbar .container { width: auto; padding: 0; } // Account for brand name .navbar .brand { padding-left: 10px; padding-right: 10px; margin: 0 0 0 -5px; } // Nav collapse clears brand .navbar .nav-collapse { clear: left; } // Block-level the nav .navbar .nav { float: none; margin: 0 0 (@baseLineHeight / 2); } .navbar .nav > li { float: none; } .navbar .nav > li > a { margin-bottom: 2px; } .navbar .nav > .divider-vertical { display: none; } // Nav and dropdown links in navbar .navbar .nav > li > a, .navbar .dropdown-menu a { padding: 6px 15px; font-weight: bold; color: @navbarLinkColor; .border-radius(3px); } .navbar .dropdown-menu li + li a { margin-bottom: 2px; } .navbar .nav > li > a:hover, .navbar .dropdown-menu a:hover { background-color: @navbarBackground; } // Dropdowns in the navbar .navbar .dropdown-menu { position: static; top: auto; left: auto; float: none; display: block; max-width: none; margin: 0 15px; padding: 0; background-color: transparent; border: none; .border-radius(0); .box-shadow(none); } .navbar .dropdown-menu:before, .navbar .dropdown-menu:after { display: none; } .navbar .dropdown-menu .divider { display: none; } // Forms in navbar .navbar-form, .navbar-search { float: none; padding: (@baseLineHeight / 2) 15px; margin: (@baseLineHeight / 2) 0; border-top: 1px solid @navbarBackground; border-bottom: 1px solid @navbarBackground; @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); .box-shadow(@shadow); } // Pull right (secondary) nav content .navbar .nav.pull-right { float: none; margin-left: 0; } // Static navbar .navbar-static .navbar-inner { padding-left: 10px; padding-right: 10px; } // Navbar button .btn-navbar { display: block; } // Hide everything in the navbar save .brand and toggle button */ .nav-collapse { overflow: hidden; height: 0; } } // DEFAULT DESKTOP // --------------- @media (min-width: 980px) { .nav-collapse.collapse { height: auto !important; } } // LARGE DESKTOP & UP // ------------------ @media (min-width: 1200px) { // Fixed grid #gridSystem > .generate(12, 70px, 30px); // Fluid grid #fluidGridSystem > .generate(12, 5.982905983%, 2.564102564%); // Input grid #inputGridSystem > .generate(12, 70px, 30px); // Thumbnails .thumbnails { margin-left: -30px; } .thumbnails > li { margin-left: 30px; } } metrics-3.2.5/docs/source/_themes/metrics/less/scaffolding.less000066400000000000000000000010521315671014200246240ustar00rootroot00000000000000// Scaffolding // Basic and global styles for generating a grid system, structural layout, and page templates // ------------------------------------------------------------------------------------------- // STRUCTURAL LAYOUT // ----------------- body { margin: 0; font-family: @baseFontFamily; font-size: @baseFontSize; line-height: @baseLineHeight; color: @textColor; background-color: @white; } // LINKS // ----- a { color: @linkColor; text-decoration: none; } a:hover { color: @linkColorHover; text-decoration: underline; } metrics-3.2.5/docs/source/_themes/metrics/less/sprites.less000066400000000000000000000204611315671014200240430ustar00rootroot00000000000000// SPRITES // Glyphs and icons for buttons, nav, and more // ------------------------------------------- // ICONS // ----- // All icons receive the styles of the tag with a base class // of .i and are then given a unique class to add width, height, // and background-position. Your resulting HTML will look like // . // For the white version of the icons, just add the .icon-white class: // [class^="icon-"] { display: inline-block; width: 14px; height: 14px; vertical-align: text-top; background-image: url(../img/glyphicons-halflings.png); background-position: 14px 14px; background-repeat: no-repeat; .ie7-restore-right-whitespace(); } .icon-white { background-image: url(../img/glyphicons-halflings-white.png); } .icon-glass { background-position: 0 0; } .icon-music { background-position: -24px 0; } .icon-search { background-position: -48px 0; } .icon-envelope { background-position: -72px 0; } .icon-heart { background-position: -96px 0; } .icon-star { background-position: -120px 0; } .icon-star-empty { background-position: -144px 0; } .icon-user { background-position: -168px 0; } .icon-film { background-position: -192px 0; } .icon-th-large { background-position: -216px 0; } .icon-th { background-position: -240px 0; } .icon-th-list { background-position: -264px 0; } .icon-ok { background-position: -288px 0; } .icon-remove { background-position: -312px 0; } .icon-zoom-in { background-position: -336px 0; } .icon-zoom-out { background-position: -360px 0; } .icon-off { background-position: -384px 0; } .icon-signal { background-position: -408px 0; } .icon-cog { background-position: -432px 0; } .icon-trash { background-position: -456px 0; } .icon-home { background-position: 0 -24px; } .icon-file { background-position: -24px -24px; } .icon-time { background-position: -48px -24px; } .icon-road { background-position: -72px -24px; } .icon-download-alt { background-position: -96px -24px; } .icon-download { background-position: -120px -24px; } .icon-upload { background-position: -144px -24px; } .icon-inbox { background-position: -168px -24px; } .icon-play-circle { background-position: -192px -24px; } .icon-repeat { background-position: -216px -24px; } .icon-refresh { background-position: -240px -24px; } .icon-list-alt { background-position: -264px -24px; } .icon-lock { background-position: -287px -24px; } // 1px off .icon-flag { background-position: -312px -24px; } .icon-headphones { background-position: -336px -24px; } .icon-volume-off { background-position: -360px -24px; } .icon-volume-down { background-position: -384px -24px; } .icon-volume-up { background-position: -408px -24px; } .icon-qrcode { background-position: -432px -24px; } .icon-barcode { background-position: -456px -24px; } .icon-tag { background-position: 0 -48px; } .icon-tags { background-position: -25px -48px; } // 1px off .icon-book { background-position: -48px -48px; } .icon-bookmark { background-position: -72px -48px; } .icon-print { background-position: -96px -48px; } .icon-camera { background-position: -120px -48px; } .icon-font { background-position: -144px -48px; } .icon-bold { background-position: -167px -48px; } // 1px off .icon-italic { background-position: -192px -48px; } .icon-text-height { background-position: -216px -48px; } .icon-text-width { background-position: -240px -48px; } .icon-align-left { background-position: -264px -48px; } .icon-align-center { background-position: -288px -48px; } .icon-align-right { background-position: -312px -48px; } .icon-align-justify { background-position: -336px -48px; } .icon-list { background-position: -360px -48px; } .icon-indent-left { background-position: -384px -48px; } .icon-indent-right { background-position: -408px -48px; } .icon-facetime-video { background-position: -432px -48px; } .icon-picture { background-position: -456px -48px; } .icon-pencil { background-position: 0 -72px; } .icon-map-marker { background-position: -24px -72px; } .icon-adjust { background-position: -48px -72px; } .icon-tint { background-position: -72px -72px; } .icon-edit { background-position: -96px -72px; } .icon-share { background-position: -120px -72px; } .icon-check { background-position: -144px -72px; } .icon-move { background-position: -168px -72px; } .icon-step-backward { background-position: -192px -72px; } .icon-fast-backward { background-position: -216px -72px; } .icon-backward { background-position: -240px -72px; } .icon-play { background-position: -264px -72px; } .icon-pause { background-position: -288px -72px; } .icon-stop { background-position: -312px -72px; } .icon-forward { background-position: -336px -72px; } .icon-fast-forward { background-position: -360px -72px; } .icon-step-forward { background-position: -384px -72px; } .icon-eject { background-position: -408px -72px; } .icon-chevron-left { background-position: -432px -72px; } .icon-chevron-right { background-position: -456px -72px; } .icon-plus-sign { background-position: 0 -96px; } .icon-minus-sign { background-position: -24px -96px; } .icon-remove-sign { background-position: -48px -96px; } .icon-ok-sign { background-position: -72px -96px; } .icon-question-sign { background-position: -96px -96px; } .icon-info-sign { background-position: -120px -96px; } .icon-screenshot { background-position: -144px -96px; } .icon-remove-circle { background-position: -168px -96px; } .icon-ok-circle { background-position: -192px -96px; } .icon-ban-circle { background-position: -216px -96px; } .icon-arrow-left { background-position: -240px -96px; } .icon-arrow-right { background-position: -264px -96px; } .icon-arrow-up { background-position: -289px -96px; } // 1px off .icon-arrow-down { background-position: -312px -96px; } .icon-share-alt { background-position: -336px -96px; } .icon-resize-full { background-position: -360px -96px; } .icon-resize-small { background-position: -384px -96px; } .icon-plus { background-position: -408px -96px; } .icon-minus { background-position: -433px -96px; } .icon-asterisk { background-position: -456px -96px; } .icon-exclamation-sign { background-position: 0 -120px; } .icon-gift { background-position: -24px -120px; } .icon-leaf { background-position: -48px -120px; } .icon-fire { background-position: -72px -120px; } .icon-eye-open { background-position: -96px -120px; } .icon-eye-close { background-position: -120px -120px; } .icon-warning-sign { background-position: -144px -120px; } .icon-plane { background-position: -168px -120px; } .icon-calendar { background-position: -192px -120px; } .icon-random { background-position: -216px -120px; } .icon-comment { background-position: -240px -120px; } .icon-magnet { background-position: -264px -120px; } .icon-chevron-up { background-position: -288px -120px; } .icon-chevron-down { background-position: -313px -119px; } // 1px off .icon-retweet { background-position: -336px -120px; } .icon-shopping-cart { background-position: -360px -120px; } .icon-folder-close { background-position: -384px -120px; } .icon-folder-open { background-position: -408px -120px; } .icon-resize-vertical { background-position: -432px -119px; } .icon-resize-horizontal { background-position: -456px -118px; } metrics-3.2.5/docs/source/_themes/metrics/less/tables.less000066400000000000000000000056521315671014200236310ustar00rootroot00000000000000// // Tables.less // Tables for, you guessed it, tabular data // ---------------------------------------- // BASE TABLES // ----------------- table { max-width: 100%; border-collapse: collapse; border-spacing: 0; } // BASELINE STYLES // --------------- .table { width: 100%; margin-bottom: @baseLineHeight; // Cells th, td { padding: 16px; line-height: @baseLineHeight; text-align: left; border-top: 1px solid #ddd; } th { font-weight: bold; vertical-align: bottom; } td { vertical-align: top; } // Remove top border from thead by default thead:first-child tr th, thead:first-child tr td { border-top: 0; } // Account for multiple tbody instances tbody + tbody { border-top: 2px solid #ddd; } } // CONDENSED TABLE W/ HALF PADDING // ------------------------------- .table-condensed { th, td { padding: 4px 5px; } } // BORDERED VERSION // ---------------- .table-bordered { border: 1px solid #ddd; border-collapse: separate; // Done so we can round those corners! *border-collapse: collapsed; // IE7 can't round corners anyway .border-radius(4px); th + th, td + td, th + td, td + th { border-left: 1px solid #ddd; } // Prevent a double border thead:first-child tr:first-child th, tbody:first-child tr:first-child th, tbody:first-child tr:first-child td { border-top: 0; } // For first th or td in the first row in the first thead or tbody thead:first-child tr:first-child th:first-child, tbody:first-child tr:first-child td:first-child { .border-radius(4px 0 0 0); } thead:first-child tr:first-child th:last-child, tbody:first-child tr:first-child td:last-child { .border-radius(0 4px 0 0); } // For first th or td in the first row in the first thead or tbody thead:last-child tr:last-child th:first-child, tbody:last-child tr:last-child td:first-child { .border-radius(0 0 0 4px); } thead:last-child tr:last-child th:last-child, tbody:last-child tr:last-child td:last-child { .border-radius(0 0 4px 0); } } // ZEBRA-STRIPING // -------------- // Default zebra-stripe styles (alternating gray and transparent backgrounds) .table-striped { tbody { tr:nth-child(odd) td, tr:nth-child(odd) th { background-color: #f9f9f9; } } } // TABLE CELL SIZING // ----------------- // Change the columns .tableColumns(@columnSpan: 1) { float: none; width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 16; margin-left: 0; } table { .span1 { .tableColumns(1); } .span2 { .tableColumns(2); } .span3 { .tableColumns(3); } .span4 { .tableColumns(4); } .span5 { .tableColumns(5); } .span6 { .tableColumns(6); } .span7 { .tableColumns(7); } .span8 { .tableColumns(8); } .span9 { .tableColumns(9); } .span10 { .tableColumns(10); } .span11 { .tableColumns(11); } .span12 { .tableColumns(12); } } metrics-3.2.5/docs/source/_themes/metrics/less/thumbnails.less000066400000000000000000000011771315671014200245230ustar00rootroot00000000000000// THUMBNAILS // ---------- .thumbnails { margin-left: -20px; list-style: none; .clearfix(); } .thumbnails > li { float: left; margin: 0 0 @baseLineHeight 20px; } .thumbnail { display: block; padding: 4px; line-height: 1; border: 1px solid #ddd; .border-radius(4px); .box-shadow(0 1px 1px rgba(0,0,0,.075)); } // Add a hover state for linked versions only a.thumbnail:hover { border-color: @linkColor; .box-shadow(0 1px 4px rgba(0,105,214,.25)); } // Images and captions .thumbnail > img { display: block; max-width: 100%; margin-left: auto; margin-right: auto; } .thumbnail .caption { padding: 9px; } metrics-3.2.5/docs/source/_themes/metrics/less/tooltip.less000066400000000000000000000014431315671014200240430ustar00rootroot00000000000000// TOOLTIP // ------= .tooltip { position: absolute; z-index: @zindexTooltip; display: block; visibility: visible; padding: 5px; font-size: 11px; .opacity(0); &.in { .opacity(80); } &.top { margin-top: -2px; } &.right { margin-left: 2px; } &.bottom { margin-top: 2px; } &.left { margin-left: -2px; } &.top .tooltip-arrow { #popoverArrow > .top(); } &.left .tooltip-arrow { #popoverArrow > .left(); } &.bottom .tooltip-arrow { #popoverArrow > .bottom(); } &.right .tooltip-arrow { #popoverArrow > .right(); } } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: @white; text-align: center; text-decoration: none; background-color: @black; .border-radius(4px); } .tooltip-arrow { position: absolute; width: 0; height: 0; } metrics-3.2.5/docs/source/_themes/metrics/less/type.less000066400000000000000000000062771315671014200233440ustar00rootroot00000000000000// Typography.less // Headings, body text, lists, code, and more for a versatile and durable typography system // ---------------------------------------------------------------------------------------- // BODY TEXT // --------- p { margin: 0 0 @baseLineHeight / 2; font-family: @baseFontFamily; font-size: @baseFontSize; line-height: @baseLineHeight; small { font-size: @baseFontSize - 2; color: @grayLight; } } .lead { margin-bottom: @baseLineHeight; font-size: 20px; font-weight: 200; line-height: @baseLineHeight * 1.5; } // HEADINGS // -------- h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: bold; color: @grayDark; text-rendering: optimizelegibility; // Fix the character spacing for headings small { font-weight: normal; color: @grayLight; } } h1 { font-size: 30px; line-height: @baseLineHeight * 2; small { font-size: 18px; } } h2 { font-size: 24px; line-height: @baseLineHeight * 2; small { font-size: 18px; } } h3 { line-height: @baseLineHeight * 1.5; font-size: 18px; small { font-size: 14px; } } h4, h5, h6 { line-height: @baseLineHeight; } h4 { font-size: 14px; small { font-size: 12px; } } h5 { font-size: 12px; } h6 { font-size: 11px; color: @grayLight; text-transform: uppercase; } // Page header .page-header { padding-bottom: @baseLineHeight - 1; margin: @baseLineHeight 0; border-bottom: 1px solid @grayLighter; } .page-header h1 { line-height: 1; } // LISTS // ----- // Unordered and Ordered lists ul, ol { padding: 0; margin: 0 0 @baseLineHeight / 2 25px; } ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; } ul { list-style: disc; } ol { list-style: decimal; } li { line-height: @baseLineHeight; } ul.unstyled { margin-left: 0; list-style: none; } // Description Lists dl { margin-bottom: @baseLineHeight; } dt, dd { line-height: @baseLineHeight; } dt { font-weight: bold; } dd { margin-left: @baseLineHeight / 2; } // MISC // ---- // Horizontal rules hr { margin: @baseLineHeight 0; border: 0; border-top: 1px solid #e5e5e5; border-bottom: 1px solid @white; } // Emphasis strong { font-weight: bold; } em { font-style: italic; } .muted { color: @grayLight; } // Abbreviations and acronyms abbr { font-size: 90%; text-transform: uppercase; border-bottom: 1px dotted #ddd; cursor: help; } // Blockquotes blockquote { padding: 0 0 0 15px; margin: 0 0 @baseLineHeight; border-left: 5px solid @grayLighter; p { margin-bottom: 0; #font > .shorthand(16px,300,@baseLineHeight * 1.25); } small { display: block; line-height: @baseLineHeight; color: @grayLight; &:before { content: '\2014 \00A0'; } } // Float right with text-align: right &.pull-right { float: right; padding-left: 0; padding-right: 15px; border-left: 0; border-right: 5px solid @grayLighter; p, small { text-align: right; } } } // Quotes q:before, q:after, blockquote:before, blockquote:after { content: ""; } // Addresses address { display: block; margin-bottom: @baseLineHeight; line-height: @baseLineHeight; font-style: normal; } // Misc small { font-size: 100%; } cite { font-style: normal; } metrics-3.2.5/docs/source/_themes/metrics/less/utilities.less000066400000000000000000000003651315671014200243660ustar00rootroot00000000000000// UTILITY CLASSES // --------------- // Quick floats .pull-right { float: right; } .pull-left { float: left; } // Toggling content .hide { display: none; } .show { display: block; } // Visibility .invisible { visibility: hidden; } metrics-3.2.5/docs/source/_themes/metrics/less/variables.less000066400000000000000000000052511315671014200243220ustar00rootroot00000000000000// Variables.less // Variables to customize the look and feel of Bootstrap // ----------------------------------------------------- // GLOBAL VALUES // -------------------------------------------------- // Links @linkColor: #08c; @linkColorHover: darken(@linkColor, 15%); // Grays @black: #000; @grayDarker: #222; @grayDark: #333; @gray: #555; @grayLight: #999; @grayLighter: #eee; @white: #fff; // Accent colors @blue: #049cdb; @blueDark: #0064cd; @green: #46a546; @red: #9d261d; @yellow: #ffc40d; @orange: #f89406; @pink: #c3325f; @purple: #7a43b6; // Typography @baseFontSize: 13px; @baseFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; @baseLineHeight: 18px; @textColor: @grayDark; // Buttons @primaryButtonBackground: @linkColor; // COMPONENT VARIABLES // -------------------------------------------------- // Z-index master list // Used for a bird's eye view of components dependent on the z-axis // Try to avoid customizing these :) @zindexDropdown: 1000; @zindexPopover: 1010; @zindexTooltip: 1020; @zindexFixedNavbar: 1030; @zindexModalBackdrop: 1040; @zindexModal: 1050; // Input placeholder text color @placeholderText: @grayLight; // Navbar @navbarHeight: 40px; @navbarBackground: @grayDarker; @navbarBackgroundHighlight: @grayDark; @navbarText: @grayLight; @navbarLinkColor: @grayLight; @navbarLinkColorHover: @white; // Form states and alerts @warningText: #c09853; @warningBackground: #fcf8e3; @warningBorder: darken(spin(@warningBackground, -10), 3%); @errorText: #b94a48; @errorBackground: #f2dede; @errorBorder: darken(spin(@errorBackground, -10), 3%); @successText: #468847; @successBackground: #dff0d8; @successBorder: darken(spin(@successBackground, -10), 5%); @infoText: #3a87ad; @infoBackground: #d9edf7; @infoBorder: darken(spin(@infoBackground, -10), 7%); // GRID // -------------------------------------------------- // Default 940px grid @gridColumns: 12; @gridColumnWidth: 60px; @gridGutterWidth: 20px; @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); // Fluid grid @fluidGridColumnWidth: 6.382978723%; @fluidGridGutterWidth: 2.127659574%; metrics-3.2.5/docs/source/_themes/metrics/less/wells.less000066400000000000000000000005121315671014200234730ustar00rootroot00000000000000// WELLS // ----- .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #eee; border: 1px solid rgba(0,0,0,.05); .border-radius(4px); .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); blockquote { border-color: #ddd; border-color: rgba(0,0,0,.15); } } metrics-3.2.5/docs/source/_themes/metrics/page.html000066400000000000000000000004201315671014200223070ustar00rootroot00000000000000{# basic/page.html ~~~~~~~~~~~~~~~ Master template for simple pages. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} {% extends "layout.html" %} {% block body %} {{ body }} {% endblock %} metrics-3.2.5/docs/source/_themes/metrics/search.html000066400000000000000000000034271315671014200226520ustar00rootroot00000000000000{# basic/search.html ~~~~~~~~~~~~~~~~~ Template for the search page. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} {% extends "layout.html" %} {% set title = _('Search') %} {% set script_files = script_files + ['_static/searchtools.js'] %} {% block extrahead %} {{ super() }} {% endblock %} {% block body %}

    {{ _('Search') }}

    {% trans %}Please activate JavaScript to enable the search functionality.{% endtrans %}

    {% trans %}From here you can search these documents. Enter your search words into the box below and click "search". Note that the search function will automatically search for all of the words. Pages containing fewer words won't appear in the result list.{% endtrans %}

    {% if search_performed %}

    {{ _('Search Results') }}

    {% if not search_results %}

    {{ _('Your search did not match any results.') }}

    {% endif %} {% endif %}
    {% if search_results %}
      {% for href, caption, context in search_results %}
    • {{ caption }}
      {{ context|e }}
    • {% endfor %}
    {% endif %}
    {% endblock %} metrics-3.2.5/docs/source/_themes/metrics/static/000077500000000000000000000000001315671014200220005ustar00rootroot00000000000000metrics-3.2.5/docs/source/_themes/metrics/static/metrics.css000066400000000000000000001122241315671014200241620ustar00rootroot00000000000000article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} audio:not([controls]){display:none;} html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} a:hover,a:active{outline:0;} sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} sup{top:-0.5em;} sub{bottom:-0.25em;} img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;} button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} button,input{*overflow:visible;line-height:normal;} button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} textarea{overflow:auto;vertical-align:top;} body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} a{color:#0088cc;text-decoration:none;} a:hover{color:#005580;text-decoration:underline;} .row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:60px;} .span2{width:140px;} .span3{width:220px;} .span4{width:300px;} .span5{width:380px;} .span6{width:460px;} .span7{width:540px;} .span8{width:620px;} .span9{width:700px;} .span10{width:780px;} .span11{width:860px;} .span12,.container{width:940px;} .offset1{margin-left:100px;} .offset2{margin-left:180px;} .offset3{margin-left:260px;} .offset4{margin-left:340px;} .offset5{margin-left:420px;} .offset6{margin-left:500px;} .offset7{margin-left:580px;} .offset8{margin-left:660px;} .offset9{margin-left:740px;} .offset10{margin-left:820px;} .offset11{margin-left:900px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:6.382978723%;} .row-fluid .span2{width:14.89361702%;} .row-fluid .span3{width:23.404255317%;} .row-fluid .span4{width:31.914893614%;} .row-fluid .span5{width:40.425531911%;} .row-fluid .span6{width:48.93617020799999%;} .row-fluid .span7{width:57.446808505%;} .row-fluid .span8{width:65.95744680199999%;} .row-fluid .span9{width:74.468085099%;} .row-fluid .span10{width:82.97872339599999%;} .row-fluid .span11{width:91.489361693%;} .row-fluid .span12{width:99.99999998999999%;} .container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} .container:after{clear:both;} .container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} .container-fluid:after{clear:both;} p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} .lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} h4,h5,h6{line-height:18px;} h4{font-size:14px;}h4 small{font-size:12px;} h5{font-size:12px;} h6{font-size:11px;color:#999999;text-transform:uppercase;} .page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} .page-header h1{line-height:1;} ul,ol{padding:0;margin:0 0 9px 25px;} ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} ul{list-style:disc;} ol{list-style:decimal;} li{line-height:18px;} ul.unstyled{margin-left:0;list-style:none;} dl{margin-bottom:18px;} dt,dd{line-height:18px;} dt{font-weight:bold;} dd{margin-left:9px;} hr{margin:18px 0;border:0;border-top:1px solid #e5e5e5;border-bottom:1px solid #ffffff;} strong{font-weight:bold;} em{font-style:italic;} .muted{color:#999999;} abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;} blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} q:before,q:after,blockquote:before,blockquote:after{content:"";} address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} small{font-size:100%;} cite{font-style:normal;} .code-and-pre,pre{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .code,code{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;}pre.prettyprint{margin-bottom:18px;} pre code{padding:0;background-color:transparent;} table{max-width:100%;border-collapse:collapse;border-spacing:0;} .table{width:100%;margin-bottom:18px;}.table th,.table td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} .table th{font-weight:bold;vertical-align:bottom;} .table td{vertical-align:top;} .table thead:first-child tr th,.table thead:first-child tr td{border-top:0;} .table tbody+tbody{border-top:2px solid #ddd;} .table-condensed th,.table-condensed td{padding:4px 5px;} .table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;} .table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} .table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} .table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} .table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} .table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} .table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} table .span1{float:none;width:44px;margin-left:0;} table .span2{float:none;width:124px;margin-left:0;} table .span3{float:none;width:204px;margin-left:0;} table .span4{float:none;width:284px;margin-left:0;} table .span5{float:none;width:364px;margin-left:0;} table .span6{float:none;width:444px;margin-left:0;} table .span7{float:none;width:524px;margin-left:0;} table .span8{float:none;width:604px;margin-left:0;} table .span9{float:none;width:684px;margin-left:0;} table .span10{float:none;width:764px;margin-left:0;} table .span11{float:none;width:844px;margin-left:0;} table .span12{float:none;width:924px;margin-left:0;} .btn{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:first-child{*margin-left:0;} .btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} .btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} .btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;} .btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} .btn-large .icon{margin-top:1px;} .btn-small{padding:5px 9px;font-size:11px;line-height:16px;} .btn-small .icon{margin-top:-1px;} .btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} .btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active{color:rgba(255, 255, 255, 0.75);} .btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;} .btn-primary:active,.btn-primary.active{background-color:#003399 \9;} .btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} .btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} .btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} .btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} .btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} .btn-success:active,.btn-success.active{background-color:#408140 \9;} .btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} .btn-info:active,.btn-info.active{background-color:#24748c \9;} button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;} button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;} .nav{margin-left:0;margin-bottom:18px;list-style:none;} .nav>li>a{display:block;} .nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} .nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;} .nav-list>li>a,.nav-list .nav-header{display:block;padding:3px 15px;margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} .nav-list .nav-header{font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-transform:uppercase;} .nav-list>li+.nav-header{margin-top:9px;} .nav-list .active>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} .nav-list .icon{margin-right:2px;} .nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} .nav-tabs:after,.nav-pills:after{clear:both;} .nav-tabs>li,.nav-pills>li{float:left;} .nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} .nav-tabs{border-bottom:1px solid #ddd;} .nav-tabs>li{margin-bottom:-1px;} .nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} .nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} .nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} .nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;} .nav-stacked>li{float:none;} .nav-stacked>li>a{margin-right:0;} .nav-tabs.nav-stacked{border-bottom:0;} .nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} .nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} .nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} .nav-pills.nav-stacked>li>a{margin-bottom:3px;} .nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} .nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} .nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} .nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;} .nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;} .nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;} .nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} .nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} .nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);} .tabs-stacked .open>a:hover{border-color:#999999;} .tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} .tabbable:after{clear:both;} .tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} .tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} .tab-content>.active,.pill-content>.active{display:block;} .tabs-below .nav-tabs{border-top:1px solid #ddd;} .tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} .tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} .tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} .tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} .tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} .tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} .tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} .tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} .tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} .tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} .tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} .tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} .tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} .navbar{overflow:visible;margin-bottom:18px;} .navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} .btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} .btn-navbar .icon-bar+.icon-bar{margin-top:3px;} .nav-collapse.collapse{height:auto;} .navbar .brand:hover{text-decoration:none;} .navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} .navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;} .navbar .btn,.navbar .btn-group{margin-top:5px;} .navbar .btn-group .btn{margin-top:0;} .navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} .navbar-form:after{clear:both;} .navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;} .navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} .navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} .navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;} .navbar-search .search-query ::-webkit-input-placeholder{color:#eeeeee;} .navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);} .navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} .navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;} .navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} .navbar .nav.pull-right{float:right;} .navbar .nav>li{display:block;float:left;} .navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} .navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} .navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;background-color:rgba(0, 0, 0, 0.5);} .navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} .navbar .nav.pull-right{margin-left:10px;margin-right:0;} .navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} .navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} .navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;} .navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} .navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} .navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} .navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;} .navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;} .hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} .hero-unit p{font-size:18px;font-weight:200;line-height:27px;} .pull-right{float:right;} .pull-left{float:left;} .hide{display:none;} .show{display:block;} .invisible{visibility:hidden;} #call-to-action{text-align:right;} a.headerlink{display:none;} #title{color:#ffffff;} .hero-unit h1{padding-bottom:20px ! important;} #top-bar small{color:#f8f8ff;text-shadow:0px -1px 0px #5f0c17;} .admonition{padding:14px 35px 14px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} .admonition .admonition-title{font-size:14pt;font-weight:bold;} .admonition.note .admonition-title,.admonition-todo .admonition-title{color:#c09853;} .admonition.tip,.admonition.hint{background-color:#dff0d8;border-color:#d6e9c6;} .admonition.tip .admonition-title,.admonition.hint .admonition-title{color:#468847;} .admonition.error,.admonition.warning,.admonition.caution,.admonition.danger,.admonition.attention{background-color:#f2dede;border-color:#eed3d7;} .admonition.error .admonition-title,.admonition.warning .admonition-title,.admonition.caution .admonition-title,.admonition.danger .admonition-title,.admonition.attention .admonition-title{color:#b94a48;} .admonition.important{background-color:#d9edf7;border-color:#bce8f1;} .admonition.important .admonition-title{color:#3a87ad;} .admonition>p,.admonition>ul{margin-bottom:0;} .admonition p+p{margin-top:5px;} a.internal.reference>em{font-style:normal ! important;text-decoration:none ! important;} tt{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} .section>p,.section ul li,.admonition p,.section dt,.section dl{font-size:13pt;line-height:18pt;} .section tt{font-size:11pt;line-height:11pt;} .section>*{margin-bottom:20px;} pre{font-family:'Panic Sans',Menlo,Monaco,Consolas,Andale Mono,Courier New,monospace !important;font-size:12pt !important;line-height:22px !important;display:block !important;width:auto !important;height:auto !important;overflow:auto !important;white-space:pre !important;word-wrap:normal !important;} #body h1,h1 tt{font-size:28pt;} h1 tt{background-color:transparent;font-size:26pt !important;} #body h2{font-size:24pt;} h2 tt{background-color:transparent;font-size:22pt !important;} #body h3{font-size:20pt;} h3 tt{background-color:transparent;font-size:18pt !important;} #body h4{font-size:16pt;} h4 tt{background-color:transparent;font-size:14pt !important;} #sidebar tt{color:#08c;background-color:transparent;} .hero-unit .toctree-wrapper{text-align:center;} .hero-unit li{display:inline;list-style-type:none;padding-right:20px;} .hero-unit li a{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:10px 10px 10px;font-size:16pt;}.hero-unit li a:first-child{*margin-left:0;} .hero-unit li a:hover,.hero-unit li a:active,.hero-unit li a.active,.hero-unit li a.disabled,.hero-unit li a[disabled]{background-color:#51a351;} .hero-unit li a:active,.hero-unit li a.active{background-color:#408140 \9;} .hero-unit li a:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:hover:hover,.hero-unit li a:hover:active,.hero-unit li a:hover.active,.hero-unit li a:hover.disabled,.hero-unit li a:hover[disabled]{background-color:#51a351;} .hero-unit li a:hover:active,.hero-unit li a:hover.active{background-color:#408140 \9;} .hero-unit li a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:focus:hover,.hero-unit li a:focus:active,.hero-unit li a:focus.active,.hero-unit li a:focus.disabled,.hero-unit li a:focus[disabled]{background-color:#51a351;} .hero-unit li a:focus:active,.hero-unit li a:focus.active{background-color:#408140 \9;} .hero-unit li a:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:active:hover,.hero-unit li a:active:active,.hero-unit li a:active.active,.hero-unit li a:active.disabled,.hero-unit li a:active[disabled]{background-color:#51a351;} .hero-unit li a:active:active,.hero-unit li a:active.active{background-color:#408140 \9;} .hero-unit li a:after{content:" »";} table.docutils{border:1px solid #DDD;width:100%;margin-bottom:18px;}table.docutils th,table.docutils td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} table.docutils th{font-weight:bold;vertical-align:bottom;} table.docutils td{vertical-align:top;} table.docutils thead:first-child tr th,table.docutils thead:first-child tr td{border-top:0;} table.docutils tbody+tbody{border-top:2px solid #ddd;} table.docutils tbody tr:nth-child(odd) td,table.docutils tbody tr:nth-child(odd) th{background-color:#f9f9f9;} metrics-3.2.5/docs/source/_themes/metrics/static/yammerdoc.css000066400000000000000000001122241315671014200244740ustar00rootroot00000000000000article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} audio:not([controls]){display:none;} html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} a:hover,a:active{outline:0;} sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} sup{top:-0.5em;} sub{bottom:-0.25em;} img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;} button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} button,input{*overflow:visible;line-height:normal;} button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} textarea{overflow:auto;vertical-align:top;} body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} a{color:#0088cc;text-decoration:none;} a:hover{color:#005580;text-decoration:underline;} .row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:60px;} .span2{width:140px;} .span3{width:220px;} .span4{width:300px;} .span5{width:380px;} .span6{width:460px;} .span7{width:540px;} .span8{width:620px;} .span9{width:700px;} .span10{width:780px;} .span11{width:860px;} .span12,.container{width:940px;} .offset1{margin-left:100px;} .offset2{margin-left:180px;} .offset3{margin-left:260px;} .offset4{margin-left:340px;} .offset5{margin-left:420px;} .offset6{margin-left:500px;} .offset7{margin-left:580px;} .offset8{margin-left:660px;} .offset9{margin-left:740px;} .offset10{margin-left:820px;} .offset11{margin-left:900px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:6.382978723%;} .row-fluid .span2{width:14.89361702%;} .row-fluid .span3{width:23.404255317%;} .row-fluid .span4{width:31.914893614%;} .row-fluid .span5{width:40.425531911%;} .row-fluid .span6{width:48.93617020799999%;} .row-fluid .span7{width:57.446808505%;} .row-fluid .span8{width:65.95744680199999%;} .row-fluid .span9{width:74.468085099%;} .row-fluid .span10{width:82.97872339599999%;} .row-fluid .span11{width:91.489361693%;} .row-fluid .span12{width:99.99999998999999%;} .container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} .container:after{clear:both;} .container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} .container-fluid:after{clear:both;} p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} .lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} h4,h5,h6{line-height:18px;} h4{font-size:14px;}h4 small{font-size:12px;} h5{font-size:12px;} h6{font-size:11px;color:#999999;text-transform:uppercase;} .page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} .page-header h1{line-height:1;} ul,ol{padding:0;margin:0 0 9px 25px;} ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} ul{list-style:disc;} ol{list-style:decimal;} li{line-height:18px;} ul.unstyled{margin-left:0;list-style:none;} dl{margin-bottom:18px;} dt,dd{line-height:18px;} dt{font-weight:bold;} dd{margin-left:9px;} hr{margin:18px 0;border:0;border-top:1px solid #e5e5e5;border-bottom:1px solid #ffffff;} strong{font-weight:bold;} em{font-style:italic;} .muted{color:#999999;} abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;} blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} q:before,q:after,blockquote:before,blockquote:after{content:"";} address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} small{font-size:100%;} cite{font-style:normal;} .code-and-pre,pre{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .code,code{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;}pre.prettyprint{margin-bottom:18px;} pre code{padding:0;background-color:transparent;} table{max-width:100%;border-collapse:collapse;border-spacing:0;} .table{width:100%;margin-bottom:18px;}.table th,.table td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} .table th{font-weight:bold;vertical-align:bottom;} .table td{vertical-align:top;} .table thead:first-child tr th,.table thead:first-child tr td{border-top:0;} .table tbody+tbody{border-top:2px solid #ddd;} .table-condensed th,.table-condensed td{padding:4px 5px;} .table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;} .table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} .table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} .table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} .table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} .table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} .table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} table .span1{float:none;width:44px;margin-left:0;} table .span2{float:none;width:124px;margin-left:0;} table .span3{float:none;width:204px;margin-left:0;} table .span4{float:none;width:284px;margin-left:0;} table .span5{float:none;width:364px;margin-left:0;} table .span6{float:none;width:444px;margin-left:0;} table .span7{float:none;width:524px;margin-left:0;} table .span8{float:none;width:604px;margin-left:0;} table .span9{float:none;width:684px;margin-left:0;} table .span10{float:none;width:764px;margin-left:0;} table .span11{float:none;width:844px;margin-left:0;} table .span12{float:none;width:924px;margin-left:0;} .btn{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:first-child{*margin-left:0;} .btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} .btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} .btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;} .btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} .btn-large .icon{margin-top:1px;} .btn-small{padding:5px 9px;font-size:11px;line-height:16px;} .btn-small .icon{margin-top:-1px;} .btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} .btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active{color:rgba(255, 255, 255, 0.75);} .btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;} .btn-primary:active,.btn-primary.active{background-color:#003399 \9;} .btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} .btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} .btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} .btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} .btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} .btn-success:active,.btn-success.active{background-color:#408140 \9;} .btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} .btn-info:active,.btn-info.active{background-color:#24748c \9;} button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;} button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;} .nav{margin-left:0;margin-bottom:18px;list-style:none;} .nav>li>a{display:block;} .nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} .nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;} .nav-list>li>a,.nav-list .nav-header{display:block;padding:3px 15px;margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} .nav-list .nav-header{font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-transform:uppercase;} .nav-list>li+.nav-header{margin-top:9px;} .nav-list .active>a{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} .nav-list .icon{margin-right:2px;} .nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} .nav-tabs:after,.nav-pills:after{clear:both;} .nav-tabs>li,.nav-pills>li{float:left;} .nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} .nav-tabs{border-bottom:1px solid #ddd;} .nav-tabs>li{margin-bottom:-1px;} .nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} .nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} .nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} .nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;} .nav-stacked>li{float:none;} .nav-stacked>li>a{margin-right:0;} .nav-tabs.nav-stacked{border-bottom:0;} .nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} .nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} .nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} .nav-pills.nav-stacked>li>a{margin-bottom:3px;} .nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} .nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} .nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} .nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;} .nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;} .nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;} .nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} .nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} .nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);} .tabs-stacked .open>a:hover{border-color:#999999;} .tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} .tabbable:after{clear:both;} .tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} .tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} .tab-content>.active,.pill-content>.active{display:block;} .tabs-below .nav-tabs{border-top:1px solid #ddd;} .tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} .tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} .tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} .tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} .tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} .tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} .tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} .tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} .tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} .tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} .tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} .tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} .tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} .navbar{overflow:visible;margin-bottom:18px;} .navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} .btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} .btn-navbar .icon-bar+.icon-bar{margin-top:3px;} .nav-collapse.collapse{height:auto;} .navbar .brand:hover{text-decoration:none;} .navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} .navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;} .navbar .btn,.navbar .btn-group{margin-top:5px;} .navbar .btn-group .btn{margin-top:0;} .navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} .navbar-form:after{clear:both;} .navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;} .navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} .navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} .navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;} .navbar-search .search-query ::-webkit-input-placeholder{color:#eeeeee;} .navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);} .navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} .navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;} .navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} .navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} .navbar .nav.pull-right{float:right;} .navbar .nav>li{display:block;float:left;} .navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} .navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} .navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;background-color:rgba(0, 0, 0, 0.5);} .navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} .navbar .nav.pull-right{margin-left:10px;margin-right:0;} .navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} .navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} .navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;} .navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} .navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} .navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} .navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;} .navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;} .hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} .hero-unit p{font-size:18px;font-weight:200;line-height:27px;} .pull-right{float:right;} .pull-left{float:left;} .hide{display:none;} .show{display:block;} .invisible{visibility:hidden;} #call-to-action{text-align:right;} a.headerlink{display:none;} #title{color:#ffffff;} .hero-unit h1{padding-bottom:20px ! important;} #top-bar small{color:#f8f8ff;text-shadow:0px -1px 0px #5f0c17;} .admonition{padding:14px 35px 14px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} .admonition .admonition-title{font-size:14pt;font-weight:bold;} .admonition.note .admonition-title,.admonition-todo .admonition-title{color:#c09853;} .admonition.tip,.admonition.hint{background-color:#dff0d8;border-color:#d6e9c6;} .admonition.tip .admonition-title,.admonition.hint .admonition-title{color:#468847;} .admonition.error,.admonition.warning,.admonition.caution,.admonition.danger,.admonition.attention{background-color:#f2dede;border-color:#eed3d7;} .admonition.error .admonition-title,.admonition.warning .admonition-title,.admonition.caution .admonition-title,.admonition.danger .admonition-title,.admonition.attention .admonition-title{color:#b94a48;} .admonition.important{background-color:#d9edf7;border-color:#bce8f1;} .admonition.important .admonition-title{color:#3a87ad;} .admonition>p,.admonition>ul{margin-bottom:0;} .admonition p+p{margin-top:5px;} a.internal.reference>em{font-style:normal ! important;text-decoration:none ! important;} tt{padding:0 3px 2px;font-family:"Panic Sans",Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} .section>p,.section ul li,.admonition p,.section dt,.section dl{font-size:13pt;line-height:18pt;} .section tt{font-size:11pt;line-height:11pt;} .section>*{margin-bottom:20px;} pre{font-family:'Panic Sans',Menlo,Monaco,Consolas,Andale Mono,Courier New,monospace !important;font-size:12pt !important;line-height:22px !important;display:block !important;width:auto !important;height:auto !important;overflow:auto !important;white-space:pre !important;word-wrap:normal !important;} #body h1,h1 tt{font-size:28pt;} h1 tt{background-color:transparent;font-size:26pt !important;} #body h2{font-size:24pt;} h2 tt{background-color:transparent;font-size:22pt !important;} #body h3{font-size:20pt;} h3 tt{background-color:transparent;font-size:18pt !important;} #body h4{font-size:16pt;} h4 tt{background-color:transparent;font-size:14pt !important;} #sidebar tt{color:#08c;background-color:transparent;} .hero-unit .toctree-wrapper{text-align:center;} .hero-unit li{display:inline;list-style-type:none;padding-right:20px;} .hero-unit li a{display:inline-block;padding:4px 10px 4px;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#fafafa;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:10px 10px 10px;font-size:16pt;}.hero-unit li a:first-child{*margin-left:0;} .hero-unit li a:hover,.hero-unit li a:active,.hero-unit li a.active,.hero-unit li a.disabled,.hero-unit li a[disabled]{background-color:#51a351;} .hero-unit li a:active,.hero-unit li a.active{background-color:#408140 \9;} .hero-unit li a:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:hover:hover,.hero-unit li a:hover:active,.hero-unit li a:hover.active,.hero-unit li a:hover.disabled,.hero-unit li a:hover[disabled]{background-color:#51a351;} .hero-unit li a:hover:active,.hero-unit li a:hover.active{background-color:#408140 \9;} .hero-unit li a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:focus:hover,.hero-unit li a:focus:active,.hero-unit li a:focus.active,.hero-unit li a:focus.disabled,.hero-unit li a:focus[disabled]{background-color:#51a351;} .hero-unit li a:focus:active,.hero-unit li a:focus.active{background-color:#408140 \9;} .hero-unit li a:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;color:rgba(0, 0, 0, 0.5);outline:0;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.hero-unit li a:active:hover,.hero-unit li a:active:active,.hero-unit li a:active.active,.hero-unit li a:active.disabled,.hero-unit li a:active[disabled]{background-color:#51a351;} .hero-unit li a:active:active,.hero-unit li a:active.active{background-color:#408140 \9;} .hero-unit li a:after{content:" »";} table.docutils{border:1px solid #DDD;width:100%;margin-bottom:18px;}table.docutils th,table.docutils td{padding:16px;line-height:18px;text-align:left;border-top:1px solid #ddd;} table.docutils th{font-weight:bold;vertical-align:bottom;} table.docutils td{vertical-align:top;} table.docutils thead:first-child tr th,table.docutils thead:first-child tr td{border-top:0;} table.docutils tbody+tbody{border-top:2px solid #ddd;} table.docutils tbody tr:nth-child(odd) td,table.docutils tbody tr:nth-child(odd) th{background-color:#f9f9f9;} metrics-3.2.5/docs/source/_themes/metrics/theme.conf000066400000000000000000000006271315671014200224670ustar00rootroot00000000000000[theme] inherit = none stylesheet = metrics.css pygments_style = trac [options] tagline = Your tagline here. gradient_start = #9b4853 gradient_end = #5f0c17 gradient_text = #ffffff gradient_bg = #7D2A35 gradient_shadow = #fff landing_logo = logo.png landing_logo_width = 150px github_page = https://github.com/yay mailing_list = http://groups.google.com/yay apidocs = https://dropwizard.github.io/metrics/ metrics-3.2.5/docs/source/about/000077500000000000000000000000001315671014200165315ustar00rootroot00000000000000metrics-3.2.5/docs/source/about/contributors.rst000066400000000000000000000160161315671014200220240ustar00rootroot00000000000000.. _about-contributors: ############ Contributors ############ Many, many thanks to: * `Alan Woodward `_ * `Alexander Reelsen `_ * `Alex Lambert `_ * `Anil V `_ * `Anthony Dahanne `_ * `Antonin Stefanutti `_ * `Artem Prigoda `_ * `Bartosz Krasiński `_ * `Bart Prokop `_ * `Basil James Whitehouse III `_ * `Benjamin Gehrels `_ * `Ben Tatham `_ * `Bogdan Storozhuk `_ * `Brenden Matthews `_ * `Brian `_ * `Brian Roberts `_ * `Bruce Mitchener `_ * `cburroughs `_ * `ceetav `_ * `Charles Care `_ * `Chris Birchall `_ * `Christopher Gray `_ * `Christopher Swenson `_ * `ciamac `_ * `Coda Hale `_ * `Collin Van Dyck `_ * `Corentin Chary `_ * `C. Scott Andreas `_ * `Dag Liodden `_ * `Dale Wijnand `_ * `Dan Brown `_ * `Dan Everton `_ * `Daniel James `_ * `Dan Revel `_ * `David M. Karr `_ * `David Schlosnagle `_ * `David Sutherland `_ * `Diwaker Gupta `_ * `Drew Stephens `_ * `Eduard Martinescu `_ * `Edwin Shin `_ * `Erik van Oosten `_ * `Evan Jones `_ * `Fabrizio Cannizzo `_ * `François Beausoleil `_ * `Gabor Arki `_ * `George Spalding `_ * `Gerolf Seitz `_ * `gilbode `_ * `Greg Bowyer `_ * `Gunnar Ahlberg `_ * `Henri Tremblay `_ * `ho3rexqj `_ * `Hussein Elsayed `_ * `Ian Strachan `_ * `Istvan Meszaros `_ * `Ivan Dyedov `_ * `Jackson Davis `_ * `James Burkhart `_ * `James Casey `_ * `Jan-Helge Bergesen `_ * `Janne Sinivirta `_ * `Jason A. Beranek `_ * `Jason Slagle `_ * `Jason Whitlark `_ * `Jeff Hodges `_ * `Jeff Klukas `_ * `Jeff Wartes `_ * `Jens Schauder `_ * `Jesper Blomquist `_ * `Jesse Eichar `_ * `Jochen Schalanda `_ * `Joe Ellis `_ * `Joel Takvorian `_ * `John-John Tedro `_ * `John Wang `_ * `Jordan Focht `_ * `Juha Syrjälä `_ * `Julio Lopez `_ * `Justin Plock `_ * `Kevin Clark `_ * `Kevin Menard `_ * `Kevin Yeh `_ * `konnik `_ * `Larry Shatzer, Jr. `_ * `Luke Amdor `_ * `Mahesh Tiyyagura `_ * `Mark Menard `_ * `Marlon Bernardes `_ * `Mårten Gustafson `_ * `Martin Jöhren `_ * `Martin Traverso `_ * `Matheus Cabral `_ * `Matt Abrams `_ * `Matthew Gilliard `_ * `Matthew O'Connor `_ * `Matt Veitas `_ * `Michał Minicki `_ * `Miikka Koskinen `_ * `Neil Prosser `_ * `Nick Babcock `_ * `Nick Telford `_ * `Norbert Potocki `_ * `Pablo Fernandez `_ * `Patryk Najda `_ * `Paul Brown `_ * `Paul Doran `_ * `Paul Sanwald `_ * `Philipp Hauer `_ * `Raman Gupta `_ * `Realbot `_ * `Robby Walker `_ * `Ron Klein `_ * `Ryan Campbell `_ * `Ryan McCrone `_ * `Ryan Tenney `_ * `saadmufti `_ * `Sam Perman `_ * `Samy Dindane `_ * `Sean Laurent `_ * `Sebastian Lövdahl `_ * `Sergey Nazarov `_ * `Silvia Mandalà `_ * `sofax `_ * `Steve Fosdal `_ * `Steven Schlansker `_ * `stockmaj `_ * `Stuart Gunter `_ * `Thomas Cashman `_ * `Tobias Bieniek `_ * `Tobias Lidskog `_ * `Tom Akehurst `_ * `Tomasz Guzik `_ * `Tomasz Nurkiewicz `_ * `Tom Golden `_ * `tvleminckx `_ * `v-garki `_ * `Vladimir Bukhtoyarov `_ * `Volker Fritzsch `_ * `Wolfgang Hoschek `_ * `Wolfgang Schell `_ * `yeyangever `_ * `Zach A. Thomas `_ metrics-3.2.5/docs/source/about/index.rst000066400000000000000000000002131315671014200203660ustar00rootroot00000000000000.. title:: About .. _about: ############# About Metrics ############# .. toctree:: :maxdepth: 1 contributors release-notes metrics-3.2.5/docs/source/about/release-notes.rst000066400000000000000000001052471315671014200220420ustar00rootroot00000000000000.. _release-notes: ############# Release Notes ############# .. _rel-3.2.3: v3.2.3: Jun 28 2017 =================== * Improve ``ScheduledReporter`` ``convertDurations`` precision `#1115 `_ * Suppress all kinds of Throwables raised by ``report()`` `#1128 `_ * ``ExponentiallyDecayingReservoir`` was giving incorrect values in the snapshot if the inactive period was too long `#1135 `_ * Ability to get default metrics registry without an exception `#1140 `_ * Ability to get default health check registry without an exception `#1152 `_ * ``SlidingTimeWindowArrayReservoir`` as a fast alternative of ``SlidingTimeWindowReservoir`` `#1139 `_ * Avoid a NPE in toString of ``HealthCheck.Result`` `#1141 `_ .. _rel-3.1.5: v3.1.5: Jun 2 2017 =================== * More robust lookup of ``ThreadLocal`` and ``LongAdder`` on JDK6 (e.g. WebLogic) `#1136 `_ .. _rel-3.2.2: v3.2.2: Mar 20 2017 =================== * Fix creating a uniform snapshot from a collection `#1111 `_ * Register metrics defined at Resource level `#1105 `_ .. _rel-3.2.1: v3.2.1: Mar 10 2017 =================== * Support for shutting down the health check registry. `#1084 `_ * Added support for the default shared health check registry name #1095 `#1095 `_ * SharedMetricRegistries are now thread-safe. `#1094 `_ * The size of the snapshot of a histogram is reported via JMX. `#1102 `_ * Don't ignore the counter attribute for reporters. `#1090 `_ * Added support for disabling attributes in ConsoleReporter. `#1092 `_ * Rollbacked GraphiteSanitize to replacing whitespaces. `#1099 `_ .. _rel-3.1.4: v3.1.4: Mar 10 2017 =================== * Fix accidentally broken Graphite UDP reporter `#1100 `_ .. _rel-3.2.0: v3.2.0: Feb 24 2017 =================== * `GraphiteReporter` opens a new TCP connection when sending metrics instead of maintaining a persisted connection. `#1047 `_ * `GraphiteReporter` retries DNS lookups in case of a lookup failure. `#1064 `_ * `ScheduledReporter` suppresses all kind of exceptions raised by the `report` method. `#1049 `_ * JDK's `ThreadLocalRandom` is now used by default. `#1052 `_ * JDK's `LongAdder` is now used by default. `#1055 `_ * Fixed a race condition bug in `ExponentiallyDecayingReservoir`. `#1033 `_ * Fixed a long overflow bug in `SlidingTimeWindowReservoir`. `#1063 `_ * `AdminServlet` supports CPU profiling. `#927 `_ * `GraphiteReporter` sanitizes metrics. `#938 `_ * Support for publishing `BigInteger` and `BigDecimal` metrics in `GraphiteReporter`. `#933 `_ * Support for publishing boolean metrics in `GraphiteReporter`. `#905 `_ * Added support for overriding the format of floating numbers in `GraphiteReporter`. `#1073 `_ * Added support for disabling reporting of metric attributes. `#1048 `_ * Reporters are more user friendly for managed environments like GAE or JEE. `#1018 `_ * Support for setting a custom initial delay for reporters. `#999 `_ * Support for custom details in a result of a health check. `#663 `_ * Added a listener for health checks. `#1068 `_ * Support for asynchronous health checks `#1077 `_ * Health checks are reported as unhealthy on exceptions. `#783 `_ * Allow setting a custom prefix for Jetty's `InstrumentedQueuedThreadPool`. `#947 `_ * Allow setting custom prefix for Jetty's `QueuedThreadPool`. `#908 `_ * Added support for Jetty 9.3 and higher. `#1038 `_ * Fixed instrumentation of Jetty9 async servlets. `#1074 `_ * Added support for JCache/JSR 107 metrics. `#1010 `_ * Added thread-safe getters for metrics with custom instantiations. `#1023 `_ * Added an overload of `Timer#time` that takes a `Runnable`. `#989 `_ * Support extracting the request URI from wrapped requests in `HttpClientMetricNameStrategies`. `#947 `_ * Support for the log4j2 xml-based config. `#900 `_ * Internal `Striped64` doesn't depend on `sun.misc.Unsafe` anymore. `#966 `_ * Optimized creation of `UniformSnapshot`. `#970 `_ * Added a memory pool gauge to the JVM memory usage metrics. `#786 `_ * Added support for async servlets for `metric-servlet`. `#796 `_ * Opt-in default shared metric registry. `#801 `_ * Added support for patterns in MBean object names `#809 `_ * Allow a pluggable strategy for the name of the CSV files for `CsvReporter`. `#882 `_ * Upgraded to slf4j 1.22 * Upgraded to Jackson 2.6.6 * Upgraded to amqp-client 3.6.6 * Upgraded to httpclient 4.5.2 * Upgraded to log4j2 2.3 * Upgraded to logback 1.1.10 .. _rel-3.1.3: v3.1.3: Feb 24 2017 =================== * `GraphiteReporter` opens a new TCP connection when sending metrics instead of maintaining a persisted connection. `#1036 `_ * `GraphiteReporter` retries DNS lookups in case of a lookup failure. `#1064 `_ * `ScheduledReporter` suppresses all kind of exceptions raised by the `report` method. `#1040 `_ * JDK's `ThreadLocalRandom` is now used by default. `#1052 `_ * JDK's `LongAdder` is now used by default. `#1055 `_ * Fixed a race condition bug in `ExponentiallyDecayingReservoir`. `#1046 `_ * Fixed a long overflow bug in `SlidingTimeWindowReservoir`. `#1072 `_ .. _rel-3.1.0: v3.1.0: Sen 10 2014 =================== https://groups.google.com/forum/#!topic/metrics-user/zwzHnMBcAX4 * Upgrade to Jetty 9.1 (metrics-jetty9, Jetty 9.0 module renamed to metrics-jetty9-legacy) * Add log4j2 support (metrics-log4j2) * Upgrade to Jersey2 (metrics-jersey2) * Add httpasyncclient support (metrics-httpasyncclient) * Changed maven groupId to io.dropwizard.metrics * Enable Java8 builds on Travis, fix javadocs and disable some doclinting * Fixing some compilation warnings about missing generics and varargs invocation * Instrumentation for java.util.concurrent classes * ExponentiallyDecayingReservoir: quantiles weighting * Loosen type requirements for JmxAttributeGauge constructor * SlidingWindowReservoir - ArrayOutOfBoundsException thrown if # of Reservoir examples exceeds Integer max value * Classloader metrics * Add an instrumented ScheduledExecutorService * Fix race condition in InstrumentedThreadFactoryTest * Correct comparison of System.nanoTime in SlidingTimeWindowReservoir * Add SharedHealthCheckRegistries class * Migrate benchmarks from Caliper to JMH * New annotations: @CachedGauge, @Counted, @Metric * Support for annotations on classes and constructors * Allow @Metric on methods and parameters * Add @Inherited and @Documented on all type annotations * Adapted ehcache integration to latest ehcache version 2.8.3 * Upgrade to HttpClient 4.3 * InstrumentedHandler: Remove duplicate calls to requests.update(...) * New metric 'utilization-max' to track thread usage out of max pool size in jetty * Replaced Jetty-specific Request with Servlet API interfaces * Jetty 8: Avoid NPE if InstrumentedQueuedThreadPool gauges are read too early * Jetty 8: Call updateResponses onComplete of ContinuationListener * Allow specifying a custom prefix Jetty 9 InstrumentedHandler * MetricsModule is serializing wrong minute rates for timers * MeterSerializer.serialize had m1_rate and m15_rate transposed * Add CachedThreadStatesGaugeSet * Monitor count of deadlock threads * Prevent exceptions from ThreadDumpServlet on Google AppEngine * Upgrade to logback 1.1.1 * Allow InstrumentedAppender use in logback.xml * Use getClass() in place of AbstractInstrumentedFilter.class in generated metric names * Update MetricsServlet with support for JSONP as alternative to CORS * Specify the base name of the metrics as a filter init-param for the metrics captured in the AbstractInstrumentedFilter * Add option to provide MetricFilter to MetricsServlet * AdminServlet generates link to pretty printed healthchecks * MetricsServlet.ContextListener doesn't initialize the context correctly * Every reporter implements Reporter interface to indicate that is a Reporter * Added support for passing a ScheduledExecutorService to ScheduledReporters * Improve the ScheduledReporter#stop method * Ensure ScheduledReporters get unique thread pools. * Suppress runtime exceptions thrown from ScheduledReporter#report * Ability to inject a factory of ObjectName * Lazy fetch of PlatformMBeanServer * JMX Reporter throws exception when metric name contains an asterisk * onTimerRemoved in JmxListener calls registered.add * Support for mBean servers that rewrite the supplied ObjectName upon registration * Graphite reporter does not notify when Graphite/Carbon server is unreachable * Persistent connections to Graphite * Graphite constructor accepts host/port * Graphtie Pickle sender * Graphite UDP sender * Graphite AMQP sender * Add a threshold/minimum value to report before converting results to 0 * Report to multiple gmetric instances * Escape slahes on ganglia metric names * Upgrade slf4j to 1.7.6 * Enhancement for logging level option on Slf4jReporter .. _rel-3.0.1: v3.0.1: Jul 23 2013 =================== * Fixed NPE in ``MetricRegistry#name``. * ``ScheduledReporter`` and ``JmxReporter`` now implement ``Closeable``. * Fixed cast exception for async requests in ``metrics-jetty9``. * Added support for ``Access-Control-Allow-Origin`` to ``MetricsServlet``. * Fixed numerical issue with ``Meter`` EWMA rates. * Deprecated ``AdminServletContextListener`` in favor of ``MetricsServlet.ContextListener`` and ``HealthCheckServlet.ContextListener``. * Added additional constructors to ``HealthCheckServlet`` and ``MetricsServlet``. .. _rel-3.0.0: v3.0.0: June 10 2013 ==================== * Renamed ``DefaultWebappMetricsFilter`` to ``InstrumentedFilter``. * Renamed ``MetricsContextListener`` to ``InstrumentedFilterContextListener`` and made it fully abstract to avoid confusion. * Renamed ``MetricsServletContextListener`` to ``AdminServletContextListener`` and made it fully abstract to avoid confusion. * Upgraded to Servlet API 3.1. * Upgraded to Jackson 2.2.2. * Upgraded to Jetty 8.1.11. .. _rel-3.0.0-RC1: v3.0.0-RC1: May 31 2013 ======================= * Added ``SharedMetricRegistries``, a singleton for sharing named metric registries. * Fixed XML configuration for ``metrics-ehcache``. * Fixed XML configuration for ``metrics-jersey``. * Fixed XML configuration for ``metrics-log4j``. * Fixed XML configuration for ``metrics-logback``. * Fixed a counting bug in ``metrics-jetty9``'s InstrumentedHandler. * Added ``MetricsContextListener`` to ``metrics-servlet``. * Added ``MetricsServletContextListener`` to ``metrics-servlets``. * Extracted the ``Counting`` interface. * Reverted ``SlidingWindowReservoir`` to a synchronized implementation. * Added the implementation version to the JAR manifests. * Made dependencies for all modules conform to Maven Enforcer's convergence rules. * Fixed ``Slf4jReporter``'s logging of 99th percentiles. * Added optional name prefixing to ``GraphiteReporter``. * Added metric-specific overrides of rate and duration units to ``JmxReporter``. * Documentation fixes. .. _rel-3.0.0-BETA3: v3.0.0-BETA3: May 13 2013 ========================= * Added ``ScheduledReporter#report()`` for manual reporting. * Fixed overly-grabby catches in ``HealthCheck`` and ``InstrumentedResourceMethodDispatchProvider``. * Fixed phantom reads in ``SlidingWindowReservoir``. * Revamped ``metrics-jetty9``, removing ``InstrumentedConnector`` and improving the API. * Fixed OSGi imports for ``sun.misc``. * Added a strategy class for ``HttpClient`` metrics. * Upgraded to Jetty 9.0.3. * Upgraded to Jackson 2.2.1. * Upgraded to Ehcache 2.6.6. * Upgraded to Logback 1.0.13. * Upgraded to HttpClient 4.2.5. * Upgraded to gmetric4j 1.0.3, which allows for host spoofing. .. _rel-3.0.0-BETA2: v3.0.0-BETA2: Apr 22 2013 ========================= * Metrics is now under the ``com.codahale.metrics`` package, with the corresponding changes in Maven artifact groups. This should allow for an easier upgrade path without classpath conflicts. * ``MetricRegistry`` no longer has a name. * Added ``metrics-jetty9`` for Jetty 9. * ``JmxReporter`` takes an optional domain property to disambiguate multiple reporters. * Fixed Java 6 compatibility problem. (Also added Java 6 as a CI environment.) * Added ``MetricRegistryListener.Base``. * Switched ``Counter``, ``Meter``, and ``EWMA`` to use JSR133's ``LongAdder`` instead of ``AtomicLong``, improving contended concurrency. * Added ``MetricRegistry#buildMap()``, allowing for custom map implementations in ``MetricRegistry``. * Added ``MetricRegistry#removeMatching(MetricFilter)``. * Changed ``metrics-json`` to optionally depend on ``metrics-healthcheck``. * Upgraded to Jetty 8.1.10 for ``metrics-jetty8``. .. _rel-3.0.0-BETA1: v3.0.0-BETA1: Apr 01 2013 ========================= * Total overhaul of most of the core Metrics classes: * Metric names are now just dotted paths like ``com.example.Thing``, allowing for very flexible scopes, etc. * Meters and timers no longer have rate or duration units; those are properties of reporters. * Reporter architecture has been radically simplified, fixing many bugs. * Histograms and timers can take arbitrary reservoir implementations. * Added sliding window reservoir implementations. * Added ``MetricSet`` for sets of metrics. * Changed package names to be OSGi-compatible and added OSGi bundling. * Extracted JVM instrumentation to ``metrics-jvm``. * Extracted Jackson integration to ``metrics-json``. * Removed ``metrics-guice``, ``metrics-scala``, and ``metrics-spring``. * Renamed ``metrics-servlet`` to ``metrics-servlets``. * Renamed ``metrics-web`` to ``metrics-servlet``. * Renamed ``metrics-jetty`` to ``metrics-jetty8``. * Many more small changes! .. _rel-2.2.0: v2.2.0: Nov 26 2012 =================== * Removed all OSGi bundling. This will be back in 3.0. * Added ``InstrumentedSslSelectChannelConnector`` and ``InstrumentedSslSocketConnector``. * Made all metric names JMX-safe. * Upgraded to Ehcache 2.6.2. * Upgraded to Apache HttpClient 4.2.2. * Upgraded to Jersey 1.15. * Upgraded to Log4j 1.2.17. * Upgraded to Logback 1.0.7. * Upgraded to Spring 3.1.3. * Upgraded to Jetty 8.1.8. * Upgraded to SLF4J 1.7.2. * Replaced usage of ``Unsafe`` in ``InstrumentedResourceMethodDispatchProvider`` with type erasure trickery. .. _rel-2.1.5: v2.1.5: Nov 19 2012 =================== * Upgraded to Jackson 2.1.1. .. _rel-2.1.4: v2.1.4: Nov 15 2012 =================== * Added OSGi bundling manifests. .. _rel-2.1.3: v2.1.3: Aug 06 2012 =================== * Upgraded to Apache HttpClient 4.2.1. * Changed ``InstrumentedClientConnManager`` to extend ``PoolingClientConnectionManager`` instead of the deprecated ``ThreadSafeClientConnManager``. * Fixed a bug in ``ExponentiallyDecayingSample`` with long periods of inactivity. * Fixed problems with re-registering metrics in JMX. * Added support for ``DnsResolver`` instances to ``InstrumentedClientConnManager``. * Added support for formatted health check error messages. .. _rel-2.1.2: v2.1.2: Apr 11 2012 =================== * Fixed double-registration in ``metrics-guice``. .. _rel-2.1.1: v2.1.1: Mar 13 2012 =================== * Fixed instrumentation of all usages of ``InstrumentedHttpClient``. .. _rel-2.1.0: v2.1.0: Mar 12 2012 =================== * Added support for Java 7's direct and mapped buffer pool stats in ``VirtualMachineMetrics`` and ``metrics-servlet``. * Added support for XML configuration in ``metrics-ehcache``. * ``metrics-spring`` now support ``@Gauge``-annotated fields. * Opened ``GraphiteReporter`` up for extension. * Added ``group`` and ``type`` to ``metrics-annotations``, ``metrics-guice``, ``metrics-jersey``, and ``metrics-spring``. * Fixed handling of non-int gauges in ``GangliaReporter``. * Fixed ``NullPointerException`` errors in ``metrics-spring``. * General improvements to ``metrics-spring``, including allowing custom ``Clock`` instances. .. _rel-2.0.3: v2.0.3: Feb 24 2012 =================== * Change logging of ``InstanceNotFoundException`` exceptions thrown while unregistering a metric in ``JmxReporter`` to ``TRACE``. It being ``WARN`` resulted in huge log dumps preventing process shutdowns when applications had ~1K+ metrics. * Upgraded to Spring 3.1.1 for ``metrics-spring``. * Upgraded to JDBI 2.31.2. * Upgraded to Jersey 1.12. * Upgraded to Jetty 7.6.1. * Fixed rate units for meters in ``GangliaReporter``. .. _rel-2.0.2: v2.0.2: Feb 09 2012 =================== * ``InstrumentationModule`` in ``metrics-guice`` now uses the default ``MetricsRegistry`` and ``HealthCheckRegistry``. .. _rel-2.0.1: v2.0.1: Feb 08 2012 =================== * Fixed a concurrency bug in ``JmxReporter``. .. _rel-2.0.0: v2.0.0: Feb 07 2012 =================== * Upgraded to Jackson 1.9.4. * Upgraded to Jetty 7.6.0. * Added escaping for garbage collector and memory pool names in ``GraphiteReporter``. * Fixed the inability to start and stop multiple reporter instances. * Switched to using a backported version of ``ThreadLocalRandom`` for ``UniformSample`` and ``ExponentiallyDecayingSample`` to reduce lock contention on random number generation. * Removed ``Ordered`` from ``TimedAnnotationBeanPostProcessor`` in ``metrics-spring``. * Upgraded to JDBI 2.31.1. * Upgraded to Ehcache 2.5.1. * Added ``#timerContext()`` to Scala ``Timer``. .. _rel-2.0.0-RC0: v2.0.0-RC0: Jan 19 2012 ======================= * Added FindBugs checks to the build process. * Fixed the catching of ``Error`` instances thrown during health checks. * Added ``enable`` static methods to ``CsvReporter`` and changed ``CsvReporter(File, MetricsRegistry)`` to ``CsvReporter(MetricsRegistry, File)``. * Slimmed down ``InstrumentedEhcache``. * Hid the internals of ``GangliaReporter``. * Hid the internals of ``metrics-guice``. * Changed ``metrics-httpclient`` to consistently associate metrics with the ``org.apache`` class being extended. * Hid the internals of ``metrics-httpclient``. * Rewrote ``InstrumentedAppender`` in ``metrics-log4j``. It no longer forwards events to an appender. Instead, you can just attach it to your root logger to instrument logging. * Rewrote ``InstrumentedAppender`` in ``metrics-logback``. No major API changes. * Fixed bugs with ``@ExceptionMetered``-annotated resource methods in ``metrics-jersey``. * Fixed bugs generating ``Snapshot`` instances from concurrently modified collections. * Fixed edge case in ``MetricsServlet``'s thread dumps where one thread could be missed. * Added ``RatioGauge`` and ``PercentGauge``. * Changed ``InstrumentedQueuedThreadPool``'s ``percent-idle`` gauge to be a ratio. * Decomposed ``MetricsServlet`` into a set of focused servlets: ``HealthCheckServlet``, ``MetricsServlet``, ``PingServlet``, and ``ThreadDumpServlet``. The top-level servlet which provides the HTML menu page is now ``AdminServlet``. * Added ``metrics-spring``. .. _rel-2.0.0-BETA19: v2.0.0-BETA19: Jan 07 2012 ========================== * Added absolute memory usage to ``MetricsServlet``. * Extracted ``@Timed`` etc. to ``metrics-annotations``. * Added ``metrics-jersey``, which provides a class allowing you to automatically instrument all ``@Timed``, ``@Metered``, and ``@ExceptionMetered``-annotated resource methods. * Moved all classes in ``metrics-scala`` from ``com.yammer.metrics`` to ``com.yammer.metrics.scala``. * Renamed ``CounterMetric`` to ``Counter``. * Renamed ``GaugeMetric`` to ``Gauge``. * Renamed ``HistogramMetric`` to ``Histogram``. * Renamed ``MeterMetric`` to ``Meter``. * Renamed ``TimerMetric`` to ``Timer``. * Added ``ToggleGauge``, which returns ``1`` the first time it's called and ``0`` every time after that. * Now licensed under Apache License 2.0. * Converted ``VirtualMachineMetrics`` to a non-singleton class. * Removed ``Utils``. * Removed deprecated constructors from ``Meter`` and ``Timer``. * Removed ``LoggerMemoryLeakFix``. * ``DeathRattleExceptionHandler`` now logs to SLF4J, not syserr. * Added ``MetricsRegistry#groupedMetrics()``. * Removed ``Metrics#allMetrics()``. * Removed ``Metrics#remove(MetricName)``. * Removed ``MetricsRegistry#threadPools()`` and ``#newMeterTickThreadPool()`` and added ``#newScheduledThreadPool``. * Added ``MetricsRegistry#shutdown()``. * Renamed ``ThreadPools#shutdownThreadPools()`` to ``#shutdown()``. * Replaced ``HealthCheck``'s abstract ``name`` method with a required constructor parameter. * ``HealthCheck#check()`` is now ``protected``. * Moved ``DeadlockHealthCheck`` from ``com.yammer.metrics.core`` to ``com.yammer.metrics.utils``. * Added ``HealthCheckRegistry#unregister(HealthCheck)``. * Fixed typo in ``VirtualMachineMetrics`` and ``MetricsServlet``: ``commited`` to ``committed``. * Changed ``MetricsRegistry#createName`` to ``protected``. * All metric types are created exclusively through ``MetricsRegistry`` now. * ``Metrics.newJmxGauge`` and ``MetricsRegistry.newJmxGauge`` are deprecated. * Fixed heap metrics in ``VirtualMachineMetrics``. * Added ``Snapshot``, which calculates quantiles. * Renamed ``Percentiled`` to ``Sampling`` and dropped ``percentile`` and ``percentiles`` in favor of producing ``Snapshot`` instances. This affects both ``Histogram`` and ``Timer``. * Renamed ``Summarized`` to ``Summarizable``. * Changed order of ``CsvReporter``'s construction parameters. * Renamed ``VirtualMachineMetrics.GarbageCollector`` to ``VirtualMachineMetrics.GarbageCollectorStats``. * Moved Guice/Servlet support from ``metrics-servlet`` to ``metrics-guice``. * Removed ``metrics-aop``. * Removed ``newJmxGauge`` from both ``Metrics`` and ``MetricsRegistry``. Just use ``JmxGauge``. * Moved ``JmxGauge`` to ``com.yammer.metrics.util``. * Moved ``MetricPredicate`` to ``com.yammer.metrics.core``. * Moved ``NameThreadFactory`` into ``ThreadPools`` and made ``ThreadPools`` package-visible. * Removed ``Timer#values()``, ``Histogram#values()``, and ``Sample#values()``. Use ``getSnapshot()`` instead. * Removed ``Timer#dump(File)`` and ``Histogram#dump(File)``, and ``Sample#dump(File)``. Use ``Snapshot#dump(File)`` instead. .. _rel-2.0.0-BETA18: v2.0.0-BETA18: Dec 16 2011 ========================== * Added ``DeathRattleExceptionHandler``. * Fixed NPE in ``VirtualMachineMetrics``. * Added decorators for connectors and thread pools in ``metrics-jetty``. * Added ``TimerMetric#time()`` and ``TimerContext``. * Added a shorter factory method for millisecond/second timers. * Switched tests to JUnit. * Improved logging in ``GangliaReporter``. * Improved random number generation for ``UniformSample``. * Added ``metrics-httpclient`` for instrumenting Apache HttpClient 4.1. * Massively overhauled the reporting code. * Added support for instrumented, non-``public`` methods in ``metrics-guice``. * Added ``@ExceptionMetered`` to ``metrics-guice``. * Added group prefixes to ``GangliaReporter``. * Added ``CvsReporter``, which outputs metric values to ``.csv`` files. * Improved metric name sanitization in ``GangliaReporter``. * Added ``Metrics.shutdown()`` and improved metrics lifecycle behavior. * Added ``metrics-web``. * Upgraded to ehcache 2.5.0. * Many, many refactorings. * ``metrics-servlet`` now responds with ``501 Not Implememented`` when no health checks have been registered. * Many internal refactorings for testability. * Added histogram counts to ``metrics-servlet``. * Fixed a race condition in ``ExponentiallyDecayingSample``. * Added timezone and locale support to ``ConsoleReporter``. * Added ``metrics-aop`` for Guiceless support of method annotations. * Added ``metrics-jdbi`` which adds instrumentation to JDBI_. * Fixed NPE for metrics which belong to classes in the default package. * Now deploying artifacts to Maven Central. .. _JDBI: http://www.jdbi.org .. _rel-2.0.0-BETA17: v2.0.0-BETA17: Oct 07 2011 ========================== * Added an option message to successful health check results. * Fixed locale issues in ``GraphiteReporter``. * Added ``GangliaReporter``. * Added per-HTTP method timers to ``InstrumentedHandler`` in ``metrics-jetty``. * Fixed a thread pool leak for meters. * Added ``#dump(File)`` to ``HistogramMetric`` and ``TimerMetric``. * Upgraded to Jackson 1.9.x. * Upgraded to slf4j 1.6.2. * Upgraded to logback 0.9.30. * Upgraded to ehcache 2.4.5. * Surfaced ``Metrics.removeMetric()``. .. _rel-2.0.0-BETA16: v2.0.0-BETA16: Aug 23 2011 ========================== * Fixed a bug in GC monitoring. .. _rel-2.0.0-BETA15: v2.0.0-BETA15: Aug 15 2011 ========================== * Fixed dependency scopes for ``metrics-jetty``. * Added time and VM version to ``vm`` output of ``MetricsServlet``. * Dropped ``com.sun.mangement``-based GC instrumentation in favor of a ``java.lang.management``-based one. ``getLastGcInfo`` has a nasty native memory leak in it, plus it often returned incorrect data. * Upgraded to Jackson 1.8.5. * Upgraded to Jetty 7.4.5. * Added sanitization for metric names in ``GraphiteReporter``. * Extracted out a ``Clock`` interface for timers for non-wall-clock timing. * Extracted out most of the remaining statics into ``MetricsRegistry`` and ``HealthCheckRegistry``. * Added an init parameter to ``MetricsServlet`` for disabling the ``jvm`` section. * Added a Guice module for ``MetricsServlet``. * Added dynamic metric names. * Upgraded to ehcache 2.4.5. * Upgraded to logback 0.9.29. * Allowed for the removal of metrics. * Added the ability to filter metrics exposed by a reporter to those which match a given predicate. .. _rel-2.0.0-BETA14: v2.0.0-BETA14: Jul 05 2011 ========================== * Moved to Maven for a build system and extracted the Scala façade to a ``metrics-scala`` module which is now the only cross-built module. All other modules dropped the Scala version suffix in their ``artifactId``. * Fixed non-heap metric name in ``GraphiteReporter``. * Fixed stability error in ``GraphiteReporter`` when dealing with unavailable servers. * Fixed error with anonymous, instrumented classes. * Fixed error in ``MetricsServlet`` when a gauge throws an exception. * Fixed error with bogus GC run times. * Link to the pretty JSON output from the ``MetricsServlet`` menu page. * Fixed potential race condition in histograms' variance calculations. * Fixed memory pool reporting for the G1 collector. .. _rel-2.0.0-BETA13: v2.0.0-BETA13: May 13 2011 ========================== * Fixed a bug in the initial startup phase of the ``JmxReporter``. * Added ``metrics-ehcache``, for the instrumentation of ``Ehcache`` instances. * Fixed a typo in ``metrics-jetty``'s ``InstrumentedHandler``. * Added name prefixes to ``GraphiteReporter``. * Added JVM metrics reporting to ``GraphiteReporter``. * Actually fixed ``MetricsServlet``'s links when the servlet has a non-root context path. * Now cross-building for Scala 2.9.0. * Added ``pretty`` query parameter for ``MetricsServlet`` to format the JSON object for human consumption. * Added ``no-cache`` headers to the ``MetricsServlet`` responses. .. _rel-2.0.0-BETA12: v2.0.0-BETA12: May 09 2011 ========================== * Upgraded to Jackson 1.7.6. * Added a new instrumented Log4J appender. * Added a new instrumented Logback appender. Thanks to Bruce Mitchener (@waywardmonkeys) for the patch. * Added a new reporter for the Graphite_ aggregation system. Thanks to Mahesh Tiyyagura (@tmahesh) for the patch. * Added scoped metric names. * Added Scala 2.9.0.RC{2,3,4} as build targets. * Added meters to Jetty handler for the percent of responses which have ``4xx`` or ``5xx`` status codes. * Changed the Servlet API to be a ``provided`` dependency. Thanks to Mårten Gustafson (@chids) for the patch. * Separated project into modules: * ``metrics-core``: A dependency-less project with all the core metrics. * ``metrics-graphite``: A reporter for the [Graphite](http://graphite.wikidot.com) aggregation system. * ``metrics-guice``: Guice AOP support. * ``metrics-jetty``: An instrumented Jetty handler. * ``metrics-log4j``: An instrumented Log4J appender. * ``metrics-logback``: An instrumented Logback appender. * ``metrics-servlet``: The Metrics servlet with context listener. .. _Graphite: http://graphite.wikidot.com .. _rel-2.0.0-BETA11: v2.0.0-BETA11: Apr 27 2011 ========================== * Added thread state and deadlock detection metrics. * Fix ``VirtualMachineMetrics``' initialization. * Context path fixes for the servlet. * Added the ``@Gauge`` annotation. * Big reworking of the exponentially-weighted moving average code for meters. Thanks to JD Maturen (@sku) and John Ewart (@johnewart) for pointing this out. * Upgraded to Guice 3.0. * Upgraded to Jackson 1.7.5. * Upgraded to Jetty 7.4.0. * Big rewrite of the servlet's thread dump code. * Fixed race condition in ``ExponentiallyDecayingSample``. Thanks to Martin Traverso (@martint) for the patch. * Lots of spelling fixes in Javadocs. Thanks to Bruce Mitchener (@waywardmonkeys) for the patch. * Added Scala 2.9.0.RC1 as a build target. Thanks to Bruce Mitchener (@waywardmonkeys) for the patch. * Patched a hilarious memory leak in ``java.util.logging``. .. _rel-2.0.0-BETA10: v2.0.0-BETA10: Mar 25 2011 ========================== * Added Guice AOP annotations: ``@Timed`` and ``@Metered``. * Added ``HealthCheck#name()``. * Added ``Metrics.newJmxGauge()``. * Moved health checks into ``HealthChecks``. * Upgraded to Jackson 1.7.3 and Jetty 7.3.1. .. _rel-2.0.0-BETA9: v2.0.0-BETA9: Mar 14 2011 ========================= * Fixed ``JmxReporter`` lag. * Added default arguments to timers and meters. * Added default landing page to the servlet. * Improved the performance of ``ExponentiallyDecayingSample``. * Fixed an integer overflow bug in ``UniformSample``. * Added linear scaling to ``ExponentiallyDecayingSample``. .. _rel-2.0.0-BETA8: v2.0.0-BETA8: Mar 01 2011 ========================= * Added histograms. * Added biased sampling for timers. * Added dumping of timer/histogram samples via the servlet. * Added dependency on ``jackon-mapper``. * Added classname filtering for the servlet. * Added URI configuration for the servlet. .. _rel-2.0.0-BETA7: v2.0.0-BETA7: Jan 12 2011 ========================= * Added ``JettyHandler``. * Made the ``Servlet`` dependency optional. .. _rel-2.0.0-BETA6: v2.0.0-BETA6: Jan 12 2011 ========================= * Fix ``JmxReporter`` initialization. .. _rel-2.0.0-BETA5: v2.0.0-BETA5: Jan 11 2011 ========================= * Dropped ``Counter#++`` and ``Counter#--``. * Added ``Timer#update``. * Upgraded to Jackson 1.7.0. * Made JMX reporting implicit. * Added health checks. .. _rel-2.0.0-BETA3: v2.0.0-BETA3: Dec 23 2010 ========================= * Fixed thread names and some docs. .. _rel-2.0.0-BETA2: v2.0.0-BETA2: Dec 22 2010 ========================= * Fixed a memory leak in ``MeterMetric``. .. _rel-2.0.0-BETA1: v2.0.0-BETA1: Dec 22 2010 ========================= * Total rewrite in Java. .. _rel-1.0.7: v1.0.7: Sep 21 2010 =================== * Added ``median`` to ``Timer``. * Added ``p95`` to ``Timer`` (95th percentile). * Added ``p98`` to ``Timer`` (98th percentile). * Added ``p99`` to ``Timer`` (99th percentile). .. _rel-1.0.6: v1.0.6: Jul 15 2010 =================== * Now compiled exclusively for 2.8.0 final. .. _rel-1.0.5: v1.0.5: Jun 01 2010 =================== * Documentation fix. * Added ``TimedToggle``, which may or may not be useful at all. * Now cross-building for RC2 and RC3. .. _rel-1.0.4: v1.0.4: Apr 27 2010 =================== * Blank ``Timer`` instances (i.e., those which have recorded no timings yet) no longer explode when asked for metrics for that which does not yet exist. * Nested classes, companion objects, and singletons don't have trailing ``$`` characters messing up JMX's good looks. .. _rel-1.0.3: v1.0.3: Apr 16 2010 =================== * Fixed some issues with the `implicit.ly`__ plumbing. * Tweaked the sample size for ``Timer``, giving it 99.9% confidence level with a %5 margin of error (for a normally distributed variable, which it almost certainly isn't.) * ``Sample#iterator`` returns only the recorded data, not a bunch of zeros. * Moved units of ``Timer``, ``Meter``, and ``LoadMeter`` to their own attributes, which allows for easy export of Metrics data via JMX to things like Ganglia__ or whatever. .. __: http://implicit.ly .. __: http://ganglia.sourceforge.net/ .. _rel-1.0.2: v1.0.2: Mar 08 2010 =================== * ``Timer`` now uses Welford's algorithm for calculating running variance, which means no more hilariously wrong standard deviations (e.g., ``NaN``). * ``Timer`` now supports ``+=(Long)`` for pre-recorded, nanosecond-precision timings. .. _rel-1.0.1: v1.0.1: Mar 05 2010 =================== * changed ``Sample`` to use an ``AtomicReferenceArray`` .. _rel-1.0.0: v1.0.0: Feb 27 2010 =================== * Initial release metrics-3.2.5/docs/source/conf.py000066400000000000000000000226131315671014200167220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Dropwizard documentation build configuration file, created by # sphinx-quickstart on Mon Feb 13 11:29:49 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.todo'] # Add any paths that contain templates here, relative to this directory. #templates_path = ['ytemplates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Metrics' copyright = u'2010-2014, Coda Hale, Yammer Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '@parsedVersion.majorVersion@.@parsedVersion.minorVersion@' # The full version, including alpha/beta/rc tags. release = '@project.version@' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. #pygments_style = 'trac' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'metrics' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'tagline': u'Mind the gap.', 'gradient_start': u'#ff684b', 'gradient_end': u'#cf2c0f', 'gradient_text': u'#fff', 'gradient_bg': u'#ED4A2D', 'gradient_shadow': u'#CF2C0F', 'landing_logo': u'metrics-hat.png', 'landing_logo_width': u'200px', 'github_page': u'https://github.com/dropwizard/metrics', 'mailing_list': u'https://groups.google.com/forum/#!forum/metrics-user', 'apidocs': u'https://dropwizard.github.io/metrics/' + release + '/apidocs/' } # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ["./_themes"] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = u'Metrics' # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = u'metrics-logo.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True html_add_permalinks = None # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Metricsdoc' todo_include_todos = True # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Metrics.tex', u'Metrics Documentation', u'Coda Hale', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'metrics', u'Metrics Documentation', [u'Coda Hale'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Metrics', u'Metrics Documentation', u'Coda Hale', 'Metrics', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'Metrics' epub_author = u'Coda Hale' epub_publisher = u'Coda Hale' epub_copyright = u'2012, Coda Hale' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True metrics-3.2.5/docs/source/getting-started.rst000066400000000000000000000270671315671014200212720ustar00rootroot00000000000000.. _getting-started: ############### Getting Started ############### .. highlight:: text .. rubric:: *Getting Started* will guide you through the process of adding Metrics to an existing application. We'll go through the various measuring instruments that Metrics provides, how to use them, and when they'll come in handy. .. _gs-maven: Setting Up Maven ================ You need the ``metrics-core`` library as a dependency: .. code-block:: xml io.dropwizard.metrics metrics-core ${metrics.version} .. note:: Make sure you have a ``metrics.version`` property declared in your POM with the current version, which is |release|. Now it's time to add some metrics to your application! .. _gs-meters: Meters ====== A meter measures the rate of events over time (e.g., "requests per second"). In addition to the mean rate, meters also track 1-, 5-, and 15-minute moving averages. .. code-block:: java private final MetricRegistry metrics = new MetricRegistry(); private final Meter requests = metrics.meter("requests"); public void handleRequest(Request request, Response response) { requests.mark(); // etc } This meter will measure the rate of requests in requests per second. .. _gs-reporter: Console Reporter ================ A Console Reporter is exactly what it sounds like - report to the console. This reporter will print every second. .. code-block:: java ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS); .. _gs-complete: Complete getting started ======================== So the complete Getting Started is .. code-block:: java package sample; import com.codahale.metrics.*; import java.util.concurrent.TimeUnit; public class GetStarted { static final MetricRegistry metrics = new MetricRegistry(); public static void main(String args[]) { startReport(); Meter requests = metrics.meter("requests"); requests.mark(); wait5Seconds(); } static void startReport() { ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS); } static void wait5Seconds() { try { Thread.sleep(5*1000); } catch(InterruptedException e) {} } } .. code-block:: xml 4.0.0 somegroup sample 0.0.1-SNAPSHOT Example project for Metrics io.dropwizard.metrics metrics-core ${metrics.version} .. note:: Make sure you have a ``metrics.version`` property declared in your POM with the current version, which is |release|. To run .. code-block:: sh mvn package exec:java -Dexec.mainClass=sample.First .. _gs-registry: The Registry ============ The centerpiece of Metrics is the ``MetricRegistry`` class, which is the container for all your application's metrics. Go ahead and create a new one: .. code-block:: java final MetricRegistry metrics = new MetricRegistry(); You'll probably want to integrate this into your application's lifecycle (maybe using your dependency injection framework), but ``static`` field is fine. .. _gs-gauges: Gauges ====== A gauge is an instantaneous measurement of a value. For example, we may want to measure the number of pending jobs in a queue: .. code-block:: java public class QueueManager { private final Queue queue; public QueueManager(MetricRegistry metrics, String name) { this.queue = new Queue(); metrics.register(MetricRegistry.name(QueueManager.class, name, "size"), new Gauge() { @Override public Integer getValue() { return queue.size(); } }); } } When this gauge is measured, it will return the number of jobs in the queue. Every metric in a registry has a unique name, which is just a dotted-name string like ``"things.count"`` or ``"com.example.Thing.latency"``. ``MetricRegistry`` has a static helper method for constructing these names: .. code-block:: java MetricRegistry.name(QueueManager.class, "jobs", "size") This will return a string with something like ``"com.example.QueueManager.jobs.size"``. For most queue and queue-like structures, you won't want to simply return ``queue.size()``. Most of ``java.util`` and ``java.util.concurrent`` have implementations of ``#size()`` which are **O(n)**, which means your gauge will be slow (potentially while holding a lock). .. _gs-counters: Counters ======== A counter is just a gauge for an ``AtomicLong`` instance. You can increment or decrement its value. For example, we may want a more efficient way of measuring the pending job in a queue: .. code-block:: java private final Counter pendingJobs = metrics.counter(name(QueueManager.class, "pending-jobs")); public void addJob(Job job) { pendingJobs.inc(); queue.offer(job); } public Job takeJob() { pendingJobs.dec(); return queue.take(); } Every time this counter is measured, it will return the number of jobs in the queue. As you can see, the API for counters is slightly different: ``#counter(String)`` instead of ``#register(String, Metric)``. While you can use ``register`` and create your own ``Counter`` instance, ``#counter(String)`` does all the work for you, and allows you to reuse metrics with the same name. Also, we've statically imported ``MetricRegistry``'s ``name`` method in this scope to reduce clutter. .. _gs-histograms: Histograms ========== A histogram measures the statistical distribution of values in a stream of data. In addition to minimum, maximum, mean, etc., it also measures median, 75th, 90th, 95th, 98th, 99th, and 99.9th percentiles. .. code-block:: java private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes")); public void handleRequest(Request request, Response response) { // etc responseSizes.update(response.getContent().length); } This histogram will measure the size of responses in bytes. .. _gs-timers: Timers ====== A timer measures both the rate that a particular piece of code is called and the distribution of its duration. .. code-block:: java private final Timer responses = metrics.timer(name(RequestHandler.class, "responses")); public String handleRequest(Request request, Response response) { final Timer.Context context = responses.time(); try { // etc; return "OK"; } finally { context.stop(); } } This timer will measure the amount of time it takes to process each request in nanoseconds and provide a rate of requests in requests per second. .. _gs-healthchecks: Health Checks ============= Metrics also has the ability to centralize your service's health checks with the ``metrics-healthchecks`` module. First, create a new ``HealthCheckRegistry`` instance: .. code-block:: java final HealthCheckRegistry healthChecks = new HealthCheckRegistry(); Second, implement a ``HealthCheck`` subclass: .. code-block:: java public class DatabaseHealthCheck extends HealthCheck { private final Database database; public DatabaseHealthCheck(Database database) { this.database = database; } @Override public HealthCheck.Result check() throws Exception { if (database.isConnected()) { return HealthCheck.Result.healthy(); } else { return HealthCheck.Result.unhealthy("Cannot connect to " + database.getUrl()); } } } Then register an instance of it with Metrics: .. code-block:: java healthChecks.register("postgres", new DatabaseHealthCheck(database)); To run all of the registered health checks: .. code-block:: java final Map results = healthChecks.runHealthChecks(); for (Entry entry : results.entrySet()) { if (entry.getValue().isHealthy()) { System.out.println(entry.getKey() + " is healthy"); } else { System.err.println(entry.getKey() + " is UNHEALTHY: " + entry.getValue().getMessage()); final Throwable e = entry.getValue().getError(); if (e != null) { e.printStackTrace(); } } } Metrics comes with a pre-built health check: ``ThreadDeadlockHealthCheck``, which uses Java's built-in thread deadlock detection to determine if any threads are deadlocked. .. _gs-jmx: Reporting Via JMX ================= To report metrics via JMX: .. code-block:: java final JmxReporter reporter = JmxReporter.forRegistry(registry).build(); reporter.start(); Once the reporter is started, all of the metrics in the registry will become visible via **JConsole** or **VisualVM** (if you install the MBeans plugin): .. image:: metrics-visualvm.png :alt: Metrics exposed as JMX MBeans being viewed in VisualVM's MBeans browser .. tip:: If you double-click any of the metric properties, VisualVM will start graphing the data for that property. Sweet, eh? .. _gs-http: Reporting Via HTTP ================== Metrics also ships with a servlet (``AdminServlet``) which will serve a JSON representation of all registered metrics. It will also run health checks, print out a thread dump, and provide a simple "ping" response for load-balancers. (It also has single servlets--``MetricsServlet``, ``HealthCheckServlet``, ``ThreadDumpServlet``, and ``PingServlet``--which do these individual tasks.) To use this servlet, include the ``metrics-servlets`` module as a dependency: .. code-block:: xml io.dropwizard.metrics metrics-servlets ${metrics.version} .. note:: Make sure you have a ``metrics.version`` property declared in your POM with the current version, which is |release|. From there on, you can map the servlet to whatever path you see fit. .. _gs-other: Other Reporting =============== In addition to JMX and HTTP, Metrics also has reporters for the following outputs: * ``STDOUT``, using :ref:`ConsoleReporter ` from ``metrics-core`` * ``CSV`` files, using :ref:`CsvReporter ` from ``metrics-core`` * SLF4J loggers, using :ref:`Slf4jReporter ` from ``metrics-core`` * Ganglia, using :ref:`GangliaReporter ` from ``metrics-ganglia`` * Graphite, using :ref:`GraphiteReporter ` from ``metrics-graphite`` metrics-3.2.5/docs/source/index.rst000066400000000000000000000015211315671014200172570ustar00rootroot00000000000000.. title:: Home .. raw:: html
    ###################################################################################################### Metrics is a Java library which gives you unparalleled insight into what your code does in production. ###################################################################################################### **Metrics** provides a powerful toolkit of ways to measure the behavior of critical components **in your production environment.** With modules for common libraries like **Jetty**, **Logback**, **Log4j**, **Apache HttpClient**, **Ehcache**, **JDBI**, **Jersey** and reporting backends like **Ganglia** and **Graphite**, Metrics provides you with full-stack visibility. .. toctree:: :maxdepth: 1 getting-started manual/index about/index .. raw:: html
    metrics-3.2.5/docs/source/manual/000077500000000000000000000000001315671014200166745ustar00rootroot00000000000000metrics-3.2.5/docs/source/manual/core.rst000066400000000000000000000360421315671014200203630ustar00rootroot00000000000000.. _manual-core: ############ Metrics Core ############ .. highlight:: text The central library for Metrics is ``metrics-core``, which provides some basic functionality: * Metric :ref:`registries `. * The five metric types: :ref:`man-core-gauges`, :ref:`man-core-counters`, :ref:`man-core-histograms`, :ref:`man-core-meters`, and :ref:`man-core-timers`. * Reporting metrics values via :ref:`JMX `, the :ref:`console `, :ref:`CSV ` files, and :ref:`SLF4J loggers `. .. _man-core-registries: Metric Registries ================= The starting point for Metrics is the ``MetricRegistry`` class, which is a collection of all the metrics for your application (or a subset of your application). Generally you only need one ``MetricRegistry`` instance per application, although you may choose to use more if you want to organize your metrics in particular reporting groups. Global named registries can also be shared through the static ``SharedMetricRegistries`` class. This allows the same registry to be used in different sections of code without explicitly passing a ``MetricRegistry`` instance around. Like all Metrics classes, ``SharedMetricRegistries`` is fully thread-safe. .. _man-core-names: Metric Names ============ Each metric is associated with a ``MetricRegistry``, and has a unique *name* within that registry. This is a simple dotted name, like ``com.example.Queue.size``. This flexibility allows you to encode a wide variety of context directly into a metric's name. If you have two instances of ``com.example.Queue``, you can give them more specific: ``com.example.Queue.requests.size`` vs. ``com.example.Queue.responses.size``, for example. ``MetricRegistry`` has a set of static helper methods for easily creating names: .. code-block:: java MetricRegistry.name(Queue.class, "requests", "size") MetricRegistry.name(Queue.class, "responses", "size") These methods will also elide any ``null`` values, allowing for easy optional scopes. .. _man-core-gauges: Gauges ====== A gauge is the simplest metric type. It just returns a *value*. If, for example, your application has a value which is maintained by a third-party library, you can easily expose it by registering a ``Gauge`` instance which returns that value: .. code-block:: java registry.register(name(SessionStore.class, "cache-evictions"), new Gauge() { @Override public Integer getValue() { return cache.getEvictionsCount(); } }); This will create a new gauge named ``com.example.proj.auth.SessionStore.cache-evictions`` which will return the number of evictions from the cache. .. _man-core-gauges-jmx: JMX Gauges ---------- Given that many third-party libraries often expose metrics only via JMX, Metrics provides the ``JmxAttributeGauge`` class, which takes the object name of a JMX MBean and the name of an attribute and produces a gauge implementation which returns the value of that attribute: .. code-block:: java registry.register(name(SessionStore.class, "cache-evictions"), new JmxAttributeGauge("net.sf.ehcache:type=Cache,scope=sessions,name=eviction-count", "Value")); .. _man-core-gauges-ratio: Ratio Gauges ------------ A ratio gauge is a simple way to create a gauge which is the ratio between two numbers: .. code-block:: java public class CacheHitRatio extends RatioGauge { private final Meter hits; private final Timer calls; public CacheHitRatio(Meter hits, Timer calls) { this.hits = hits; this.calls = calls; } @Override public Ratio getRatio() { return Ratio.of(hits.getOneMinuteRate(), calls.getOneMinuteRate()); } } This gauge returns the ratio of cache hits to misses using a meter and a timer. .. _man-core-gauges-cached: Cached Gauges ------------- A cached gauge allows for a more efficient reporting of values which are expensive to calculate. The value is cached for the period specified in the constructor. The "getValue()" method called by the client only returns the cached value. The protected "loadValue()" method is only called internally to reload the cache value. .. code-block:: java registry.register(name(Cache.class, cache.getName(), "size"), new CachedGauge(10, TimeUnit.MINUTES) { @Override protected Long loadValue() { // assume this does something which takes a long time return cache.getSize(); } }); .. _man-core-gauges-derivative: Derivative Gauges ----------------- A derivative gauge allows you to derive values from other gauges' values: .. code-block:: java public class CacheSizeGauge extends DerivativeGauge { public CacheSizeGauge(Gauge statsGauge) { super(statsGauge); } @Override protected Long transform(CacheStats stats) { return stats.getSize(); } } .. _man-core-counters: Counters ======== A counter is a simple incrementing and decrementing 64-bit integer: .. code-block:: java final Counter evictions = registry.counter(name(SessionStore.class, "cache-evictions")); evictions.inc(); evictions.inc(3); evictions.dec(); evictions.dec(2); All ``Counter`` metrics start out at 0. .. _man-core-histograms: Histograms ========== A ``Histogram`` measures the distribution of values in a stream of data: e.g., the number of results returned by a search: .. code-block:: java final Histogram resultCounts = registry.histogram(name(ProductDAO.class, "result-counts"); resultCounts.update(results.size()); ``Histogram`` metrics allow you to measure not just easy things like the min, mean, max, and standard deviation of values, but also quantiles__ like the median or 95th percentile. .. __: http://en.wikipedia.org/wiki/Quantile Traditionally, the way the median (or any other quantile) is calculated is to take the entire data set, sort it, and take the value in the middle (or 1% from the end, for the 99th percentile). This works for small data sets, or batch processing systems, but not for high-throughput, low-latency services. The solution for this is to sample the data as it goes through. By maintaining a small, manageable reservoir which is statistically representative of the data stream as a whole, we can quickly and easily calculate quantiles which are valid approximations of the actual quantiles. This technique is called **reservoir sampling**. Metrics provides a number of different ``Reservoir`` implementations, each of which is useful. .. _man-core-histograms-uniform: Uniform Reservoirs ------------------ A histogram with a uniform reservoir produces quantiles which are valid for the entirely of the histogram's lifetime. It will return a median value, for example, which is the median of all the values the histogram has ever been updated with. It does this by using an algorithm called `Vitter's R`__), which randomly selects values for the reservoir with linearly-decreasing probability. .. __: http://www.cs.umd.edu/~samir/498/vitter.pdf Use a uniform histogram when you're interested in long-term measurements. Don't use one where you'd want to know if the distribution of the underlying data stream has changed recently. .. _man-core-histograms-exponential: Exponentially Decaying Reservoirs --------------------------------- A histogram with an exponentially decaying reservoir produces quantiles which are representative of (roughly) the last five minutes of data. It does so by using a `forward-decaying priority reservoir`__ with an exponential weighting towards newer data. Unlike the uniform reservoir, an exponentially decaying reservoir represents **recent data**, allowing you to know very quickly if the distribution of the data has changed. :ref:`man-core-timers` use histograms with exponentially decaying reservoirs by default. .. __: http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf .. _man-core-histograms-sliding: Sliding Window Reservoirs ------------------------- A histogram with a sliding window reservoir produces quantiles which are representative of the past ``N`` measurements. .. _man-core-histograms-sliding-time: Sliding Time Window Reservoirs ------------------------------ A histogram with a sliding time window reservoir produces quantiles which are strictly representative of the past ``N`` seconds (or other time period). .. warning:: While ``SlidingTimeWindowReservoir`` is easier to understand than ``ExponentiallyDecayingReservoir``, it is not bounded in size, so using it to sample a high-frequency process can require a significant amount of memory. Because it records every measurement, it's also the slowest reservoir type. .. hint:: Try to use our new optimised version of ``SlidingTimeWindowReservoir`` called ``SlidingTimeWindowArrayReservoir``. It brings much lower memory overhead. Also it's allocation/free patterns are different, so GC overhead is 60x-80x lower then ``SlidingTimeWindowReservoir``. Now ``SlidingTimeWindowArrayReservoir`` is comparable with ``ExponentiallyDecayingReservoir`` in terms GC overhead and performance. As for required memory, ``SlidingTimeWindowArrayReservoir`` takes ~128 bits per stored measurement and you can simply calculate required amount of heap. Example: 10K measurements / sec with reservoir storing time of 1 minute will take 10000 * 60 * 128 / 8 = 9600000 bytes ~ 9 megabytes .. _man-core-meters: Meters ====== A meter measures the *rate* at which a set of events occur: .. code-block:: java final Meter getRequests = registry.meter(name(WebProxy.class, "get-requests", "requests")); getRequests.mark(); getRequests.mark(requests.size()); Meters measure the rate of the events in a few different ways. The *mean* rate is the average rate of events. It's generally useful for trivia, but as it represents the total rate for your application's entire lifetime (e.g., the total number of requests handled, divided by the number of seconds the process has been running), it doesn't offer a sense of recency. Luckily, meters also record three different *exponentially-weighted moving average* rates: the 1-, 5-, and 15-minute moving averages. .. hint:: Just like the Unix load averages visible in ``uptime`` or ``top``. .. _man-core-timers: Timers ====== A timer is basically a :ref:`histogram ` of the duration of a type of event and a :ref:`meter ` of the rate of its occurrence. .. code-block:: java final Timer timer = registry.timer(name(WebProxy.class, "get-requests")); final Timer.Context context = timer.time(); try { // handle request } finally { context.stop(); } .. note:: Elapsed times for it events are measured internally in nanoseconds, using Java's high-precision ``System.nanoTime()`` method. Its precision and accuracy vary depending on operating system and hardware. .. _man-core-sets: Metric Sets =========== Metrics can also be grouped together into reusable metric sets using the ``MetricSet`` interface. This allows library authors to provide a single entry point for the instrumentation of a wide variety of functionality. .. _man-core-reporters: Reporters ========= Reporters are the way that your application exports all the measurements being made by its metrics. ``metrics-core`` comes with four ways of exporting your metrics: :ref:`JMX `, :ref:`console `, :ref:`SLF4J `, and :ref:`CSV `. .. _man-core-reporters-jmx: JMX --- With ``JmxReporter``, you can expose your metrics as JMX MBeans. To explore this you can use VisualVM__ (which ships with most JDKs as ``jvisualvm``) with the VisualVM-MBeans plugins installed or JConsole (which ships with most JDKs as ``jconsole``): .. __: http://visualvm.java.net/ .. image:: ../metrics-visualvm.png :alt: Metrics exposed as JMX MBeans being viewed in VisualVM's MBeans browser .. tip:: If you double-click any of the metric properties, VisualVM will start graphing the data for that property. Sweet, eh? .. warning:: We don't recommend that you try to gather metrics from your production environment. JMX's RPC API is fragile and bonkers. For development purposes and browsing, though, it can be very useful. To report metrics via JMX: .. code-block:: java final JmxReporter reporter = JmxReporter.forRegistry(registry).build(); reporter.start(); .. _man-core-reporters-console: Console ------- For simple benchmarks, Metrics comes with ``ConsoleReporter``, which periodically reports all registered metrics to the console: .. code-block:: java final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.MINUTES); .. _man-core-reporters-csv: CSV --- For more complex benchmarks, Metrics comes with ``CsvReporter``, which periodically appends to a set of ``.csv`` files in a given directory: .. code-block:: java final CsvReporter reporter = CsvReporter.forRegistry(registry) .formatFor(Locale.US) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(new File("~/projects/data/")); reporter.start(1, TimeUnit.SECONDS); For each metric registered, a ``.csv`` file will be created, and every second its state will be written to it as a new row. .. _man-core-reporters-slf4j: SLF4J ----- It's also possible to log metrics to an SLF4J logger: .. code-block:: java final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry) .outputTo(LoggerFactory.getLogger("com.example.metrics")) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.MINUTES); .. _man-core-reporters-other: Other Reporters --------------- Metrics has other reporter implementations, too: * :ref:`MetricsServlet ` is a servlet which not only exposes your metrics as a JSON object, but it also runs your health checks, performs thread dumps, and exposes valuable JVM-level and OS-level information. * :ref:`GangliaReporter ` allows you to constantly stream metrics data to your Ganglia servers. * :ref:`GraphiteReporter ` allows you to constantly stream metrics data to your Graphite servers. metrics-3.2.5/docs/source/manual/ehcache.rst000066400000000000000000000115101315671014200210040ustar00rootroot00000000000000.. _manual-ehcache: ##################### Instrumenting Ehcache ##################### .. highlight:: text .. rubric:: The ``metrics-ehcache`` module provides ``InstrumentedEhcache``, a decorator for Ehcache_ caches: .. _Ehcache: http://ehcache.org/documentation .. code-block:: java final Cache c = new Cache(new CacheConfiguration("test", 100)); MANAGER.addCache(c); this.cache = InstrumentedEhcache.instrument(registry, c); Instrumenting an ``Ehcache`` instance creates gauges for all of the Ehcache-provided statistics: +---------------------------+----------------------------------------------------------------------+ | ``hits`` | The number of times a requested item was found in the cache. | +---------------------------+----------------------------------------------------------------------+ | ``in-memory-hits`` | Number of times a requested item was found in the memory store. | +---------------------------+----------------------------------------------------------------------+ | ``off-heap-hits`` | Number of times a requested item was found in the off-heap store. | +---------------------------+----------------------------------------------------------------------+ | ``on-disk-hits`` | Number of times a requested item was found in the disk store. | +---------------------------+----------------------------------------------------------------------+ | ``misses`` | Number of times a requested item was not found in the cache. | +---------------------------+----------------------------------------------------------------------+ | ``in-memory-misses`` | Number of times a requested item was not found in the memory store. | +---------------------------+----------------------------------------------------------------------+ | ``off-heap-misses`` | Number of times a requested item was not found in the off-heap store.| +---------------------------+----------------------------------------------------------------------+ | ``on-disk-misses`` | Number of times a requested item was not found in the disk store. | +---------------------------+----------------------------------------------------------------------+ | ``objects`` | Number of elements stored in the cache. | +---------------------------+----------------------------------------------------------------------+ | ``in-memory-objects`` | Number of objects in the memory store. | +---------------------------+----------------------------------------------------------------------+ | ``off-heap-objects`` | Number of objects in the off-heap store. | +---------------------------+----------------------------------------------------------------------+ | ``on-disk-objects`` | Number of objects in the disk store. | +---------------------------+----------------------------------------------------------------------+ | ``mean-get-time`` | The average get time. Because ehcache supports JDK1.4.2, each get | | | time uses ``System.currentTimeMillis()``, rather than nanoseconds. | | | The accuracy is thus limited. | +---------------------------+----------------------------------------------------------------------+ | ``mean-search-time`` | The average execution time (in milliseconds) within the last sample | | | period. | +---------------------------+----------------------------------------------------------------------+ | ``eviction-count`` | The number of cache evictions, since the cache was created, or | | | statistics were cleared. | +---------------------------+----------------------------------------------------------------------+ | ``searches-per-second`` | The number of search executions that have completed in the last | | | second. | +---------------------------+----------------------------------------------------------------------+ | ``accuracy`` | A human readable description of the accuracy setting. One of "None", | | | "Best Effort" or "Guaranteed". | +---------------------------+----------------------------------------------------------------------+ It also adds full timers for the cache's ``get`` and ``put`` methods. The metrics are all scoped to the cache's class and name, so a ``Cache`` instance named ``users`` would have metric names like ``net.sf.ehcache.Cache.users.get``, etc. metrics-3.2.5/docs/source/manual/ganglia.rst000066400000000000000000000014021315671014200210250ustar00rootroot00000000000000.. _manual-ganglia: #################### Reporting to Ganglia #################### The ``metrics-ganglia`` module provides ``GangliaReporter``, which allows your application to constantly stream metric values to a Ganglia_ server: .. _Ganglia: http://ganglia.sourceforge.net/ .. code-block:: java final GMetric ganglia = new GMetric("ganglia.example.com", 8649, UDPAddressingMode.MULTICAST, 1); final GangliaReporter reporter = GangliaReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(ganglia); reporter.start(1, TimeUnit.MINUTES); metrics-3.2.5/docs/source/manual/graphite.rst000066400000000000000000000033041315671014200212310ustar00rootroot00000000000000.. _manual-graphite: ##################### Reporting to Graphite ##################### The ``metrics-graphite`` module provides ``GraphiteReporter``, which allows your application to constantly stream metric values to a Graphite_ server: .. _Graphite: http://graphite.wikidot.com/ .. code-block:: java final Graphite graphite = new Graphite(new InetSocketAddress("graphite.example.com", 2003)); final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) .prefixedWith("web1.example.com") .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(graphite); reporter.start(1, TimeUnit.MINUTES); If you prefer to write metrics in batches using pickle, you can use the ``PickledGraphite``: .. code-block:: java final PickledGraphite pickledGraphite = new PickledGraphite(new InetSocketAddress("graphite.example.com", 2004)); final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) .prefixedWith("web1.example.com") .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(pickledGraphite); reporter.start(1, TimeUnit.MINUTES); metrics-3.2.5/docs/source/manual/healthchecks.rst000066400000000000000000000041561315671014200220620ustar00rootroot00000000000000.. _man-healthchecks: ############# Health Checks ############# Metrics also provides you with a consistent, unified way of performing application health checks. A health check is basically a small self-test which your application performs to verify that a specific component or responsibility is performing correctly. To create a health check, extend the ``HealthCheck`` class: .. code-block:: java public class DatabaseHealthCheck extends HealthCheck { private final Database database; public DatabaseHealthCheck(Database database) { this.database = database; } @Override protected Result check() throws Exception { if (database.ping()) { return Result.healthy(); } return Result.unhealthy("Can't ping database"); } } In this example, we've created a health check for a ``Database`` class on which our application depends. Our fictitious ``Database`` class has a ``#ping()`` method, which executes a safe test query (e.g., ``SELECT 1``). ``#ping()`` returns ``true`` if the query returns the expected result, returns ``false`` if it returns something else, and throws an exception if things have gone seriously wrong. Our ``DatabaseHealthCheck``, then, takes a ``Database`` instance and in its ``#check()`` method, attempts to ping the database. If it can, it returns a **healthy** result. If it can't, it returns an **unhealthy** result. .. note:: Exceptions thrown inside a health check's ``#check()`` method are automatically caught and turned into unhealthy results with the full stack trace. To register a health check, either use a ``HealthCheckRegistry`` instance: .. code-block:: java registry.register("database", new DatabaseHealthCheck(database)); You can also run the set of registered health checks: .. code-block:: java for (Entry entry : registry.runHealthChecks().entrySet()) { if (entry.getValue().isHealthy()) { System.out.println(entry.getKey() + ": OK"); } else { System.out.println(entry.getKey() + ": FAIL"); } } metrics-3.2.5/docs/source/manual/httpclient.rst000066400000000000000000000021631315671014200216060ustar00rootroot00000000000000.. _manual-httpclient: ############################### Instrumenting Apache HttpClient ############################### The ``metrics-httpclient`` module provides ``InstrumentedHttpClientConnManager`` and ``InstrumentedHttpClients``, two instrumented versions of `Apache HttpClient 4.x`__ classes. .. __: http://hc.apache.org/httpcomponents-client-ga/ ``InstrumentedHttpClientConnManager`` is a thread-safe ``HttpClientConnectionManager`` implementation which measures the number of open connections in the pool and the rate at which new connections are opened. ``InstrumentedHttpClients`` follows the ``HttpClients`` builder pattern and adds per-HTTP method timers for HTTP requests. Metric naming strategies ======================== The default per-method metric naming and scoping strategy can be overridden by passing an implementation of ``HttpClientMetricNameStrategy`` to the ``InstrumentedHttpClients.createDefault`` method. A number of pre-rolled strategies are available, e.g.: .. code-block:: java HttpClient client = InstrumentedHttpClients.createDefault(registry, HttpClientMetricNameStrategies.HOST_AND_METHOD); metrics-3.2.5/docs/source/manual/index.rst000066400000000000000000000010251315671014200205330ustar00rootroot00000000000000.. _manual-index: ########### User Manual ########### .. rubric:: This goal of this document is to provide you with all the information required to effectively use the Metrics library in your application. If you're new to Metrics, you should read the :ref:`getting-started` guide first. .. toctree:: :maxdepth: 1 core healthchecks ehcache ganglia graphite httpclient jdbi jersey jetty log4j logback jvm json servlets servlet third-party metrics-3.2.5/docs/source/manual/jdbi.rst000066400000000000000000000014631315671014200203420ustar00rootroot00000000000000.. _manual-jdbi: ################## Instrumenting JDBI ################## The ``metrics-jdbi`` module provides a ``TimingCollector`` implementation for JDBI_, an SQL convenience library. .. _JDBI: http://jdbi.org/ To use it, just add a ``InstrumentedTimingCollector`` instance to your ``DBI``: .. code-block:: java final DBI dbi = new DBI(dataSource); dbi.setTimingCollector(new InstrumentedTimingCollector(registry)); ``InstrumentedTimingCollector`` keeps per-SQL-object timing data, as well as general raw SQL timing data. The metric names for each query are constructed by an ``StatementNameStrategy`` instance, of which there are many implementations. By default, ``StatementNameStrategy`` uses ``SmartNameStrategy``, which attempts to effectively handle both queries from bound objects and raw SQL. metrics-3.2.5/docs/source/manual/jersey.rst000066400000000000000000000051701315671014200207320ustar00rootroot00000000000000.. _manual-jersey: ######################## Instrumenting Jersey 1.x ######################## The ``metrics-jersey`` module provides ``InstrumentedResourceMethodDispatchAdapter``, which allows you to instrument methods on your `Jersey 1.x`_ resource classes: .. _Jersey 1.x: https://jersey.java.net/documentation/1.18/index.html An instance of ``InstrumentedResourceMethodDispatchAdapter`` must be registered with your Jersey application's ``ResourceConfig`` as a singleton provider for this to work. .. code-block:: java public class ExampleApplication { private final DefaultResourceConfig config = new DefaultResourceConfig(); public void init() { config.getSingletons().add(new InstrumentedResourceMethodDispatchAdapter(registry)); config.getClasses().add(ExampleResource.class); } } @Path("/example") @Produces(MediaType.TEXT_PLAIN) public class ExampleResource { @GET @Timed public String show() { return "yay"; } } The ``show`` method in the above example will have a timer attached to it, measuring the time spent in that method. Use of the ``@Metered`` and ``@ExceptionMetered`` annotations is also supported. ######################## Instrumenting Jersey 2.x ######################## Jersey 2.x changed the API for how resource method monitoring works, so a new module ``metrics-jersey2`` provides ``InstrumentedResourceMethodApplicationListener``, which allows you to instrument methods on your `Jersey 2.x`_ resource classes: The ``metrics-jersey2`` module provides ``InstrumentedResourceMethodApplicationListener``, which allows you to instrument methods on your `Jersey 2.x`_ resource classes: .. _Jersey 2.x: https://jersey.java.net/documentation/latest/index.html An instance of ``InstrumentedResourceMethodApplicationListener`` must be registered with your Jersey application's ``ResourceConfig`` as a singleton provider for this to work. .. code-block:: java public class ExampleApplication extends ResourceConfig { . . . register(new InstrumentedResourceMethodApplicationListener (new MetricRegistry())); config = config.register(ExampleResource.class); . . . } @Path("/example") @Produces(MediaType.TEXT_PLAIN) public class ExampleResource { @GET @Timed public String show() { return "yay"; } } The ``show`` method in the above example will have a timer attached to it, measuring the time spent in that method. Use of the ``@Metered`` and ``@ExceptionMetered`` annotations is also supported. metrics-3.2.5/docs/source/manual/jetty.rst000066400000000000000000000026121315671014200205660ustar00rootroot00000000000000.. _manual-jetty: ################### Instrumenting Jetty ################### The ``metrics-jetty8`` (Jetty 8.0), ``metrics-jetty9-legacy`` (Jetty 9.0), and ``metrics-jetty9`` (Jetty 9.1 and higher) modules provides a set of instrumented equivalents of Jetty_ classes: ``InstrumentedBlockingChannelConnector``, ``InstrumentedHandler``, ``InstrumentedQueuedThreadPool``, ``InstrumentedSelectChannelConnector``, and ``InstrumentedSocketConnector``. .. _Jetty: http://www.eclipse.org/jetty/ The ``Connector`` implementations are simple, instrumented subclasses of the Jetty connector types which measure connection duration, the rate of accepted connections, connections, disconnections, and the total number of active connections. ``InstrumentedQueuedThreadPool`` is a ``QueuedThreadPool`` subclass which measures the ratio of idle threads to working threads as well as the absolute number of threads (idle and otherwise). ``InstrumentedHandler`` is a ``Handler`` decorator which measures a wide range of HTTP behavior: dispatch times, requests, resumes, suspends, expires, the number of active, suspected, and dispatched requests, as well as meters of responses with ``1xx``, ``2xx``, ``3xx``, ``4xx``, and ``5xx`` status codes. It even has gauges for the ratios of ``4xx`` and ``5xx`` response rates to overall response rates. Finally, it includes meters for requests by the HTTP method: ``GET``, ``POST``, etc. metrics-3.2.5/docs/source/manual/json.rst000066400000000000000000000004721315671014200204020ustar00rootroot00000000000000.. _manual-json: ############ JSON Support ############ Metrics comes with ``metrics-json``, which features two reusable modules for Jackson_. .. _Jackson: http://wiki.fasterxml.com/JacksonHome This allows for the serialization of all metric types and health checks to a standard, easily-parsable JSON format. metrics-3.2.5/docs/source/manual/jvm.rst000066400000000000000000000010041315671014200202150ustar00rootroot00000000000000.. _manual-jvm: ################### JVM Instrumentation ################### The ``metrics-jvm`` module contains a number of reusable gauges and :ref:`metric sets ` which allow you to easily instrument JVM internals. Supported metrics include: * Run count and elapsed times for all supported garbage collectors * Memory usage for all memory pools, including off-heap memory * Breakdown of thread states, including deadlocks * File descriptor usage * Buffer pool sizes and utilization (Java 7 only) metrics-3.2.5/docs/source/manual/log4j.rst000066400000000000000000000032061315671014200204460ustar00rootroot00000000000000.. _manual-log4j: ################### Instrumenting Log4j ################### The ``metrics-log4j`` and ``metrics-log4j2`` modules provide ``InstrumentedAppender``, a Log4j ``Appender`` implementation (for log4j 1.x and log4j 2.x correspondingly) which records the rate of logged events by their logging level. You can add it to the root logger programmatically. For log4j 1.x: .. code-block:: java InstrumentedAppender appender = new InstrumentedAppender(registry); appender.activateOptions(); LogManager.getRootLogger().addAppender(appender); For log4j 2.x: .. code-block:: java Filter filter = null; // That's fine if we don't use filters; https://logging.apache.org/log4j/2.x/manual/filters.html PatternLayout layout = null; // The layout isn't used in InstrumentedAppender InstrumentedAppender appender = new InstrumentedAppender(metrics, filter, layout, false); appender.start(); LoggerContext context = (LoggerContext) LogManager.getContext(false); Configuration config = context.getConfiguration(); config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME).addAppender(appender, level, filter); context.updateLoggers(config); You can also use standard log4j2 configuration, via plugin support: .. code-block:: xml metrics-3.2.5/docs/source/manual/logback.rst000066400000000000000000000012241315671014200210270ustar00rootroot00000000000000.. _manual-logback: ##################### Instrumenting Logback ##################### The ``metrics-logback`` module provides ``InstrumentedAppender``, a Logback ``Appender`` implementation which records the rate of logged events by their logging level. You add it to the root logger programmatically: .. code-block:: java final LoggerContext factory = (LoggerContext) LoggerFactory.getILoggerFactory(); final Logger root = factory.getLogger(Logger.ROOT_LOGGER_NAME); final InstrumentedAppender metrics = new InstrumentedAppender(registry); metrics.setContext(root.getLoggerContext()); metrics.start(); root.addAppender(metrics); metrics-3.2.5/docs/source/manual/servlet.rst000066400000000000000000000040021315671014200211060ustar00rootroot00000000000000.. _manual-servlet: ############################## Instrumenting Web Applications ############################## The ``metrics-servlet`` module provides a Servlet filter which has meters for status codes, a counter for the number of active requests, and a timer for request duration. By default the filter will use ``com.codahale.metrics.servlet.InstrumentedFilter`` as the base name of the metrics. You can use the filter in your ``web.xml`` like this: .. code-block:: xml instrumentedFilter com.codahale.metrics.servlet.InstrumentedFilter instrumentedFilter /* An optional filter init-param ``name-prefix`` can be specified to override the base name of the metrics associated with the filter mapping. This can be helpful if you need to instrument multiple url patterns and give each a unique name. .. code-block:: xml instrumentedFilter com.codahale.metrics.servlet.InstrumentedFilter name-prefix authentication instrumentedFilter /auth/* You will need to add your ``MetricRegistry`` to the servlet context as an attribute named ``com.codahale.metrics.servlet.InstrumentedFilter.registry``. You can do this using the Servlet API by extending ``InstrumentedFilterContextListener``: .. code-block:: java public class MyInstrumentedFilterContextListener extends InstrumentedFilterContextListener { public static final MetricRegistry REGISTRY = new MetricRegistry(); @Override protected MetricRegistry getMetricRegistry() { return REGISTRY; } } metrics-3.2.5/docs/source/manual/servlets.rst000066400000000000000000000102421315671014200212740ustar00rootroot00000000000000.. _manual-servlets: ################ Metrics Servlets ################ The ``metrics-servlets`` module provides a handful of useful servlets: .. _man-servlet-healthcheck: HealthCheckServlet ================== ``HealthCheckServlet`` responds to ``GET`` requests by running all the [health checks](#health-checks) and returning ``501 Not Implemented`` if no health checks are registered, ``200 OK`` if all pass, or ``500 Internal Service Error`` if one or more fail. The results are returned as a human-readable ``text/plain`` entity. ``HealthCheckServlet`` requires that the servlet context has a ``HealthCheckRegistry`` named ``com.codahale.metrics.servlets.HealthCheckServlet.registry``. You can subclass ``MetricsServletContextListener``, which will add a specific ``HealthCheckRegistry`` to the servlet context. .. _man-servlet-threaddump: ThreadDumpServlet ================= ``ThreadDumpServlet`` responds to ``GET`` requests with a ``text/plain`` representation of all the live threads in the JVM, their states, their stack traces, and the state of any locks they may be waiting for. .. _man-servlet-metrics: MetricsServlet ============== ``MetricsServlet`` exposes the state of the metrics in a particular registry as a JSON object. ``MetricsServlet`` requires that the servlet context has a ``MetricRegistry`` named ``com.codahale.metrics.servlets.MetricsServlet.registry``. You can subclass ``MetricsServletContextListener``, which will add a specific ``MetricRegistry`` to the servlet context. ``MetricsServlet`` also takes an initialization parameter, ``show-jvm-metrics``, which if ``"false"`` will disable the outputting of JVM-level information in the JSON object. .. _man-servlet-ping: PingServlet =========== ``PingServlet`` responds to ``GET`` requests with a ``text/plain``/``200 OK`` response of ``pong``. This is useful for determining liveness for load balancers, etc. .. _man-servlet-admin: AdminServlet ============ ``AdminServlet`` aggregates ``HealthCheckServlet``, ``ThreadDumpServlet``, ``MetricsServlet``, and ``PingServlet`` into a single, easy-to-use servlet which provides a set of URIs: * ``/``: an HTML admin menu with links to the following: * ``/healthcheck``: ``HealthCheckServlet`` * ``/metrics``: ``MetricsServlet`` * ``/ping``: ``PingServlet`` * ``/threads``: ``ThreadDumpServlet`` You will need to add your ``MetricRegistry`` and ``HealthCheckRegistry`` instances to the servlet context as attributes named ``com.codahale.metrics.servlets.MetricsServlet.registry`` and ``com.codahale.metrics.servlets.HealthCheckServlet.registry``, respectively. You can do this using the Servlet API by extending ``MetricsServlet.ContextListener`` for MetricRegistry: .. code-block:: java public class MyMetricsServletContextListener extends MetricsServlet.ContextListener { public static final MetricRegistry METRIC_REGISTRY = new MetricRegistry(); @Override protected MetricRegistry getMetricRegistry() { return METRIC_REGISTRY; } } And by extending ``HealthCheckServlet.ContextListener`` for HealthCheckRegistry: .. code-block:: java public class MyHealthCheckServletContextListener extends HealthCheckServlet.ContextListener { public static final HealthCheckRegistry HEALTH_CHECK_REGISTRY = new HealthCheckRegistry(); @Override protected HealthCheckRegistry getHealthCheckRegistry() { return HEALTH_CHECK_REGISTRY; } } Then you will need to register servlet context listeners either in you ``web.xml`` or annotating the class with ``@WebListener`` if you are in servlet 3.0 environment. In ``web.xml``: .. code-block:: xml com.example.MyMetricsServletContextListener com.example.MyHealthCheckServletContextListener You will also need to register ``AdminServlet`` in ``web.xml``: .. code-block:: xml metrics com.codahale.metrics.servlets.AdminServlet metrics /metrics/* metrics-3.2.5/docs/source/manual/third-party.rst000066400000000000000000000142721315671014200217030ustar00rootroot00000000000000.. _manual-third-party: ##################### Third Party Libraries ##################### If you're looking to integrate with something not provided by the main Metrics libraries, check out the many third-party libraries which extend Metrics: Instrumented Libraries ~~~~~~~~~~~~~~~~~~~~~~ * `camel-metrics `_ provides component for your `Apache Camel `_ route. * `hdrhistogram-metrics-reservoir `_ provides a Histogram reservoir backed by `HdrHistogram `_. * `jersey2-metrics `_ provides integration with `Jersey 2 `_. * `jersey-metrics-filter `_ provides integration with Jersey 1. * `metrics-aspectj `_ provides integration with `AspectJ `_. * `metrics-cdi `_ provides integration with `CDI `_ environments, * `metrics-guice `_ provides integration with `Guice `_. * `metrics-guice-servlet `_ provides `Guice Servlet `_ integration with AdminServlet. * `metrics-okhttp `_ provides integration with `OkHttp `_. * `metrics-play `_ provides an integration with the `Play Framework `_. * `metrics-spring `_ provides integration with `Spring `_. * `wicket-metrics `_ provides easy integration for your `Wicket `_ application. Language Wrappers ~~~~~~~~~~~~~~~~~ * `metrics-clojure `_ provides an API optimized for Clojure. * `metrics-scala `_ provides an API optimized for Scala. Reporters ~~~~~~~~~ * `finagle-metrics `_ provides a reporter for a `Finagle `_ service. * `kafka-dropwizard-metrics `_ allows Kafka producers, consumers, and streaming applications to register their built-in metrics with a Dropwizard Metrics registry. * `MetricCatcher `_ Turns JSON over UDP into Metrics so that non-jvm languages can know what's going on too. * `metrics-cassandra `_ provides a reporter for `Apache Cassandra `_. * `metrics-circonus `_ provides a registry and reporter for sending metrics (including full histograms) to `Circonus `_. * `metrics-datadog `_ provides a reporter to send data to `Datadog `_. * `metrics-elasticsearch-reporter `_ provides a reporter for `elasticsearch `_ * `metrics-hadoop-metrics2-reporter `_ provides a reporter for `Hadoop Metrics2 `_. * `metrics-hawkular `_ provides a reporter for `Hawkular Metrics `_. * `metrics-influxdb `_ provides a reporter which announces measurements to `InfluxDB `_ * `metrics-instrumental `_ provides a reporter to send data to `Instrumental `_. * `metrics-kafka `_ provides a reporter for `Kafka `_. * `metrics-librato `_ provides a reporter for `Librato Metrics `_, a scalable metric collection, aggregation, monitoring, and alerting service. * `metrics-mongodb-reporter `_ provides a reporter for `MongoDB `_. * `metrics-munin-reporter `_ provides a reporter for `Munin `_ * `metrics-new-relic `_ provides a reporter which sends data to `New Relic `_. * `metrics-reporter-config `_ DropWizard-esque YAML configuration of reporters. * `metrics-signalfx `_ provides a reporter to send data to `SignalFx `_. * `metrics-spark-reporter `_ provides a reporter for `Apache Spark Streaming `_. * `metrics-splunk `_ provides a reporter for `Splunk `_. * `metrics-statsd `_ provides a Metrics 2.x and 3.x reporter for `StatsD `_ * `metrics-zabbiz `_ provides a reporter for `Zabbix `_. * `sematext-metrics-reporter `_ provides a reporter for `SPM `_. Advansed metrics implementations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * `rolling-metrics `_ provides a collection of advanced metrics with rolling time window semantic, such as Rolling-Counter, Hit-Ratio, Top and Reservoir backed by HdrHistogram. metrics-3.2.5/docs/source/metrics-logo.png000066400000000000000000000035131315671014200205330ustar00rootroot00000000000000PNG  IHDR;0tEXtSoftwareAdobe ImageReadyqe<IDATxڴW[lW3ݙٛ/&iT MZDAjEBQU oyRQ*yE"*BB[;qM8u"{]wco[ȟ{f1w…G/({Ƣ0BAu=t:|q<+ E4'.x4q/ S?zq}'0~5,c!g\`IAؔovupb08opꦰ/R|b8)s$&xOb^AUE0߈M^8Y ({s~tU5Usk~{e&mR<mY W g !xKN.&Ŏ,RVSQ J4jv'Wh4j֩}s$IjI$TwGAlRb9[1K\$߿]qxF+Yy)M{V=9.%1M.v:>4pDݷQD> p{!?%TnPDX Еqi>f[1M@01p D"xdy@^M ڎeG$3psꝂ;=l&f{OyĩcTGϞ^Sjwr~ ,*Ҭy Z(JH =!#'Q:vO}KdEg7WK;4Ր: VhtE,Y:vOA-m9b,>+𬍌ϕ^3&;Eal~,bIBS9ERE͐40sh8%_ӾIyFK2Ee-^BO#(k:~[Tv]DO \`q7p(qr _Ν? tЃo[nW`mv+O7ĄG)8LIKY-bvEK߆͟D+W*9ҧRumKdZV<+/ql;e uM#`FL?~i9abf*U5-Oe([֡B`Nxw~>=xG\lٲzNXF}YrcF,$T];33_1\B ӃFD,y,:B;kIYQ:.Cc"5h$9P*!R:*9k-ysj+!mJ< l;NrVB麎"KY$P96lJypUpM(}ШD>O-Ufߡ|~}>:p '0 ٖ},*bշtT*8٩012fu#??xЯQ.5}h팾-ҽ)|kz{;y>n&ѱq҄V@?CߣDLc9{{I駩I\RFz Ízeb&VdEϰ"XAfW"-$JGhẖR4\u-&4M[;Gl,gIENDB`metrics-3.2.5/docs/source/metrics-visualvm.png000066400000000000000000011172141315671014200214460ustar00rootroot00000000000000PNG  IHDRR*~ AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs%%IR$iTXtXML:com.adobe.xmp 1 5 14399/100 1 14399/100 1874 1 810 2013-04-01T18:04:93 Pixelmator 2.1.4 d@IDATxyeGyY/`ۀ=l` $Q(QgȧHG dc0 >[w;S>}{ܩS}Ω[C=tu@A  @A  @{{"@A  @A  @웡@A  @A  @SDSݙ`@A  @A  @}3@A  @A  @{ Ⱦ{;LA  @A  @A o@A  @A  @A`O!wOug A  @A  @A DA  @A  @A )"L0A  @A  @A ȾA  @A  @A =@d=՝ & @A  @A  7c  @A  @A  컧3 @A  @A  "f  @A  @A  }Tw& @A  @A  @dߌ @A  @A  B @A  @A  @1@A  @A  @SDSݙ`@A  @A  @}3@A  @A  @{ =ќ8qȑ#>{RA  N@sgP?>{nG{nH1 @A uXgaSOZI @A 3\pWq<=eri#` \~^xn% @A l>>_K/tg56R1 @A`y=zK/|iO<M/{?(>#A  @Bdjƒ.C՘ A  @*^}g}v5>ox<=;ફڿ?cW[ A  RX_ϗkyc4 @A`,ìRꫯt1[}|b}wVě @A l/x @A  @A  dc> @A  @A  D^ZA  @A  @A`D]21@A  @A  @^"n/i- @A  @A  d.~ @A  v />kkko7lGic"w6 @ }ݜ @A  @/I&e#lc? T".A  @nN>4f?}pYbkױ#]"LqF0[5 A`!<ΊA  @ Ж}r9N髫d_>wg(zw &߿?mwt[ ox%?~^;v|uY yj2>qD\~ZÎKy7| @A Jp8{EϦ!9-t}>5ꫯ>S;sPЋ/{scOZ.ޣGBX4]\NRmcZ!ƽXH͍gv2Mlu-IV4TyҞ->|W Ns_xᅆhNVvΪn)VncFt M%[`bry}Y2 @ 5@?Ƿ^{%|v++1w[Iw-8\z@:w40 A  @77,NDW_}5mX9cMo}'Pn!qQGVPKBo}; <|/QKDKVͽ@z~mUw&1$VŖqW+>yB'_t8펛.=P56QM O{#&q饗몒8J ݃8z*>w}[oo@θq  0d_?_bsd;̓Ѥ}NG?o𖷼d߲0=[[6p/\aAܚH" @A`D{衇=+8 O(8>?W[ >c$@4_Z(qJ-<3 %Ag#O?tjL]mo{* dkx0Vr817N?n![vYս}Ow!(HEih u[!V4~$Z'p@>'j-ېTߟ|G+Rщ Is[zIq+3{|2Ƥ[Ãv?6z+5DAd_jߏ/"}s_Ԓ |x pfx}rZ?/vo)VMv g?SeN˷ԕZ U| )Dy_kc{W/<4|b\gw'tc}keB ZR ,˒}+*spM??u3Seٺ[@77馛Qb xsrx{lj,#V&<7`ch>r4 Wr@A  TpTMP)\sVLDjF~a{V;d}]uPB77mY'l 3T4oȴW=v"f*dY?,d+`| la,eKqB2=vyb"]ä+TOh[`Դ~/mk?A\XE@OQRXTtmX -́P{ו%*v6j,CØ#2~ }je"$5DzAڶd⭕89F l!rV Sj1[>4߼ xZAW UN0.>/XqlFU x5%ioH+2& 5p`/uBVY̐{\`w8jq[ge]bB$@Xʾ(K{ץMN5-ai>.gb~w,v"BѠyefT}~*ςwJt dzAV(.?qoEx1g f;'+ٺ=j$]&q@0!~{95 'dpF\ށCHc I<Ǻua|;A?#4z=.1%p##Ďo6(eDw>tVr-:VeH!5gFX##tUV.;M~Sd)^Uݩ4 8P1}gWOeG&T hܶPR5P!: (4`s7Nu7m0hMPG:!9V=s+6(M#RY5Ս+E]ؗu.PL]Bݧz I^VWEO*7 qxBfh *]FWL2;קъFīiM  H{F!^WpV^P;Ч4oLY-vGS1np|֭zНX^yV@i{DGϳP8yhD5準<{ؚN" 0RI(Ǯ#MNMHJ"{}Y0j39-UoTsbM" jyFna͙=ĹJɲ̪ +@;d[5ngx+/2x'n,;1Lǘ @A ,_ig82ab2ptH2}V*AU9J[(EyaS*wQy(t!Sxb.1juu)-G[4҈a,i,Ihgel#x /]3JUfFF XRW_TNZryX!HZJ.QK#P>5` \@IC-6p1 G'_Tr{+0u=TZ 8ҫ"mj]ovA!F4"5\խ8J81=m0,{, B?@$ Ϋ;Fx>+.Kv}6BqOrjI8]9ÔOKzjWZOid]bVzQ(9Isd J0\فB,塶\啃W {n=J΂ǵۓns碐##=VupQ1hw 9 z828Uv[O: .K}qOF,W<@slϣTT@B {z+!yrf_ӔK/GkԐ}udn:f#[+f_8M97i Z2fkg|hr2x&z@l{'4Iz" @A`!@D| !<8^ha\K2]ʇY[cYH`tF/T\GõNuddzNƦQUWQvƾ׺$J.H9bfs^GٓE_W:WJ* ZwsF1B&44gV8=UGȘ!T'?կĄ&( Ds'!$E{Kt뮺%K/%!Ac\zP05w}7: %%wCJe{/Pf;S<ҋozơ Qs9ՊvPy,@*;Bh}?%oGa .k^ M˱k 0^c@rmM²sxɒG-IZRSp>K[ TM)L4!pM7,SJr@1Aݕ攴ʃ$a&joƧX@ZOmɸQFuod8JWE"OJfL1 Gy7L ~N](v~ j." J }d..7YaXuWh|m}ybWaTsOp]p'@Xʾ'G羫.iu"(K%2c]=E|?g'@L&xv8cjvrЄZ,8嘨LQXeSt^n~J"7YIYtw39A  @`GE. c~p;!Ne1/Q:*#£p-FCT>0_O2WE D»p?fZdm@T %#JDOSZeG%8K"$+"RńϨ-}Jd_*ɯ4i12[QIT i]C6QP 3)6X0^5HX6 b0%QqI*}G}5ol8խ:<:Z;D5r؟LGu*=8+zGFOhWs";LU6 03np+YrEQ*y :]>AI*"RDksP o =NW\>+HOAION}g݌50Nf:O<^6PgKsC,+7^m%uP X[y }͋Hp`;_y{P ӳG3 8t[6ꪺDXI#-'a0]4EULִ;&![05䘴}.<1ZgLh36tA  v,nEM#a`\JDTx9[z#G~5yőj2[ ]DP2BnnZ-kuHZhXbȧO<݊=N%&|f g㥚IMTWJ&@Dk?㫼Ezyj'[{'SOF=$*| G,欏%ڌ..JzA1OZ(tN XNi|* A6[tcOO}hB_4ϧ#;bU,?ίEݧeok.D *-1X6|qQcdSPsmCEruw tNǢ̡#`9KIIFjDǩ<=^U?I+ C#)efnW pt*0ZrÖ̐th59 pR V4VHSX(sMpa1q\z&c|^+.Q yVC%qf!0݀ZMns6t:ẖp1%_sO}? ZNiA  v %:>Y;fe=7]t++|ǖƦeUp̮Ϫe]v8fU:ȈmD,[:ҋX`ѢvR08׹r>WVVHHdHD3Yh `t-ܖ.axWKwU5\(04u'R*xĽזq-PROIhP|#S|jByp`!# ;H׋G,"Vo&*ni s~t_)K-!R2bTEʩCa9ֶ6]iF,'FQxƏ@T3⹧n TB-nT6n EpXSt奍5 vsһ)œֽ?+?YEpOvy'byӭ3x2@x##GiI@==%LHI uj$yMͯHw~]&S M-FLc3kcծrDR3ƦXM~̲L?.q!x{M&8EK5mzA$mkCP%+«$zص6kbvpE B9k6Qo]^=K<%yu'L1n:+GoXk;8R:p+ӊtwT(g⡇IMμ= ?4@x 0;.iИ>==eN:L5e2bb0dǩSojlӺiۇQCrʰ0!@mXˢ,~0Q-1N @A`!Pےam!)eǾ[B4 ծ2KRF)PhWm[(|!$4<;]OZˬCSonKKRL-e ^NҘ|4Xܕ"gyh3BZs^L;yr&VtUf,UqPt4⎆Pw!OYF`ML4/j, 9 )hO kdvSŨ .)Y|%.:?5D 7k!L*#_~W %#Є2]w{rfXNz4?붒.la7YQTnmU 8 UNvƾFJ-Jvˍz j9+}|[ž(J H[B¬júhjˋJ^3<-GZN?zR {Vj=>YQCc 67t#4t-WPDA`8 'Jzj o 2';5p4 xIxzW,{cڪb4{=|㘆1D^ NL`=Fr) @; ×.,J%9RĈQE>wYwU{u3i O3B r%.mI^!1*I}Tlל3&9|Ke Hm)j(^pEe+νҘZNKЭڎc@*AEh@)qF]y[*Zy1B?W%vX?R5V-ݱ}'j$'rHO/7jqREyw@jfQ(-N&'Epwݧ_?po3 prީ{5Y1^"#d_)@ppW P5w|߭}]22ray/b-7sԖo!"&m2#!~򦺇ׄm`bi" SPQ4 kVs֞S޴yD6ꉠswl*}` m叶Lfp5jkJN@A  +m~?;vBw@%ZdSF̢!=[Z2Kus# sΡpz[Ҽ\C+8 #'-d1?oY],`Vbv!>p (yt+RM"z*υao8%WWX}D贠+ "ɣ6lXZ؈Dᆒn*df %)= t zc>㒅,qt%&L#,(lYq&8{cbo@jƀPЀb)4òcNӈ%' 0}͋Tdn+]3/}h'wyy${w`Km/{:i./?O d fD?0bO;3?'ԥu1,Fќw"KA  @ر}d2T %v&$FZ~K %Ch%#$97"H \epo0f#6aLjAo~\e%#8[w6=iOC/miMF  d Y[nկ~q^J)9ov8%9KDeKo-Ek{D C1bU=tV (ɤz~M7PZID-.dP·Eo4XPvmǟ__`M-TU19+=_PGu]?_q,I,f4 xTqH;V|PN_ pN-" *OA#A"O1jQuD&YIv'9,|I'"hK=՜0yU1BLHLHVGDx>yi^UD` rW .3%$GC0%SC]i(Y NGpQ Q/H׸M&@X9|*CCHsLTyXF/k1lw|PUĩWR!ogh_nzuzҐWذS12.*O}T CVpZW]0^)9l֮ά|e]] 69N`%L֊OҢn(J| iAKBUZzH5 ,hFyUzfs/_~XJ%Z166.]/{o(]^\Ev ֮&9j}!Zq{kQDբ~C3[)4jSbK:]L;FQX0c\V Dj 60lgӟ}@ v%=%Li~:&`.S5f X#h1vZWc[^[O1e : YK+R p-nLu l V'Tʡ*պ2QDCggOzPZC~T 骆k=.fFPu΅nY7Mdb!>+{ Xs֐j)0b @A`,[ݰcKRYjՒZ*H S=0=tܯ,lL)P'4IR}Ɉ8,bvֲ˾=hТH_@ʴDɾA K=WBI[* nxŎPKt(afnZ)̌n ִI-hW7(,w]rW~渨͗/1r -w)]f. |д2 EW:} Juޮ[}NQRg:)Jv*FTyR8םEo:yx`Yvڜ5J7g媵y'@A`ʾ5<=IqTxi l.ł@A  0vsZ|LNOtvi^f;vZKg<+_d{ÃWXZ+tPV7ͤ]2_[֖_4Eo!m,33 @A =zg9p@lSuV@IDAT@A 7}(@A  >#<7Q @A FO=W^~đ/}'<9Lr@A  }+_}8q+Wɓ'?K/=ǎ[t.6{9^m @A -FO}_<^>G?oؾ-&ؽ A  @ʁ}Z][9z5W\|5'/_֕'N꫏=-R> @A l)}u`muږ6cA  @A`?w/]Cې~c?tקp:矿܎A  @@Oݿه-vɹ<ƁeBl @A`c^_Ϟ}|/s=뮓/A  @A ^zeߕ\|+.~ו yA   kkk~wo튷>O_o>x_cPz:hk+X0\A  @>we|%vs= @g7ٖʕ|}ii+QRln%n(9A  @A vd_t8-A  p ?~ܟSRlOiLbJ˴A&df%q6i1\pA  @KC_]Z1@A  0FJv9r B={|s9GJ lA  @A u=vWOBos֡s>'s @A`#@uok|[/^8x ՗櫀*yС(v+ܪJkEA  @ٗ3=Op䕣']~9o䢫rϝ @A L|_z%/] omp]쳤:Zp : @Ӑ^64 N)@A ݅}> k'ȹ9z\|хG]#0 @x"@%+,ͷ+ÕK&|=Gvj|a+/uYc\'<>ӰiϿ⋹lgv`;( @A`w;K=ϽP_?سKa߿9/yה"U0NA  2e>߶wKtKiomX~{1οmo{LfD,;ΓO>i4.y睧g}O|nv yÖɆK @A .o OkO<3/㗜*SM}muǟ~<{7l|`F?C;?}g֖/Ef#L @sI;Ϸ_?W@1-Ip_?)h+bZ>#dEVlf^xCɮ^d7կ^|E]z܌nt&K @A B` N,iyǟ}O\9~/"{OyޟNU~y|9t5o}˛.Ic4q,7}C{aA {QJh$ (F#^d%]b~):Pµٷ WRo[]O0K}U>*[ru3>o1@A  !))@@l}]>=س/c#?ÿ+?b1ʾc+/yX{|fy,)߂w]z饶jI 44.ĕ@A`' scQ=%l58_>(.h.uq;ͭ^Ku4;񎒏', ߦvmYmfa` ;nEJ @A  }5SOʹj(]!o [tͽ|}cv\rm,q9pfN4},.Uw`mk߱Z෿mdZfؕaB;,~n=Vk`aybm,y//_mЃHZ^[c[eig8|Py8ς{ش@KE`^r:To!駟6 jEM 6~^|}zG|k-^=EW{m* yQ*ݯmE5fHf1OĽP^U1mSe;~̓iZW(rI.i|ƣ}VPs<.io&^B{6Uе2pdQ4^c őDWIJp[V8 $' @A`3lJ0‡7 ȷ!rcѣwudS/y1[/Xh%kc~P)w O8ە}Z+ ᲛF+_u]w|碲< /|vo]jG @x#!@a~~J?oF$p}'PMw4׬qH2qrFM'_#^ (/Lm+\TOT+ ?&X ~}·-kolIf"u~EqOފge0'xgKbל_!-h3sZvW'GxfH,S{ \ 2 (>F!l2Ѷ!2z~ F `? ;Oq8@A  CaGCYHj7ڗk['N ʍ6h녽4ݑ;Vr_׷NK!je".k5b[%6e_epzy~`'>-ǁi0i(,hTaQ!MT ѯm :ž U9Jάyo72-lخj0+zc^hQ_K]C?OG$_,Z/Z1Bo]Ttd@A 3jIЄU[go7pY;K495^?4p^W+ͶFqtɓ?r3+vptծ8Y3봪kN[&zSNQ;MdÂy_(YV]y>4_B-a'4,ZOJva YC僸 MEƵV8<*'ztcq'>uD50}idN@A  zج5IEO)mgAFz"#wD_#B8o=7G81,u33^' u&FQ\*€H =_׬[lްfGG `I*|_},<,H=DI{RlW 熾U;b-'>n{h[d,8- cSÇ~-{iwOJ~9}%[![n5wԵ#ɚ p[܎$tw zY4SU; ~i댳\A  |LNd`fST2ujMH ~RS! J3)KYUXRHN6o*՘gۚ'õFNjˤL*uɄя~ԴP*'Zv]'/r9xx5Md߁ziّpK&}h n|LPlA1!u1DÜ]o+ d=#^;^Uc{YlDBJܦ`uf[-q^a>0:~[G>D yl'AAUKI @A  G`+e_C:+crH`6DF V7Q^ʰrrm;ǧ%ћ|Kq MoIħ.5Qz\d\e [LP@l^α ڨ *d+`UGǡՕKJ+&X~hjBT _j7QնwI&hFֆ@[v!#ͽǜJT0&kpZQ̒G)̪8mAE* M 06x Tdr%tX5T?fc/A  0 4@䥛Y2` ڷ3P.!l",̌QT[1L[5o0(U&A$3mWE@KS-VcoocPtljz+-|@S ~N0 ];d>+Y"YD` FȻyC:"OLC(ϔZ`+S4ӃZB'08{!IsGxnL= s@A  ЃVʾL-=i (<4ǛK:o~$_딕RGU6phI(r7"cvU:n~X/Fn;*l`'*reʂ C $M%Gx+AZ|Pݺ#_uB1錦$ӲD}c6bP]-՝Ej/k9!FNֲS=L[TEWal XvwP QM׿nfbX?\%+, ~" A mF$KNkʛ S'U.̶X|| 3@Qթx>XiM+YJ(2qk.W>2&GV`:U +Cl5ɲ9b3fVnP>>]l(F"^ٙhDmRxk*ؠ*!qb#ZI>NE OSʁ'Ibh"o#"wX@A =˾@p`$3t) =ECѻ᧣(4_l>^OU_$qmWǟp[*{ÝV1xW8} $? ]*.!ZidC!YZ0.M^ٴOTR15T$e$NDߊ'WӭQK v9`J݌<բm?*:q 4W-isQٷ@RSmY`|ߔOCy f@-a G8"ʏOE,ȴw_(,;d5 A   S)m7ș }6g3/bM %MN1"i,Ab?1Q7OkAYWJC0ͳXA ݫ%{M #p(Ӎz; 9]jhOi3>x /):$ny9Ƕb81h`h4ڃ(EkK״C+hkSγ9<〹> :$ 9 A  @XשB/NdAqD)@ #_fQ'7{s,ҥJ$eN7;ʒ){\tB"7-ul"+˷ Y kZxfběnVR&SCx?̾`k Ȳ@Ǭ sJ`ڍZ1e@ (洂4QwcB>UBv7YXf㘸|Z3ZkYPan Qk"ժ#k #X#ӢQϳE @alӗ .+ngŌUꂪE&$@A"`y&]JS4ML l^6Qvo8mr7ћ5܈^EJ"9ҸAqbx_O@Ҡ+֊ L\Hul \P妥O~=l? PDB߰ځfTD8L_MPEGcwxF^¼d?hzɲ"(  k!R8 @A[m#i־ݓVT ni<9ϱ;:$kSx-UQh-@,@/ud"a@íՠ%C*7Uf`OMЮnr kkn+I @8.MS%"Ük5*SkEЛnͤ53bhzuEQũZnNk%^nuŀ8뙁hQ ^0Au.obBCgz;nWq.QW 2nvt5%9ky^< gFeQܖDA  B,W%IiK$jK#-"퇆VO;=%\FGcH]Q(t׻}WOw|qT`"k1">K\XEc^3prHTht7_< .he@M)&;)4u<'SZ?Hu]k<UGOAyV^Vm)ȋŦ,j_Cif+p[cX"?meb:\bѴ..n^iLAV˛/@A 0&Z'QBU gN,Ӵf^br˙u~tLĜ<&qUhOTyUjf&2V袀cR.aEZ-;JTgɚ:Q, 뭊D\ۍe%DBkj"yjdEgj̈́ 㭉ZG3r&(3-&3 Ba "}kz2%Z$34?>swԎ`}o0G&VE]e-h F+!J(jc,Q=fm|{K(,Xbeﲅy>RuoRi)E);c^D\¢lK>IAE ]Vp|%4W[0'WJ}芕[@k/'} @A  oW]|w1HEQɾVkON=er,so,8tvJ;,16/ymÇE MmyI&/%-fTۊ%g |jl}N-:{IY< ~6؈dEd,G r WrA  v>>ߪKUɱ;ٗo_>q 3X~6uvuw2+LݫIB(T0_ͫB[YszIJBEz}k˟y>WUɯ~ucO:,S#y˥I)e6^3B\l?4!ʺ +CHp X8tq @A L#oVLk9/J%uL7?$kgnۑ}O.Ywq=ctҟsĵGyn@1i*L@g5⯥f,/Zs Vuj[Mn[o UkV>xuY-ttEsȸV|ȱL]wQ@詂ܦ-LGA-bu=Ju mczft A  J<.C?8 ̄l(@N\ؔM tmtM"$V*v*#n=zWYx`o,9 T;jMjN \9?>[g C@HDB-BffI a~m/P?jQ !BN! 䬎e3972h ƾK[`X&"nN[   7*.51hw%@A`7"`67>oZ3ciU3K 5UҨӼ\8 Tiʦfq yvU7 }!p+ @< f;YE8J`MrZ1 n8R-:\pb`5MFQЇ>"1R񪅐 GXɓZ4ARGPMPD9 A  @<3t̅ņl(8eb )L]]]9q\kvڱ;*_{xw^8W+#)iW-x,w\YS[b/-*r*U9Pp"y6cyb5/7@{H=O}S1v؎a{uv[e}Nṉn%e%c@K?O/Omr??""q,_kmn馿:V*?(_] 7`P]I @uXOoJ# ?O, l@ J.qݪuլ*~b7ϚU3N μjj?NIs#$TmiLjTnr--$b}.SA *< +_i.٫+S-Mwk aRxqBN*sa&ъS-Cri$vm!sqըXA  @6%h&.QϺFƲOx1hwFLj%z믳9)3MUmwN waN`*m+&(K#H*"2^Ě VMUH-k: zX 6b-r[X ]j#յQtdf-`nF޲DjH hf >RWs,kuS(ƙڋSGupoɶ@>U@@To{x 9\W,U}Lp2ZJ}Xa}!4_r3g/ @A`#`a DIi$X:[]W$gZ;uiU>MHNj-rՌHqSoǺZZC3}.XZ@A qLR~Vl݄YtH8v`+Y@ug6e#-(U$PE+5iG*bWk蔩I@A  .}ވ_emlcHߣG^9q؁m=fQ^;lv+OX=qHGj :mټ| %jWiiUH!mWٝqJ|ttb2e*ݾ0;&Cs߸M/VXL+47ݢbzS*' 9gui!o7hȅN |6Pީ;e 1S9HpiiT5So^$Uwg]vWU!1 x1L16,j^WZ} {{^1`3,I_]J%TN9%JO"##vx2OHVkA  N ZRC]Rf,o+aa{h:KI\/}~>J޻BlyA  @+E`r"p+m3GޚmK9-%vk=^A$$jI<^xs:xTevW~jRkmo;Hu~:RPnĐKLyA  @A  @8i]S]wmvޙ:r贐M;m2MBRkwvlzޙ{tQK"^ҊDZ]E[1/тR(T&k ]XD(wcOPq@!A  @A  @A`Ng/\!@.|@wF[^'POę{O߷g V [ 㢀HUkb~JN>gzM@A  @A  fE`ӾwԹg}򬽖wX&9=pel+u&Pwlׅm޲VuY֒b~c]G @A  @A  ƈz}B}k̸ݥ2ۣ]n?Ͳ͈Gy1[`*j @A  @A  :;;9ic+T1 @A @ůFA  @ 0|E @A <p>믿~LOA  @A о@12@A  >K+Z_݌gLA  @A`]=vi@A ͍Ν;9W^) @A ̈́@ht63 @A ߉XE]tStd @A  LA  @A  @A ƈ=MNܾu׎wضmk @A  @A  N)6}o{o?ǫ߹sZk:NNMϼ{~pd=;wᵴƾ}Lڣ @AD@ \6GCDy晶-DhbM}>) +؟gYkCnN(>K?[o`f~bV9)Ӿ6=?#ǎhmD۶kǧ>q{U @A  7|n믽;m'?ڷk+l&@A SdZ|?k3.–s2^xロm+tEy4O>.RBClnF}]";\=F+ҥv‡ +o|;wطsvڹ;ߣr'MldѾy߉-TnLl9:އv_pmRA  !WG>3|*{z[K /~_e..*Ofv@A  `]T(`,ڗep袋2RmXw}cg}VĮ:nEA;[odQ9)pmy_?OXZp>rtrbg.{۷Y^ 6LO6T,/z~u|b~bҵ78tdJ @A lngN 9<;+(l:ڴ쥸}e7 @85(}gK VZ^F$_!_~9kP^g7x{VVH2Y믿nagbmUV!Ss9\rb@%\L=w @#-ʞi 4J,%hpnV yKb,&e5hiR_p( %&e7xah_zt\jk!O~Ҳ#ltW世о )j*A>cUMsE!'ʋf(W_m۶zgW~{!/LG衇p,Rk .mVx@A A?+jv+2w_n;ËacdY@,2l<]\05]Բ" 6^s~1oY3r/{e˪=)73C Ȗ\4.2.N]`F{dzu'5a^7иWamjMZF`,nI1FUW O{c|2䶎}s!ĂL@B䨦\%tlUL_͝gA!gqP*&FCh)_2I0%&UZ Ï,B红]vGTwZ^e: ջo\N8TU_M:)3]@AH>a_S Π<~bF*,d:q5MqA 4fT3B%>+w-@P_Ueu[$shQ2%'Ĵ+0@tV6F5&2FaH˒Qdb*NPJ!d7 @'&JXA0|bϠb098_L^%a@eUƮXV:U|AÛoYCtJ8EwbbY cݍJvq5@e^&C{W e ujަihf{% O0q1R?ɉn6>;nFI{{cpH*nޢ/C8_;Y GB% /ss( N)QeJuG]%5@Lӊgw{->ᷘhL*L'xW~Y5F헾%=iW Lc~& FL֤f+ CBA  @8^BL_f ImHeS`Yao jYV,);}!7Z%RrtA,vIpu]J\ /uŒ$'ކjt|MIͶpHWX%&%h²q]8!2FI@˖U@D0M7RBIjp%O`#ϲO X)Eώ%*u4ɿ68is!LvWh__{kx%޲=·ʊ Uo*O,0 !կPbFx2+qZh\un<G@ʚca31{0T"VT/] +1Dj|wWU3{d//=~|-PUӻ!ܷZ崪rutTz3!W>9H @KѾ'4;|D%!E+UѾk;L'H|s (λ7$X];@ZƉ:\Mgy|ѾcҾ?  OIUF0(G+$ @R 8\>9Tn25 v%ML%Κb̐8I|2ͼf[f}bz4VB-k޴L#Vl$ٲ4Q5HaZ,? W ]B*1Q,~Lvע'+"]+,л#Ls4Um8f޿jWpC/h39 B><նc\t " 8_ pQ%|A7ےXAXEcMx2bT HzvUsa8YEA![ZӜ& Xv[*Q DOu$"4}$v5o~W/9o+24=B mY 7pPĽ]Tx1t_=P0`qPWgQg @&\ˌ~A|&@A`ә֮`C̕,(SgT懘\T329A捙! ;GTHGOf4wlBEO#S쭭Di&5-$#Tڷg Pa(?pXi_cB=g 6"z̺H> NM} 2DƜb~aG5p<xGnͭHD饌5S|%yGm%%Re ׅykAY5fsSyKw @Ad"R`VM_ <E/~=MbA|V/Y!mZƲbY՚OmIib I9XV}fc3b \'. 'EUٯ0Y yNB:LOJZBν dR*|A 腄%ryӊ 16kSg,ISjQ> JuK2mwj f4̕ΘtA=_,*z6p-ږLN( ]ݴ+f}?wRy.2DG+kyLZ`% @xl LS%S3oGyHƃ^Fzggi fKx"B:+$L-:CzOVEZ @A!R<ƂbPeZVL/vдa,+i q?0c|$2BQ,Jd,vM^Ğ~`ac]RH-E@,6ev[jՏ Q'e?=nХ+v%ֲu#A4@ccPjn;QQi Tu+\x[W >sϯXDR҈ v z2 7&twO(S!|ET1 u%W;_M7䂩兀b?+v>i`) A  ROTfOݚͧ%*MeJ} 5{̧݊^i׌)[obBqƒ/+0UO @A L0{tc X5^΄AU넖Q? 瀸jβoNIo_}B{ݥZacbYHmo]VlelPocY+@mnyhJxQ!!nK M2LIJq Q`mzN⮃7cdVnHT2n<'Y4ё|Gi>U鬉r[B0ԄЍAEYCQ?ehSUӃo5 g6u: O?ydpAE5Յ9謕ݯ˦.³Ap^]EsXPR= 6!>JL T` z0Y)l'ݜfyf%SpM&Vm,SUMXjKKƧU24+ SM҅&W @A`=#^b`{8nbj[ځa]q%mK.oYiX,]$qEn}].>9bvc'Yz,u-ۄOͻ633-vm /QWņ%cXgZ#(Bc),([.O&{:ve +>/W.iXٺBȥ|駵WBHҪ u/WkX uU:S* Q/xIGṲfC>IG}ݬMf'eLXw\ƀ KC-]jc{tAr Y'<^tb1n3 ^VZD/ҁ;ēСSαQ/BRùpʕr(wohQl&$AUNW/M귆WF1L:-\PƧS0:;R&*: gS ͧ<&CiVVáa;;]a\sZW2LuR`'@A t`oHݒ3ٰ{ (YJUcح$ocL`G&w[dX>$Sٙ A  Dždy*A5:ʎZe DJ셩&ŲDfCu k sQ \05f] Ѿ"v9e[ܻ`c'*n-G}'[W7y.{3U܊{ jEZѸPqsAU"i9JډTb0)SggΌ~Thh:p:_A %.rP]),aI2Ƕ<5-i o|ӾF E_2uA;>ۿAS@A`ә+Hf:۲FdQ 45FĎE A  NqX&m VaYu[PQ3 Z'F}r 6 %Y-*zѾs'&f',DI_P쭯9 # fh9t^\aW&Mű:m\=1*;ZxO F)A IEM I&CO,AJPBkMع;wOn9j|h_.l s;9towzOrQ Qx81@ z;fnKG gqf: т* l5!VB Rx[s DhzTN2b!QO`W$k:J8P*z1ͭVf(f/ CE[̦_\gp ށ`}9HFCQ|a *tӻ[)o }j2 k4(} rvmr/gq:t%{(1q1b_}PO\5ܮ7:' ({le^m~VURy)U`7Pf6[;oy7챨YVv͹GL{ۖ A  @Xei8v&mʤfE`O.nxBLpfyIuhmkOLOl]QSej Y!oUh(I}]Eq5tIrn(n<% yJЎ {B/QпRWÒLEԻA}T tԑ ^#lSe,,WOُhɐ2{mwuGb.z17|$wOW )0 FDŽĄL[y-v=jA A  AQC, @A  B'Y 'B`$ڷ'tgNMO5,ҾÈ0–-ۚ5~a݊2訫NU^ΰދ۱Tꬼja 툪v2u8_9+buajk 9F^C[k=:xj]3\5c zCLD @A`"PA8 z6c}to;9rtY4v{@3͑cG|/#sǶ睹G/Ө%L5+l kZ>HkSϷTr³+آ޵zpbf u"PeCA  N0S3Y5@A   x./bMg/3zKyx|Xy&k,xm;9qiM -d8pEZ+ךCC[9 @L'o:EFa @A  z}wsY{-LrڣV揕Mخ G@\)gf^]km߶f2A  NZrF@A  6.i͊ pkL3A[=\vXB{ĶbdG, ׇ,/Qhnf8A  @A  @N8bvgZֳ- @A  @A ,RR@A  @A  @nij@A  Q +)ICfjjZLV^!CMk  FAdZ˔^1.a`=A 2PA  @~<)رu;}'5 ?Oy1y{n3 @l|^|dcA.aEݥD`eC A  @8=z?xo'mg8V%*|>?Cm{i*) CEjT+kP5ٳg߾}guRm[7t3{mG @^?bh9ra)s9'Ԣc]¢bSqq]4A  ǃgyGW_}!s뮻UKoy~_ڪ#_~˯J4&f>,_H;s, A@ѓte]s57x>E.+?|wnᆫ ?(Z?Oy8"sK.\Į^|$SO|衇xS}:oYL43|A  L{T T Uyj^b5y_=-j.aa}FD`cо|q/yCӇ:vozdj goо#^i@A }śo2g*Yc+M|W{K ͳ?x#y8}-J˱Ao-`o8J]t&DU/\ ,/r-_)>]~kUNNio Oe#,UF%.ԡ "U+%lUDl/fC"+Y9ŸgDWԗ8h:nXT奰> n#{UA  x0#"ŊؿGL,ck X9`x^Q%wDN|w޳{_cJeg=ᮃG.`fA  @ |"vһ_W0"%pїH{i/4تhVk5}ݵ^A8\ĺjK981Z7FE=~=VzV,@cp <1%BoqOX+{1"V5CÌ#WB\x1jۑ*ǖ ͍R9:fmS@A  3rd3Ͼ1C BcV%LwwjzC_?#gܹcGJ?xwff==MGyI+۳{Gu^A  >~"s{%A(bZ[PbTE `ᐈEb`YbHXT,rSɠM5/Vp$mbABk"jk_up\#[ QkEq͒iq_4"jEC#9]T(֪T%Q~E[Q"qhEfYi_^`ɋ)Bԃ#XLU\Hj[[LקmLA  0vL yـ5w3Ͼ?>:5u+4{#4jPxOyE{f\RA  `QIj)ݷ9!<: Oۦ# ގ: U% ĢtѠlNWǂXEђ) ދUC.Jc9Qa^-b?O8ea5\,uү(DjK1QpHC[7_РJJ;8R|4@(V/o84GLab{9ۦ$ @fj*ET5BIY22iJϹG0DκB`$ڗ]6:;REYqW:vܱo箽޵s1wGNl27;{d #ǣ}qD7G`bщ> @A S&(W\'ӛC̣ QAwb9+Bv,摯?ajH%+kT&x rQ!js}WuX,I:Im}L'k2Q2PrEK 8_KO@cDB}E8b|D96 8(98R8١28!ew/NZ ?n@A "`6Kl23^QKUR5=S07 }َcR/i#:s /83o0C:^oi߆u_ٹRV")jކ}nt;7p GTA  6=8'œbW}QgoFk춴/CQB2"@1R0P] G1;$ѝep} _(` kEA8|[XBq&!q4$A.lBCPS`\Bi8&Y[| !PSб(`\|45lW#'jK>H곔 @ eH =!SvQULF?m-} *,viLa9^?)ػgYgmw{mO\Tpڮ]=nဋUE-[fY[UA  @8i+ ~"|1m+Dz D-SMXf!;R1Nݪ#<ߦ4$qm KL/WKrEW\ *HP'PIH[p]c{h(ѣ]V;0}IVTe0|ڠcyhk j3i_ػA  ƅ@* e/ Jn j: vZ7 #ѾPp1ًN>WЁwXz0zU~_X䡧F[/Njamlڵ[w֣ @A  BP8V[AbZqT#<3կ\U S(5X/tbN#8:(`}6ef ,oV]NX yU&+NMIsmP,ݨDƂ$JCmj/ I4ڲiaLwC^؇:A  J`]~v){I C>it +97c}M,đ#,feR3] c/\aGg]UM`fhhܞ]^:C}Z<=E!i$-fcch6k6FVQmE[SV갭ݨZ.t*$ NjZgJ`\TGآ /JPL ib:Α(PRN]BVQ-( +P@Y .;v @#"`H\ix[V353aVA5/1EJvˈb>)֘nDۦ2 j۶`{W#o PFԦ]ȔlWUP(_] d}2Hu(V}* @A`,0BX;@&L*f*A0 X&>VT2 ~z,Hy(IW xٰR_vC6 kLܦg6;Ϳm8^8{ nr<>b: MY T"D,s\$,o}}cc5@JshYN/~XΗ:8Bo@Be1wXֳRoѦ>?Doۤ=>3[(ɠȗz뭴mOz.{hxk/Atn)\}G|;Mڽ;8x @A`|Y#oU Db rNl*F3TfӋ^&Snc[GF0S;$qG0fXdQ47:IƣZ6A]l\}-* n,c_Ei@A !l@ڔ-χ건Ő(D}f:KS)qҾ?/cBJ]J*¥ɾd 8[ab~ m}k4+럢@6tx^шY4ЈU|}وn-qْ|Zu 22eRݴO?431@ "'!T,|VtHU c+4cxq&Kf-ib);U/=+ft=G@al;H R\2RCSK[0 @;,/d͢-E)Kź'L\*[Ȕmr7ѳޓOxUՂ˻Pk(vNOjj|_7Lǟ8k ]P5׏ڗe/ ȸRF}Pb/4 A  3 F"bZb"]yL<|._ ÍaU-\O,п#V;[a?&L.stVR:r<}V9\auXVRZ }5LIA  <fs d $ò,wKGb09*A.\닲CJ` RL+ӾP` ͨ %3Uhee-.L1h_қm:k`o6j4-V~X} U"{Tmj$d&=vtpHl݊nH42.u{1_Wy¸KtVUZ$3 8ք,RMP$`ϡgwpnuE(|$j 8PKE{o!+BFs 'D`aErBLU[m,@G:cd@A blYInjaY1$fu 麕c,fg`80PiľXVFGgPÔ @#FXD CX]K "bK"лVoO}Sv8%^Z^ ۂ!Į*e=}:'4,;vkvEx[ me۔W֟:ԫY5E UծΖCJn!F@Z 9UuT(S!|T=nT5Ep\M|-K/ү/p(pnG&#Zw H[3D$qڪ53'CJlQKV9?j4?`)7 chpmV/m_p_@9gQ$ @A`z!kIys.b_җ:Ԯ/8a@FaS9D $ @RaVݤY[HmvM71-H/v;6nAX[wtPjVٹھr Sz2%Ͷ)j"'67ESg$]ĥpK|c7:nH8˛}_k9B*_[j&%P-K(WOÍMJ9Kp!AE(ыW(QM{eL\p)iCd":=HP^ZVJk˓ A  r-Lfm+nSd?etx:Ś$l fml=;lPgdD2xobۀbL5%=3VETIW6G5Ѳ2c@.zZ-;iyr(\1:DH=>$VzCCE, @A`@݈d?3 ab);) "7aq[-ևFCҾLgW*ɚN27Y+Cy1tvf"s{ .I=G:Ԙ6aMWf GCy,O"v.NZ@A "WU+82IS2 @80M!4bxK]t|j*V]>t^BI`B+N>9̿RrWi>-@A B =Sݕ"3qҾ.D?[٥)Ctsgw){wD0"shкh@z6){,k͚m:M`h5zԻ~CTW+G=QZ[]/⏅ZE@:qe2}_>o )nWL"n\3pK*hNI$8iN[o9[z A   2E\vEh5]v.V]Yw`wΠeJFj,j @#^%,/?G7}**";/JllB}'63-`6 rPFuG]ڗLwX5>pоE; !͢Jx^is2NV୨ VfQVah(Q|H"\ppW׌6ALS$2ʸ(qxj!2^BSE˭_OC^!ӟjCVPL @A  @A  Ebjd+6S&nqggf;;?7w{gk_9|q[6*"܄,p 9upE:)+]fb~br~b|#wBFT):[BbTH"R!E^pUGcu*uȥ>,Dk(WWdk )>3+ESIYvSNCQUb 29jPՅ!LrPi @A  @A  p!0[hu5o[͂K-;De˶faj7uF򢣮:Uy:zQ1V}饗E ߿KEӷ)D2ȯ+LA  @A  @xص}yg~ȑ [w09vJ]2bFwlzޙ{tq9*v**-L kbE2F,0ؑQѻÌkP,YL4WA S/YCn/cy+g؜pABA u &>&ndE.aEݥD`(cT=1?u/pn:Vldl5&'{=w6A׾5V~,&1CA  -8'|Vr|| n]ovFOA ZDV1 F"]BnD;{O{'kyeۣ#{v(߅,x؈g3k\_iȗ.> @EoC|۠O8D.Nwb^Kz}ÔֶV}-Y*cU/Ɉ.ZkYUG[Vܔ1^u;>`ã() A   d219za09/vZ}TZQl;틾ssX`Y|E1^RƊX]78jo?M,ۋeu]h!>Z/%2K`;R=RX?ꫯE-Z(LqÌmLA  0vȵjEX^HbX/?.*x]u[MFЊJf3!w3͌% @' c'l&;)"Up+_ iA x,|$hE l[MHK*#y%nmձ- EbH)רNZV+)/TVrH -C"WDm1BiU Apfh.$b*ؒ('xQTҋHZYuV<"ƧH꾑bBԃ#XLU\s O_ @cDTΐ0ћ ^s7#^ KoB" l3850VT2 оlf,A  @xX+>:tB\q]oZXW g!EZA8b`EY"Xe񭪡tѠTWu!U-H䯥y׫f>PƼ)+ s<(1fC[nV?O8ea5\,u/J!JWΘ]C78_+~oC G"ٶul0"p?_9zT /|5 _.]$Ekjhb- Ue1Wщ84Z;L䐠!P ot9m}-?R2Aej*i_ػA  ƅ@*{\|W𶰭֭UKJK~ h_zCGz+#ھ=;:}Y{|#) @A`( {k%\xX$; 3Ejy\w܁lQpeӲm5-zh+神Yt-NMInte+0XWıave(j{WSجaHu~ݾɣ ycFr @+EuQ7"HZ%v% kI1Vso6;CSoCS3Mo_ Sӳd|]@?comٱ};wl;åNX Mňw薪u9{φ,> }=:(4^c,RH|b[Ѝ`@\5ǨyA  B_ E9q!^83-RR:,_N^k2XxA_X`r4OwĮvwZbh"kmhw$etע 2Ut&A  @ΑNfgl/bSTՔQ"}UpZ.OnF}WS⵼VXéGgN۶-&Y}xX%J o,4f o9zKT(H[8'V[Qɠ{YJ'c@&TqN" @X)fm?x^kFNȱQnCyދ-?#02 7 dg-jA~)SvB 9acZ}9'Q@- 5Vנ ] }5LIA  <e7_{5J 8]X-Q};v{H 媖-G74}V4l!%nftlJ۶L-pXMcXͶJc{YPe %hCU~ų8n7Lh A  p !_~加 4jזɺIƭ0qjqXz*6T0'WboҡTS5uuXV>$ @X9A~_E沑P2,%k[S >\aa|ƒ\u]W_ReHmyst#zE/\O|ۧMb{'5K=L1RX8p_Ѿp|JѾp۶e7im['v؎*޹}q^Zx+RcH͗6(sd|1w b|Zaþ6m"IZ#Z Ij+iVz'񝭓8;OqE{fi @A a`Sy".!ӸѾKŨ>CLG-!gQ/}K~͕Xp1XalV 3eCoJ鋒?8K*{W|_w3'|;~iGkPUceD}P @C@-܂=2~~򓟰1*ά^n&5Um_2KhթVUҾzb3]m2'O$6XQ_ri.17z7^ɤ`/HRb.J-ABHȸjM\kM0g\uUjS%¤hY1]DӰh>=^bKf+G1YYu%H +;_]< $) @XS001~1c kɤsKCeĔ .c0Z5/aMUҾL[דW\!i_ Ï{N۷g]{'06 мMH~1 %͡Ji55d#n8 ]^ '0/_"WT8eǻ~b:ĿnIRq>-Z4nnnٰ_@46}W~Q"~֡j?Qԑ__||CDؽ;T bU/Ol @A`= ` #dLE[Fev!I_[e0]𧆌szwRaYmFBQ=wFevmuI%F,1@jftƫjB+0ȩa^mHvH}@nA  0vʾB|[uX2ſy@.–P>a05<񄛥 23`0v#p}"J`\IZ Q4h18N{F,U"szzvfv~fGi#|6Ѿ A6y/:4;?wtvnZ4 h_1)szb+gtz }92hMEK \GP,3"\`6ɠb2k;[׊&\>t95|'>B|+41"jP.Y}E\ꎪ~w@AA 1c ;!)/KV) A   F+HFτe՘,c0$ދVL<Y,){TSꖫ,7>l!F#I9mH22U6.LHKgB_rFkpj?{weWq>3%eA\ xPeרs]|#/sCs=5N6@zԦo)-]Iرb͘bb7_Q$@"$!+KɋM6;5Ϣ] ()2WPF+鵟,Ɋ -[ Qg%&jl-7Z4Y6:4Z|#y.F-Ӿ:V'C<:=y_+WzyPP._wΗ_~?}衇\hM;ĉx*kαNto^C|'s.b"R(Z֓29Z2N+5c !zR?GM*&~0]5q ua\5д|ALA]ə2Zg:o8+&@"$LD B~TfT[vYAFM6BMX&H 'Gg P+b5\}-JD H@}-Qt}%b֟O2 78ߖ/ Ր^QZ4}Oq6JD؍0 sήSONF*2CA\<6ي5JSѻ q`bfDWm%L< &?[o:dl%aWLt^.߉nc R5K\cx1’r_+1:6 7&#8?&}474oA HѾ@jl#:=HD Hf&n^£'tVߕ5׫-۪"Jig'@"$@W:T\Byh ʻW="=c ZJGlJuek,^ chX8{A[v=pM'Xڱn w}"XQ:lS$)fM`EA#1Usڭqi:]v-0PbKKdkKᎊD |s.`r)OڡCB) 4[UY; v6  + a@Y=cBq1ԚAғ`xBPn%@"$@"$@"$@"$Ai_ܜ MOD\aBzA_C35ҷ3o-[#}Dm[=W9η_=Qu}xk:[= eEb``g~E+1U2,, ǒ!~%+Wh-i*h R[;ۺAgaG,k$Dc:2U4\f-.,<OLtYRT&@"$@"$@"$@"$@3ҾcC` 1e~Sr"1Jt)+>/բ}<|uF­:,oU/: |5xI*W*^RRe0Tz˼~M9yZK8 U+EbiQjj|h)R - FL+<ZQ6lPH[U"ب_E':xp@^YeG0HD HD HD HD H#0(( Q*a0*׬G掜['~U8[TS?Dzx[&Hu^%*J'[rtPr'SMA'.xD[z|-JPD^5R aN&T@ (,Xxovau[H+jrN CEXzQC$@"$@"$@"$@"$@} |"081BJ8G!W*&6uв޾=WC{v2~F%op)!tpsÕK ĥ,s=H^'MBH~J}3i+V W OZ GIsT5:`um@Uīѡ'ܶ!_5űwGM 4[*вPed6#i7ݓ0HD HD HD HD H 0)aD=œE $-ˉCXd=S "h-T\:T A'8TK>X]݄UyL+"sYjF ĈE:'*8Y_Ѿpm`n ݆挂 7#} Ps sfghb*>'Z_ }#3!ʩm=Ћ$@"$@"$@"$@"$]틔D#b!F*W^EϞh5ֿ Aֻ?I{)[?%Ջ}]h\hA;MU*Eڇᣏ>ںu+ kg'|UϠ)5лNpcnF'u ݲe[yGBD9BY7~m݆ۦM#$p$@"$@"$@"$@"$ph_(KԧT$7G=S~ҭLy7s;7[K*lz? ._^/2^y&.gW$I曅~駯WAX{@^zW^*dlp3XXא`Da'; B ;Ehk}Au0M1#WhD HD x_./OM`؆WH;7 D'r"$@"pXb`q,u}5 lzX PlwP'Tkv"g*wNUl{=zk~UUZ(riyGeꫯڛo}T nUh_\! (ZArN01~+Ѿޚ7VϗVE^!tZTh#.hpf0iD0h4}(D HD F@.R\ o)b-y督GZ3XXNXn}ɍ%z3yRD Ho8_!q>uWc'SsYyz"ЛtZ 7wOh?=DoH[uLc+Y,nRkd@AΞgOW1\7~9D9jSc;QHqI :%MW4,@' pas\AUMI !SnHW&i⬮4,Q=db J:Xƪ,!@"$@"0}؇L?nn m!psV> m=8b. ؓX^sA(ਆ Q+m,ZYYYS9+[ẍvXm] -}ތx*$@"2DZYYӯ= C}9*zOWZ™iߩb'Nn߆dn9ޝ3:2Lx7NEx&AeU&ThGDva{ %@"$3 /`+*!Iy?_u1~H h8~G&GwZ+]wu>T]?W+>$K×_~7Pkh, ef\Vhn<f\SD HD[gދP񎔇VAb =5gs婹b~ֵ\ .[ӝ;% ._) ] 5|5jS0箄>GG玍ϛ|rPK |Tވ e*4t:&@"$@̾bi{MU%Os_z%Dd0 %E(`%HLmr&q96~#wΝw֊t5xc)Vsk5M0PN%yg}gbgO+$ ,FXHʼn(骯VjuhBCg)f.ZOرc<.DG0r@깧@+#V뾖{\r;h; %_ېH*}o81&@"$pGByzUĵ^kedc1 갪#|>!v'EMNNQ/_sḴ#%[>DRRO?tW`퇬:b֖x^{S8~Ơ]J"gc ޔѾVGn݊ {~X1^|)?S"G}}Gu۷{1"V5Ctçh!>Zht[d` ,zj_Xlr;ࠫ,ID Ha!6J I3~ ?%7PCx[(0BTffӝ]01;W,Y0rvoSQu·Y@|F:\cږsD HD H#`2Ik-yѬRd8$Y>IY V:agbi䕸1>j xABk"jQRup\# o!u)+CGQظZ/QPB,ZU9b*$Q .֯_VP"3W+ Xl,աV/S\]Fdpb~MD H!"Vn!FoـuF׊JM ݋P*X[>YM: .لt}[{7uE!KmL$.WQD HD HXAY>U#^(XN*툋,KúrWRCTupZ1&js:ܖv7tVPhP@(V(ZN_[7x/RnvySv )d N믿kjX8]BS8cw)&Vip|ɏdWM'zm.SosOgKK[<HD HBEr)2.EEԌEQgp KʙV$;#ID HD` ʊō"퐀QUī_).R UD [[Rp"-SQo\nD±EW\>h{\ _UqD,։J&FE+㎱J(E˖ -6w>9$ "[;G[{J&HD H@,Tjcxz (,+(@2?kHw ev$HD HoEjC":ϢS# d} 3Ej7lذqF="2ܕ}@;+y84NCc׆vKIZauBt-|׭[ Ũ𲊾CEj//UEC3(c]gD HD  + ~ "+"GJC o443}gH@"$@"Lx&M`3QCXvT#-U` QnDB+W^:Q r'y;寑8]prx>eG a{- iu. I ~BC/QU@Sb C1 %򣉢\Gh=PK 2*h}o_D HD`(XXuH!ZH%h_ +ڣf QM$;F3$@"$7rSnVQ΋/õנ[*x% gȟ/3-O쩐@h/h>e%vUÅE#)-[wIy&h QkC{M)@"$@"Ѐee^K6SO+о*x8- .A:;Hwvc"HD Ho\jE"m Nx]YҠbeš2+5%8Sw2nY[ m|ז($?4Fm5+V0TӬa$@"$@gܗ lEa0.ŕef#ثʒcf԰ +VHD H WKR0x礝rHwǎU*^Uĭ`KMUD4q;Q}w}e˖-&^PhBw W-v rl[L> v@N0hj֡?@3 }5̒D HD 茀-2 +cd7;w}\4~w.;#~Q5̣3}cGۻ۳*L93iMZȠD HD H%HRb7WWIEJ䷨D+&L(fHR DeGW-H:E^ŕ_#\, y:*iOϊ2״VӡBέ CmڴI`1KլFXݦBf $HD H:#` $y"k,1\au=pڶm*kդ$x7bbAuW`oAGtq?lȱN?q57˿6HD HK)1`qyŌV#LNOeޣȱuE8__֫K S4D i~ ?r:q7p\ID H)B}{NVYV20$-`-[SVמp[iXW0E]H }1{w}w.;qvسcy ǖ_ojM*ݹc#ޱtș#G\hq_%|?z18EW:Zf=≙}&ҾLڣ F>4a!NNs>珞Ga(-I//IH4d I:nEٝi7 zS)@pLdKh_w+ I]Aʼnv~Il䄅Cs {1VmCKM)|ĥĺ!2ejbf)/vPYħjNPy(4E4?laje@OY@"$@"/{΋["3^[}_}d'j…3';gtr|ysaw Y艫ϟ`vn0gGs{!-L9E!OsC`Ilk ǰjUp;9o]ѮGu P~^ R]u S:3.vbXhr.˒,}r}#3k$S8'|UDEP4L &*{x4Hq(3ݕ2&e 2e6 S?﫧YTH۶m vv]Y8S(?#ȯikoڴlFƀyĝ {R)+'@"$@"$@"C ivȔ'O=ة+.]hقw[%-wty{挌|g.?6z;=6qډ+Yp顝+/\zBF*P, K̻T%c񃢮xʇG{wvq:`,\FQu4$smWte%q՜'W(ɦ!!~EqdDM9X1, Dd( 9mp=@y66HAa#*"!YQŽ;((8Gڵ71R!;$LX5Q !>!:uE99'n>'KМC V,@`B1v4`g^G* [^:ᰟvF9l%07 [;D\* .0t:Q1't&>Z.bz cݖ(z  vupB@s+hkɏ0wc-0z?"DA5L 1 8 w<@[/ iJ{rCħc?ߜ+pӜh}ı IM~)E qn=I;LzEP!ӟE+a7 Ԯ/Q0et:5L桰B{И\[k?$ԺA90DY KȄLq9U^7YR*ox hEBkzr'4Z4Jl3\qL9U&\"D\]ЛPezS(5 -+2mN&orcnJn~b{kQk,ID HD HD ooϟ~.[qUW_{3gcWGȩ#KԨɯQvZF]`"<*OG>*?VZ-o_l(+cws:wp9ނ+yU"TS<[`p4x []󱃓RCfоREmt8;HgE,r9k⡇H#]ڷܖSb ƈ.*g@DUeJ R߹NO6ftiytHh%P@XhgkdL XM%%Je!B˦]d)mGe2- v5׫4#O.k:RqzSqI np D%F pb.+F\D@Zus협ZYl}:5\eBLjtӵ WYnzZ6%cE%Tit>2crt X] iZӺY]Ey`&GF:&C)(i̐Q.Ϻ$w b|ouYU 4_D HD HD P$sE.d{^;zh"Ɵ,;W|'w<5#,;r7To<[q4A;rѯ>qQA}~:^Ez5^Ʈ*$VT&RuOO,~ ǿNAm׼z5UBZV2&7QHC%BB>ڴu($ `+'-Bm|!9ljh5W4_\^2h Gduʟx )*=&qH]+*٫ #Ц-3_e O{3:*J(W K0A2$`QFħ2L`ՠP@,7 . D&"\瑊suă vHmps%Hc@fzhwyC6l(+Fg K= kYa"S5vX¶O QZaTw1qIUAF2W{C,!ydhRډ7Ԍ<06X|u!P^a??j2RU..ϊ .1C:AXʍ%BzPId3*.6z_j0 ƜKCƃl [5LU6(Q{qvŤu2f?,$F^d{Z }Cں 3*$B2,ͅbC(rn]oGSL+8 XC7;|m9gn@N{r(UMD HD HD Hy ёQcs柞sN]xę>{lȩ~lȒoW'DH ڑ]pT؜?kڨ~-hʨq@D HUDH<"-S0;d h#%Z _Tsqs|F+3I 6 C 4XC?>K9E)Up\PCމ+;>>D G zÉQC+@h s #;S1jBJQc6_:~@+_k%X97ޤ&P_C͚3$IH2Ŭ&Q#y8n&I&o˚2RU..4bP*&)꣣ FK(􊡁0>&AkMjpC+"b1ln|R6⸰*W @m,b=h1QkǓ3I3m.i1 d7hJg:)_Z%20c0T[ R6t91>[MV~K ]_.OH q)1Ej:sk@R<(W|D HD`Z!`QxRtdp T? 9c]uO-3x+N=xs\uw.9c/[sEmW;E )xhL"x 3_(X`rQu*ǹ|-^$%ɉr%q'!BtS"lI$E(QO_I8QD@s+ٟp_/hU,F8e\)7G%V!3>Qk_.in?byD&2x0 N_X3Uvo(8_8W/Y\>WpATAV< DL3^ ,W B*0'V&$WY f֢^pDž̲jĪ\e>:xs@+faoH&FM̟'>$ =5jSrOlQ` S x >Ű-JC52zZM*'{f(Xk׃'5XIORQ}\ ۷oOڞHe eEehQ"snꑤq>R5qr*goC軃3!=S@7D#kV YtYPeX'1T/d4bUquL[Xє0 Q`ƱX5]NKަ٠9@yز"13xBW#JuV+MDx䮩\#6V0a׿ڝٴ<%?# 0y+cs!) WRa-S݄l+ ׿Iۏ S莧@"$ ErŢ?Ƃr2EaR~'C %M>.,$}F掌/=uؑBV',^t"-Y`'G=v䡏Nyy΁?X4t|pёyKW|ybў|1V#J MgaOkHҺ:#%ҵ퓯řW#xD_1DZs9*#ªIY[> CɏiŃ+_,ќqxoxr7k2K =2Lnn n<5aoaRfi<#5Y!.[r! j Xi{136Õr%}si.[J)r5ǍTGo=LD HfKVVV/ b)Q W|gSˎ.,$}3/艑-~4zꋛojbE{%8֏|/ldܑёGS?G=>N-vPŅEG7\^o/U8BYʇdrpoN|-h8r^3L1fR@jcҮF<=cC73qddܿOZ濂AF"2)ɃjvU1fpjȴeh FatO VOk:e$жؘ0aЦn!%S2Xf *p'2SYcq2;f 2:e4%M؅yh݈#RjU]e.P*L9j^x$S2wup,(x˞uXFoZbL``C2 ѝwV\tgxDd(gc$KC{ʻǙZްzjVn?p躰zf@"$@"kZ{a"b>ndMgA`j.\Bg C iC66g|lŗYxٓF'.SsϜ;:r=ђESĆF_s{'V-NƊ8N. څgˁ$ɍ\*Hb'9Q_eߙ x|nl(NЁ/aW_PT|6%cP/ )0[#:WtX3mx Z!G_Hm)<gv["FxQT, B2ڀvgTZd3n*Z.%ݐl "m=~tA-ɷ4036@ZFZy7qHj)X!<;R0 &{:nnR|Be|\9l` :@QMFDZ'c TiAf-xJ١R|A1кP*c4WvL&14S !p̏]a̱}]wڡ^-x16+=> n{0ƝfQLDLfmL S0ƌsIII,zKg[.6;[hW:<% րU] /RUGxf0vm(ML.LÜZ\Of/V8|Ovϳ<8z-NYuj}?8?qT\*1t: >0ZMܓsO!ux>C}=T"h܉iQs8x4Rpl0A1w#"\\7 -kP8PEwڶ-iNg~Pmd1|9n8vZ10hhx nПF},9D  e'"HgJhhu;۵<.GyPi C4zTQ&K~GoFE +6& YN,c._ڱBWY7S8.4 c+^2EjHfc=lpCqgA3rM\P:Zu;0 !h r`tsb0fw*w1~Yprqqѩ @e&.M[URQh4 .o  Chyt ڒrgxd\g) ՠX-s+t5gzTfx>O?)d%Q(mn#M 4a4?oD+(cfbj[2=i)#6ٝ'U@tX\#G-@YiH@4=mI;^ؕb \h͘}Jߛ͒D HD ȼrnFҹN֨nY]X~-/w0i@ҾUcbKW~帱9wilP3TqMkU<))O>Hx" ۀ.GT#E(K=AuuFc6V΍d4q i3ΚY&(˄1Q(F_M tF**.ZM>K`9]6L)e. `h–0,SeH F' ix њ_Rue {k(ik@%̙Z/Uɼ75{桲jztvj /hH+2NmYiAR7nZ!YC5_*6{\XS]$@"$p+pw߷lFr64\pwC>5;EW QM$hƞv3K\;~#__t~lm|LW5'B186~z1Qos}TOSSBs Qgw%2EOO<(*˵ zsSq4->S>^(TCjHpj!ϏNjq%bC8 @T&6)3>j= D} n() Y!YOA,:Q%*;TAC'jS7ٞ[Z؀vRᡌ(诧@Scv\Dʥ?d\ $ȣHׯ \zQ ~O~צ8.gHy#JL&Hxzz.VWkzͲzҞ>Ժ NL-$.I?m옇jjc &uc3q1:qgOQPLD-Iӑq6< q+|onj9{[ycmayEais2(7:FˉBtiʁY\bD`-nE.KOex.g4F݋P0b=Pn 8(xS:5\Zw$C,VLS a8]BS &M_/%d6c2ᩰ錄Qa!h\DV38 z,\^efK)wvl]]i6 7V1W@=nTu9s*3~Pb8>ҜVE#CmPX|կ:[+(y-}P4+.Zd \:hCɍUlrZ=wpBK0nhz厚 fC<:22<2 NW!+>v5]lmyR>(i50.Ak LnW@1ʕݪ] @[e9]jpl~NQX>ڂ`=2_9+]2 LOZU{qVfD HD`SKٱ̰ҰזQ5yX2e5PYlp?.`\v sf̀{V˞_dA G42%12x]8`&8B|~%2QN()ל>BCGnP K2섆2e:ii,FdXDeb'BT \ay+{De5_ICɗ%(F|n:Tvlnw:42Q*ǧ].! |04|V*/5k>шĊ2ٓ>ӹۺg\,'tO ;bZѝe1A#v-ph\'. n BL"ʘ:ʒIզC]]ȓ,'ZSY!@Rk*prлjzİOD I+ Imtk@P&YL':]|V*B9Op%d>HD H@XXHX9XYkXŢB5)E% /)gZ!ñh oE\ۅ~K.*LQG\& nOMUVJiH4u*ҍk8sV]X W?R٬*[jeT4X`a"2PDv|&ϧ$vW$8JO.|%Ls(=rJYʅn %򣉢\GhRkO5BL kD HD  nV>(R(AZXf QM$;Fs#dr+ѝ.(Cn-a.0ħD/ꄥycՂe(D̐1`JQa鳬.g"o;׵_|ɋk`OrI=܃ %l_Z]+RoZ\rG-J0zA1%56Ag B]JT#OOD HX6XVPܝ--Ly>DT%5оKȁ$;+wcƑ.|7nx"?fg&3s$CVEƌKR"mO0 .pZ89۷O Q?>9&CVR{_RH8SwM$ڍ|ё 9\5m/ԏ~Pﶚ[+v*i0KD HD3ٓrFPl5'<b7mڤ 4$25J, Vawt }5̒D HD 茀ww$OdmeVooJm۶͊d%BoJy+r#|W`oAG㘽HD HDCK/2N#Z$=.UX, n?}շ @IDATWĘ_a,O=f P>(WJ[oƍGnt禛nz' Z^u㘇; ;~C(jVoh7%@"$@"x~+(lo[k*]>#~k"G}bҟy B i4Xj"$@"$Ft<]jڵkouyW{!5,զN`W1Ɋ%W/3ay5Cqay;ipZT\[jINʔ3NW-+9/iDL2yݮu 9P%QBm5X]F^-@"$@"0Xk$o YTV#?+;=EPR ݆Z--|}u[.a*O}ᠤJ@"$@"Lk?0^5qz9$8*D W|4.KEQ%qB[:(?T:^Ĝ>1JTD!׈&Z~hJЯeMNC7謃Se!)A>F԰rI+ VsOVf[Q$@"$!lя~=',+ "(%kk %'.@"$@"Lw|{"I )g+RuNEa5ȔubJgpc,׌R\eQ]Ѝ+%Ηiˡnv2LGr,3!8VsO"V5_Qvey"$@" eK LǎjRbMbhzC*JLE En2]"$@"$p?Cl F|h)E^\g%GHɏ>}eV[V̾>g%@"$@ )GqWdkY"r}{woEX3Ӯ+jisk"yYx9UtnX ]k"$@"TG\^j)vp -fB iA s/;z'o\sXzmD HD HD HD HD }{ldcGsɥܻ㓏9~TulQ'-^zj[kWl&v$sa <'Npx밢6 ` YX*/f,a0,Qs MshWév6\ͧT7]^B2Sb OD HD HDۆ@Ҿ8=~_uı9oy;+Þݻ6o~']Ú_w}wfbg?o0tE9Ymv }w?iv4Űn'i&OqJ? PҾS7FaZ"y*l`z Qk2#G/^H2̚@"$@"$@"TA i*(w߹c{?y`ώy-[~n7 wG{ǎ~g]c#gr]#>}~-["{yf^ql+52Rp"|'\s {=i_&^|uA4a!NNs>?|!v;wafpUWiW&!*|^O6HlP6,ZΙvPg: ,qưlVU]VjB3"`9zwYmEʘg>HD HD H@ҾU g~ϝ;gl賅 &.YV,YK.9mkk/j',w֭K.K,%YU׬7[` ۷o߳gʆ =>3{ortdXz8 ^/ˍ7 ;[}qo֛oo`3zꩻKڷ(zo V7oыk׮W+,eeݪT}7n":e]vtMz+(OYqׯÇ~hNXy#_"ⶺ5i8'|UD!ݦ3LdZ(Ӿ*{x 9\bw]LA8/ퟃIgLcO M.TMO=\qT0m۶g;wTwPEQ hoZf 7@~ukn[nSΘ_{av詧Y9HD HD HY@Ҿqٓ_Lҥ-8|pؼe/^h޲yGgl{c^̼cXrڹ…h(2BWqbamd\b޵* @u܉P><&\ԉCg2:C$+N"D"|;N 2T QTGFH j5PB'KМC V,@`B1v4`g^G* [^:ᰟvF9l%07 [;D\* .0t:Q1't&>Z.bz cݖ(z  vupB@g'`xO/0~/!ɲ$a[jOkA]:%uE)+wʵrZkgxj۹w69sG9~`GJ#{8[_:#]^#stK. ,U.=~IX~ʉB$RxGK?FGR@G(`p=t\sdDڗ0ݻ;_WQuLF{0))O|bϞ=-|OrjO~ "3v{ 5%YoLřzsIO܂ E fQ06֌#agy?1^j$ۿBoۼ+_]> j2޵ yDf6oJ=釼&n,;SV=${"\9\WPK-hGAd ]F^C6bj,l |FϠ4!` rݖ6{(tlYh "6Ah"5r5qؓ%5"M <9i酣^=~쉛{L)iΡ/OO|\gݹ\N{D4QDS2v η]-q&^p%HJEn˯h9FK1k^M j"i0!T };"[ma:JHI(P2DZjAQfDY YK1Fm|>ǧaMR)O!+u%!Uchj$n66bQ͚qYj2 [_e3 nM̈y4kdҙMi`L<*dqmiDgMI~ѧ[!nm(=ƺ ,"XÿkW7P[_@b:5CeYbhNan,| >nT %(dd IS9_I%Xr6 M<4bP@˥Aکo7=_-u TJ<~ u V zQQ-kyI}0Lg5f\gBQ4[Zl'*M"ouƈ[g=k&ąEQ0 WӲ no׷NO,U4 fұ k).MbSE3'u_&[ʊ6,3[R'@"$AcFlF0BZ=GT.ǧE0v2; nYUʣ3'6ܶ?pܙ ⭐Nu^ngLgTBtְg(e?,xǬ˭|QhDF/R"|e2$\xBt>aJA8s.MLsM̩\=]BS&Ͳ!LFM rE(c9'.I DC45ܰzFKC!\* ;S=S$pM* D%fMpNc:/ho=,f\`΀ƍZ=MY;,g N_PѲf6 ̸VV,.6.*]i?eU2n)\4IˈQcХs-NlZB{T3ya-NYHf謄]FyMGf[FTh7$ ʴ+jb 9xo*w.W^x ܱfF[hZA/11quW{{ŜU:WmLu pZ7+_~#n |YDXƏeWU"TW/Y!Ո̕3tڈ_/Zj&EA%d(IJq+K`Wa†!`*QJ*FM/-m"9 5E"[i5iw0NH!X"FACN,3ӌ~Oa+ʒSz8gAWWoVȈ qK kL)G1:.VڻaL}))h٧00IXj#Dh5_VȍK3t*'82Ap~N6Dj?@ P@zʲn=SM"bhu?ߣ L+LS M蕬P#Q(NUƙK]=X:`.ZSL\ͬ/y@Hdxv.eft/ha2 bwE{Zj}'T}N~5NP`o n\uw o4`P}QJыP>)|kY퉕E g,zVd"$@"x#ޮ&Z1q~,+V,,SI6+W/eObә &njˮOM<2֯vۡ;ܵ;X: mid%PV޹DXmbrREZ[X-:"t  gmɳ)r42 D$ U@~1Ax`&Š(K{IU뫥$#YDe"jJTK$Rs fH1)6M\0h0F)c #I m4 A8&1.t0MFW:` tbDRv6#% ~lB2+ -3H %ayA\';"mԈwz҅|>MǙc+"a35ZlWo.$,hB!2,TS*.f$wYr_lP #GUJmjJPlY`&dX?YH:J}#j).%怯.@†Pn{oUY0%@Țb(#OzIYovsb/}į45&`}΄&797qh̹0xjmNM͝7Oٶef`Xt*'"Ɩvltwtw~M%**h qH-6"2&((N11:B3SdvcXtEqA_n &N2DD 4p_>K=4N5ouu4_A-EttĤwuD]t@ƸE%tDubքdzD"~@DG}JTl$KϠ"@$㤘Dhϫpz҅ &T ͱT;!o@*] C^C a14fG%б!7r54!FCf^ؓܔۡ5lY6;*׎'gɰ6%r%-SU`͠K糸߸[hc+(F.j bFꮍѰ׶@̻~{+nͬtMVڲ]Yֶ,D?dClѰpĶ!l .KxvPio Ezj.Ɠ1tgPWI!P* o8 GP^Cu Uf'jVjE68zij "_鼱H` zTh,j"MI2akxIK,$@"$ayWg#W :xΙ{܏[^XƮܬǧ$`5"ifm:Զ7:9yù]s'==_vm9ZG^tJk K-:JY>!*+lبo-4zK!jz5qw'"rB bHb6\ H2J#%lGcqH$ڣhKIQIӟT ,2_1!ZX] mY5 w0&gfv=h:@\eN&i;Rx0 vUKj/&ߡzӪNjHAJ-kaI7q=YĽ879N ͱi' hRH굦(XhM bH WY\C ja/rDžfC--UVh^Fi0n|i&>$5kZ2bt[fefN<,MJD 6WRwه:R-t)3 f%hal#j[,vCH.4!rA?)z+k޸z~Y)}_*j&CJ,1hllrl=ž5gP- 3+0$*7| geuiGuqWl@!+!'qfH/9X|* a/Ms,aes8gMD#|J~}&HD H!^ c+9-p'4r\ ,%2l:&-=똓N7lٺcv359{Ԇ<6^~~Cg68әt_/=ܒ-yK8vR㮖zZA,D/!CP$SHPf=xZЍ25s!MQV?&Q.HߐBbڧqO>cR~c5D쀒A߼BW(H O$%k,F Ƽ[koQ-}![ u3|zķo@f|=°˜tBpd!Ҿt{Q-[衴H$&"04YiƶPrBa_OhcV+Ԭqf*RH $'(vY%rԸl HhM*!F er-Olkf( iʅp[Őrr'n%ȮP0W)JMnQ -t?C>EUg>cL"`Rf=vQɀ(0V6%[t e;N>b`#YyL_T FU(E'˸+/c\bJS-bCT ؋<&n6k#sx\ ,%2lKcT g6_n/t9rcܸs'_|g8uݫ޴oj.1)?XCuq-,fт˂g\#6(ʹc0 x! +S6BWю͚֡PX䤉J$#x3x jpx}Y1 ;%Ja bP /yTU ƚip9Q(H6дؓlKM-VlY3PHt2_O ҵ.,Yݻ+ZM4 ˸д~'i!KXf\~Z2C 5%WƱGX/R~ZKȏjo2#(ŭ V{ĊnҊ6ۆ0qI @ad:Tŷ=Z= 坩Ce/+}@4H Gâ 0&54qn`W4\˞?ؖ; /PSʴXF[/F`ru`i/M l֘Hᵴ/vfдY*@P\V6|"Uը' a6m.1D]y[+wUdu\jMn4) Y^wW%AоOJCeX v"KzAzjF3b?YzGʾ\oˌ쭞Z5x0W.ep"SEQempMYl%hn 70XFBY2.< :+&AM؃ b31=pKc QWfLZnYYEKaf@/<-L;9X%5A{^aO'LD H."{p x\nL% 'A3W XʯkL.=&m^y+ͭjb$3uv~b݉c9u|+^<{frc3Osꃷqw~xɆŬ.z;a1dGl|J%*^7 -0QpIgo#Ex# !XE)*؋'F129&щ6g1[b'_$[ru zYcS6oj!/ sIz"$! nP  5-`sJeĈn\1v$vctjcq]KBt C̈́!Bg3i#q/)i,jBx|=/L$2e<dm ri2.Ԡ).]V Ni>ʠɘh"ԬfraB&ȏ`i'#Kj 3PHvF+-6X'ڭa5|Q6.+=>2&F̲y+*ea4۰H$(e)B.'߼C ,5riU܏UVBDf_VϰBMH8N03 Uif{]\fӧzD\Am^oQ˜m:|Ri_5p!Ht')H FG5b5g3a(k\c3X*,i* t@(ٻD HD @8vRY5ۨ _hd MjY^3/Gfl[︻pK&u. j?:Az-cSPҝD폡(pY+T==%5.QY%*kIu]@p2X* /"+{%l$@"\x[9?AλQ*j|u)p<¯ХƌO2*~}kgvfæ׾⺅ɉ GϬ{3No‹Ͻss37+w뭷׎ԧ,dԪR34ȨZ-aw6 ,1lj*12gDatPcYP GD&@EX"PoRޥfnB R? [D( g|>ǧx8š.6C }[A d:'Yr@R6YL>"&5FK!tbRH$L(h$R5-j%I@l~z4Exg J Jf>z`/Csd"&̚鳾a)ܙk-Cc À ׯt̒۔K~,=dU' )hN*v6OTYGM}Sjԯ1TR ; D.Z0&tD63D4< uKMUX,$, adere kB}-mj*.# %,q&v(!кfQiV#Cl)&쳫}br);GGİr|ʘA:I+$j6n鰯ʀ/e4*CW҅~ Q 2XTJjX ,tɯ@"$@"N/ůЀGcpm< ~o,ƧPHea-!ig3;oyd-]߿ՙrOg-o.}EPr) 5/.%.!"C]?!hR>HooQ/\7^F A1hɂDMjP+c(H6K =Oe b}ٳZQJ K(/ cJC|A񜰶sAɌ FL @4F6e! fNO)ԁh 3&P MYLRL͎H,X5jֻ՜ASFĻ\IϡccIp>2S?ILR~k";=!c,R_ ,hiv_k˿KkА4VPHI8+k&l$rUag33|V?Uv[6?Ѓ&`[ -5qƥKw5$$ ~)QKٵ4&ots߼YS9 ڳ$:J!HL"#1t= ٯ[X=Gs5S6ttMv+r.sze_zbXYfEn|_Ϡ̸_ޠn_@hueN"ix%5P0'=(hA)tɌե:q-e6tЉ.(,X=h+D.@"$EmEs\'13Ը$"4 n6SX$c iыwn޾k箛PY NZ<;a1*7:^=9W8!5/ 6Wi%mX Ja)52]:\z͚(DؓrWA`&2Ri.qH: Dyȥf|>ǧ fr83R"Z{"FXx4MYrBa(XMI 4Ai[(k4C3i]WmnI*nlלW$hxkБT07. %>AWլàФ O5W2^SJ%w?J$ [U6lD)N) _~ַl.E䡬1 S8ٗ [J}0ݬZ̔ԽiX0mx2Q,_lZ9k(vlt2KV]A,iӐȜlIT2 ], f(w7'燎0PեNFW-ķiohŸQ0n,%{.${QI]Б>N1Xm6F*Va$@"$Y{=oYx˂&n9x  a7XjؓcX 80|!v%sM/7wM2/okϾ54. R+j6iF`Jbe'7j}L a5jJU(=a(ht'jthYfIR1,z\ HVWsI^. :PCSR[G%eNx 5QnRQxy7[SʵS߲=J4h yS )T\h*tkb%%3Fs.5^g=3+%\d.,`z+pڷ[-7ҠAg{z@i7w< F袠xF4%EY!/gC YY=y(tR ޔ-,T%:c +26Y$@"\n؝ G2TQPYH8~!~pu5ߜoݜsodLrU*o}ǝ[nޣ5;|+>Kۯpg"&Q ]qQ\m:qkye"8ZeD&kp孞XEO %Y=9Y҆ 'C5۽{7y@z5K򦍫2~_ -K\~-B>۔HM hղIYvjfd6!,+D`%%="2VN@e<۞`53l(R' /DY@IDATWW{RIhˢ$LeaCl Ԓc}#r.t353Y`2=EHpM4Mr"Ѵd _&kE4Y=x&)GҜ4]K[&QU|"uԧDa '>6P0 GO>KUPop?u!\_^uZ 0-={ל'xy;8&c|tzᇽYD+)1j 72V$_6?Y$@"$XɟA93w '0{?Uxqקr|Pm#iߵ=F:H"#\QҰD@t:$ l7`⭗A:GdCx*NѺ)fD` h7[|i{-x9Y"q6x\~el{z ^WwL}=]-/;{++ ҋes$AN>ըGJD'jk)-`87v/c\ݭBG"jWbe.\){@"$@"PWJĹsmHޅJmp>(NdC8!jPU\L8{)Wܺo=de"FB tfYs0Gpb9^KU2` x-.,t}qi\,m|eQ~ˁ4BK/^V+ 5Qf9NJ)}*}6}tM>}l旃ޛ}Y;K Vgrux`ҖZ}qoRj}b& f fİOo 2^5b!|+F*Mp|kv,0e5Xy5c Y]pу^nD3: j0n]8Av̱ڒ+ͺ׷&bYND H, oP1p! b8o<x#\ M'lJd*Ͷ@Z\&sRĥ F"p` x D?&+R<#&o`]<&+갉w[ҩ~E8^%_ L=KH fS yUW^cfMTK~V}~%Q+1gƳccՖ\Xi濒~e}"$@"Co庸 inga?JM)TR(Ҭm9SD HD HeD@d" +SٌF^8ޏ$}#">}XCbhF}YC3n+HD XgyA-ǖ밒Oa D Ӿ+pRD HD HVB7hL#ۓ4dwp5tjTjl$@"m83c3SX_&{p|ͽ/{sl3܉^zm;uzvݻw_q 3ӧ=zM6̬_~rrq~].(Ҏޏk?NSJ[mm5i[.&D X9ܹsLܺpm$j۸q ֌Ӕ• mRND H&?˿B{_GBf֬:2;ʔ>:nӓg>[>{/y8<}[>o~~( #/3Ϝ:unعsJK' d͋#v~\e@@Wɓ'ʫꫯB 42Ͷ6ʧI22D1H# go N۾kt6;qĮ]oWD2Y$@"4|{=) 5\6^ 4+SI'|7֕W_sɃ}_8:=~;qG4p^9~˿Ǟ{S:gxoe<$Fe8P\Q.\g~@]quq%@<(hm6Cˋ게r_|+-vڰ-2I$H  ^i9ryW( +cǎfBvp{[=j,$_ڈN4G))R>:Nʨd%yM mYe۷onn[R'@"$Eñ_:$pvԏ8Y"{:_=>Ջ^rLG}ߍ=9yio7mjٱqf:]suhDg?'}o}-_7ﴯS=^UG3J?/8v|aR>'?9`Ӻ8\]QG{WDƷO;r[5Q^@"QGj'7oG?ʶyͤǣ}&ozO?7DkqЇ>d;ٷ r~v}̶[ުU ]LCs;oSY!M7|অs:890~jĺ߽no=qSo7ڰa}㥧&w@w۬xJ}q8Ҏޏ ]orފ~3Ѹs0 \Q_ԑ KIAخ7DrF /x'2Y1Jnɵ^+,+# Yol%(=3sEm©^ 4~ꩧE19_X8;#|r7WT*ރ %[Հ$@"$*E!?/,L\C\A~ҫ*)zՋ@};9ߗ̟h;_u’.YHD HeG "D>"n&.΀'r>n:x5四]$ȴҳ0ixx7OLocۆ͛6Lܾm#YxWcm9tnn7;{ۑ~yR5Yޚ|.tqF3IC;_>ψs'i"l"8Y1 dH=}`{4q,,*/c7h͛O `ܪD |(CX}ǠG"F8ŌDa Hp'gk[?wE@%@an~5AjS3_b`'A%) xHO̻W* &JMePi*J>򑏈 +ʔپz >`vm_#QPTVs[MLZb%OCٱЕrƖ.YHDKd!mlDI0wvcX?fSR}փ/;D>_}nYZ-ɾ})&%g٪cJX; 2!{V~Y][ "jO$8ga&qf).ݨΕcvF="zN?)w>k"M4l?7kD HD`#{̫ !/ql.?;)%Ћu'' S(KC%eu۷zsrGޚ_86;}xsfӦS Gz7ٙΉS#G:<=su:;9V9e-y|I\B\|e씇ϒBt!]b#jz䋅L*QF2\{$1SkRp F%S{5i&Ara b*MjIDpR.-q(pO^A,>Ym4 PFg cʹ #CaΚs025f*bZ IL%TR:&|#f]#mcő(A4_\Gp u`l ۾^B\[xI,jc$EלC{@",@鬷w)sl Ѕ,tGem_- .Ŧ),#'B_[sXfW[ HqE62qEx 6/{Xr-tO@cCTkcLhƾSŰBߢͰcWiJj?eyއ$Y?WЈB=?]k"$@",#rƾٻR M߆.qƧPHea-!ifsk~W/9=a7gł;v¾w;g:[7.zH⽏!+?/Gta[D졛S6K DA?~ w)y4͑c GqO.Cy%"HF؀+A1Lj 'BCNL'|| _^5y)W (9C35pS) QE ^Iau*'mevQ9LN0 5w!;ӌ1ԩp]wa[j QЬ/EDjl~ h rwSEa]w1OE+@M3ˆP'ҾZmmΰCOj$ ʞ,\eVE "]^0~Sv*5W~MDۓMG(3̸>rX<~z״l,RY6mcr֖mAP2#,3?īhSfxۉXf[ *C*pfkvvB}? ?6n׾`cf{9y _nرd6q'VwAGKl5wu̯@"$@"ة.~#8PFk4#),8Ij ib6m8>qȁGOߙ#s>[z?o{-|Թjsgfs:krx~S #JM" f/֫iŠh,tQdb6-cE  HulV@/Ū(GGM&y)4=Rl)~7^&@9_ANH$r(B"H<KPJ<ɀ3$ % }+/vhBL jP/]3(-Ō!0 t5Il90oS1}x0qtCO{"y㠓҂XuOVK%f'&Gf\Pb GyU{^O$S߾5e+뎤=W&BYM-P9 5 ۮ^5H4 ;i/xnbb V-q Y`GB"؃@1Ś%*!bO[('vv{~dckm١Zi{A1mj qo"{z0X=ͶvU[X9G5o~MD HeDNm[w ipkDK} OI-kL2]\gnM7^q?e'|g?S_o~/U.8O/rĂ[ANbNN#R9D~D ScPLJ6Q;mcLwXE^H dI ܄e雳}%RHf3vL 6!edVPcDE go#g=͇ ^e5_MОbhlW$@"P;9%g+$epT/!٤d{cR Vf`KrU71ێ*(5A͆0ĦCpKP}Ge;֣"Sj (")*|UR};`ImUsCI!ˉ@"$@"\xpT\ S|_US-õ(mƧ\$9bf3apfrġs3:/S6o=}vjNn̿~ȶM-Cm]INJeSdI8+$ (DVx69+U<pܕ]/YQ G1((+dq"+$U¶hBJP$NX B\kǍE!ʐHx%Q%$;Y@d'A#KdI]!`m2b]HBrPM|( %ses?>x^}S3-_ɗMO,8>,TI4//.(\Oz~_% $1ԋN |1ȓS̐4^Y4S39WȺhermd$(`pxNr2`@ ӾKO78yW{'GnٱM7w^u;G{Ov}駯3;ݙ꬛̝=u[?_OMx[NG\R2P#=;n"/|ysi߂!pECy[Ԙ !("m (iws#Lz` &JԤ߯& _MCȒ\RcYOOD`0B+ >`=cAgp{ /]E{FƣSjvݰ} SOǓHʆ]N7:7p<+J.;&1]c7ʣaE:]~fM"$@" F@%YòKDbO=Kɍ -j2[;}ۮ;,-G?]qW:q 3~ŋqE b[t$`[Fܨ-kX ኄp"WҎ""IE={VXSe,gW1l据զWMײfH_{%Yg}5g4"'@61qUY4} "bB@2$/:̫0 AWz:sNR0_3J=nM̛XҚ{G !mP/˶ڊCfy9 "sܬL{+#ljhP4ԴV&VR1;59=Xϻ@"C>K3*= lݖ|ۨb^D~]X?^׾5NE?F8'{WDv:x6I Ѥ?p-inq'Xh|dM"$@",J+wǐp@ͮ_{kG5_)k4VjA Ӿ35aknarb3>vs[=sgf knOz#Y{BH<|Ji/!hC#sR*VeЏDP!|(ׅLCQD (֜D G&_Ux6Ė 9_8T`ɸ쒀PW]r/F͐ g8?R$0- F"‡T/ef8WKw%v}jH¡!l4j$V~ Q*ǭ.ᡌE;T.w}uiӏ[0a% ht.U)04LcG"+#OzNNe$pOdB n&@ƃڂe!dokUXD2k8g9H&lK%h3~x2fc~ex]lz'_-sg ˥3.T)ÚiNt1_uג`8iܦAC1Dst et0fNܲIA|ڒ"^ѻs+HD H.(\)3/83 vmW89W[8-M_m. I| 0o'C/;_q?ls9W.;{ugO_{f|3[sY^k ,c# )DRj%rx%Qr]vA.frrjL@c-k̚HzB!P5L\5Jc"ޓ%VW" ԰OI6FDWpMٓ۔K丑'HBWp>17 ǘK|46d88$ MWW!bN1h G!/Ȕ6T;wx9ČB 1`j4,1]"+P%y!L1Ia§9VԦkNa% X'pA5ڐ{aDTC]*۫ P?%9JcMYk8k͹r"#Xƚ3[ʨ5?xD궓zɜ*;kc>75y>w֒Ǖg/%IK=3d48ЃUhlL}ClBBٞhx|&J{ס6 T%psĒjJ!lͨtj4ƒ 5@!=NwMoj̎wUF-)+Ҭ@jd/pe!HD H.ٗyY\^O7!(ᓨrpKDP8o< x/\ M'dW8ιY]<;7oߵsM8_ NZ<;a1*g:DB%-y8E6}լ`8tӬ2.A%,1DlF44E_G0 -,Wj0a`@eJLPmjEp8+J D^@OHJb ?.gr2 aLHGʥ<[ J] ?w&WW=QxQtĭ' c0>(C9= d t^,ǗNY@UC& :R4WSfyj=1Jc2Bg9HHezPgHa'e=%V'>öG-:UCm2fCdf'JdtIeWCsa57ˉ@",+'<61ӌF݇m.Ƨ f,MFfw^]u|K坑$@"\ؗ=5w*x_a| ÎW wRf7oyNֿ÷r`+|pUI_3]ͻ'k %=Ged{m<.43ę ]GBL{nZ%WF2k6V5 ƭM_MXכ# z_MNW"\M5e]hQ ~mPJcD H!>;۳˰Y4{tF4ֲ:5mAU,YHD HאB(f#i>A=?!%Ob7K2~yoN%@ _Fžrl~0 "d~$ԀQm?TJD HD HD HD`m iߵ1)އN,'ӊJz񹴵w9ԡsNO2{\h7{'fmɓGö"吉@"$@"$@"$@"\\2{q^ 9\y5xa˸-t[92*q<^ICU{~W`̠4}u va۷)dM"$@"$@"$@"$k L ]qgzWbxs kPHoö-D HD HD HD X7Vv"$@"$@"$@"$@"$kL)ND HD HD HD HDB Ӿ|@"$@"$@"$@"$@"Xp]&@"$@"w81XOէm^s&@"$@"rS3\,,s.a/9Y-dwT$@"$JD@@⚟?~ܜdzzzvvg'+q’D HD X{>qiƍ}x@*͊d(HD H@zE#G9zɓ'|'R9_͛m۶}v.Q$@"$EŕV7|O?Er?DpND76=5/f-YtvUD HD CAݶm/왺wy׭['lL"oSX:H>G>z'N\}u#o)5HD HW^y뭷>3\ؓ+"}5B~}-9 '/]u*)ߎ?-5MD H lK/mٲ?d0>䓧zvRyh91&Ȥ}wǏvϩK?ڵZm"#?HN^lLeNk{챍7Ne0oK.Yz5Imkvji[{+2}&@m|} /DDEeN_lF,*&`͚5uO!iP0Qݾ}$,0ZJ|ϱ{{C"O>Ç:"_."o;p./ܵk!;.$Y`G}sNBjG=-]<\i@"$@{R3r8\veH1 _r%[lĆq֥̋g}/dr_pE'OW|10~hpp``/.] B}ӣGǎέSG}dxi#WaxDVm7;횝ZZ|vko#@ܨCO/ie*$Y#mvnZ9GoSH318 vN'A"R8=" ,tB bE˘AC>bσ .$_l.s䔂# `BKOSu9;y$@"$YDqXbưjއa9L&#JvkRI./U'[p/=ܛi߲m%V\&Lt\f~ Vꩍۻ絗~cK? Wu17't` iҪ/nt>~ G-c 5J@/aFnܪs.Vfz"p!0<'؟V3O?a<֢ t'`τۃE ;/Y]I98R +X?w$oVfV FL V5'/U_dz"$@"AX5}bb~=Up$:j\t˶/".wu_Y{`u9ړXig>>蝹@#y#<^G"SC3k*k_$b_{5%|SMp(h_҉E= 7pZXoE+sڵ.WN %dڨؐLID HD``0Z '>)'EJdI }'M_[6q6yq.dv#96:uЧݿ_:ɒeu 9_LaYL[l ]]Js9AC$ '㙟muXs1vEv g͓;S {J|z qkyueD HD HfxLXU?%:D\6̹'e.ЕcG[|x#vzV iҳOcC6 -]|QmC_9Q ՝^8B#y,+S׮y8 <>){%FTE(!ιm SK3K,c)B"g#&A*kFeڤTWΊ-L?7kL QXilJEvҾ+Zy8EEJ-zb- G ;:T)B'pt'u]ж)dQ'y M48iբ4O](3&Rjuh;F'~>Wr˘([SZH7 Q]^3hX7BV3RhD 4k9nB0DJSeV74$61!x'~ jMMeC&—Xhe6pLyBCSo_@"$@"%uU$1C$$ۈI}E/_~_ .i=+W,ZdQW>x>|Xٳmv.f=+k;kÛlݎ;Ӽ )@y?1ȌU/lN1b^IU}cJa>/B jnq&>yPD_>(Ъ7"' Jo«t{iETTjEiQT=-ꫯ =: azj0&Sh`5iO@8o޼hŜ[ʍ$0lD'6(b˙WE6U hC I ]υ6N|81",7d2JuJ!mNUIS\#tަw:#JBAsghiz 0m+Vhi:U|*;.h{lx F{U &I"C̆TusLtPQXt,JGHd{x,$ˍ̍ڧ i,xej"$@"$s }Ş }r0lp MՆ1 -Jh9YRc`^PՎ?pף{vm;bjc=18~hgG~wo;Q;xockk(?Xs֓ۃz{x s*7y8Wt!D|C°qÂ8Gs/a.*Q떪B y!"Gr&i| 7kIP+":!SX]z"M4 T#Z{YT]ž4jGGpwLRKu1Z@+ 5^iEDzH4`1:r-*W1`i(X @XB+XdNtI_ZMƘfjE]vCiD!5Vwt"5 ۪`lOzn_wVGogz"4AZ%sM61Z&g,'ggaM(R !4GZ)y$@"$YG;̸r0i(qùbpNk q Yun;ٳh\zنSc#'j>\-_?яQQ{ÝސFsL`RkrLYCoAg~X1հ$IT๩Mrl1bWqƺ2%HD HC[b2RE:T:E2`'Iǎ} sjhɃ{;6%.jn#F| wՒڪXe-p5&dbyq}B2>H:l e|<IR74Ff䚂L¡prE8ZUopGCHRCK">WRШR/LDd"/$(E-2E- "qCt?a.nj #' zW~+>,CWi :;KgTOeT@U! nH^=B l8KAlBQ1N$`l E_xaySUDʐF8eۧjA#JBA-;-mzݤQ"lj:4!,=M}Nz!uq!ݿE$HfoZ1)ۃu&s*JFZXV3V?͓X6 [dUG쯫Vs䲉B;mL7#QS ?HD HCSc 8A5ˎb29 {IvqF̩INOdlh62ђ+[sŷ.[w2}+d͊}W\Ğ tm86X0DO?vCb gsLPc%W ȏ"ʔX1!\&Z8?$}#f$ XS8HQopcqL.6 _)I(hbR/ -‡i*AhcE5YF\ :95ڀ!f =S-p"z$Z 1)*$gR_F*{,W€wob}KpLJrLM(чj1b5lVCkV2bg-X&,']e":Cxm]`u؈ `er` KBټ/Z4Ummut&LXJLN%dD HD Hf 9Ye\f',AK l˦Bׄo>s*䅁@ҾcOGooߢ#=FO!_,]=yݧ/~5pŒڲb['4Im|Cqa}CyJ4)E96>iDp{8T 2q:ܭPUGR4A"ROR5\;WxC)Ξi<$c!vҿ19r T:jDvu-ڮ-D y[h>uD2c oV;*ϸŷBIAJ-$A+QUDGU a+ b]o:b@hK!< x*kc S=?TZ.h@TCcDN>NN߲Lb)4"jH@Y۶Le(*,m$))nUb0EeD HD Hf*:(_/Iu,^c3C]`$;y"|uTEC˖\qᵣkk}k{|w! olRÊB{znc7*\MPu8T ~'r)""e*R\ k>& P~ي7ȣS!äGT-X(R\ RQEQ{\-W*O-jl`1Z"O/K+eӶTwčlOlZdߘHq tv8H0HЂƉiOO-VΑ_Uhb׎(kTY8ef:/7&2O)tFBSk>:s5nrͬ;}(joDTD'9Q;=27 y$ܣ\7eiza936k"+EcT#E`E\ X1YrZ&ĚQVOU̠4S8XqAYeL֘$@"$ 8-XSl!/Z(j/'&IP<<9:~bth'݆]n{eO^|[w`ygW/?T[}.터ўh_ ƸLK:W81]0#Mdv?A-T%TH'/Ԝ 7ui*o}Ljk wA*nMQ2Btnt1P{Sxh)SL2lAπB6/آ"LpۣύN*;mQY~sc 2MڪHo!;OCCf g>t0;#WD`US:՜禮Y&(}Kq9Mkhbb-qDfee+ 'G,Qf08ijoYW*&@"$@"0kLO|pˆ8;kdE 4we+}=ҡzy}o{\T z{j#cCB^.O?s0q5":$GťLnś&&}W "V-i.F&aG) yi?m nT Jq8~*Zԅȉ?_ˡFdӟ(JVW) yHrq'4eQ =BM"J+M &j]2lYPN8WM>-Q`_퍨[lD: nK @N!СD=3o ACcgSm%j`aM30Q!du:NN䬞:7,MуQmUo7)SЧzTrWRlp Ec\ ݿɫ@"0Ĭn5۫_u7UհdYXGM QU$C+dK@"$@"E*uR.ižU';y .XoGzVM=V;tޝG?_v޾c} m۾v[61tBEf]qc#ao~(ęH+ uRWO$Ž8Aܡ}M%Ns mΝ̲Ѫ7qXK=FD*% sФ*jU]+(Y^pF0ܰn\-.IB t4"tm𼶡jJHCk y)B`!jHge;Nq^)O2nH74y+EܹV{*`Jq,`czAp멺^6NB6 lڴ ,z,JN[TL#]1>U@Ce<9'cQsPFс=m#$g7Qv}3sC:}IIkTNfHD HD`F8c.Mb/ͨ)|a o~\q7n6>f fvGosˣU/]o7*"/ƸH@VϞFH%Đj@|lMK( < A>CaP{!*| ܴBUM6Gyˠ JꕳJA_.b{}^gT#&O"ӖNR*!O"P1,*|VĘ?Ϡ89a#J|ZVƼ|^ I[*jCpUQ[&}_'T,&T:fPY1zM{,JN[T*?TwBALe㙄 "#go{dj"$@"$@"$@"p#o0h5:|x_ϢCc9>&tt}G?y]n.ޡֻƍkLaF Ei)Y 2"G&+1K;T"0$lJ9ZDqMI&!G,N?+jDtOĺ*K"8_ rD}WADR5Jf?Ҫ]Kf8)-|iiE"'oZA5JFN[WƍDFHqJPhld mM&hRZN[ӱD.AkAgBfںdVXlF_W4:Hص Q\TM{,bZD)epvݤ9С`hsWt7!.ʽʞ:qWJb1c}t#o"$@"$@"$@"A i6Lr)}[zO-nͯ__`sm{1"ĵM"2nc{7#%B ˆ9B$\o[4I;k4G"7hb #sT[*$4[ To^A4~[A!ՂzGI!RGCl+׵ G6i!Dh" CaDA顇IB0zAK]Zɿyf@FDXm-*Bq*Qhw*m[#g } 4Ft} x[X͐+Ks $-ћƞj읲REu1jl˴(uM1Vw~&!(;F1Ӵdf0B.r$@"$@"$@"$mHڷ 8\=n {u}&8Pt$"Z\ƌ Ah;n0/f# W((#zD,S@1j FU7Ҿjw q>^9$(BJ 1r^R( G"%j5 7JdN`oĊjJq{BD!(FJIPZ i! y Z_ QQd:; Q넞MQyZ[S 500ѧ$!zEΦ}*ȷD !ƒq!VԪw iPB&CQuъTƧ:DGLlgv$2 LeLܹrtDI#WR]TӜq4sZF+O׋3zryGb*K;ih_sN%d+,%PxT(mtҘ?+tt&;)ܨ)!+}ھRUDgmz't-R;sGP]'C-g7@)jM9AV,[W0&@"$@"$@"$@#I6bYeW,gtm=|+VSK*([#Cϋ) kb;m(9Wh\0{A];؏S)& ڥu"TS|fki[V)3ѧu#l.Gз׿"eUi3Hh:]ևƻʽ3)2ݿ@"$@"$@"$@"$#/vmS[LsIͽ^a4;98pKWw} =NЃwuWmLߝ/9@޿s3RD HD HD H@ҾC tv 3%ϧ|ny~݇Q:+zt*N]E 'zmHNwU5)(HD HD H3x%k!}v6󺔺:M: Q^kfy|xo)tZdkz2;7:D HD`"h!yAq,4Fc [e좹wǎ~ ъ IF3wH̉@"$@"$,8?kvD5QbI6lp5p 6֟f4+ƍ:@v7q{[oZX? \"[ӓѿiՙ$@"$sF 'ݻ)as]hh?|ΝlW_co ꫹#XHXnHUĞx²XZ\ODJS,]nl(rMe񽎭[ڒM/3?&C}E8L|.x%YpD4j-JSLd G2EUK#^t"$@"p!P1,߅<[QˊVag%:;e !,*6b]xֲ)05dSZ,%a>P#l!oY@"eLxoXZR;)oٲd41%∵dbS( jL~g5%yn;CtvO?ty`BM[J^_ gD HD "acX(]6M9sŗ^6[E6l)Pa=BX8Uk,ES|5i_z슻ロ#y}_|'8xӋ*Q O?_f $+ 4/XhȌ!;U}k"淌&mu98Ow^tS*$@"$<@{nW_}U *R24+9nHh W5F#{8:_:λP9Jo]j"7!wK ^ j,>o"V,F%[l* 1,-VЄ2`lG@!n:yYX4gڤ)D HD,"PX|{6FXL#rpZÃ޲N]@k5BĘmryCb!|ql*ƒsYz6'I oeYF%(/0z-ou}MwuV$@"$<@t8pZH^f=tjagKgKc)\0E 14x5<-"0!z W6Edlpc {B1٢ Lg}c\,(]l$AtFXmL0?/`UӮ.9rρQJZLcKE:$'@"$#P?  Eدue)^ꩳ bePUSޫWȰb+\aVۋ\^*)b>y94e+`;!܋S]h(竁5&d6=>`"|9Iw=uoL0;5~87K.5ħ+:'@"$@"0`#gN\ۉGLkAIZ,%@:| t-UrJtR˖e60ZD%(k$n6_)h^<5(t)o۶[Y8 0D Z:hu>j6m)S52"$@"b}g)1u<ƍb~Y>{"Q& k!gѩ,fIB7pS@(1#VWL5Q4E<ǟodyf/@XA+3=F1 =ef6 $=CCVN(M%Vsȴou]r|t{cO89rw,@"$@"Cxca"[(7+Ħo8_] /x?S|y#{Ѿ`C~Ĩ)Z0XZp]j2#F `~H.сbwy`!ќ'"d;g:yK˭D HD jt* S!f*6{-( Ty̺lz\{_?:̦p\Ćt&mVJ*2+C&/Ӎ2E-D"RGC6Vii]3HD H 've$xf ۃ၇21` |dwu9L 1%Ol$9y:L 䐟J'KU&Y&Sl0yhʨ,[^AӠS#ïPIV]?/B㹠$w O8+/p%6yس{lio{LJ6ɻ=ij;n2g]SK+n@Zh [<XHf$B 1eMh:ܼj79hV"6n_՝Bd1>[M+(ÑO 2Jm(tS2Rr9 2:H+?D HD` ``Z6O.5z"gnj 0+FX8($Kb!y=g0PÌR~RGЄ.]KqHCQTb1혔uW|޾լrro՞:.wn/'{?Ń篾ƛnt52O=;;N=ݒ] :_5g"Rjcpi [{+f'=Vķ9ݴTV٩}kF"zRX}gD }<òl) :S;%89<NNR$vqX\ i{$ $iqWB-&^(UGY 89@GQiE'e%fJ"$@"qaE(ܘ^lv/*L@m.(]vcρT-qMqѵxGY0"$)~BXbэ/ BfyViəTHY\8Oڷj/|_z)7eKS7KZ|E+ ֬/~-6o^z/m7\_/E(-f]SK+ntO}S]-<vY.-+1V,D `9}f?Jj/v9i6 2~@<'&JIxm7d@<p{'gFB&E<oBl94G`Gr<HD H"챿^\xlo&Wғ-\_j0i(Te9̞B:#dݩ1V[C}!CҾisBڊ]NiɒK.0~jd}=6dz.^~eqW-Zxoھow~ӭ& W 6%ʙvN-GiHTA]h*bbVD HD\F].sÎK>]R$ 9/iLDt$v#\ĆƖc=Rlx+ZJ6 >hjv/#dZ|i4nUheZkޞ7޽Kowա,_]4\FoB1PkI/tnJID .yg]&v^믿ּ馛D߼ X.aWSi/9bF1,@Tua#[lg[ ֦۷K 'J'gH5 iBCbpr Tj@IDATd8M31QfMeyW떇uh;nɧvѕ1֪ŐȓD MtD35фiP\ [ozKf90{?s9R BYW#[kBԂ 5Heq ê|"ts KH6OhIA 3~zrNd!\f΄'13(PP(8V-eD HD 87$ 'l CGta.4lfLv'#mwb8)B,p Z"f2XqxX9a,.Zb;-ĘŊw^{ގ=V޿xmok֎ ݳ6yxe˖xG0 R6m侊^Y; a5?[m^P)mnpT%ZaH}?/U7Yɝsin{&.s{V׫EۦEQ 4*=0=5w)O[Hs08o޼_-%7[ô6sJZV2>xUWb02?5kṦƉ~7P4kw}e-j<7 iCp2U'O9le{4*- ;ΝE]n2PlЪwVey{flucU+I"YVulizXRY#(hl|SoT-%' o,&+/߀.ѡ8!&j$3EyW_}5W^1OC,,UkjUO[,E[mN:r }ӟ~hQx8cƵH [6ZT4M$@"$@+ꩧ<īeh٪ 0XMA]8wg?,){O_ž!K ΐHD)2p4q[PHAr9\Z)wc (; åa΃RUtWE~wihE /ܐÝC/!*Q({YMjL^ipS;UõBqΞ-3 M85ޗxpͰf y:hb("wIm^^XS*BftwƒVs9V} rw(K =]RQj#dz$^ +m-i;qRE! @1e&I4x-3TVSY>|rڌ KWXYɊxJYkAӈ<с1o,ͭtRTP ij x=;T(RXW;]ukZӖ@/&@"$\F8yG9`cmBhy&MaA~yq#& u,(Fl!AlþQJ)q'JEPb]LvM!ٝ7[V)C]"]].̦k ,Q+!%r- S郤qRh[HÚqKoR*L_2Gu%tL5CX#zӡ BHh8# U 7DmE9{z~詴4MSСwZ=6xU-]cZ@"bpWAH9P.;J+!Tr) wMYBł̖$@"$ m,:43*3Lū̳3iW2pŞ+.\cNo٥\v~?{Վ~Q7ւC9`*1xT( &qҴDl ÍBBcw"TH;C2ٖmݺ_Fk"^;ӍU4M^x[tpap߅MK U`؈M e䏟R鈍 "}t&k~45)Yw2kS>&@"$@"$@"$@Hwr$'OZ4m^t+V G/[xGc޺{?zɮڲ7hſ6:3>B90M7DPP\Ow_ T7k8M8m6 /#qs@1)CIAɌ#jӡ}}MV{ȃHzTZ^!v2&8W )ehb.}>jj}O?MOY)E2iQY:-[ odZ*En ,?۴1]-NV n%!Wb,&S46p5c{i"1[T/N4:8iQWXYxK݆F'YRqrfʃI b 3O)ɔC¾ˬ ҨwFhT"2n6޵hυ!]Yi"CK?A-h_%:OHaK.͉vw*KpWR*#DVMu~jV-s #1J~'@"$@"$@"$4Hwr0\qd>=|5\իO?|vK߾`mS:.}$CёvE 8.b0e}ԤarwĊ% "…"(%,wA0!C\ JuarsQ+WE* #!Qi"zY"D҂,j'U5\C+M &c]؈NExT6R:;fǔ)z+(qBg`VM4kKqR%0h #D6?j+5VK kZ!ӨwTw"g,չnjz#VCf@"$@"$@"$@"0M޾%KW-@Ъޡ=V;tޝG?_v޾c} m۾v[61tBEf]-"aoI;+gZ8&$l5#i{Wbq*q!;飕 2_}Ud(JW)4DŽDѓjYeIIDsUkU]+(Y^;bUaOaao1ݸZPSܴ`U: ΑچB)# !-h_At[ %Q 4WE:+ \wwJyak7H$JQ B IK.iV$s-}7SPsu`iV|{ [OqvaӦM`yLzh;eQUwڢVd!Јy1B*ZvO>'ckɫ@"$@"$@"$@"0e ݊.qݵ1< N6?xr[-"|w~U{~y*rms#h{/blTzM7FH%Đj@|lMK( < V% IB(ZB+Ҷm۰/t.[iFa-%vUjebiUYyRoPKw}gT#&O"ӖNk@FL?aB8b@ĄJ_rC}G+1> 2WȁW\шR(_긕1/BkRl֭C0~#lQI}AǹD:k$Q1buыn;eiUwڢMDETA}\˚Н+D HD HD HD "IVshђkhYth#ĕ.'}6v Cw\t%_;tz߸bM$Z8D(-]dDb~rZb%|b'qJƓ?2-Tx@q)$ሥ駺 Uؒ }"UY G'i z'O(.UdSz+E>zJdF۩"nÆ (EMH+(9逷"Q:Wuڢr (ƍDFHqJPhld o+d7EJ-TEVǭXCQ߉5 IUWtJ[ 9` Y++ $Z b SS1G-pSdb28@nx0`й+:Dۏ*֪!U_cND 80M329X@"$@"Af)ó+mJ/M_BYZSEcߞGٿԲȾ 6_@N alof]<]B(0<+of5_ZN_C7 ϊX)+ k3[6LfyxMN צj*)H-G}j"E8hb[i%]إI ]jD“ }-)4 zH)'SmCsd e7oiW)-"L"QZJ;;U(:fvѦT4i-RFU=+R&hFt} x[X͐+Ks I˶!`McO5NYZjޢ56eZnKsL_ФHD-UB6r"OMsE$ݶQyꡩY.mLD HD=l$/Rwu@gf>pt=n jX{C&"|MpԩՎ$rK;v"M EmW*xؘs6QՍHiE& P)By j%juoWzIV^ sMP)r2nTd (Z`g@]&OȣHRR=yߩNrl̓BNEܚb^.񆙍> p+r6SAE'h8 1+eԪw iPB&CQuъTƧ:-ڍǤs儴$ /Pӝ9\ʥx|nT ڈ_U"b!i5LzةXҐMףPkE5!~*k̢9)hq; &:; &@"$FB̝d,y#v k1#!\g<_$;N袵?C895~kB ܺN&Q]Hdڔ dhK0ybJ5- YJcO;ih_^%P0by4Mm (\j<\*ZW6:i\:L:BBMnTxҕ>m_*B6r:Pҹ}jwuhq0z͔D / m?`o㱜@g+'w)&TeZ)+={+)ˍeN5kO{mGy,OZ;smyP} %@c9պP&K(x/Wű߻8l'5y"UJD HbV7}{i0Pf;CVgK3si/L$;ݾ^슥|u+Zp*fBFwcBQ)Ҵv]xwmj ]x2&F]CxAv,)5jO24^5lRVe#lO2#,쐿|/ffDdVzz "0zOD H Wd5؉_oկ~e9fQʢ^R*fb/D>k6si閞ӗPe-+lYt_,CۑסQ'm:[nI/kq2xnTMJtkSE^JD Hl$ϛl[|wO?䓧zWpEb? %@ iЉ]nɅI38]]`@j㥠yh(DXİX)^)y&47uMY@"`rBvn$frѣ^TA.,П~X˷7xC 0P6I6[PMq6OloqvpzyDSdVMrERU{ 2 R{C3IWBWO*1ҫK&Us^ȔNsR>8e֭[&jF#oNE3T *Q%E`ٍEnvF$@",p’a U، 6x({3bdd>(KXK\pi{T8Z[W1!1D--]SA4M~>v$@"E.=)9B O^] z쇋t?}v{(H+;y=DćÃ$EVɩoj8S !;D.4 UmrI;!D /*N\+SjX// xzʊ:O>58{(o[o%,Sƭ #yCñز"'V:NJ90 oխN$@",$’3D2X#Cn.}7"e` ӗPʓas-Dz`4;~*((6E@1Mb{Mnrs?~z?dcHp7^Z^QlN(ާ;SE1qƪךkZ{5{u\L_v~ֶjwZ,$@"/?+Saki9ĥ^(-q1ML9LѲ_1Grę,Jr$NJIZa-J̬vAz]]8P,'J= /w YL"qlwE-TwUbqn22C)]7ބ$LdV]x (3E =%AC5t"$@"nZ[mHl-,L[R,* iEmiTf5dBpZ$2W*px2T \E٠Ѿs?[kȭ[yt_\-$ Oe3-I*HyvAyGP7Jr/U玪^|E V.kfEh7?D He_`H3\¿%eLP@Ҿ +ӐD HD Hᫎ2(hKxrQpeUSŅNU0(@|XNzLbDA.a{*ARPK)b%[K $&2V c9fuK^g>CvCMnnE&CI~p+¨"H{l дkpP@ɠ''ϢnəHD H0V {kX%bŗGI[j޽ lZҾWr@"$@"(zJW ,ZNP?L䇓2шW+'핃u*,+C[_':@PebW1 ui]to6^d n( V{c)u_9U}3*ȥn-݊3$@",`2 hlY+^BYZ I.LCD HD S -<&J B x c$|Ţ:C;$/j|%zɲ2eJRU"9KSY.iԶC4'ǩutXWTZ9(}wspTܺ +_4;;A<(ra>bM3ӭLD HV“}<6.[x)+Gvȶ[K(Deb!!Bʹ%HD HC"&o~ 7̷ !hDD!GO& YH[bQݭXq @Xq+h_gn߾] vQb+nZ^$:m O/"[Tǭ0p{G[88 aN^6o 2,)'ԭ,*Ӊ@"$BE_p!^pX|m`=lBdH QXH$z3mID HD`! FD8u-[ c ֕GTdjrZXf;;W/E ww`'?8puu1<7V*_Өs@*Vn!-,{4;wyʕ+ٮ]j8o6S'FXtGϖne=3$@",Tl0W?35ק}eZ™Wbeu[g PNۇ󚼢dW`'хgU }\@1_v}ӧN=sv >9z]3D HD @q8Sa ~ džٵk¥RzITDRTr_04XrcpCDs۶m{饗ۇ [npk^ 0wn/㠑*ҁՐpB]pA^ ʜD HD`a `ݴɱu"믵iP}/,k׮[-6Ko;~kbVu ,XxV$;>_:716d/>ٿwׁ{.7N:yb_w)#KW13im9mj捴ھ3n΍]sJ+p/m뭴 {NSewa[KsSMD] Rf"\8ѩ/lujpr,cFEJ+n:&*%ُE*WCEI?fI>hp";g*ٳGxUku[+4 Um:arʌl/WS#*).bC9%@"$+ fG{%`u6wл6$2y*W_M7\]ϴQRYG iߙ@}?9y?{`ĭ|\zC<ӿ˛oSOmڴi&ʹu暘 pk{˂Njk͹knZiemV1<>88xwoM>{[;w#Kf17$Z᎝wUcqknZimVsEKRN;Q^o^ R3717/@۪(bb{="+ b@"09U\Tt18֌2#$@IcbA#C\l|8?XA|V,":__ !ASʑji.N{w[/ł-2TmlBCt-Y-VOM7h 'Vx"P[t(Lƍ9z}WWb (xv٧103HD Gƅ:{`]Ӱp9"`e펬vVpE{%o.Y|Pmχ\1kWy~"6vCo_޽ot,Fzq rscܴ Kz+&sωCk}'}E2.0J} at.:xnZ7JùRiD-s$+{,r0_dRKy ŀ1D?{%JbVDx)X`BĊd#M> ƤRE@呒Kj?)1Z)4w??M&__^Z]Dń.K$=mlEZׯ_9#Xrh(%n@f#ҖautАR A 06 %]D[, (M.D HDBk Ym} c4 7olH Y|AKvll*JMw;Iu74yn/z&ě<n}/Z~oק~6}{JZP DA1ժRscܴ Kz+.v'0~틎DY8ؖ%b18s mma%|Jwd;=9& ^ DGp",`8}\_9']ދ ɟ1]RjN᱔D&&ߐ_WZʺQNw^.i خLˢf`s83 B܊t5)@ZV"KDDN@$Kf:HD [@c_ar&U+f΂DuAٍQ8lmlڲ|<ܵ页׭;c_8vǧ[qǿ#|ۆuxdr@Et>(*J}ol?&M7浰-BuоHy݆l2-ɒ@"tWrZqKkͷ4 ﮽ n\D*0뵹׭Y2HD XREc1[ah޽*dy@Ҿ:hrWCkW,[|ZW/X6pZıC;::x+=30;rВգjW߷Lmpx4ᛌߡC|(F؅r4ݻS5s=*zqn KۻVn '.ic9B+$Q#e|ݥo%h/R]%+Z#"R_[a}?t) X90Ѣ]ad DZ=rE"Q}Q݃ɧ @zy(rU4EPinƳ&A7b}:6Z>1 hnI+B2i OYxpj[rXzӤu9t(ۢh2qs{tQqox#L$@")+騱m[Ωʹu@IDAT^\k X* E'i;s[YL'@"$/+{uP,9oHw^tb~>˧=wo~xŲEKZlȱc_}#7\gE[9|7tmkƆNZQ_:X `+=kb<"6o og#8T$칝 o}[bژw^x{P A&TJ.ޮpyp: m4Ss 2z"^|;>PkeP P gG*]h[kYmJu1c *?H,DoOɤLC.CI6-ƁD7b)ɂ}`Q>k;nQ!Y_{:@xxZ=M3YQקЪwZOa3~-FꋸL;Ԏ#L>x՜JOD HD HD HYG i ]44z_{lϢUV&Mo8SuvDmt;S+Kc3/ų`m9aS, Y>$RU<歷z=(凜K]+ %jߧEuEpsXKDQxXZaTA0j%3B=dA|;AE"TBTbbhDM&-,b"-`i.h9NՂ4C)ov^㉯~Wؙ xLe:0F1>+᧱}+8KVl*": %Vh} C=d(,/tHE\FLoj׀C}4۶N8bQH Si2 Nuh;DhnDH*xTSk}"?gcNN7HD HD HD H};Cϙ'{;k#~~ ,^暵={p؉/|WV. &*Ӿ1!Ek˖->Ѿ@|- 9885pA32> !Vv #Tf:B HCJD*>hƍq87x<j CaqvJĖvtƒvDva"h x"[aQGs*!.yN#3'[%a_wL LHdGg4ODQF>U@W7$6}tqBD1݌q"ck(7"JuJKiTY&#L$UW'}T/_U, !!y$gQmo"$@"$@"$@"\$ MY?rm\ܴß}?0zW_6CTP]hJ, M#ZVvїX'ƧtC̡2fDiD)[& S 8!H[f-k"È!]/_u.5] A`($Qs64G `|޾]@!ivbl(EsA$,0f =%p$3z>L`!&GDQwʠwX xdh) 2)UpsD_hDMoc n̤$ʒ(fBXo}IIf1ZitV҈1%#Z`#VNTRȇyoxWfCc91ݤGH..C1^8a庻#Pil:sD HD HD HD`.Hڷ3{j=~{M9<~~|K!ϞKj--L>N 5|)ѩ8Y[9 Q|(18Jz6Q q"TW +AaB[hM"RQ޿^*QF a]LΎUQʐIt54z"qahk,0lg ,dt#䆨"QzT/=vjQ9iSQ-)^ < X3Q)D HD HD HD`vHڷ3<{z/;4hE׎:zħ}K5[gkM}75TbTamb?28MDXM Fm:ZaՐBBcw T)cućb #}믾*Zy#8oenliNvۢ#}}6.3WKX%jL Q׿Ic^ѩӜCN(iI!@3#Zi[Ed80%9H@< -_t8/"~PaKYa+(9ϗ4UA6B4oݺ,zD*gfd0my$~ɫ:<`DmoY +vyQK .lz:}f޳^;lXmh6:XCjFW|}vЙOk_Nm/+R,N] D/-R5y,L[1&-S8ZӁn3`oF'YRurf*ptڃ>'S&^lo+0ҨEZ>Ш.~YCdnuυ!L7DaK-h_%:XKdtřp-DE0@u4*44Te*頂߿,RISX̦2 z9oU%b7 x\phRwdZT*mA#T[MGH*3c5J"[&mJM, JX'nYXP&(ZV1e\Y Y,9^7ZK7+V(eъ Ojj=C eD HD X9Aˈ?Աb^BYZIN߉}ҫן:[ޞnTjUG[7FkޞDi֌~㿟_?ș!My$Etc9N,x1eM70]5X N7 ݴ(%,wǎhSt_(j p޸O!<)Ĥ k.=Õb14ԨuE* #!4Y =])iAS*n$߄|[TVAeJ`QL^1qʿ?CVPrƍ_z6iGY7UЬ,E%0 Lj+RL30\ !du:`[Uz'JV/ߩEeC,՟j7GHs^Bp>QU2@"p `կ~~Y݊fE;no~cڲe-;|_{l-x'2bіmlqX%G?nO[7mD7g̢n%*l)[;K|;;}O|ɳtID H9FBoWrygp6T8KwVKu/Qf\$;} ,Y7|CS?JnT_?wUW NENME;13߸g떯U3u{t&HXU<Ҝ_WͿ  61!1w8\G7&˗ъkEl۶ jQ 7a/Q{#fV]F\$ǚjUg%+[Ci™pb9H-Pը=^Į*Cmwq׺LVe'JA6Ge ͉BP<@-%i2T0?] #CaB @*wJ_rڂC}G+1⏐?<ה_PJ+q+c^NBkR_7T? Ncz_#-zF|޸ۦSV|Ow4S6b޾@~Dͬ$ Y뻙ܜFU3˺`K`f~'6/YȾbb)q!dcrJ][Y;-j(Ξ"貲ۓ:4oC{ǚk`%6ސ]![Uo?[5@"$@"0g^^­(Np_{;%@2Hڷj -^}=O~y񥏞MpЖkޡ;zצ-yt=Atl}zUwK؝>2 G_ c`*9 ˱"mZI2L4%n):BgM#I1uWAO0DXp?/G (Kl+"'V]2vjaYD=tM&>ɤitdA5Gj-U]@} Կ+)V-*FN a+MQ Uѩqk:Peۭ3YibYCP )OH-г{Qft#0D HD [_e1*Kf~qU:nYm xٶ@FK҄>?;7?Zř=O|~sK^ĉ/kWoxO=etjG#ؖyqq'8NͳWPl4?Ol@U]g?3k+Hb9iMMi\&ڭSw2VMSIE$`~_@ B(CCl+e"ݐi֣F^c#GDQ@1(?kǖ!=n]J[*\FLd)DNNj87(j; M [*ֈyYg0ѧ@0\slSX?3 Ԇ\Yr03FGi)K^^+-bSҦOo,QU нI#E~zh?#qn*3@D H˟geѼ5-ޤZ4y2hZL8bX̥?EA/6Γњktd(@'Tub(`Ve PTt^pg<#?&Pq<*r:eOL]K%>(ḡ/9Ervw>j m,:\ ĸx`2vh(f%u"Fms< 2K<[d&D HDS"kc̀ߺϡsˏU 5.[P QXH$;ތ3,_jM yh页S}8Pt̤{t?Aqkج{扵Ǣ67)H&x |$yDZ:G] V NTh%x"9\9@QF&%juJdjLШм=T"LdY- ӐE*V\2/T( CTTYiN]ElӶzEܚb^-ƭ> p+J6SyQia(ĸYƞVSȬ^A,jLHaEi*S.߽!)n5je ^i=vh9@""`Wm 8*&1!F}Ej5{xݡLSI^y +v4(BY"|1XyeZh+ʬeru%ߤgEnEa^*Fwq*bK3@\4V%r yeaL#!dʧM\ >:U4T9VntAXGCsʀY%CD]Pmt3HD Hfumabٟ>q3^LUlZ|H;뻍Shfy֚$m]{ǤHgpyUm(!Xȷ1رα:+-PAA$5p=RGRϖna~&@"$=pPYZ(ǞDWr숊溗PBB in{sdK+W ߊ sQ< ϠX#ehci:վyvl<=>yP|-.y/ SvWEsaYXX)>!b Pֹz+qk*gn,G\bK$+/[7*˯V("a˴'[LKI"d݉[EFt[kU-OWXU^:΄4dP\4s\2o(L&Ys楠zvW iK[>(YuFЍ&nE6ՐeBabpoeHb {I Rf2 0"F @"إ.}!W{4T/Hsj Y YѭL$@"$y)rf-.p0%(i? Qn{ ei^0\D>q`tSnC8` CB_y0]r_^/68`-Wz1!Q"&o%'D H:EnQ Oձv\\pY-hVJU%$`$bT&ѻx`'дU[*bZ ҄6Ai' !qbJMӨRj8 ;"J OTBdRb.~w{LO0M:&~[3QۼyyѨAC^D)fI6 D HD bZc/xQH؇DI;{ UbI^^5rx%<6EWG4 W7c o*zt6I#PՎ_cidK˻@"$*'XMR][MQy X."g-"./,Vp+U9^7N($kpĮw @IG*/+"|0&se ߢ6pς Ӫ 5:ι$ `cC=//4v pVE.ulV4D HD 1VgەX 7*2b2K(KA iӕfI$ "&RfMEěBrqoF&>,\sO񳆀Zgin"$@up6ήhEJMe9&s9vqb] #X\~o+N|ŷ:-L%΄{壞zf9tPv5 iʫ^% O;2^z ڀwEN =^KT5 yfwD HD Vb=Be+/}HؓV!{ L,$.Fo!>m1˸湒M3q\Mf傀宮mKΒ@"$9:@Pm sKb]"dqJY21{ؠnVAuhоn߾L|e,Ų_FO{ ,F^M%pƢO,"PH_:׽w/J׷ yZ C,)'ԭ,*Ӊ@"$@"03l圙VD HD \k{ [y$&PB˖C2ѾKYk##]S,w,Va#8d,s]n>S'3~MUacC>U)S/yh3!-,s}w;ӱl5A o6mQ'FXtGϖnai~&@"$@7X-^og^kOAο+cuJv]-AE vJF{Y }|YtS*$@"$eo>l)vR/&7|U83|Nή]0|n _APuX(a$r/ՕpDЩܶm^z%ejXJ*7sB760{ǹ_C6UF]mBh.hߋ[7fD HDJFlcJwkz%+ضǰԭj3`S+>o㭼ֽlܪ.>av7HWrgۓϽ%@"$@"pSt*džDDIKrЩ|:;qLbݕ[NK( Xd %VRݺ]tE w kڳgF~-(% ߌkZPPЖ+{n9 __G US 5.n]D H++ lýW*:na/=z׆G7^u꫱A9br~%ic"$@"$@T8( 4U:((BN>(^{7}]UpM qwj8`pbuFt dt-E1s)@+nEp.W0O`8-OTOdWLZ.Vt[cgN"$@"t@(|yW,N{"뾝ձ{kd?`g_{u u/#z>C{9po|ݴ~e23HD HD=H7v|7ݒ&#'X<~0:x<t'_g#!oNѣХQ{tAbP{O8',?ҊxBXG2tGԱ"e UirgF[Ԏ3Ga9 B-<",Ѝ%MI&D]zҖsVXSTɗlE*1QRXl[8]VQԦgk>] ֜_I8'] .u+,@"$@"0+/^<؞xiM7{%Xm-lHԲ-M,U$7| dor/r]榕V^[i578{N^wDO>'}d2~>/6npVD HD HD HD G iٯO~u$0<<&ύ9E_@@O"n}/Z~oק~6}{J1Ç8D:Z^jn욛VZa}i[o[N7hy睗^z {?~츣o/D9ꫯ8s*?:l,Y Kҭh"$@"$@"$@"0Hw>`hgӖ+8䡯Mnݑv85>7uG_?<mCg:<2tBDe'۷̙3|O9qްa-ܲ~DxvNi@"$@"$@"$q`w`򫗎 ,8uxpbP_gء]nܿ3m` }b+дN7VdDMF4r`+ h,^V4X;rnt[h%EMYwVFNL'@"$@"$@"$@EE7^-w/~|38zW,[dxQ˖;g;?2U{Vçk{^}CܶflnϞ=ʮ] 9ȬYfwuynoayژA9 ^ QS\~FnUoE |(Zm6vZӓ9x+9 " py֭bU.tO}oFb˅+ *Fa ̿W^m]2D xw>eꔬ2`1D1vА&KS.\裘|ަw:-#E!٠yo4}Z=M[ѾЪwyˆϟ=Wp?>y_{nv v=kk3Xo|9B  >n|⃄֪=ljpb8GS #q*VZ<3+3r4)uI%j\ 7UMfvꂛY#hi(~SsKO/ۗ/_ޥ[(t  G *}&LC'R^iOJu1Ze *JD!DoOɤLC.CI0ͧA@ ", KH4w`bX|u ASi2ƘɊ>TVRӥH5 (a۴gwWъID HD HDJF[ǡ ]1^BE iκf=V-[n87y>gj~U.z';5OgyF(Ln$xEOo=Ty뭷s= `l?#g>%ҢBqw9K8UF<,08ha]55Ggzر^{ ' ;NjQƩ!^UlP,MC]ĻEXXD "2GZC&cAt"p6+v&S.p@Q JF+Bftwq~>?T䡇2yz^" .F&`|7kDuPOоJۂmNcT($;Az9@ӧd oTVS,4k @*x_3J8νSSD HD HD Hd)_n~%-Iv5}}|8sءO?X%koe5׬}كcN|rrIm6QE3 &E YE,P6[lE*Sli : l x4,RXFJoB)\SP,zB nH[kdZH6q8DKU|oAF 5f2"J0M8;%ebK;:!&)MTi&vq(HP,Bz43\ 3|QzWy>sdB @ tw$.i&(M}V! nH^=B l8٥VZdT !7"zxayuR|f`I3MU#"; >0i;˗G~B"K>e@k41d,3ӡw 9A_A0 nii_48׭GNu$ғn^m$by^ 1ӂ(Ncuj$@"$CfgDl06Q !bAw\e}P@Ҿ3āW O &w j6.]{n>X+]RvOU02hJ, ]7Ƀ)8Za<]@IDATb>v8DQhƆc"٧ h_K f3ꖵ2a1Re&Uw RӜEb_A5*Gf `|޾]@!ivFEA$ Xb6Q'J2GYU QJ"(U;eP|f;d)UpsD_VMoc n̤%(fBXoBIf1ZiQ҈1%#Z`#6hߨUw7aީE2՟;oAn] -Vfk6(&|C_[jˮ[>L;L}/n}{w;?-f~9O"$@"$GM}.[zy³cP8wu:w/N`{#z#෷oDߪ?d?6oزH82϶F5 #p4Ħ1nTF D* Cu0Dt,VjHUFyzDQ[%(!ALX ɘDWJMs+ل]aڮ΢hv2YOcKĺ"v1>HDmž# gJ~XZ3DWid:J)6J81e2sXszӥBHhiЍLxk c S<4کE2@i:4N+/ թ7C+X2JZճ f]V6f~"Le^z$oz#T?2[ծd>X:$@"$@"pi[NBp)p"Ud܅4U{ Mf@Ҿ]o_e\ڱSGϝoW_fwlߵ:{J]J>:Aթi NqD]_ ϬÍjg!(V$&יqFS(o*O%TΎ믛КܼGyđ(ct!}zmё>F [X ;fD052S)9,Ye:;#)EtS\uD+mR诐 $"wŀXT}+quVXEh JbHS%d-D֭[rNԪR~fHQіAĜOth2V[Iq>3;"'A2=.hybVq(Ӵ`1gSD HD H8 'RxhG=?(_/Ϊ{ ԧK;-^4ttŲk6_4EN^=0>x%{&jCcj睵U G^Ӈ9(ytIoSE1]e@;cʨR:wirTfSy$D! SSUi |Sh"h=V\Sz4}"`xj%R[Ȍ:w(F&nTT|sdcZM[ {TLnvID[$$|uD@4ASGso v!vkTV pz)]6&2ǿ>)tFB3O=M3!.NafD8e 3$ x^W:лY_Qy޶xj0[hvחx|Ώ2J'eEy+5.RD HD H *Q7t(}->㌑p[v>"|gW^B!* }MlٳgƇ LȢ֮ZlZb}鉣|㪣:|?hſ6:39˙Ẹ0M!DPP:3\QZq_4T#"UQ"8frSG=sdO#iо~'ʔ#G1Vi]H7Y244EshV+AUUD[ڵg!w9sec m;;:us9^mɴ(QjnYwj=]+pݭ$Ab,&V @]FN+iӪd7ޢ`(0M4] 4gvSL|"TArc޶hɭZ1X%c2O=:1{cV!KwX4bMfWa`RX-?`kd_cL"$@"$k ccXk}s-;. ,WaK QXH$;}oGϜ>yQK .lz:}f޳^;lXmh6:Xe\m60#|Zwm{qbq.RP$z17.lnrв/amq Ƅ"D__C+4}KU :Aad#ǣ)0Ҩ\IhTi!2Y7n$GQNXLh_:itA,@%R2:xpsNf wAohMQ2BtntzA[ aua))l@4z̦2 z \1g} Q])^]PJJT)ߩEed:z:BUta=kD6U^x8Xq䴷1&|FcurqB"f)ȺoV瓘 l,2%؜o]pׂk3S)o!ñ|kإMe,JҜj2!JD HD  lwgѲ˲EĹ_cl+$w/@Ҿfv}go~hقW?ڹ{߽`mS?Кo}=gz9wαiÞ[Lr#ttKwc5aʚVoEޔ)XiE BiQ)ˋ;vM thbש%C*œj'M6f%JjZO yARN(Y]A44K2&"5?|y&G/z[yv %% &OblD95SOʴNnpʎ8~!+(hD ӎ: npoaJ3`4bmLg,K 3p"LNG@^qJD;,Ux7՟j7GHʅ^R8aEw2%( %NEND_Hm^BYZ IVeW]}jy.&O;{xsq[-"|?Rʭ=q⿩r7ls#͇y8B/R˷c+ynRw$(ď)çz D9gEg|- w+D,Zq8oW_}U>-^v=ޝĮ*Cmwq׺L VVZ'_*}IG}]4y: g"O&O!)iKU 3,2BUw*޵bKZO8OOnyιX<XKT̐tSTx__PKjE'ɼ)Q3dp4w 5R^>.b#4I_(.Q W~+"X]2vjXD=K$dҊ4 DI:pY^1#4*.6m/X1r@VJQS7Hd9D[I~ *P]cE[ƃ~'֐$UKVںeV<|F'pACyo~d3iT,ߑEnLSh7>ḾF gEqK%͍m{E 8HcUFN3$qSi5xWg=̯&vsٯL];jy7Z\EaMt:fa1Rҿ2P4!uy&@"$@"PƆVWEfQ\Ff2`;`L3Cߞ'>?J﹥_~?nƧz2ȗNUalo"%B|P`'$O~NE*g85b 4*V-BS25A nvw) ;[MTRѧYF+_$P9EJ+uvm7dFל#CRhP J?Ees fLR~֭"6H#S&Hv6Kiz}GPzJ5m-qk<,Pճ`O)`$I(ا~ H7`g>$.ѩ`McO5NYZZnQh26}|czuM1|El{ )#EcW܏N ݎ >ͱ^L>e,%\-lf ~9\R:Z#,Kw"5+o]7HD H  5;_mb<, 3PBB iߙfdUko"e`CgN[4ŁSg&v<`bѩKL.If(LCHBqɼRe¢*A!*L\}Q'!v%%mE5żPC[. 3} L]WlڧhǢ@ Q Ɖq"Ԫw _0EP4 c (?Me|!5-hEc[57TqNfőg"\khܘ$65lbbgɿ||Mὸ)VLLm4@"$@"CllbŏJgٻ渎+OwDQ}iIlݭ~:z{+<_f^͋L{lEwq'Aľ?JEPE$@+t+oɓ̛'_7RJYV."*n%]imܹUR<7^]SMfs/@95/>Nɤu&%W~8_!8!z@LNDmsOEVʒXT *%J*etS$i{ꮢò-$i"Jk^6D@IVAuj'eh- LJw)P#( Qӥ%Yv~ʸT)"<$\N,Z0v]݈2K_Q*AoįMv X1SS0J坰k\~I>@ MOB^TiT 7B\h_wbmN Ԓ:vz3|x Vj8D2ɾZ4w:U=?~z@޸6&FyEKRv (0)Qm:eb'\*$O^KX fo4~&["\KRP'μF˷j z;eV>@ `՛1Q,ꙝdՍWhaXuƒL 9"asEvMP^ؽ\}ܞ :\>@ I ʦ7q_9n,x:qʢy9hvZǁ`vmmA|[~=S_^ kˇt:>!W9{nwz)FS `c~h4~}i4@QfN2 3N;R- ktFky#"Mt 'O?԰n?{ WL[,VU@ @ }lf){̋vPnY2 AkXGtM>e:{_=31&W}8kم]?c2k8&2k?l4hqWF(`1:~XdUU?@ zGelV'd.Zkcd+^ӡGwMZ5?~ofc},=}{ɣоyPXqTL5^`[X$o#N'hK/&PkYЭ5/4?i}k%DH `<:p CE+7x[Ս]o^ɫ/ۊΑx?$V^@ D4l d /K¾KK/ls%o㩿bt'k.]fqQꪥLvcXp#kh#f R7qzvQ(WKtnb{:0.K-m]?䓒pcaH2({iq f#d vLhY"<@ vD+{̂+*.?8o_7d >|0ڗKX_Ȅdn0@ @`^ ^Hڟ+Ђ\T_??M10{}W_|Enh_d.+E|L9AZkMc18_$877hbr,: @ @ pwHlN?Okl$L29n7kךMY\7q2Y2#4rc{Ǡ@ @ G`l]P?qp6 ;̒8aA WB(n}3~jeVG@ @ #&9&EiSB̋0-'siR田 oEf %@ @ H,⧳ ܰR0&@ yPp,hW.#D h5X)R @ 8ɟBX} @  Tk%E^i+jCk F @ @ @ @ @}+PM @ @ @ @ A e@ @ @ @ @@[oFG]<44333>Nw~_E,@ @ @ @   O}ۗ>1<:2:1>1C}#GW*:@ @ @ @ @ 8A6Y06:wdOW{ճxLKPh!ndxc⌍Otvu>o߾;v` :ΔTܗ*o8?|ttt֭۶m۾}uۑQ @ @ @ @}Ѩw?e1n91>zҹWΖfR{yG}t#"p=w/' 3#c]$ogw\w& /՝ G^z=66ݿoooSSӝ2<<|(#9@ @ @ 9j777kp^Xh(A. "8?+^-]ׯN޺mK[[)]__rk*L]~8qCWu_^~!o_^(LL.Kws_J;w.\35:mWȩS\bFp_̐@ @ @ po#,bv*%4]D^4hfnVc7]tSnm]GGL^4[.KbS=#=ع}mm/_:w١^-m''']Ct~h VK]sgugrY ƻRZp5?=~8vww;v+pWk7nT컢Id"[;wf 2TvoT/9J{n `N9ohY]CZ @ kV=QfS&WvDuV.am"Z2A.gǟ|vyhin:ww. LMe0S7>/Ã?Og;?Nn 6 8-gb ^|ꫯ;^}7`EG@` >HnVӾlK}|ꫯ>'AI3@ B 9s^3Bz-??|: _k}s=;z:{ۇ/KvwzK#}ҕBSřh7.KiuS]z ;/;:y4"ȦmcAե(~nS㝯-8i!9l"~%Z퍄SSS&a<T*.Wk\غ^J(A) ɞF6͟9KH`N8d Wٮ|Jqou*xm]*S,dܹ:JM\Eկ/^|_*\"Gd[W\*LEޢ7pKnFϟw5J>W`a|_N*Q4O `ѷVfzpRyU 6~}ڷIvΔPt֊ *Z/^,7oHp*iuBu;KRZNʃ[5&q ŧ~j_vo!a7/R=S 4ĉ@ @ 9n7{:y$(vGxpgEA[EFE h뮳_˯=>-m]m{{;n\trKsF '-ۏlpb9މǎCxuwLG}衇#9K СCVOd޽ua9 \4}OT3tI+ Vu'ѐJUu(J$T"(=EG)M az*L8_eQ"*;/"Ry.'|??{0nՑs5 uj)QQyYZ}˖z4K.AlۨKՎת:Zk-Tj dSU>vlZY.{~k3Z`~;`h@ X]5緶wi fuuZP;@ EFcp1hկ; ;(]r [vGCu5RN ѩ SW.h혛o)L;w<;wzx>i-# }O' s6E(bfPo\{=(g )х*Iq.O_|_bh-kş919bj$%O (%G|=I+j0AX7,=>L4JD^T:nrW"W ~֖4dԬ'WEwPM T  Sv9. 6XY'T\㦾Ԫb@*5R;Qdi.P 6P*_ƁD X !0IXDN"Y]_@~lISLXPTy5K/;(ޅN=W:\yyBf)TT[ e4΢wPUgtx& NSDdǽyߊJ_?]bH`b|XDuq4ô?Eޜ$7\7@ @ VԈ#Yljg̣Lo?s4_J\m-`[m]6]=svbzmG};KxBO{F~}o҈7KBw[>GS?]zt;.wK+H9JƲZdab;-/BZaT,09ʎ̓/j魷BqH,:I*,49Y!kS?7,T"űPV)Y4紫8\#+Hɠo6=W<ȬtRaCûJ=n{9j̅<":DiKJF#& w5E Vj-dՄ$ە'3Qi Hd=ٺEŎ]O" U#`S^TTC BVS4$_4^@?)Cbj(,lRLd7Ua|ID'LN-?OdV;%]-ZIMtMN ST)> $s\i1ғ@$v۫[iJ%FXrk-@ 6 ,}S3YI ƌW=Y F(rоR˥ܦ챎;7ڼ=?y7.|V,vr~OWÙ\{vrN8p>3pFVm=+jL;XDj3&U J/[()!+K!HJ[/G #w!|#ܥ+B/D4RH%*f*-"_< \^HD3 ] nKN>>]૟1tUt5:}{  u*GDW/Y*jd4"iX| 㸟f8iN|]d꾈[(cp1Z* F1 ]k0@ 6&0O.)˔=TBRBTCr A hƪ4W2uTOKmnHW-nzaB_/W^uT SVrI%)XwXx:L IR"$;I*` `bkZoFf}1H4y.b`UP9+;\iLrI!3XY@1 KȬ;,# 8о>Ƣ qtIU(L8ӍA3 jT!sYcǎ7!W^q1$Q6ɡmCuGg*Z*!yr_pCyϩG?O?9͹{A~}y O;+@ HXmt/&H/r!RL#z}$nôT˴.c̐a`]i>"SMm @ @`- dn!k j9?]njD3ezzr kawyHruGvwutw>0>2:>9xf-_k,vzLaP26ye{kP0%f5I[2}+榇8#Q* 8 )@Mx]jiq Uق)zDgn<|d^8ضة/+d_; BXhn*LNGEs.s//i!ЋXDzPd,% hy]5LXzdD8Jy"~hS,ɐ5K]@IDAT)IPHxR_B$P6%eJO ǑSDKFZ_(JɗT@0;eIEVgAO<ȑ#Rd(oY8#UԷVL7:_5Zwtgۿ(%}mLZ(l 30NtƤR":5$h(rj!j_ai!dju*n婝3FKT-{cmZ6̉R#ՄbTC_{VSAͱWv 4O@`c"`|4CuJ }!!_ .nvnhnl5[}׈?SGoMQH#ظ @ @`]#V5a͙`{Q ?CPn"=] hߕKXK!R|Rn(vtl,5nkn>JVjj) _=_>ӽoK9n<|Z?q0~mݻyǃֹ6; ;.3NV#7Y a Hx+(`W5 UdUdb'mrWGd(L*ܴ>C-ѓrL>Ғ&2BS/& {jU1P2g\@t;%ٻuHz_~e&t0.Ġ裏zTGȂGz 25t9ck)^FQN`Mazt%L C+PҊHN-@ygUqd)rAMܢZU+OyM+ |N(G!M> d1TuAX=2B<&:\g$ J2G d?Fo9EںK%?n՘WWeo(; //jˤǤs>o{uH֢3eAhTKU˯DT<Ϸk 6=2O]1 m ?;kO@`!`2Vz'ia #hz!Cv> !:@ )0MYl׍IQk"jsv"CRfH<3Q~ fV@^97'PtرSGSٖkCJ?4{ӳ =lͭuz{>3'/)&D(-=԰P6_826*V`/; /&'ÉUS.8Ifb$>EXβFl>UZA 鰰H4?u=TOKi\Kf*;zJD=4Q&VQ ŤS>LGQ)B̞|I?ǢRT436EnM$2[/3Ȼ͂3iP`,(" S77^u1H //dz`Fo+XBhB,H{|͢Kϱ IZdM% OE_$P!E$v)/(W"V'$AHT(E&}b)4 W_}UʞGYҥ8"KE/"JH#S Hv=DFNR8R(jԩ;-,[_Xy fyjDMS@I犜- VJ4j^CCR-;6)<զjkZZr_E}˔tѷ P-QUA`:,y zyL`RXzuY BJx=V*@`#{xӍ { ۺrԮ&Y>cU?;~Wl!ʠ`,Ձ_'[Wf8;,⮉8չ} @ #)3()μ/MPʂ4K/dNA6O^YH^΍"o}?۵yώ=vwv|uOҖqoc7r$B1L`2r^r|RKptOᄘS#FQֵ].fm"IBy T(Ԕ\TyrxaKx$_QE)Ё86D n,RǥTd (J$aEO7A'GD !*j) U5=EIes!''nb^QC^. 34 ^%uɷaeS;LR[ҮTFoRS?|rh)dR{dnT귩+!I[sԎ+T>oR$K=]j@ X/xӭgn;n@f4ShЎwa=G"P=^eɢaXBT5=eԐzi ¡g @ l4Xpּ-N8dȼKOɌhs08Q7,tT~KhnҪ߹sūyo\Q^]La'_6&$'l^|u(W\_ Pi7Wc(2SI* IeIP,G2M}uWa4Mȵ /"yB w$+ TYI:2j4H&%ݻuuȓc8i ܫRB]Q]8+=7OOVRL3[##id}Sy5iKX6}P#W%d)wFI6g|V\+@ ,3w2G9M]L,c3Ah1n /2)P.HX b@о+ųok3c3&g[F:E!SޝlEsShژCkK2d]ohgA>^P.K ?OjS)@Zt'\*$O^K ivTB}_ꖈ4W,eyq:yR@ @ *摺r yr8k}x88Ϛ%1vT\"K y'sE:[Y(P$]"Q9 D~Ư!@ @ @ @ c{U*g}QuUC@ _F1@ @ @ @ X,UuZP;@ @ @ @  @x9mjhxz(z@ *!Zni_7RCL @ @ ={ߜ3O@ @`#p=YxV!l?>xw^}'xbw?O͏8_Vx7]]5YT @ k$3gμk*_.\Tʣ>\Dř~}oFG]jzcϟi)w ً72(RFH'N奋nyĐwQ{Gy@WJ^G1o;LׁD4O*(ݢ孨7@ @ H#SO>Ć*:dg12qd<21,͗P+8 7o]|~^,tw_R8qRG{-=wnio-'Y;w~'d+7g.8t8<½&u7݊X~cռ32oG.+/{ 5oom޻h/.sMݝ-=q~+Ҧ8GuQ,^ãGm!!y|^;~~B䍑LO#k=/0PM*O^Tli<7Ndq_|œ--M(wxlK/M0޿RE EP.]ȹT|&gg&`i(jϋcU)}l:x(9{, Ѿ}t9 ׿&q_x9$Ş}:J ).zb]4FGqa[oqƀS9ax'N8>C+#-jvb_[[˗Νxevhl䯗/bU8$vlR*|;#vrgj)RSOiZ>mzg4\#mzŒ+Zٺo<-6$][7fg ogk};Gu߆~ӑҥM{=#yr'K۹W:\b#p{[4_HĩhVFSmR$S _f:*b ;G.EH3r=IIN,?\!he>'vMF)bA'bDbDH2)I̯;*p낀K*zD>J Tyu@ @ 86̘ɘ*`{͠L̲Lm|6`ncvaݺ2]%l7TI]q'>O=䳛vȃF}{~዁kFF$YSn|_ڇݟv>r?NnvwG@ ;rsOgq{ou,疞, mIhiFbMLoM}EppxSc{/\hvP;'yRx;sdr"uKʻ+Xέ:`0]xV:(.?Q*1~E¢t=69̘XL^E #@4ϯ$%:K>_G=Tk $o|]v*bԁ2,c92P?bL)ڜUD+&GFNN:=R]EgA&>>>1;xٔ}d|r0:6;< Yҝ>f\Ʋ&IOPҶ7#bj&h}8?iEϮ=E,9ďmsw1mbN ۙ+mf㓳C#>5譼\IB *~[kdػKyX*mu#Qm-)ع{ʜϨqȈZy| ۏmr Cc&~VGUcsIuNk<*%mlVoʆkӣ2+Q=)U:*-fMC{c,{ R̓vBCvlܤkS_)o17FofuZn.[ҟV!UjlW~ˣ[?>570EnTr/Zv~YѬUrS yd._:scݿUz;t/:[xOLM͟NڗjQPR|mrA33=bLFֻo[3Wp?EV[#C=B2&X& !:Us1a-W_yJB>b'wfw@g&2 [#e-/9:xA*RDL9LHcEvGjN2bQCga,Zޔc @  דdDO} f/nQZjiq UvpNo}WMGޖ޶ͽ7.|m:޹}oGvO_R8_|blk޷w:K60cO]@ˊÒHǡ]myZ,`..M}~Ĺ+lJl;˻:s1wQ'Ώ}S^xjs`[ zbwCYZZsW&Y©,wh/GO9V2#3ҌΎ.O]:qvZ]w$/G''QZy.v蔅4877d<,Zmk$]O$wJz~TG:ڛzLԚJܳ3=_/\>fe0Wi-áW'sO'4i@S*mK$|Z݆.,l-jrbm!_^ZmVrCZ̅k^wML8m0|G虋KY}~f剾) xyU,B|Wηvvupz۶Bӑ ٺZٸ3 Rª8n\"и6יx\62W'85ù[y}`0r˵^iom싅K)M0U޵T9:=Etc/ \>R/"+ɍK3X88"LЄ-kuM r*~1D1H)3WVp4XN'j./!MU\^L&@ 6&NiF&*ůVUYjiq ocUZڵms3o''vnٷC[64^8vpz3b]Ɍ셣]óMfG =0}hSop&6``1bF015VJ. ki϶V"Z\AòAcMW*V#gxG'r֦w0m+zz4>'m[k϶]_Pͻjkk(<ޔ85"M%t~Ž;3&wf̥sg#3۸VFNΜp}0< od|>ִesO ͕+I skvf㳉{k4^tʁ@m}5PzG:96RiocA˶+8Yvfͽii]rv,Vݥh|u!0EsNd}V' kk PΎ}x|FVNlM/7[GE)mу9hko"@ K.vz[A9U,f|qk MsO)v]ѓW%P+o[ƿVTUSJo]Qn'qVNj֨x"W]ތJ {K~}Q;;U'/VgǏl-+ήE!mpsϦrTho^i<oxWRc\A\|1ڛKY*bOnT#7Eq;|Z.1p[I*^LJQW*1H^| !'Jkc Ȭ<]4 裏:1@Os%>~85]D!ܐ524!JgAԖwUJ7@ @ p#`0?3nO.%sb:dcde=+`r Qqs/!f8cu09{M6}{cO ׷{ |Xe| NB sG'_xw!0ٙ14GZݱKf3Wn,|Ύ~86َ ~~vcܗؙm=ELqj8ar\#}7>{™:GR4 J#x07&G7wc$yx|˰8(? Q%>tcNlk-36q Qb:G6,-$Y|mjQKv禜@su-Yw$D;s:+/ݖg<&*$!K}=E޾(Ryi,U^nRr-x>(!h^i?Rͱo}疲wHc:x"n޽-8'əEr?m} BY-%G?qfONurYJ`m>+vVM^l}SӖ^&|̂3 2Q4*-{Selo_F zbm Ϫ_9g *2[*ՙ?a8ف9|5l-^g݂^`Z"'O^x}M-(NʫQ뷄{ւӐkʜ+c!J#Űz{CR62QN<ɛGA}QeUX5™4Z8: bTLƌ6oWȑ#SX+~~6ty@5q$~駟%eb5C评<|Eyi_(2fFJyR>"@ =Iy o52 򗓯)Ecde"Vmӫؘ`&{ (N-Qǵ >抽Wi)w{_O/|^>[q'z_8>63h B9q /\NJ|EN(r7{Pf8YWorα#`N١O BdaT{x-~n{c`DZ%9}"hvs]l=ɹyx.+/lG]V2j*qV$}~O$]S)6puz 秼7_@ڒh kǁiQ׎̸>2@ 6f& 6KucR;k"d40Ͱ)>x,?3.`PP o|#Xmq@ -*u"kb NSM}] SF1y&*ϕyMM$3`˗@s)·0lyRqeѾUH\(rϤdYxоdJ| sH K/m AtL#vo/yeW,YZy2UyUH ov\Ο1CSo}:1ğE&4=*e(]v`ׇ4/9d,$7?hS/'1ZM1wl.`qz!?ãR֮^hp߾>y%m^^);uwt8̂P 7vpom=e8]x~ir0ꌌ4RmL GON4^+Ś<}9[o6k3EJqǯʁrVב/#h"3i|s!8+E&{9x>|XϯrqrO,<͹@IDATT~GD@ @ XMAMH4q̫P+sK/٢d~b1?K1H^X9wyr--]=ݝvL6OFך& r>M&=[&:UQ̆^%t>LwgMvW~k 1 El5Qܭ??~-_GEJWI(b @ wjQ[:P\;bܹWܹ{OSC91잫JntcBcaoſrh_4=pѣl+`c{뭷"O"@yV܊и] ғ⬽ \(%<eY,{k{(_oBZ-D"/v9*+G>XyzvmJb#GIRX lje !\7;Q%$@F-Nihx2_7FnMD H3r{l+oYHbjKOXHxgbSj q~ZKRtĖ ;u% o7\548-xߣco({.}f#E]a7 z9A@,Aj"ҙ=gM x4|<`8hF 7d/^[uʍMD!FKnq )Q-M7=K{uMk~A(ToӫٹHgLr&4p^sVOjh놁ۮѮ?< 3âZ-fkV ݱc= ڭ;Ek{gl=њBw"Rt4"U479;DgX'a`*Y,b80NqWHC%Gu [ \2<b6O@*~W>hV%v~$ٵTG+,> KܹHjYuª&S޸4uaQS{ D HD "`=`Ucd!!YBȱJy衇,,`b dU(s I}C}l_`.7s|_}z` [n%#sl܆=7sĤm1y˵&)y ?WxvJ;V)?* ll6ʽ5S,YYU8)[ w*$up}TE1Xb3{&Q:TJvlc` 3oCtu#yCiEsX)BEy33ujch-U5ݭXN =J^#.NR!D㹗i&("6/7o脉 mӎچ{쬍\ZQ0Xnjc{7 8s]iͪHfHlx8sp6Marx:$uѾ` J׵j]6uرC1 HU* 8$P$mINˤzP08hMPL"TC{f:HD H>X9XNɋJ[9X,QV %^9+6΅7jpM.GN*#ηݷ6w|_;\};GN]cbyFbGOM;ǥz7㪵؈y)>Aw{:Xr[{k !'bڹVQD@ao8` oHw'Ծ/8=w۔ GO_Uqn߽̔ Ґo,yk?U~.N=ڃsvhe|"wa-k oW@+Įzg߳/񝏄^(NڥðC?5Ft)=gzS93B NoPE'>!}p`=瘧FܙXRLOn5lY6ξVa[74a>}.؍mӥ9m99=f3P5> #Cvs Љ dh͏s 7A{ 3tGo[J,r,ށǝǽ.l훼"1CՋ,zųqϣЕvEpSrU[QJI]Tt"$@"|>h(Gcʒ?V=?7ܶms؛o׏ƾ#G ?wٱc7?:gOb9|S$)‰MD;6ȩf^2]r4:IϜ ׮jagG>&Vu=@ҨBR'fF|o0By߹߶g2Ê7Z}6oe6O0 `a.-Q/Km {{!㴱lҘ  BX#60Z.8m }GCpҋpf!>h\&b:2g갗v1(:Z)@/tոjDhvMS͐qܪsZvkLc @.gmM0=s1u.%ܬenܗ2gcj~SXFw[{jMW+f8o>S_uENQv'm=A z|xtma-tϝ;yܻヒmW>|_mjC$@"$@"$Şk6l߲R=<}FoT}F6)!zxPDnK7b!rSpQÈolX/Cwg~j5v)p/8|āM%DBajOL<'=4IPiRM.vJ/"KA%i[ oihGmaXzLm[c . ö2oD,>Gi]-=޶Bi3fO#AJ|?m!<`?@/X-hrE>68-!-2UsHEq61U5%<:\(U8(Vhl:p4i?{̶gb`{5o5P2v0%⪡ƍɓHcsܸ~`3`٬wjr[]g"HD HD H} ֭!EbjzpZ\8Vǩ25ί$O4socK(3$1>TT9] 0l;udֵ bO03&-A[>J4u ; $ϖ(qq8&/ѭ\r f+\/A`=-Wmh:=@zU-7M b Q]^ǓU4s#HD HD HK@Ҿ EztڝkVR.&m[L 1AIoVi9 JH5G]Sm+f"Fga *[ gvq"mNM%Ol ,SvIFs%5_Rf6m%إKtC] }FQsfוjl<2>)T&3v;2· z]ҋκfND HD HD` p|lҮ|'[~ڝMLD ?wm,n>?4#h#FI҉@"$@"$@",1I.1)~#S;ogdا4ND H.5>.jS}pU76؈I*V(KD HD HD`. /g8.ʲrE i3W"$@"$@"$@"$s􁁁UV UuYxy"Н,6 ֮mǪіlE"$@"عoh=rO:uj޽'OQ[k]~ z81spTmۦٳg799aÆk׮^zppr$OD HD`:sÇ=je5eq522biӦ͛7[; .\B]ZWI.Nz_?S˥GRD HD`Z;|glꫯvmB*//Y;ν{W|#xeXPD HD H.S">>/i=}4&]w裏|m.̼|Hw]~.SD HD ӧ/o"oO{pll졇 RQy뭷9lٲk. o$P|&D HD <#`M}饗344$ ǎ;r䈳W\q۷ m %/I^}'@"$@"#`ӆf{5i_{{?*=.>ې$@"$@"Y#`cgyg}wD~߼馛|w}'Z Oh%k$ -,(#%,o*`u(E ЅYA i߅g3gΎo;*:OD HD`# W4 ?ϵ"\ep]n Wuɇ~>Ո"W'|ێ{/VpL'@"$@Z:i]_/KEXɔ}x"^5*QXI$;]}7O?s?;?yM7\y56y8t~}xӭ}ͧw ^#`G6Z J#۷,A] zp@7V2iX0Nbs5o)0 $@"$@"pY Da jcZqL k(9z}]p Pdtrxqӕ[ϟ=c>>\5Էawu׵;nQ@;sw:;v:3vny䯭j/Dڐז,P֯_o(׿"\X1le9\*˷Df}MhDFh.D.Ӡ_y:rK_v* IUb{m >JF=Xڗ+AUW]S BbGBŲX"$@"$=N%ʇ)*V/#s2bHڷ+q<_z瑿HO?1WokX%Wm{; rG/aY ]t{?Ǝr-DSڞЀV_VQJZDG7xcϞ=8 )؜Sd_cyZ[-&I.J_W_'{M7}߸曥cm+{?W&3{~=4Ѿ-Vl!oP ؐw9!%{ d´aJ$dD HD H.G,x,2XVcdԴCºH1 j]yNsgN?~p#jkazjCBUSWnw=pTRsc*|>  $ _Nu-,Z:LyCC!#ș-U%s"N_ >kΘ8uо?](8*LͿ[A!y-D(.4 k=bXZimQ,vOj lϣ~ǰ1 !(M6U1ȳv#F^@y7O,o>?.5wii:e6pjx\[(*@"$@"\FX *^|rp VoX"mzUz.#Rr+7_{{`m6y󾇷_}ͱo8r鱓|$d]8ީ73'ǦGo]=:vx۶m{Ǹ\koy/6?ylՄ8÷z2o0[~,Kgo_tXb2{/לwat8Ի.o_yu(yGA!oN0n\yg5סq0[vx{ \<,l?#ggnOLDO<Ŵ*/3hr%uhS[@ƶq}J6xT>&@"$@"%Ydze v1E 9;h߅K@Ҿ]9=yqp߅[֍_u6\?8vthrPOC L_7tس<87y֍rrU{ >sTj$R9YDWh'6('ȉiSo5 u:֑_-V HDբ(O!^ac5<6a93c ∺bM Ho4P2fS{Ng 2JД` .//YҺifGs D uV 1:UY1#sٌMW㎘~i1C1{|]wQz)rXf\fJ0O 8V<jo"[JD HD`%q{W-5A^M;ݐJX*íp[FQtޜB VkkXH9}#;݊]5uG{|ޡuw#kV lٰ~'M={_Zѳ={W޲mb̡=zF4Twpw~g&7bט}GTǬ=FR*SL+o~SfBP߉ĩb5xוP|<#,5Px3t(VP-靈-[WIjᮓ/^I{j"ګ "FOJ[atpDr\ߺ曺OϦ|4JL:L8?6H+Cfcֿ=uQR~DƉ_=iE[ͬq#dFFV{Bns V 2d$P?'h|o|C_7/EVbܕ`$WYڬW`X@0xȨ\(RCF1eh&!җdIIEQ^dW Tr )׿dyK;ۧū>riM 9`h>nA_8 = y 9n=h662NP(); FLZf*?gɯaE&@dƾl!$@"$Y5YMaYM"EŊxM +p[{Tͫ.#h˨AK*vkúF g9ׯ8~gRG?sŝ^;sDу{L^3B!Eq `Wōm(wfAuhĊ@?w'+Iꫯ?,Z?i#@:Zc05xq(bcL7I,F]&9\eՄZy"%Q9Zi i稛\=*|*@XފV #+A%BȂ]^.Yiz'tD).s=Ri5ͦb2чzT҉)TBO-Е?[]zqrGVF%6#Vh7(k/٪=4q?6T)@a@y>Gv(RDI7 ; |+*\SL4yI~z9Ꙍf(oՏѳSZ̔a9}mzU^ܞuITg JFRSi~9Pp-db:t0PRD2+၄mFe2 \GPNaLΆyR.K6<50GKEi+٢D HD Hcg *Ŋbى}X6eD?``D¡w-dZZH0~Z(MwvY\_JJ ^y{rwd\{ کv|z0ٳnUl)R+/~4yP=2 jwB?^H6cQ廊 zP<()^AhK8sD%1fH L18bۢoL:D'Q\e^1Eg}Ԣ he""S4UPi [$L|Z榌^S!}8<`qgEuhҭ(mW)^BDrLͮ9D kn_Nƭy3T|Є4A7NӋIHɧ~qJCzj.@݊"VWsoWQ2ّM,!'[bQnocW ܆AahHxf$XU)QR;kR4~_J"C󃸵Pa|YtP^oj~۟.'a֏L؉& ID??M !]%KF}< 7 cBQPR<Kh=?30Zx]Oheg?{' 8At1DguZW[aiw/~!&#pkOVgsD HD X,VSVPVSoZJq- | _GM8t,e.'i:h+zLCϏ<3j#ۯimWn&Ntݼgၞb׈*"1 y{FޏF4,}#AM6 oKh)^.q"dxR &$׊l"f S ?[8 ^Il3 <Ґ󬀿iJnj:z1hIߝӊBҧX׸J )0SWm%<c}DoR|uP@U ^Vy FEY;KjwGw ƾT87BE윳 41˳ki#m?R1pQPW9 %gb0j i]vms@dVljF@1߽KRs7(iܪX ^`Fe#&X5 }͵Κ(!N94UaJޔSC¢tP'0.ALWVƣ 0c=ڔ7/`*i6[Vf< u |ȱ#ߪZⲆ1OD HDBCdn XPYw瓰pgmZ.4+}ӿX=9<31ޝW\{n{ߞ^{o ^莍YmχɊ$fv*9EDpt$|+6G0?)r^D#Iy_#0F,߉W2Y̦VעF`i)sM]gDh*T|ʛ5tDz]Hp&aBCaLT7^ފV:K$*\ q)+X|<}V%¤"AhZ'K6WXQ-uz2i7B0gnÊׯ3~1ƚ!P)̒@CqtzSa?k=KƗ2V xgqaM`0K:cVdVC V49u~TiEK9 neFεCqvk!|(9{Ag룠suim/~y~! نۄ2{ j~`*Ƭ6zh#ݘ[i|ErQ~%}}U#@$m)65FqvSҽ.lXĵ.u$-@"$@"TXZ)Y;}[`]Z5x=(9E+zj,QB UX#Vvغޞ^}#[&z^8|7zz/]sa{ֶZ'q*Aa6E1䣢kqX xA!)ʢysLSϖGY|1'S L>p*&A#" L=U9f>լP[Gaj8w%|pëԃ)OyK#H^|--Gu&#$h&W.qfKIQRs⮣;t U w:9ފֺggKXN=RՊq@<f1ԯ춫VPOYhA7]L5dЃbe,qV4TK]tg )+ì PE9M5CcHQsGK.;֞Cr̉I%[Z9}upZmFTƨ4%lrP'%zH u0TGt_%A"Tc8T?̈́zksfSNYzL'@"$@"Rbl>kcep}رuBQ(YYv.V={8Ѕ/Z3rqvӕ۾߾cϙ{f2wVu|Qn$ҨգqtAF5!9h8D-&E)Y+n9R&8L4":z);˘L:, :;$h6ZDvT~2ܧR!eK_<6Eu!w9 [хr3EVVCDU ,lM &B&y)0i19t'#ÜvzX)z[Z몟- ս0&կXKY+D"m5*,_ ۄ#(t~HPoEg g=FU_mq 4sAf0=*Df\չQo2֞Cc1)ud:,oB`v2zb"x<&o?zG)xMcch1N?S_e=wAD HD Hĺ*ԊW[?;\tj}5o^k \52vWza鞁m#N=yciG'#)9jblI*Y%+YȬD,. : k8t5o%JtY\R]3IwbȏHrLO5\(X8(ӡLIҪҳ^cί+GZ C3j#a(U~W|&jr橧gdec'[L'@"$@"tF`o۹p۳ Vlf^^$;waGM0<2۷bЉg?jUw8? ;?ӈ[ѭJt+9ʣ(ɯT> N vMĄ*4=#3VKh,]>XXkPNW0Z#di8F YaDHU>(9vîYEcSXC@L.~ l[ 06򆭙_:5|4)zb@u ]S >K(oBKF>cDzw.^ 7"6K@IDATsOkBaTq29˼+K+_|ID HD HD`Hwn0{z]81k׬^vԿk׭Zubw'??l_N ]\kԀPMהy{5 p|~rAaq*T+0esWac].B@9K`\h"O!u~ahToY]x4!΢-!Ur\BCGER LD.@U肶@m~IO/CŞ sCOQuJlD-Yi,t5ֺ]/bӍ# Rv`2l6S8;hYi Jt.Ud<4p [0xLgF{.dcԮdw7]KV ١BY`9&1TW--[@t5;(TV6#UƗSnrϕ./fx^gݏ܃u-<]jis]ԲXmm2E]0S̨S>l|eR!Q/}O23n4 ɉ-_xD HD HD HB i߹[=4fKM'{7 7>*=0uo `G75"|''N@KFFsQj /98O*FψQ/+Q B*~+IuP+"-GWaUhQF{,G[=+u] _9gC7Qnp9rxAFu4(`M+?JQ mU+('$C戉GCl2}IڥKP`.:&;KBMBIPR;uw,' ci#YK_8U0κV%||&TFf*LuMA(*P{1=v⥗^ 6o0gTQ ^ ܌}MӹP\et  Tf hDfyس&cgy+JJ|ot:x6?/Cӯ`07H=!pC1Nc\&5̭-FycS<ܕL"$@"$@"$"嬿bӝCP#2t{l{o#ηݷ6w|_;\};A pկr>%\{N>5XδoTxG ^pY!hp@a %^G$$4Aj >z z 7 RUOJ\1T7]M+4 bN;G۹a3B)ڂQVH2]UފV~v\։z'A/%5!)ЛjjTMn@, "OzvR*V摠ZѓO>!o$Rͺz/ф|ߤ aNA56]>z2)&ǡj?E_bMP7:JG`p\(UU'p.ֺUKK|g85# \{h /,X&3ڵs }/sMX3{P}V%/OD HD HD}KYOm7;rr鳓h/}o|xg;ݺaHη&6[$Ռ(6HUNq|r1&D9ϊxEAxUI8 Ft*9@Jr4QLEBC骤 :P3K *q*=(ǿs]mKٮRgآ@̦r_8f][h]j`9Dź6Bq3[ kaf85iÖ9ٮAo*X &!VSOa:e9M~uF-7HD HD HK@)Yۚ8s߰s'0mK~>3&|~758? lܸ c .+0#L+;T"{I`p >W&g^nmUͶ'?E%/忠S_WQYrUDx4p*6O;5x7VI6*URgiPX&mؔc+bK_s!$h&~L8Tp99ɡ|?孠[Ĉ?/łyW+|tMG_P-m!F9n//8ZE,:&m,4aEZjvaEjmoXlW)ϐcR1±֡v[@m׭Zd£ [,}~˸fxtc$ LY][ZGb$j]ᬢ.L'̓gf isP$"`͇Fa( :$ͯk[gq1iX6R \WR`wTH0ֶ5*ۨ$#_ϙ-V8EzQתF؊Kf Y!0?-m0DUjZupLL%H"rT[, 0QW|--Omi;iHCtsӸQQ0;Ų廕ȿ u`+٨D HD HV.%,]Y-\R1_]([no|=|k5= jcg촋t9˃}'/*bHDt>@(I8f*t6=CLhrgJp2]X?[OW%?i%CER@8[,o=R+]"9ZҚ(ʷYh nJEĤCu*ĿP"ۼ(=J.||O*[u&jE秆\3R%ȑخ.j=w>[\_WuSO-u+Mjw.߭.PWxD9[Ր6HD HeW,.+.D;Yml]v?EÕ[6WQXX`C!K\QZw-N!x#4]' Ly֊背,V )KF1\•snbiiժyd}ڔZ5ՊCr:Y%?[ =P)X, C`]ڥGf5vI1YtyK,uR[w5v+z糒uF(mU6ۥL-+ ̯8t7//jsdܸ1Ҫm̖3'LD. n 哨hmT5AQ85ID Hy .07%(4{YY7s-\B]ZWIN -t25p%hLY/N4e.(L@0\-4~__}0w.gFrU|>[)v wuU!'㺕_vcn8G/2g~ʨ/^S_8}`O~s'JivhW_-Ch}2χR˰R"$@"tF s_}B˓u.T>zGrӟX}/.Ba[j ZϋC?D2Dt!R5q]숻aaRh_ZBlK(V[ uyMD HrĜ|XYQ:6eL1|p mf@ҾoͭH>c. wK&o --JEOguBI-? tHؠ_/Yti]˶.idUqVYCEB9Ae~k_B{7 #o ~'o5~ֶoZt|MQU.P${ʷu_,VK[4yk9l{{sl d\hk}A哎rJ.n]UD HD-6v$g}w֭R}lR=DHDMbJN8bE`))%D٢uݻ~i5Gb``^U0RզAzN8\SoBk<Z_{դ!~ӟj̵⅃N@Kŀ+r(Ëw*ky+;rh8V1]ep6@~*:j҂4 l/nHD Hrbc鵈]8XY>ţnw|E*` a䨪XJT&VI̶$@"$@"p*p 'jUu"XT![. S Hū"IHQAe]"|Q\ &frpUSԋuH+SV;nIJU\BgQ.Ėb{]"#LIIC%E7)4qdZ*/QEm by(Z ZtvSf.AfT[4ݪD"$@"t;%{7bɂʺŚ*rC X{\յp ])/\z*LD HDGPS.q!}Ж g8viHBk\E MB|1>%'VfW4ty+B.[l `ae]^{KHbű \wE;IkJb"ZU&6hVB:ɉ= 7*d44BCYi3Јs0BEOZ⍄CRhѯShV,!gv@ +#핃uQw9b׎ ﵡBtA:ʔоx^a_;ŪkH&t8iAJȁiUݶQ+8x0wH@@/#ZAO ]V#õQftD"$@"e ;ܽUHYYL,WL۲!@"$@"$r3":l& "d08؍. ()Q]QG4%Wk%ķڗ#D\;q$WORMOzDGu$^~4vUy>B6(~rܦ xpX V9@ Xtk+*3D HDx%jǓZ+KeR<#aQd* PJByvirv̑rhXNN '\}͵K@֒$@"$%C@jD̬R^ 爂jH;$Ko ƅI|97KnS|k W{ʨl]ip;nM*U{uҰʩi XUfb]QpwuitEF }wW6o 24|ynU3$@"$@ ar)DGۺUMZJT&VI.7qO?{7O9;vvͷuwܹPy}"$@"$ >.6XHASIgu51MQfxEM̖]b0Vkxʷ҄WC`Pu9T'DRHR !}b '&1""Cámtq6ѝw[okj;8_|򗿴__~–԰oJ[OL'@"$@X_[bS[EXԷPz_e2bHw>]y#&'֭Yu>xF7vwV5ky|YqqNxe* mŵ2G`oUq. _KV9[4'&ue݀paXJU;oHsS~)dD>rB6aQ}++}_-h˜ʒ&(WNLRR#lc;?yſ*_5\U*2}7O?s?;?yM7\y56y8t~}xӭ}ͧw OL LJ\p*Ҋke6(hRRoߪ\#$E^M. YV6ܢL%!FكιU w ïU<]emV~[!E&d|bػ߀(D?s/nSppiP\~OEz 7X*B(>0 I`޹Kt Bս O?޽{M.ڕTCy1tV ]@R:LK-fc0ЭRD HDwdK /*k{S+aEᑶՅ7|e儗{^~eNKYܥQI3̥G iRϊ=|PMWn?{pp$OpP߆-Wy]E{?|̙{y7^gعգk>瑿?B<7ŴO K.a9~ ef$º0dU͍|L7`>:h7lv¼q+j׊/ (|Q"΢]%H\ֺgNkP0`)>˳iVp˷>vqk2G2B6.D5e~*\asm%/;:( +M@+V-+d$)61#rx4o=X8<Xݪ5R`anar@ZȴۃXYx\WVyhxҾ}'#50пvͪczvUÛ7۶uO>m_ֿشysoÇ~/~3? 7}#F1&)oS&5[c,KXG_1~Rn^^=zu7LOM;zwzhp`p`WW N]nl#ܽёU>vwKӧύ[%Ķ.H*~Dz6JCi1=yXcpVX|09ϣdN88Mag԰(}oϩpW/ |cbЀ>pAa^@D TNVֵE.q9i!% =j DžN}a0S =pe|'Ek9QQ]`uQޚCb.6CC9dj堃ih ۣj#ȉU'gP@ULGzǺ_ GbED HD X85ʪC~,6,r=F&m\µM IM8oml{}oc:qc'OtGȺpSoUgNÏMj[/ ztz';]磄W/Epf\l Ry9in4_?gj޹sgPw+?˗ t s^#D_sû.QY:ʋoB1<裠GyXX`F"\yg5ס>FhŌe.,sFO$&. ,,CDPV,_yA#g+C%5_*Z3u kG͇7uw?`q90qaIϠ$FxApRWFnUHJIN7~ߵZIrV !jqgg ΚƮ}*"\-)uQ]*OU2 :HH5A^[@`bU-E `2D HD X8Έ]q5%e۷k $7zb 3!mJ,Hڷ'/N}˺N?7~æǎMp#b"sƆnh0r("-Bɔ0({,>AdHz"ui:AZ]r k)OS4 U]\Li18 #-IRCm{sž7fVJ?Y TKX[oYg7 q=zu륀&,}Dfy+,>gC,L,8cHDfg1Zͷ'YD?|%B3W6""h]bL){.UޮfϡCvI;g%[Z׳> ud@YZ-~Wi6ebf3n 61}mȡc0Qi$zi6)\fJ7䱍؄6uAw̦;24; mr7DL щQLŚip[v[aFT,*"M'2x6eb>H2nXH+sLW$ /PzND H `+EM9V\卛EB fΊD͚xEs!]=ڭUSw>Zw׮/o\?f ׏8qj[Ow{6Ᵽg{s<3HL >zQxK|¬M*ˁC 7N Q!"֊)]Wh"Xoi6ۍ΅~{:zgYM(Aiߢ*JNg56ZKU߯T w/zqjUɕ^&dh0r l> tS%Zܮ8pko>|?q\\ӷ%}0e `?EE?ٻfj_s]Vf vЂ sKW8_yy*eճ w1`HB.r~+M9u#YiR3c{{}}ˋ8ӵ @E ߏ+yA/WamYΣ!'vN+zrrfY9Jw'&dŭ@ 1G gOv+K/dy睘,qyW!_pU65.>n0X"|VQCYFF9e<[ jXܹW5(P=F/&R}zKjA:o~|R8k.~7n}0%,E DmGIT!v: U}=^T5z+F VJ슟T%3&at℞8*&l]WAөXc=1vy1c " ARz?Kq}fhe:Q8*>*mcE\YfR|!j+ }gvk~i&ߤ[DPC=5J0>@h~Ao]*HAJ(_p*21S,fCĜ|G՞;{޹-U}p%=6Snӫ-l,JQY+vC,G Wjg(ÞY:j{K,>Y[4(%rq!)H∽}Nte UY_t>zW^4W2uӄVADIоX3l-~[)R+$/AM_~YiŐnͯF]xXd89fa2^.->|s@/ MHr_AJtKz:_vLr_^Cz2Zq6;/E+9?eL4 zf/)H|KDͦϵ\Aق SÉ|OjC'5˽Ki?u‹LI|C=ݫ 0 [?dȯz'aGe@+?dzNY3aX@P&r4f^-' ֥%h7fy^GyCZxrj1 +@'&5(ns&z_4D HD HD HV}a}rph:|``orv|ľc{>~/|/8d`vzpEa0>}PV3Ϡr0hU&o-%V߾KL I(7|3BOĕDZ! dx/*74 ,ccSr1:z1hcI i]ZЋBq}5ZIsuz`lΗMϵ 奘Gp`TQX 54T~~w@м8/b\Ua.O<]8ׄV)#ǑdFB~ؚFq=4ۮot jV!|5;RZp7)Zܪx ^{}r%xi?-SdKFiM'2$<Ey|bێ%@"$@"$@"I.uӧ/xr'te^qݖ{_|?fzݺ˶7wll·?Qu'1 ({嵘`_W8*qE#煶HR\Z~QY* %EpXcIwZAtƫRCb>"A Nibk-TB|y\r{PtV3Ss85~}`BCB1 T_6^oEzɔ8 @% #. BtK?WCq+IbUAaZѓ'`)5^ԴW׬>G뀅p?@p+QϘVي3Sjul%ܒ@ST2Loz/uЦxgy}#Lښ4K1kP!V)}ޭC:\K/,/JJgM52~wMpξQtߜ5-m ?Q{ Th@@+SC nF쁨s]3ru_㡑ʡO@"$@"$@"#U\hb0aSۏO=qz7q6S39}dM߶qek|) mD}pQѵ8,䒟ʢ0HO(9Ze髫b19 ?r*&A#" L=utu}1j ]RtTחuDpp`4ԃᨖJ+hz=>w^v9 Dd %cj:}%0i%?7;C ͂^j^wF!8qDUC&xNٝkz5pR($L5e|U0Ͷb e.~FO | <Ɛ1 cUQ%#mvc\Qx8n3NʌݽC@:ǧ1潠[,YoisAlupzHʘU-:D)N^jODR^/?Ci'@"$@"$@"4ICxrD^wzz['wbw?7\y.{W߃Cƾ2wlh 4Rb) ~ p*tTr鴬$JpZL࿊7.T =fZ%̺NDtv8&㦥 +lt5TGlq<AөOԈfJ_zG%3;p]Net^r,}ř{v7'1&NhӓzX'z?ޤ׋潹m%'* ε! a7x@˟Cv] U*I}=w£pt04@k7pSPhbUa-#\:Ueu !ڞ}Y;|pCET9Y9鮩E'0111 ůA]e"oZ.P3|flZ.ڟabh1!ڲg .e_An~MĬwk75[-HaFƝ|u-hF<*A:Wwz5gD HD HD H}sbrz0#ޱrn[loۖ6?>v'֙Nĩwl[G;9Ov;°RSI{W W(YζHrPERSȶ8?6 Mr`jl96J@$~O cv@s-K*Bz+ф(1FyJŵuᄞa L$~KIQVV3J‡ x+tUQv}qO>DT!HBz}9*'̍=|_ؑ'#Y\5zn ZT/N`HyAQn)j:s媢&ɞJǴ5 tU nFJF8Av"yS[BX !{Qy)j#Xu]@[⽠ÜK,\k^z]꘡-|X8k {^e~&\b>u=ȣ+[as*U?&@"$@"$F@΂Ԓ`r^DIwaܦf7m/&Nkhf6nw>7xϼpM3#*]~[ݡzxK"Kn׮]rN`G3 ~|fh^e?"E`萳KH@xѡrM쓕"Rr3X*%S@,Ϣ_ozsZ5/[vUMu=h^+67Fg(fW䣴ER>pҤ 5uƁPiid,`n89b! 65{s1BHed𡯌-&8LvzEiH׶Fgȯϵ9j941"zꮩF}?BU98 +9eD~תb\:oS^!4GL8*4Jƕ,¤# q ;lkKc=X|\J\ڡg_j;ntU~8Cs;35`rSǴԪw/ӄJP!_Z}%U5?ϛ;ytϳ[19f8 Wo3n>tNMhhbe@,|)d1k`+.vK/Eʊ A&I[ p(cpdqx^h"e踦Nロ.jJ~ vՔ a*2K]]GM+i!ŢQ]f~F(-DVUm+_|; bAYnW 뇤@3a4Kv%0)&<.0"93.Jari't|\=#Ҙ&ٞS To}hyկҊn0à=_U@{(qW/~r&C%J}L+U]Y52{EzҖ ӹ?}-59r {q3Ԋ+sD;͂`h1k 7܀ٳ4V`w=:Wj/0![ Dgۿ u/;*%UyTy1~|k1@q0HD HD`"{ &!*rԖ_ڨ$ V}kA]qeRɹNNўr݃^87Ho~ʞx[DT&L>>%ҲdNS+l|cB/[TCAi%fh90NȡRR+ݥj:Ԏ|_1'KMj3x(;BڶKBsM86ј_SBMXX ]+$ +"sjRXi#qPN2s"U'ćpAK[τq; :q rKƒHmZ^LƍQ݁[vN"B=}Z);pHˁ9TS +ڸYuDGVp#yL̥^9̑ CvE ʐ&k*X%5@nQ@A^>۬\0M ?'9tD&uWBALUm0a *WA`hkjq=v'=Z]ݸj[Jx 7$E ;i4UP-s"-SY[, )o&FkSHD+ Xȧ#dx/,rԕrfq51C4ʺt-xBjwj ,e>tm{̫@"$@"=,W#B>o ":{,] Mvؔ)@a%z =;';OLe<'o0=sU.qBTHBdP>?TH%N2gt|[ߒ~(ԨٗW94IY$B%Hx9j|PUrU!%}aFI7FM #+V@Mu$8TݐBsh@$&C};߉<&J$t׊q8$sHL_A + c+\0!ommc|Qޛ;馛"Ytjq׾5~nxkEsm14.ǍC#kg}_ !LyHȄm YW:q8A+_Aڮ)6HD H[+Nqi\~{klp];7_cIYw-sG;~mqҹCs4K|0 3A׋g&E>&Q1KmF+iMcĵh0 7G5 $J_ qH *:3-JB`$rHI#$ $4JG覄- 1jJ5V L`sQ kIQ/EQH |FÙQ :8Ac!sDJˋɼbI=J}W_Zh*=ڶT5o4el;ge0\H GS6&g ; ''Ⱄĭ?|dZť*4bfcc6%|$2{ל5{aJתYZ㟔1p60#g(1^q?eͻX1@KB]MpOlKv1>I*– (urTSGsB^)/C<7qgtG64q'%쳫1|ݖLZ x C (18mRydD HD 8o 0"2툯dd!1L\K|eg NzquBb8#G'bG618J+SDSRaj#% Ir(K G %:Zՠm SoxclR^R;U^#)R?@ZM(K%cKɬ*U)/b5C㚄x:7eܠyq޼uk\ܪOBL>YM5\%NzZt"/&`r ='W|}hV"=h`jw+[9w]QqE<Ћk/K{LKC>}'@K櫚]n-!_U"K@7/M#) ;|=}5z!'x)^uU4_ 飏> FjX>0!$@"$@ xh'22AjoB/!K@xE~*QZ0v '?鎅HTƸ8M$vXN( SODt-"T`-"=tG:95(Ew"s%?uIi 6yt % |t[jρΊ3)okV5(EN Jǟy6iߥik6m7\;|+;CxƎkS~k m aȯ\~5Pa}͡Fi77jﱾ . &`4ERVm+%hP3jz΂tq%x}\yV$\y .zU6ı\Gf=zKʕ|+sܳ4=`Hf:넿 %q}%bR8.tI}YW\n{!$G.KR41(pDhuPzwwfE~/URgmS~'@"$@"PhGOD,܎(E4 \h$zA7&y--bX}]|Ex^ hMŤ~;\m. ڗqA_SRE 2i= zb V_:NE,֠chH&m"d5''K(bV㔹 I+VOnaB{iݸW,.!qj[*v{I(n ^6^ovg{`]CxD3B {@FC`#<"d׃§I] T߽MzD HD`!;uM<'ɧC$ F\+".jb!l}gaDJFǣ`F[MlPо A^Ԡ$\#~a/(dH1PPbNK^*$!b J1b&J ;sCפ}W^ifqP>DZ̫H$WL@gaNҐ­*TP>X2 iQ+]ԗ :Jf7,r΂'Wl]+E~{ND[{nl7Ytϭ_7_w=A;?'I>1¶ft`ZC"PGᇻ:% %~zW(%IYie"&@"$@"p!tĞhGF&4BVHEh KU2;EDYSM,$ӯ8P IJ%!MB5LhW™;OG`KkЕwQAD4A ~Em !b Di&xg"XbOLے Ոh F 6o&=Tһز JƩa)r*SHke2|o)0g'D~P}\'"AEԵZvjӲZ?3:ii"_ֶk;DM}d .^VI6GDb!J'n>Ul%Y%yaa(ozlFHUG]ɀbhҖJJFyy܈䈞5K9ccJtIh\/Wx^^vI@" GQXT-ㄞmm;$&@"$@"F@IvM%ϧ(\P~l f5ҚtBT2E>[_*@Bz.L7R=wSvW+cPRPT E>1/ C*i.}J%<+bLF:tA.p4 Tu(,DRkXB:P"n4)EEfU;OwՠΎD HD XSeWk?!ҵY/X"= z5?vX5)ݠ dk$ZC!CzW O{^tӦ|8t- iּ@"$@">}1UW]sV#.}!REOc+Voэ?_b0"NyaT14{#S.ѣ/^mDѣ 0P*`oQ*˦ɤn2uPd˓]iS~"$@"$k`Z4F_wuBdDDgx^q0rfѳ \@Bd\Q@WB N(z/cxNU:>܉*Bo;x(zDkd "EO[Eʆ@"$@"p"7DyM^+®z_)@zN|Dqȉ̈́P_)ы`;ѾB: Ƃv0NNā. }3u+,9)D+OW᫝H ,"3L\F}A|-Z !j.%\HD HD H_D6 ;_V0a8Xk%UM&#5$pC̭^-QbᮚNk:>`^"/Zʘ]l$@"$'}Ԥ7\v^OczŖ71JǼWK A9]J1GDz>vPa;!_rs O9ŰAKGR(h$oŞPNğvN %4Յ&>Mv4SFt+KBST2\].$SN"$@"$/~ s=4#"i슕Ha`Wp/|؂aʚ8Da1~;,VۄrPHh%v]ً!'&f Z-Ћu/T2K(l[WdKmD HDB*v@JK m6YnG>+aNɺH TSF]qrvҼ^y?N#[0&FOZw[Ehrw1'寽ZyO #.z 4uz!)Eje 0X,%A![!i3%@"$@"p>"?@K6nfbkeb_xO,VY,Jj['> O>)BxC=ܣm݆Uý $I׽5U] ?K-?НB鄰۹(GAUiIM1#{DBӼ$@"$k HBN]"IEE& uоhJh_i`#믿^ }Ezxx$Z Q51O7]&xCOJ5u׿81wL.rq&Um)U%co Bև~)/e(ziK9|/o9\-UtOD HDD@##+&{a+ FeY WWElm Ռ-k"Dϫ\"BDE^$.o 6t.8wɶ/^(KbwCHlbhҮIB[[J%@"$ً>QY"wl, XQF֎pBjG>NP'6s*v _8M0&*bj0jA9(= l"F8TRBψi扽S\ W!i( 7 m;X )mw ;)Y =Wv{?t?^}߽w9|c?OӟIpD HD 07 "9DBs՚[92Q`?:R %>XyI߯h ]n}@7Ƀ0{א)WǎOdѩ*J+>VD⁓C$^sw)/n Gj.+|إ2I&{N/T\9z5!U権> .i/5T׷QCM`~th[/K$[D[S8HmEͱS-D HD Hj#?+}9zis(!FEYLAJ'PEE%*O?ݪ#D[%^VZzWĄd:QBAA7FH6FDt>aKzT֗7EibN|K.b2d+q}{G[Nߺyþ_wԌ-H𡃻~:=i|K͚kÿG9ƹ.LVU]<湵 D>\gɂ-I}_Y>9P2G6=X*wן~GvzAmh+ukͫ~P. "(,8Q.BPY:Cj~&qOO~t|$ǖ$\sVAhVWZhJVw;q;r=rVE5 KKC P?^Hb?FQQkd($8]-Tchjf1=Ďr:th8*HD Hg꣝WڸȧqjMcRBAGG,BP*w)՜DѼgiVp{rB s{ 8a ~u9&u5A߬>GD fI9qXIG;V!vH$@"$G; Ray@2EY޻{ k(lߎt[N}}k ΢pm#ڲy;o vf/h^av&d]zw]|%{~zw^|/@3n_זx£>2!Yz^o/VΏVoWjy.|ƎrpI~OO,K<`@Ǫ_ o/|⯦k^<ճ_b>k[}en ~ŵa~Bd?ߚp1Y7'?c=Q_fw"JbK ID HD HQ ZT!?G 9Nw:~CNff^mܸzw=517nz_Y N]Lq7?z~j{pk V-Wwpd>%q~֚.ï110y;9E6Y7-p/cd2{Q:h߶/p WIA!ymi4'8#bZ,i[ӮiAuFy/BL6䙄UIdk<8bd'aLXZmUD?/nU"$@"$@"tlj}-K}F 7Om /K&'>s-;;O8Nû7:[ɩWo4e;j=H֞K͛$JC%׏~#/'?i5\ø~ +oqoǺa!BJka9á9 ?䓿կ^}U4 cf5y֡q_{5NCWW^y~^Ug'o<1FO=# {o&'p> _}Aki@ӖD HD HD 8HڷbN>۷nڶ=u.x˦u;sr[S}[gLĞ;99{ /ۺ~a?^vt୰}BlAVKp[Ai*cR(o%cl{SUI 'VK! +j :QQ^t C]J;)E}$2zXKFŤ6p16tbHb bOɕ)O7/5/~a&2/'ra$@f)W̝K4)m2"C>g=ӓ-AdPd,qi^4bf9V@|3

    ؾ4ǽ?YoiS϶ZQ{ |]wܬ x9c6"s|ӟ?9mȡw#g?YJBc=Fό Jy$@"$@"$ 8oڸ+/n87߽117|m7o\?m6~;so~W.x~wgjC^~|[ l9SwpvzǑ&.&ūozF_cxi U"~=M?+:G}ԧKoBhPm꫋6Z\ߺ@UQ`u}*36,k׵] "]'K_R_d/ D@m OJ[a pFreX7סr%J`7+(// -񹼘1]l ~㈌xq+ٰ6,  kcFa!CZㄗ„L 9 /饗;E61j0 {U65.>n02z/ T*A >ԛed8SYoʳzΝXtT5kf}Q#z׿fl5F Ck7M>7mݸX8 =Ly %n=l6P()n&&m)3FngW/ΐ'@"$@"$@"u4rݺp릹 GNwhN̽{ԱmO758iљ{_=tk{{^~zlXe\TVSq{$QTB$85ˆA>쓒nTJeUI /?,Z_Id8`j`P0Ī%my3~z4tTY|2KX daڎ1MB$Y곎!H~cm +A%v!d.`頧#}6i [lŕ`t;XcO8mq>FQ 2*Ht Ve4VG+бJ|ƹVW9h+*Z% (1 fT*B|&DBnj_zgI7s#Q2oPa̵ͪDA uIz9g(ib㳌c,;K:1g-W]5iXni jfJXJ4@̏)rQO&xk<ƖJ_:{ aYFĮ;KVioj@"$@"$@"I*tyNw~ve\u.ZV{jtlc;xtw'NnzJTÕŌ9eЄ+4A7ed)q1(奡 ==$'C g^ŸC~3ZQAؠ獜n2޺˗Dl[\E$M05xg$X86tR;ok~OS'Ȕyg8c>߽ `Cw"ɸqZN 4c;M!ޮ5>5 @zab.xH^P? jjVZJ"xm]Zv\nFG+G8A -(S9pܼ‘tb2Y6azIS [.ЎOwl嵃%lNNVӾ""=[ޮ:GʊyTM*?m5dwi3x$)oFI1H\ 9L!U'KgK"b/jRՅ|`knF4<>C]Zf]QdER6ɘDO^B[{8qqİR #h,!JKD? 6A.#l@IDATVC5Eq^!uC$ˋI! 'n17(ݥ(ח1`] )0WשJpx|Ѥ|\P@а ЉI^^y FuxP;~JC5@}GI $) )8ͻn !"vUqܵ sMhe2ra8@f.ᇭi(9kcH3jFw@fU2W ,^ЭJXʮ񜡵έWׇk-G\w+j82EI`VaIt+C£ P'k- #@ řQ V@"$@"]X-}CK8GK$D| 6=>8>Ɖk6]pمW\e|8O^m{aUIac!_uK·bs*B%hDI*M(H%QPrP$Y g5jȩƩeqAīR#VD r@IrSgB[T( ]nʛjfj"3>RԯVebފ%))p@JǢ qiX|]qrJ#5\!L>Ư(ߨ +zl#>Ƌ uhpgnx%ӊ7[?xfU͵MIJ[hj ,Jɠ7cCϚwzAo>¤IC 5 be(:8õ¢ǕP( ܛkdF}9$[ 5q/ֿ9 k[#p$dV$& QW1g焻:?Bg~C7(~$_Q''t-,Y&& tL6:~[U ~zIʉ@"$@" d"UJ0}2mzuN9ijj51wrjɩ'8={zΛ޸yةW>ypo82OUY>ETyj$&V+Uadž TՅ!))LFU|܉L9pK BDC׺PA:]t:¾h5΅.`v):QK~p:J"8Gb8}0~@A Op\ i4|q;O[GhEV%$'JXLx%0i%?7;C ͂^ķWs4:#:u4єei^\+͇N@i,T הU6N)+痹GW7= 3i*0C Jv*UCGHYحCcHYopG98+3wןCrJĤCnfE!:5"y*Z%lqЩ'%zH upTG _|$z1J5pbnuO.lNWo,9 HieQ5:ӋA(3\K`#qo.k&@"$@"pV! Ab10^D>"!P.uK0Rl$o&&ggם։k:5y;u-W^w~?g냱)"r'Xly#P!BrȋN g8Y2 }tcyoF¬kKd_١/IdOj-k}#8"^hө$~J_ybԦ'J /^wb悽'789}S(9)ݙrKzMVU5,2GYDh8trR $WYk ٧tWnli7t0:jܼ(Gi_}2RmI5dut=1ӎ3fy.I, % dr\d Vr%0 >v7'~Ц'ܢ =oE綍pQrڎu}ڐՌ՛k/>t5Ⳉ*'2K5'5&&f=&i.9BY]sqUl$]S~{$`ZP<F (dUZLR\Ԫ9)Xtكh\Y$@"$Y+?^[U!B[p#XbE i߅;jIMر_ ڿnز}E9~_}}['_ӳ-,26U;_)F$u/y||t$RZ#Y,d``5!'19X0&#+D P!gq_Gl,%GCHj=>4rH6RE9KmiHgbh_Ύ` [{j=>5%RV3_Z5p[nP&&DpT-Q{M&`FjepZ[CF2[ct•? A=<&j~eլ*UMe ~ |W.+yv]wi3Ӭdr@u CS Ж?kШt)gr#&bֻsyᵛYjq#N>}4#N kmk?W@t3ݡ{6[غUMve=u~ 0Ǽ/eAn~W( w8zr-nLws3HD H7ԏ?$F_WEPd`9|A$y >2Y 5ooҾ `f뉣Gcܖn߶-[7l|b#Gx[OLӉM3܉Su[/zئ1xwr<$KfdI$<Ǯ~$W(VNՑ;cZFY5jnsXnf96J@XVH0{T؆`B}ɖ% qmɝEMrXѤFyYJ&5pᄞa L$EU4{K/|4 ݀z`OcH5}c1jg YNL޷x]sTOz!}_Z?:\5zn ZT/N\xAQn)j:s媢&@Sia71\2ȮXdw$m #c5efG3`=&1vms:.roz5;vcWp cሮ,26\ {BwI|1~Q٭9*wn]0 2T9XhYƃ]{UY7Y/U6" jjpxJG1#-k@Cu׮]zW[QdcjCe2\#%iea +e%Z6HD H 0AWzEOc-1; \l°fpt Pd_])>6x6qO]{Em~x}nKyKf3ӃɉGT|G飓C3HEX9G&# Aؑ$?ꥂI|KhIݯ CJl !]bYA4YVA9̍4ɒ [wHR55ԋPj_B咚rzZ'D_^FgXd}#jR|Rʻ!UiRcn@l4YFu%à`KGrkQf,nJtLJuL[[Ic2dWFC#I)`Wg_}m;ntj,m\㙪C#$G(Ir~qNUAN䵪뮻.F\i=+o8kz e@?(oVI+fAЅ9:8#9NȄQ薳Ib1U;~^oi g{^q3+2bE=>|zeR#Q/渏Vuyuƍ#1%zKJBdMFH5J#X/Uv~hP>51g4d-h!4e$#BH8?`dK_VsW!B ]%Hwng c@' D HD 腀,F"~OD&^)hK At ET%]x4'f&7άۼ}jŧdrv5ӧNLL87傽=95wwoq uMo!%d9_.g.O?4T`Td BC'206H]cX]Un#Y9eBe,nHbhMFªq:~A) 6ˈ8Gʑ5eDuT4*;HfHɎFZԈtC&iőc 7L۫?+B6ETMsDBád\97`&'eȌKP][[barBOBKPR;Rw_m{ǍNJ^s#y_8ոpΦV%~~&/JP!_buSsb+WբN/b?T'5gNCiev  TV hFf}eVsc-^Po]f{3p t,~[x vZ~Mhb#IsC1Oc^&5̭-9yp/Msb@MCom&e:IY 4U1N kzG,њӽ[[D)VEBXmЧk Nf4oXޭ!V j*[I!bU> *D HD E$C!J$RA&ʊK(d-!wLZ2lmvşI/yZsG{o#η}SdW^㐊|+D|fRVd#RJJiqmȱ4@V iV[_l" ej 蹫huGSjZ욉)sTeLg: d4V0ČJDv j<Hu ZVVqR|\6F'Cod hCR 0 5ڮ&'@@Ks =4Rض\Z (_?׺{7F<4Fx bw&h6{Y?tGmP)l0((&}W>/3fB)qW{~r&x IC}G *UϮu~=iK\X{ejEr^9"0 ,+7 ^ Ĭ7p2т.l"Z܃p_Kb}7ely.|m:@fQa1F%~YwsMX+>ÊuӴ u,}p7z]$ϥ!g0 $2IXO=9Q'(*k&@"$@"PH Od7+A+t"LrJKPDZB iќ]qeRɹNNўr݃^87Ho~ʞ$ɒ9v|1h!o, W4dn!G|(!ٚ$6P@#D5iZ%WG@r5I;_]mXѬbsY`j{R:3'УU&6c}U^i#q!(b CRM 6!g¸r<0\\Gjb2n@-&Oq*H{0r:Ƕ;tsm\Csp[ݡv|Ds8 C+9d(>ڮhvA҄z}9aHUEM p]Cc'LyToRs7qN]FY_:"+\!v Isͪ 0?k9 9ƺelo.n 5ǭs&gF% :5nCxZ,h#J``"Y#5R"ȵ>B(PDxTRLD HDb*C@"8D\9)%QS%4A iewb?-]v{r3\.ۇF:jVKd ("Er"/A 2o}[B ;ʥ:Ib#r]ɌͿɕ,H^ro*9rCiXɒ5(XqGM %+V&~SMK$8Tݐ_Bsh@$&Ytw=2&JEr׊q8_LN81޸򐯚#6ɸ6w1iNy{@o^q0R>RC׾5axkEsm14ƋJY8={_ QFF&tncȺUuЩ; ZyHN!f+|'5㨉5.ѝ;wTB2BrxZ]5!`AY2bzAowu5 75pwv!~5\|UBiXKk-#:.Ft #]2], a.CF+?zY4l!p!Iňub%@ha-6ֹ&>Q`7^Wr=JR@ҋH;y`{XJ`Z7 M5rVM-$׫5D HD 腀HCӡݼj: ()- =5cr饗yd:95w:Ȕ=;9;D^ie,HB%{+ڨ(q!\Pg4t:[sST,[49ޝ5qU{?5K* ,˖<#X2ی6h n;@DtDtߎ7\yOxg[Hf,դNOsjXTν^y?WfՏ+\Bu\՜ o})Q/{ GExJfHb&ѶY.j$R9B]ymom>KI[ 8_d-x-k{_ ya֝$f=+xajhyH2հq/&tsIV؃mJ~F< cڽ+;}pnR#ninmymM6K)]Q;Zފtلۍu3d-#R_ߦY$@"$@oxw*~)⧈oxȭā4KKD@Ҿ붯8Ҷmլ+a䖢wE;W\!Nְ00+>hqRr-AYp57mtҪydl1m+7vFM/xyRHЇnjл\rj-G!O*CĶT3"{ rcT[S[s rد|ZuT0V&8v5:mMR)w7wU7YWOD,U$%S+\ƐW#%h_ h_I Ѥ~ZsLD HD$~bQc6Q(Sxv5+!Pʃs }ϥlE:nRFDY"|0L69m/IDg}*JYIj%7C䲙d==3*7M=zS83f/@7U-Y$K[U]iS(X} Նu]̍U۫;k~ko_/v#:җMXWnW+d_Gȯw< WE^Q4'KD HD X]{g׋AW?xlæ}*Qyp.!4cЅ* fEUd0+\|9 ?9A^Z[Ũ:ZIŊgWEЩLgFl\UjSD`"{-VPwp;DX`%nn66ke"j{TG~s me {~PCHd^x ~Q%mCz}z+9h_?&*D'@"$@"o$?~`!FSǏ~&au-į&԰WQ0"߲̎$s$,AЬ =L#i+)rPZ=AgI5~XWxL/JqUY>4Rr"J1*A=Qwq#v1-)lB!FT"2*t_}"v׮]n7p= /ʲ{ckwg2{g!?O[6ruuD0%V4!z[PB,&*?|SgoMj"$@"$G טā=KH^WcW_W-rxT.|i%$h(Z8-is{n&6j3rn¶3[v"p6*eD (Cq@"vM1bZ(f֣\y(:\EXgph_8Y?B+շ\HL'aK /v| ۵Q m}YX{*N!{쒘&b3"6"Y$@"$K_P6{~ gT{6~o'6aO% GR JJ-HD HVj??뇘S9<,&UCDkV" AL\Ɏ;Q! يfEv|bGyգXvyW8n)aK?ӒUP$P{qu.:1H@=@Vue9_j$@"U/~/ld7Y?WN6yV*v;r ӮqI~E%@"$@"Fߊ?E"XZaZhS<ɼ*OO GI"WE WT|[0„K 4?YQ+q&9u]@.Q\**hId5ND HD Xnگ*!kmk/~'~<_~KXn[R A i2F"$@"$g‰B&rc a$YS`{dW,ZU+KW96XW{)X͑3_𶄨\4au'/F$"B#׋@E4BIoj]r'@"$@" b|w4/W3J#ϕ/}K~琟O~JXS D i8(R"$@"$+Mh!W$VSq"-Un(%ًOHߨYo[7^`c {l2HbQ>6;`+uD HD XR+sk7_%~`~NVTypn#Ӝvu@"$@"$K(!GQօ¸5"Q!(tlkk:*ѲjD HD ERyhwK/r@/bHwM*$@"$E@"ͫbV:]}d$@"$éS2/ D N T)HD HD HD HD H#پ .ZN?=>lq333aw%.#tU%'@"$@"$@"$@"$@"j%X/>{8>qk_Ŋ@"$@"$@"$@"$@"@Ҿcj}8q|{g'ׯ]}pן>ow]·'N]Tk kh>|arY_. 9Crr_䰝:uʟ#RDG @UbWZ4/&}eGi{4 U٭ʗ~n_"Gy)HD HD H#B0>ߝ8v.=yrbthw^p%>wUW_hΝ k}O8q%lٲ97iPW[~W .fNMMM;vlrr6:::>>n_n+Y;TmQ &f"9'"4'\zu=zTYkG-QH&@"$@"$@"T$[ }{> Ow}{dHp-p㍗^q R}}cǎǏ̱_ _j*Zoܸ7eK ۷a-$šgji8rȒ./X߉ S;CA 8 ڰa\f_+i,gevie xlNh b~rB>icuϞ=\:5cW6gnT8HD HD Hv-A#NкmzokVm޴~녛V /z?7ohM߷GxGvW^yW8p Wz_^*.3X?b~;.3v4|p?ϥl8#T͛ 7pUWm۶# 4iHJ#->ݻgy_u~ 뒤KPv[IҾK2`ۿk)D^}{5\8h_X7xBnG|37tS[(IlI!@"$@"$@"#XMؑ?`tl.j͚K.rvf}C##Dꑙ?y~WZg;wkHڿ+l;82{AuXE4ԣmzCV.ɼ8.yh#|ᅬ9XyB4I>/}ŬIi4ҽiUYҴ_LV] ZW^yw<0m3 :&ד*yoqbO(wÊ]=K܎DZyaeD HD HD H@ҾғDM;oݸy< ܰm_r}㇏ YS8[}WuCCo֌Ϯ[ujjlݺ;Yh-&y;R Qx~G _]w۷Э_YM6r-2/2 "~'}X_|+Y> _onPDj6 q~19@:4[o [~x{ \>*lO<#Bb'pD~[.K$@"$@"$G i߂1>5ydlpjۖV=58aG6?:=w50}h3׏N xt`ϯF6P(X%rb iMTk0:iXYdJHxl>TAeQ'JP-2Q~ex|nPLX.[mUw %Sn>jc,caD惒D.8F88,Ş8ܹD1[g,-&ՈuCLOMO@ѓtKƥ;zQŸc4یuX83

    gS,\,M1Il"Uz>,UD7ߌWfU2RNqr]IqĜR'CV!ܮMs0_L?/Yni]ϦZQs bj;nV:cu|MGW>֬olf_1oFJBC=Dό_+,_@"$@"$@"5B}yW]_޴a5lܰaС#;Mn8u`Dn;wSexk׮Gyi)@bR|vΝQF_c~aR*@q*g>?%7!H4Q﮼JZ -$D* 'l뮻4t0t?裏R˳xt,צ|-sUflXtתkY "\'_r_vEr5|ljPnES(h=C`8\*mqlٽ{7ॗ^2|FNq(ul2+(o~AZz~i1c6=/+~㈌m8׋lXfV  k#_q~߰ۼs/ `AAr(՟- _<o|,4qyWa\Wh^VC! pJUyf8n֛la&!&-q(7kJ~1)rk[?ms;'s04v> [W)6ĭVx%eAfhn"і2ifZ9=-H~ y$@"$@"$A<3^[׏%NLMW܊Q`GKfx0Yicz9m5atQ\ Mc5uvi1c " ARz?AW`Ufhe:gk{8FXQ8*^#p`LU P"Ļ%!jڦf_KL3n0w8%C&*\K,47KSV/TV'>CI#jcdɧ Yᇈ9cmy*YuƢ~1!{A,XK? cuJ\ԓEh׏~җx$ %Q ź4CR-{$A,*,>zW^4W2QOZ%AbͰER8/ R47eXJ~EK@Wt0B+tRv*J$#ǁ-%SQxs *GW#P̘Svm 0W4A7%Ǥ)q1(奡 ==$'C 'y^/Ҽ[_!V1q4|M.)㫻|KDͺϵ\)$ SÁ|OWJz1_,ey?/2%=?@^&^UAXq6;jfimM,XFo[:U^dðH_%Rςҳox^GO> b 4=oP8!=KzQ(߭/cڃuV&|\]>+%Fs4@&){y)&U wj/*HR>S6zcI*Аtm `j @IDAT.ws:,5*fJqcQP5 %g0z i&]oѻf P*H9& 9,e׼IaxNZV+_bwׇk-G\w+j82EIdcVfIt!Q(t,ID HD HD`Hw!؎ :odkr5ϻh;m{_Sixd ޶50Ɋ6$fcUb|!^%[9ފũP+Q6"Ibi-De,2I$+LƂXo7O ᥆X}D r@Ir]gDj*t QUܭz+7ԜExg|"N _C;PZ(f˭^r\.sťr4rT`6t @z;:;L4}_Q ߨ *=yREu[}z:cX 3 7i<=ՊZ]b- 45;ML%dqy1СgRmwzA7aԤ^C % be< g{l!Qp%q%3 QwMp~9$[ 5p/}Yni]Ow\!`kB|2%9$p6aZ9k='չ: ݸ[qT}G'@"$@"$@"pGƎZ~VMm?ujT 7fə7XۚzwdІ5uxWœ]D0E:kqX*ı!%ʢ0HO(9Ze᫫b19?bT,M, !GDz6]Bts Dvw{ щR]_s Q9*g/%<)Q-V̧Qr|6P$ <2BhEȅ f2 1Yv>:_L>FIàPp`cgy׋>}f_ݎ<'ԵjtP٩BB}͵nӚY8)&2Nی`my 'xdQ]H;t4,=)EVCDU ,|? lp ڡVsV1%t'eÜvzX)-;Wson`<&.l%ԏ+]Q3=eo֠Ti{Gó|~# <A/C1,_U yA+ k +wsmZf4od)Df]Ӭ!rL=~/5-]0]HV'G >@d8DŽ}|q,>f\b:NKrrFև D HD HD Xnao{m335l^5n \75|lkxh|O>"0<djk L>8>:5OwBMM=&ƪNZU%YR.9JNTmWu!8F/2W"E: w^{ٸz訮sj_D٪&qjLjTnS $c ZQu`at^6 [1E h*u;훃D'I=O, =_׋漹M%6Uj۪Ӷ;$Tfg_X˟C(]W:!qP{l {£pt0Ԧ@7԰im(SibUf-#\z<| OEчF+QAoU5kk LLrLO=Ҋ]UV%p΂5fiUghU׵]^>7G #a(U~W&r.jr橧4Hjk:p!*ost$HD HD H};*Ymت?6m[ŠC;xt-7mx>;~kkf:501U۹_ᥐA/9E%qhGAV$K$HjBNDb`L:6WM׿y Cb)9mx02\EstrH{$_d.b!R%jɁ_O};ڈWr|Qg?95jinPl$IL.-Q{MS>r0#|ċٲ||8|csUn6{#pH? B=nrU¯̾Ul! 'm>W.+yv=i3ӬdR@u CS Ж?K(t1ѽ6Dzw.vSoE F72dZЌx8U܃ur uD HD HD XB́H]vumX~CO<ɽdkl~iMFoﴎ Α=7Ԁ0Tyj@ AE*l$U$4!y\+l撛(H16J@^^\D?ÄP䦞J4!ʦkg(7R. {dhUh9qd_@o z]/.HLD!@Uڞd)1h~UHO/GŞSVE񠣽k }͵z7 uc G+/N`HyAQnĩ΅Ó'9 ik~܌υp,Ey'yS[BX 62G3`9&1vmԗs K_s.-|Xآk {^e~&\b>u=ȣ+[a}*^MD HD HD`Hw~H~d땿50uk/۴A3ɿ[6woL >wpkp59:תb\v؁1O.\UԴVPlSO5fDrKъYta1!&#r6 %ß!Y^֮rrK9>6׺el6C!)VT>i2[>ԭL sGcJb} 9?.)!HD HD HHw~F׌2S#c>9<350xu} {ky7eNe::ݭtcorۚ!VJD`,F2~VѓO>b<1 <#Bg%xF.D{r:, r - GWaUhQG3<96 8F&5=+}] ^ei*3=MoA;Qwо# b{l%VVM('$C戉GCIr؃]̸6%v1=X|\J\ڡg(io-' si#y_8ոpκV彛%|~&ԯFV 뚔\0֦M7d.{ų> >oŰgTY \DLi:7ʊas,fp?˭(Yni\ m߅E~h5NDx74aR\Kozbs9Oq|Ծ%$@"$@"$R! &br6w 7њѹىC'[2|{wSo3W?GM. [P~ODPS.d @*7] >.9H^x!BVlZ4=f_b&H?z vQ{E-U5Q?p|`WM+R1]ax%uٴB#J"S(y.\W 3.Ja GjUnE6Ū~aeh4qXlP{:54Za{،&CˁIerA s<>C~QEUشQ|=Ҙ&lOM)Ѭ+ogM8Ϸ-ZF`X]~W>3B)کK\ XG_4+U]Yl%2)/LLrlȒ,-ώkwݥ " ߹'br*@{7{naAepkvs_dD HD HD HHڷUk\*  :<=щiyS::ڋonM_ٱ[.8v;˷oYؓ`[H-r Ԍ,6HUNy| 1&D%ϊ"xeAxUM8VFt*9@Jr4QMGZVwjSqUӱtGHSfIh Gd@/CMtXX ]+$+!ŜzfSV:!z^|H&LVNMw5#&KZlji16FCtjnI7XE\L! 2Ԋo+ՠiBfI6 UR kyiSyګ÷YW`8;w:fQ֗d*;MJ$ӹdU@Sf_iBmKjei5?9DRPs V['1O-ڰN>`4UP-s!VSOem]ă\~:'@"$@"$@"p0#.s˕۷=cٙə_zrps?~/\r[t}AgXQ ,K|B ڪ/U XX '? :?wމOTN:" SYA}݆ǻjԩJ[*iǶy\0Lq"#JOs,M`ʭGg?޺\0`фpPlX1}4#[׾ƝЄc j!yGjEj^ڭcpP#G*k=zicvjt$?4]t~Գ/э뒀0uqhXIpU@Hsz {ؚQM)x1L^k X3F̎teU9d,ώ}98ä$Zgic,5,zӍǺ_JPJ>cq*a,2@6|́;Vf3:-j9Lbt>8pl'&fOx-Z9e7QFW\c'@"$@"$G }>Fߵmv%)#[=11m.wh3+maW6(nQD'"G((+UmbinųBY X1XDjPTrښ;&DaRǧ/ÿw_@V*dp-s72 KwԳw.`~DCZ5qMd49 '0*srRfAh٨W)*.5uPydScs%u#ʛ2{|1&*OdKǵd18Cf?q~ 9fY+!+?z}"$@"$@"$g} nƽ%L7|{R{why[/,5{EWQ1$qL S 5컵 < й.Sbrf|^Ut nV[}KRnomH{T:',VDf9zB&+;N{V,!&=+TiBQK^n kW0nݙz1Y SC+c.WrȌ Wês•3柬6^uH-JÍ&-v--&@"$@"$@"H]$붯8 5m۪i =$E]N8ud0) b k5tJdLdIrH#j^{,ɶ }9F7jj^(Y5[Ӣrcg }F&Kʵ-g9ʹεSWj#l*$3"{ rcT[S[ǹ6+_V~/褜D HD HD H:"Pul+N>GDQ_L`宮4+h/})2Xs g^OL\Aa,[RWo~zeNw,7g㪲ܘD HD HD HD`" %S'5хh+c?S12jgi듾4,W]?$ C_} H1 Yfxy |u}f@Ҿ#D HD HD HD HD 8H,T?HD HD HD HD HO"'ȳD HD HD HD HD Hr=0OD HD HD HD HD$I<,HD HD HD HD H}LD HD HD HD HD $ß<ͳ8~}{=:m]nŗ\~eeD HD HD HD HD H@ҾW^:rl''Y+:'@"$@"$@"$@"$@"?IY읙\v}o+{kht"^{EO_+89ɓYfb%,/EѩSfgga?000888<R.9pH':{}g^|_5_$ҩg? mV}죦Eb0حokyz~ W_}{58,V7oc=Q3M7F_[RH"$@"$@"$$;?V'?h[rǰbvr/l2/1KoZ4?cb3A6^P.9ph*MϿA!y1kADl*AtoZke4-i´:tǧfB7V;;3;~Л/?9v5V[nw#lZ{{ŦI޼;TB}Q׿>xu]'lA=t+W~/A`mӦMr. C~|I70_|b|(򗿼h clzs_L%>[oqBgӓ-AdPd,q^ԭ6cF+V N ԋyNVwj6t6XJ q$tuMXA 00e^~\{f O Kr i6Hcf}Ue>,sv7ŕ p`ܢ\lDEts51ԉŁŐg}*fn:>rk[Z׳Vc\y'"ZD]d.qSѕ~|=ô5c#j"GLoC3# W*A"$@"$@"$g}y|ͪ+.zfw}wFx7mXvͪ-7l?t/?x`{}N?z:4v['ǎz5~hڵGA` ݹs@T꘵_*TC PiYOwIM@+B@k+ 4C . <4 O=0^h2&˵){>Gg]{Z֤B\]=z\M2@A[0J vJ$G[.[vލ5x饗 Ӷr%J Lh0e 鷿mV_ZLoBOy8"ds/n}?ֺq2A)rB.jǴCܨ7,d6\qKaB&XPJmmgOn;_x&>Mbޕ`$WUڼ׸`xpx\(RUCYF:9e<[IiovK\,J:*%R26F_L=\Od1ri[ξIi; ͻﺭU.fʻM(qa8FI{w8f̀zVNO/_9C$@"$@"$@"pf(!ό.+s Szjv]C'79uPkZ73:~uPkG_>g;L"[DPC9% 2M z6T 9ՉPƾY)vr3u!bX[rks._Lbz{H^P2 RzOk-db:`l#:e$ 2p'u>U7 vݏܡ\XM\~]nAy^':2t*/:K^ULuӄVADIоX3lNKB(M_8} 7AЊ6F]x qkKspfa2^.ƭU3攁v[hB̕;)M ,x1cih\ ~yihBO CjIv׋4WsoeL4=_xtKn._QosW $!p`Uy{KipLI|C9W>`A~m&atGe@8l2|=v,0n (&r峠f[8Oh[=?31KѮhew}> p3/xrj1 +@&5(ns&z)_5lj@"$@"$@",IPpͪc'}=q"8Wz3{o|7im^nMӾ""S;v>S(*~ꩧP94hDȊE/'"F?ag HRE,+b0ۀۄda9,xID@d&Q^ʆΨ._s뭷P=}ںpZ/f$hQ70m1(!{^B[{8qqİR i#hV/!g%%cw@W B`j[9c/wQdEW`yd][dbRQѓAMONHOҼ^w˘`] )0WשJpx|Ѥ|\례~! ЁI^^y Fu}]#ç[3=6ٚ}e.xu>W>_h+6mmko>AMcZz+:OO#iXsKM CS0zq\{ tY2A❱EG45BɪBX*HǽuqTkE%\ ~\ ½.Df]_sn+D z_k[Z$-dLI7MA,bZ wunNC7eAC#CrQ?@"$@"$@"1:gﳱրUC[&O;B Y{rfx=&ֶ>aMk]?-t$nQ%) mD}pNŨZ ?qlrɫ. )S!JNd+VY*qLDOK BDM׺PA8]u}1Ѫ]BtTחuDpp`4@A OpTKi~d ;G,gr!8zhLpuLV]ΗQ90(T~'DbhtWjc4:#:u4`v켐Pm_s[fN 2S6#Xo[;!/_M_g3zgSWqo )`c(cUa 6 QұzaobCrzK> _Qw0sHcd^L=~/}Yni\5]XmETƬZ%lqЩ'%zH upT[ _Ipur1y$@"$@"$r#o ްwjCkW6yw]z[ow߶ͧ~:v'sv #gk)P!r E6QCbP]U}J`á|衇|7S*`ֵxXwb`67-7UCVH4 NuxF4ӗO'YT;9]9 DϢr|zpA/Q b&&°vh|h90՜|L iF0$<"VJnKnܛ*?/IE ku cFhLOr5g(UZE,_*GP=nEo W#jb}fšupLQ@+ǥb+J.&CN  mzR CAOǗ49osf }Auڶε6 թ!ٗC=2|pCJTu[fUAZzSblU1a:wͅYZiUZumf#᧗wucih2C}c/" y_IZ\"yi59 ښt',ID HD HeE iŎJV=15jdOx1Сc]nM'&'^{_|5Z>YD'$N ~LvWx)D`7 bN{Ga8pIx%E,QA& D5"ac㳾G&vXJr&(;W݄/\/K#XaFZr`W@h_NF<9;#so 5 # ; 4dzw޷h8>BPbQ<, ;"A Mr#iq^6 &10 54wL.*$1>4ca4W/ѲA0!-!gMx4M|T(K6a".%YDZZANG:*>$Єn@[`K}%2jP"!YJL:_Q'07:U~Qvzp{ZwMsCs=kw7|\&;sQjO9O*FψGDa()D +GNl* eqC\Do UX nԑe3`͂d%ѻIEMJ_bco#8HWFyz醀[Ў:!GotGf^e(![lUn" Ц9b"QPҭAf,0)`d!3.AyMm]rLz6v/Jjmtzˉtr\3qHw^gj2*W'i5.Uy&;x/_ 77빑$*B&A(*yM7;ytϲ[19f8W/n>tN+hhbe@%2,Ɵy9&<*r+Jj[Z7:@"waQxZ~Mh"$ 4Fx ";SxJa4J{Y>tGo}Vt3XVפU1衇bJlv*g.h:ĸ!Qd$͊sCgW:[̾|=iJ ӹ?}-/KK~wl6CHn@w=G /X,x &|͞[X{l"Z܃p9.<HD HD Hr-jl՚-} CO~ptbZԺξۻ[ӗ}vց 7]cv$Rb5# 'iSojGi 2Q""^p},lUS!i"Q8J\.MTӑVUq\t&iDԶYkđE%PG w =jH10Tyjv!$3|Ne ?nq  ƒ[GmZZLQ݁[M:'VSB9h/5RRز@̶J_8׺:ED}na{{11{`Hx2G b[fw5(mPY㫼 Cb*jz;y@A^m`.&Ν;9e#2ٮJ+dӮ9t.Y)Д&laZ6h}YZq=O{ѶjTn3nIS6l5MT x)SY[! )/%@"$@"$@"$L:?KrOXpvfr^߿95=7/\u32]am_aFę8VwƅE<(}08P0 E+df ?"E%HO~Nxw "'$i"TmPzknmn5u:֦ m걭ħ$~#Fov),C$0ChzHdS?؟r+9b?Ϥ}`.̻}c k4cA=<[:C|/~!=͈q'5E,9&},4EEz^}xQレvk>G>HmƑ Z}ژݰG6]6,}a˼xt$ Ly]G31V&+\U)`y0"^B;'tAc",^L% +$S1(̶(#&jlwUl8}ab c_n`!0)?IEK K;t㱮Ɨ|7XJ m M}k5sl試ƌ~iN?;9%.$Fg45[ ټ%+r-nƅVNM!QU9WF$@"$@"$@"l߅Q|wm[]I;fOLmZ5G[tM [ (<#J liہXZ[,nPC'V N5'AFk) 9Z9BĐjYDjӦ-JBryV$$ $4ԕt-ts- PRb+B ]10ސ4_D'!0YXTVJf|1sP$a0;ÙQ :8@cIuVYZLKI /UZ@PԒWۂ)=j.~~-L煭*[w^b{VJ˕2հq/&tp ='WA6Rmc%Rp㣱aKݻ~{K˫@"$@"$@",} `k|k?Ά+oaBM[ajrda,rѻGj-z-N)x69qJ㥶m -YRȮ]ywZj AɈI q**"/=@Sh_??MM`%*$OI $$@HI $$ eLHMr! zXOoɩL 5gi둾,pޒcj/H1F,3<^~??ϒ@3+qUiG%$@HI $$@ {3LU"/hHHVQ:$iRd'j(IG umN6Lo.ZQV-,l9 $$@HI $$%0\Ht,@[KSR֮8<]EIu,Zp ϼ& $$@HI $$ ' ]'$@HI $$@HI $$H e/wI $$@HI $$@HI $N e>i~HI $$@HI $$@HIR"|@HI $$@HI $$@XR]'$@HI $$@HI $$H e/wI $$@HI $$@HI $N e>i~HI $$@HI $$@HIz6͚'GFF33k׮ݱs @HI $$@HI $$@H&||gKcW&&}/MI $$@HI $$@HI $HwjcgϜ^3xG;qHOP֣ёC+Ckqr\鮙xիzz&pCڵk"M ~WWWooowwtff^w}?V|񱑷|6=:vyК/yo ^Q5UǏ~zDH:$KXollTj# _WvWq_@@~톆!.FTH&@ܹsrGĭ"n4 $$@HI $$V"}O<۳vSCG>]=8i--[=ߕU<{ԯ^|W_H\@Tpm۶o~ۈ{~aUA>ɓ'?#;Z Flc*dt1A4cN8!B#'sk3JI $$@HI $eN eD=/;~6ycw<ɻΜ02vqdm^<|魿Ǟw90ꡙTrۍAy駉 J=ᆌH(cْ?s=wuLlU>oP9%~~hqկ~xc1ոxϗX!h|y9e4 Y8E/BF"%rm!hp)Z Cl UXte9ؖ6$$@HI $$"Hٷؙkn<[r`%eAjP 6Ђ\BfvZo.Yr"ZZn=9-0ɹ£)hd;6yX0M; vIս([% G^ }TeƳc^~ee,~d48L`?zbar+]xvGC5-ic{+cۡK2 lnM''bBMG,LI $$@HI ,8};#Z][(ӧ|?޻7[f׭p'>n=|kΎ>| m6Ԏ׆;4C"--(g߾}r? GYO~B@JY!OvY3^xs!hІVbYزDZ`dU|BA'p!F͞K/1`;MkP,\@Mf9}+>I' }v~}v<jT! "Hu/F žZUi9oj 5BWBtOO -LZŘoagdՐ_=|8RCdݲA7S g# wByF:T` kVꩧLC!=DK`pٮ'&҂lr Zw޽ӠPK~{q.^w>f9lL: s$*UA͉ǀX.4neRG<Aߞ;x6XiE4"!UaX5Wn\ka 0L;/l_}t,~!<:#]ӷ}o7|f&s6bY@HI $$@XXdȅsf~xhz>:sġuMMt?sG#y7+5?={պV{Z Ó+"L*2Ghj1dO GlKI1QHDPY7 Oz5_M)Nvl~AţjVsI~UYzHBas .!ym*|⏼G&Fcx54B!\<B}ޱ_Ts ~ULAvT90:qNG7%|!-h:p0w6څe*<&D.c:#Ec\u^4׎?\pTDs>,ڡi`*͢I/WAwq?B$Tg~{[p"uVrP>LX ~XrD5;wXuv(F7|fY! $$@HI $$Płjfmg@MϞ>ryhjkuvm̵HR{Dթuws-3j5瞳_Q$ @(OuCB(@ WPVroC '(E%zvWX7((N`jōBpt_Y"pPH~ޣi)7QsUpanIfXhXz~2[YfDR,!Ce1T;dOi4lVI`v(>v u Z5B*HlVs<"8vuU C^mh_ReF3B`!S_NRN T6caЕKT$+\.r+ҶSCZaH`%Ojō&y"49U@`3S "(t⭛ILe6u})юX8oOÓg(z7K}ÝYiψz1#R_g>UF'JMpӢmPb(f+e v*X1/Z};Pg=?O}y@HI $$@XT)oWKowϪ͓]g^V#I^;1kkjW^XsRG4.Bʤ 0PExKy!׆B S>".h(4%**^L1F]46K\KύF(3sZ*D6G:}|7𕜯D ߉w1z Hj)Nq=Lq2%x+zQy|+,=yD h.k@8Q;.`R(\Tᇿ8+q ?7r\DAazR],#~S{p 0s[O04HT*)"1\o9nU#c!-9'Qi$;/jZ%,N E>{4SyiY"*hXwV\i*d?2:KT(ZI75z wT(kcJh7gH+͓$@HI $$h',j+kN]5m;?=q]o跞d~,|!ȥ# }8H nR+(,>6(6̶DŽZJS;T1CsAKri%w 6;8/5*#8D:/Œ(חGbꫯJIEz=NhŠ^Uyu>ZhUk#y$T|JUΰja1zŘ6MF]`HR;;E©J\JPN& z^bTC4mwSe;$&B4Ϲ'z1Ŋј< sZ3({x>("K%LU\bqlY^-E&ƢЉ3r b|mj}8oOgkXʭKdyT5L􋍦Ym_ ENaNu(z/,l*:͓$@HI $$bHٷ3a?fkJ\5vxú[ڻu3w 3rvjɮ<:I3:tGͱCvgIgZY(E;Q-.lJ[HZF6^W0+DZ匎mnh[M9+77Ո;m#xG!pa^9VG~ICуWN"k*leb0 c&f'w َ>mS9H=V^חs "b||ǟS''>6h ܮ&\OpR4x'㢗9HĐ9SxNINCQlGaɺqbτ">#r2|)ḽ~5tWӞq$ Z^4dn3b}͡~hbZؾf,P)MQSxTq1q%,b[%h Q9'Qn6(_@HI $$@HM e΄f\XwO ڱ}s#k7ns|raopGw>;P[ W$^i.\|no)WJt ڟd[%_<#Ho ػ*BuYenVKY԰DNOҩdO9t>ymڗFaXTGjX?EuCϜEΛ|XO+7|te ǥnPYJ&Q+Pi$ЦI9J\z^Q[5^Ii ndMjSC|UYM,zҴ9QmP^uɶ҂(ϕ<+bIie>o-zBTs=gU|ꩧ<<]bHb;xZV*KM-,Wڊ .Tӱfġm#B]m1~%Tf8J=G,LI $$@HI ,};S_2>zN5nɮ+LzϺ&kCMtm7Щ+.u_6r[v? D4;aoijޒ{ڈ!RRiRKXڔ.ۗ+H&!xiȾmjgHԷ~+FV#T/Gj *!\98 ׎Q]ov]Im"`}M7lMx'f \Ox0 9 1uOݹ᣸❮}R"<4n6喕BwTk֢NijP7c}LYbL4ڜhNqiIONŚhƀ6Jb8PƚUѯ8z_o*f$@HI $$Hٷ3*;&js{k`:Iݼs/~ڥ-Zo69U(i`m烑v+-34k{L'tE!z[Y>9A*jOK%Q舳B|$y{h K2lwTm}wF;SA435]{j#5V M]p uFiCN`1O2dzJS2%UP%emjauG>2q: _[\CL3dwsVR'`.L*[KXp&M/ 9%Z m&{?UT+NpG!t2qr~h抣Vd}K0!D-K XS؃DZoqzRv<.1"֚hS}P-ּК͎'TP5EFB׎ݱ<#ܨ(_XUJ4A"hmHb% yƂF~2w,[g~ .b !$!z;[0F ­{NRؠ#]w#VLŖ0>c-/( s<żX2eqB]ND/_OʾVݑ-r{*%RuO6Q0})[վĈkJ_pHYlc1CmZ|mr#MMiA:i_mhӔ"V07']U_mD BGGFƱ?e[ՃmWB/iovfVHI $$@HI ,}\w?V򐇞뙡3&~Y[pǏZGKvk.ΖL/b#TJk{7 ^<ڡQ4Z'z8B ($~8,!jxJ 𩭾}jwB TIrTo67|DU4IĶLy%|'Fg _h.:DV^94V-\6F|O>p韢״\Vq:Chr0([L N@Js.LagÅ.>"Q"Vw-W (h|(QWnx6/~sǬT`_Oo:.wGVYyTq%C8X|>L2@ZB8_y`%qnԌɹ6ArGOT?)1|衇|ɚ_ּpӅm#o,-n/)WkHI $$@H˟@f}♿ko߼}V=6se|` ߞUוY9eskaSMҎ}fmۆrd?O !;ڥ+!MPd 7\K0SnY\Fjَ46ŮU; $T0I|~t5=y96%Tu()qTF{`j>caVKeIQe9oLTxfr9ExbRA!Q~E9kc/$644m|9bhsdXE5'{ojy};Ηw –Wr:8Xo<oyӸկ\:֊6e M{! 5l{3E/QI,{*JV-7ɴe#bcs Y_iU*g[y^|6.zKGSfʅM>sRUY Yroņì÷s\H$$@HI $$@ʾ%e˶o?itgV4_O%YĶB |Ⱦ2!RE$ulŒ6ʹ9\o.B=vZi(.)lS'̫o zD0/t |tfmhrxqI4R.'Kʓc/G_w-$$7Ηv$@HI $$@ʾƷџBZޒS)2jo% ٔ2m=ߓUK,@,0j/H1Fy pweI $B`|`ifHI $$@H+@ʾ+ifkLU"/hHHUQ:l[[%I{.0;i7PDIh舰!.Y-ߩކe;^KKHI`1,o.LI $$@HIKN eߛ9ȅK r)j9G=c$_ =;MI |X7Ηv@HI $$ [b+$@HI $$@HI $$@H eߊZHI $$@HI $$@HI`eHweSZ@HI $$@HI $$@H eߊZHI $$@HI $$@HI`eHweSZ@HI $$@HI $$@H eߊZHI $$@HI $$@HI`eHweSZ@HI $$@HI $$@Hb֊'GFF33k׮ݱspK< $$@HI $$@HI $$xR/[}it|l|}Ϸ> $$@HI $$@HI $$0{)ΞYvy|SSkϟ>z{'z476:rԹ|ebh;[nK775/^^z@___Oubynȸ_vMIvw+y.c ⰿߊo m-ż(>ʓE kt:pe$@HI $$!}2(4ߗ^s5113u]{~[S'Og<{۷o.t||+Wܹs}oAnҏ{h[ĵQʚ5k(n7D.Ylyz*O'''Ha&EKH;oܸqӦMz;^_hbD8G$NZU%#PȾ6FMߛ9Pʏ@HI $$@([p2.I6޺eO;uob`;߻w_QAﱣGGG>|񱑷|6=:vyК/yo ^QZjqqqׯADw1k* # >x/]Sve2Q[n]i\[ Cڅ _¢$$՗$vr"㦓Fرcg]\ To[lQtiN | ovCȐ}#*{$BΞ=A"Hڷ.,caQrci}2 ( $$@HI $z)hwN}O]3xTБOWl0mˆߥMmٺ?䮮ӧ~s#ܾ/y/?w7ܷO?g!F8p{ݵk0ܠh?YK?[nAnx>^u)}d۶m7xt3˩SND^j(Gޒ8s?7KH~衇ݻw#P 8N;StiN ÇL+؋~#*{D}͋'O|%[M kh׾ta D>4ߎjۅ![KI $$@HI$oa#<ұ`Vy™F.ufuKoo====ˁVͬ]Sgnt bO?MNWz7t[-9:ݏc=s]wI<ܺVmf_DϏW_W =F=_b_y_2B,"ۗ Q{iIu,aleJk1_m Q-*H^ҥ 3d(eߥ/پ%3WnqY$l1 $$@HI $G e C63um@훇 ?Y[~ڡu}cgNԺ.:;}~K]'ش`UmRTܮI!y@<1~\6{EU&~z 6GsKנmROfHAraz$2r챏;) 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-annotation Annotations for Metrics bundle A dependency-less package of just the annotations used by other Metrics modules. metrics-3.2.5/metrics-annotation/src/000077500000000000000000000000001315671014200175745ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/000077500000000000000000000000001315671014200205205ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/000077500000000000000000000000001315671014200214415ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/com/000077500000000000000000000000001315671014200222175ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/000077500000000000000000000000001315671014200237575ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200254255ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/000077500000000000000000000000001315671014200275775ustar00rootroot00000000000000metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/CachedGauge.java000066400000000000000000000026271315671014200325710ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit; /** * An annotation for marking a method as a gauge, which caches the result for a specified time. * *

    * Given a method like this: *

    
     *     {@literal @}CachedGauge(name = "queueSize", timeout = 30, timeoutUnit = TimeUnit.SECONDS)
     *     public int getQueueSize() {
     *         return queue.getSize();
     *     }
     *
     * 
    *

    * * A gauge for the defining class with the name queueSize will be created which uses the annotated method's * return value as its value, and which caches the result for 30 seconds. * * @since 3.1 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface CachedGauge { /** * @return The name of the counter. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. */ boolean absolute() default false; /** * @return The amount of time to cache the result */ long timeout(); /** * @return The unit of timeout */ TimeUnit timeoutUnit() default TimeUnit.MILLISECONDS; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Counted.java000066400000000000000000000032071315671014200320450ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation for marking a method of an annotated object as counted. * *

    * Given a method like this: *

    
     *     {@literal @}Counted(name = "fancyName")
     *     public String fancyName(String name) {
     *         return "Sir Captain " + name;
     *     }
     * 
    *

    * A counter for the defining class with the name {@code fancyName} will be created and each time the * {@code #fancyName(String)} method is invoked, the counter will be marked. * * @since 3.1 */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Counted { /** * @return The name of the counter. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. When annotating a class, this must be {@code false}. */ boolean absolute() default false; /** * @return * If {@code false} (default), the counter is decremented when the annotated * method returns, counting current invocations of the annotated method. * If {@code true}, the counter increases monotonically, counting total * invocations of the annotated method. */ boolean monotonic() default false; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/ExceptionMetered.java000066400000000000000000000043071315671014200337120ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation for marking a method of an annotated object as metered. *

    * Given a method like this: *

    
     *     {@literal @}ExceptionMetered(name = "fancyName", cause=IllegalArgumentException.class)
     *     public String fancyName(String name) {
     *         return "Sir Captain " + name;
     *     }
     * 
    *

    * A meter for the defining class with the name {@code fancyName} will be created and each time the * {@code #fancyName(String)} throws an exception of type {@code cause} (or a subclass), the meter * will be marked. *

    * A name for the metric can be specified as an annotation parameter, otherwise, the metric will be * named based on the method name. *

    * For instance, given a declaration of *

    
     *     {@literal @}ExceptionMetered
     *     public String fancyName(String name) {
     *         return "Sir Captain " + name;
     *     }
     * 
    *

    * A meter named {@code fancyName.exceptions} will be created and marked every time an exception is * thrown. */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface ExceptionMetered { /** * The default suffix for meter names. */ String DEFAULT_NAME_SUFFIX = "exceptions"; /** * @return The name of the meter. If not specified, the meter will be given a name based on the method * it decorates and the suffix "Exceptions". */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. When annotating a class, this must be {@code false}. */ boolean absolute() default false; /** * @return The type of exceptions that the meter will catch and count. */ Class cause() default Exception.class; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Gauge.java000066400000000000000000000020361315671014200314730ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation for marking a method of an annotated object as a gauge. *

    * Given a method like this: *

    
     *     {@literal @}Gauge(name = "queueSize")
     *     public int getQueueSize() {
     *         return queue.size;
     *     }
     * 
    *

    * A gauge for the defining class with the name {@code queueSize} will be created which uses the * annotated method's return value as its value. */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) public @interface Gauge { /** * @return The gauge's name. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. */ boolean absolute() default false; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Metered.java000066400000000000000000000024151315671014200320310ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation for marking a method of an annotated object as metered. *

    * Given a method like this: *

    
     *     {@literal @}Metered(name = "fancyName")
     *     public String fancyName(String name) {
     *         return "Sir Captain " + name;
     *     }
     * 
    *

    * A meter for the defining class with the name {@code fancyName} will be created and each time the * {@code #fancyName(String)} method is invoked, the meter will be marked. */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Metered { /** * @return The name of the meter. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. When annotating a class, this must be {@code false}. */ boolean absolute() default false; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Metric.java000077500000000000000000000037421315671014200316760ustar00rootroot00000000000000/** * Copyright (C) 2012 Ryan W Tenney (ryan@10e.us) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.codahale.metrics.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation requesting that a metric be injected or registered. * *

    * Given a field like this: *

    
     *     {@literal @}Metric
     *     public Histogram histogram;
     * 
    *

    * A meter of the field's type will be created and injected into managed objects. * It will be up to the user to interact with the metric. This annotation * can be used on fields of type Meter, Timer, Counter, and Histogram. * *

    * This may also be used to register a metric, which is useful for creating a histogram with * a custom Reservoir. *

    
     *     {@literal @}Metric
     *     public Histogram uniformHistogram = new Histogram(new UniformReservoir());
     * 
    *

    * * @since 3.1 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) public @interface Metric { /** * @return The name of the metric. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, * use the given name relative to the annotated class. */ boolean absolute() default false; } metrics-3.2.5/metrics-annotation/src/main/java/com/codahale/metrics/annotation/Timed.java000066400000000000000000000024231315671014200315050ustar00rootroot00000000000000package com.codahale.metrics.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation for marking a method of an annotated object as timed. *

    * Given a method like this: *

    
     *     {@literal @}Timed(name = "fancyName")
     *     public String fancyName(String name) {
     *         return "Sir Captain " + name;
     *     }
     * 
    *

    * A timer for the defining class with the name {@code fancyName} will be created and each time the * {@code #fancyName(String)} method is invoked, the method's execution will be timed. */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) public @interface Timed { /** * @return The name of the timer. */ String name() default ""; /** * @return If {@code true}, use the given name as an absolute name. If {@code false}, use the given name * relative to the annotated class. When annotating a class, this must be {@code false}. */ boolean absolute() default false; } metrics-3.2.5/metrics-benchmarks/000077500000000000000000000000001315671014200167505ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/README.md000066400000000000000000000016771315671014200202420ustar00rootroot00000000000000Benchmarks are based on [Oracle Java Microbenchmark Harness](http://openjdk.java.net/projects/code-tools/jmh/). ### Results interpretation Please be cautious with conclusions based on microbenchmarking as there are plenty possible pitfalls for goals, test compositions, input data, an envionment and the analyze itself. ### Command line launching Execute all benchmark methods with 4 worker threads: mvn clean install java -jar target/benchmarks.jar ".*" -t 4 or specify a filter for benchmark methods and the number of forks and warmup/measurements iterations, e.g.: java -jar target/benchmarks.jar -t 4 -f 3 -i 10 -wi 5 ".*CounterBenchmark.*" java -jar target/benchmarks.jar -t 4 -f 3 -i 10 -wi 5 ".*ReservoirBenchmark.*" java -jar target/benchmarks.jar -t 4 -f 3 -i 10 -wi 5 ".*MeterBenchmark.*" ### Command line options The whole list of command line options is available by: java -jar target/benchmarks.jar -h metrics-3.2.5/metrics-benchmarks/findbugs-exclude.xml000066400000000000000000000003251315671014200227220ustar00rootroot00000000000000 metrics-3.2.5/metrics-benchmarks/pom.xml000066400000000000000000000061271315671014200202730ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-benchmarks Benchmarks for Metrics A development module for performance benchmarks of Metrics classes. io.dropwizard.metrics metrics-core ${project.version} org.openjdk.jmh jmh-core 1.0.1 org.openjdk.jmh jmh-generator-annprocess 1.0.1 provided org.apache.maven.plugins maven-deploy-plugin 2.7 true org.apache.maven.plugins maven-shade-plugin 2.2 package shade benchmarks org.openjdk.jmh.Main org.codehaus.mojo findbugs-maven-plugin findbugs-exclude.xml metrics-3.2.5/metrics-benchmarks/src/000077500000000000000000000000001315671014200175375ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/000077500000000000000000000000001315671014200204635ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/000077500000000000000000000000001315671014200214045ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/000077500000000000000000000000001315671014200221625ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/000077500000000000000000000000001315671014200237225ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200253705ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/benchmarks/000077500000000000000000000000001315671014200275055ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/benchmarks/CounterBenchmark.java000066400000000000000000000021461315671014200336050ustar00rootroot00000000000000package com.codahale.metrics.benchmarks; import com.codahale.metrics.Counter; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; @State(Scope.Benchmark) public class CounterBenchmark { private final Counter counter = new Counter(); // It's intentionally not declared as final to avoid constant folding private long nextValue = 0xFBFBABBA; @Benchmark public Object perfIncrement() { counter.inc(nextValue); return counter; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(".*" + CounterBenchmark.class.getSimpleName() + ".*") .warmupIterations(3) .measurementIterations(5) .threads(4) .forks(1) .build(); new Runner(opt).run(); } } metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/benchmarks/MeterBenchmark.java000066400000000000000000000021211315671014200332330ustar00rootroot00000000000000package com.codahale.metrics.benchmarks; import com.codahale.metrics.Meter; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; @State(Scope.Benchmark) public class MeterBenchmark { private final Meter meter = new Meter(); // It's intentionally not declared as final to avoid constant folding private long nextValue = 0xFBFBABBA; @Benchmark public Object perfMark() { meter.mark(nextValue); return meter; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(".*" + MeterBenchmark.class.getSimpleName() + ".*") .warmupIterations(3) .measurementIterations(5) .threads(4) .forks(1) .build(); new Runner(opt).run(); } } ReservoirBenchmark.java000066400000000000000000000052231315671014200340660ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/benchmarkspackage com.codahale.metrics.benchmarks; import com.codahale.metrics.ExponentiallyDecayingReservoir; import com.codahale.metrics.SlidingTimeWindowArrayReservoir; import com.codahale.metrics.SlidingTimeWindowReservoir; import com.codahale.metrics.SlidingWindowReservoir; import com.codahale.metrics.UniformReservoir; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.profile.GCProfiler; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; import java.util.concurrent.TimeUnit; @State(Scope.Benchmark) public class ReservoirBenchmark { private final UniformReservoir uniform = new UniformReservoir(); private final ExponentiallyDecayingReservoir exponential = new ExponentiallyDecayingReservoir(); private final SlidingWindowReservoir sliding = new SlidingWindowReservoir(1000); private final SlidingTimeWindowReservoir slidingTime = new SlidingTimeWindowReservoir(200, TimeUnit.MILLISECONDS); private final SlidingTimeWindowArrayReservoir arrTime = new SlidingTimeWindowArrayReservoir(200, TimeUnit.MILLISECONDS); // It's intentionally not declared as final to avoid constant folding private long nextValue = 0xFBFBABBA; @Benchmark public Object perfUniformReservoir() { uniform.update(nextValue); return uniform; } @Benchmark public Object perfSlidingTimeWindowArrayReservoir() { arrTime.update(nextValue); return arrTime; } @Benchmark public Object perfExponentiallyDecayingReservoir() { exponential.update(nextValue); return exponential; } @Benchmark public Object perfSlidingWindowReservoir() { sliding.update(nextValue); return sliding; } @Benchmark public Object perfSlidingTimeWindowReservoir() { slidingTime.update(nextValue); return slidingTime; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(".*" + ReservoirBenchmark.class.getSimpleName() + ".*") .warmupIterations(10) .measurementIterations(10) .addProfiler(GCProfiler.class) .measurementTime(TimeValue.seconds(3)) .timeUnit(TimeUnit.MICROSECONDS) .mode(Mode.AverageTime) .threads(4) .forks(1) .build(); new Runner(opt).run(); } } SlidingTimeWindowReservoirsBenchmark.java000066400000000000000000000047061315671014200375770ustar00rootroot00000000000000metrics-3.2.5/metrics-benchmarks/src/main/java/com/codahale/metrics/benchmarkspackage com.codahale.metrics.benchmarks; import com.codahale.metrics.SlidingTimeWindowArrayReservoir; import com.codahale.metrics.SlidingTimeWindowReservoir; import com.codahale.metrics.Snapshot; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Group; import org.openjdk.jmh.annotations.GroupThreads; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.profile.GCProfiler; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; import java.util.concurrent.TimeUnit; /** * @author bstorozhuk */ @State(Scope.Benchmark) public class SlidingTimeWindowReservoirsBenchmark { private final SlidingTimeWindowReservoir slidingTime = new SlidingTimeWindowReservoir(200, TimeUnit.MILLISECONDS); private final SlidingTimeWindowArrayReservoir arrTime = new SlidingTimeWindowArrayReservoir(200, TimeUnit.MILLISECONDS); // It's intentionally not declared as final to avoid constant folding private long nextValue = 0xFBFBABBA; @Benchmark @Group("slidingTime") @GroupThreads(3) public Object slidingTimeAddMeasurement() { slidingTime.update(nextValue); return slidingTime; } @Benchmark @Group("slidingTime") @GroupThreads(1) public Object slidingTimeRead() { Snapshot snapshot = slidingTime.getSnapshot(); return snapshot; } @Benchmark @Group("arrTime") @GroupThreads(3) public Object arrTimeAddMeasurement() { arrTime.update(nextValue); return slidingTime; } @Benchmark @Group("arrTime") @GroupThreads(1) public Object arrTimeRead() { Snapshot snapshot = arrTime.getSnapshot(); return snapshot; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(".*" + SlidingTimeWindowReservoirsBenchmark.class.getSimpleName() + ".*") .warmupIterations(10) .measurementIterations(10) .addProfiler(GCProfiler.class) .measurementTime(TimeValue.seconds(3)) .timeUnit(TimeUnit.MICROSECONDS) .mode(Mode.AverageTime) .forks(1) .build(); new Runner(opt).run(); } } metrics-3.2.5/metrics-core/000077500000000000000000000000001315671014200155635ustar00rootroot00000000000000metrics-3.2.5/metrics-core/pom.xml000066400000000000000000000015161315671014200171030ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-core Metrics Core bundle Metrics is a Java library which gives you unparalleled insight into what your code does in production. Metrics provides a powerful toolkit of ways to measure the behavior of critical components in your production environment. metrics-3.2.5/metrics-core/src/000077500000000000000000000000001315671014200163525ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/000077500000000000000000000000001315671014200172765ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/000077500000000000000000000000001315671014200202175ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/com/000077500000000000000000000000001315671014200207755ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/com/codahale/000077500000000000000000000000001315671014200225355ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200242035ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/CachedGauge.java000066400000000000000000000035221315671014200271700ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * A {@link Gauge} implementation which caches its value for a period of time. * * @param the type of the gauge's value */ public abstract class CachedGauge implements Gauge { private final Clock clock; private final AtomicLong reloadAt; private final long timeoutNS; private volatile T value; /** * Creates a new cached gauge with the given timeout period. * * @param timeout the timeout * @param timeoutUnit the unit of {@code timeout} */ protected CachedGauge(long timeout, TimeUnit timeoutUnit) { this(Clock.defaultClock(), timeout, timeoutUnit); } /** * Creates a new cached gauge with the given clock and timeout period. * * @param clock the clock used to calculate the timeout * @param timeout the timeout * @param timeoutUnit the unit of {@code timeout} */ protected CachedGauge(Clock clock, long timeout, TimeUnit timeoutUnit) { this.clock = clock; this.reloadAt = new AtomicLong(0); this.timeoutNS = timeoutUnit.toNanos(timeout); } /** * Loads the value and returns it. * * @return the new value */ protected abstract T loadValue(); @Override public T getValue() { if (shouldLoad()) { this.value = loadValue(); } return value; } private boolean shouldLoad() { for (; ; ) { final long time = clock.getTick(); final long current = reloadAt.get(); if (current > time) { return false; } if (reloadAt.compareAndSet(current, time + timeoutNS)) { return true; } } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ChunkedAssociativeLongArray.java000066400000000000000000000265531315671014200324540ustar00rootroot00000000000000package com.codahale.metrics; import static java.lang.System.arraycopy; import static java.util.Arrays.binarySearch; import java.lang.ref.SoftReference; import java.util.ArrayDeque; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; class ChunkedAssociativeLongArray { private static final long[] EMPTY = new long[0]; private static final int DEFAULT_CHUNK_SIZE = 512; private static final int MAX_CACHE_SIZE = 128; private final int defaultChunkSize; /* * We use this ArrayDeque as cache to store chunks that are expired and removed from main data structure. * Then instead of allocating new Chunk immediately we are trying to poll one from this deque. * So if you have constant or slowly changing load ChunkedAssociativeLongArray will never * throw away old chunks or allocate new ones which makes this data structure almost garbage free. */ private final ArrayDeque> chunksCache = new ArrayDeque>(); /* * Why LinkedList if we are creating fast data structure with low GC overhead? * * First of all LinkedList here has relatively small size countOfStoredMeasurements / DEFAULT_CHUNK_SIZE. * And we are heavily rely on LinkedList implementation because: * 1. Now we deleting chunks from both sides of the list in trim(long startKey, long endKey) * 2. Deleting from and inserting chunks into the middle in clear(long startKey, long endKey) * * LinkedList gives us O(1) complexity for all this operations and that is not the case with ArrayList. */ private final LinkedList chunks = new LinkedList(); ChunkedAssociativeLongArray() { this(DEFAULT_CHUNK_SIZE); } ChunkedAssociativeLongArray(int chunkSize) { this.defaultChunkSize = chunkSize; } private Chunk allocateChunk() { while (true) { final SoftReference chunkRef = chunksCache.pollLast(); if (chunkRef == null) { return new Chunk(defaultChunkSize); } final Chunk chunk = chunkRef.get(); if (chunk != null) { chunk.cursor = 0; chunk.startIndex = 0; chunk.chunkSize = chunk.keys.length; return chunk; } } } private void freeChunk(Chunk chunk) { if (chunksCache.size() < MAX_CACHE_SIZE) { chunksCache.add(new SoftReference(chunk)); } } synchronized boolean put(long key, long value) { Chunk activeChunk = chunks.peekLast(); if (activeChunk == null) { // lazy chunk creation activeChunk = allocateChunk(); chunks.add(activeChunk); } else { if (activeChunk.cursor != 0 && activeChunk.keys[activeChunk.cursor - 1] > key) { return false; // key should be the same as last inserted or bigger } boolean isFull = activeChunk.cursor - activeChunk.startIndex == activeChunk.chunkSize; if (isFull) { activeChunk = allocateChunk(); chunks.add(activeChunk); } } activeChunk.append(key, value); return true; } synchronized long[] values() { int valuesSize = size(); if (valuesSize == 0) { return EMPTY; } long[] values = new long[valuesSize]; int valuesIndex = 0; for (Chunk copySourceChunk : chunks) { int length = copySourceChunk.cursor - copySourceChunk.startIndex; int itemsToCopy = Math.min(valuesSize - valuesIndex, length); arraycopy(copySourceChunk.values, copySourceChunk.startIndex, values, valuesIndex, itemsToCopy); valuesIndex += length; } return values; } synchronized int size() { int result = 0; for (Chunk chunk : chunks) { result += chunk.cursor - chunk.startIndex; } return result; } synchronized String out() { Iterator fromTailIterator = chunks.iterator(); StringBuilder builder = new StringBuilder(); while (fromTailIterator.hasNext()) { Chunk copySourceChunk = fromTailIterator.next(); builder.append('['); for (int i = copySourceChunk.startIndex; i < copySourceChunk.cursor; i++) { long key = copySourceChunk.keys[i]; long value = copySourceChunk.values[i]; builder.append('(').append(key).append(": ").append(value).append(')').append(' '); } builder.append(']'); if (fromTailIterator.hasNext()) { builder.append("->"); } } return builder.toString(); } /** * Try to trim all beyond specified boundaries. * All items that are less then startKey or greater/equals then endKey * * @param startKey * @param endKey */ synchronized void trim(long startKey, long endKey) { /* * [3, 4, 5, 9] -> [10, 13, 14, 15] -> [21, 24, 29, 30] -> [31] :: start layout * |5______________________________23| :: trim(5, 23) * [5, 9] -> [10, 13, 14, 15] -> [21] :: result layout */ ListIterator fromHeadIterator = chunks.listIterator(chunks.size()); while (fromHeadIterator.hasPrevious()) { Chunk currentHead = fromHeadIterator.previous(); if (isFirstElementIsEmptyOrGreaterEqualThanKey(currentHead, endKey)) { freeChunk(currentHead); fromHeadIterator.remove(); } else { int newEndIndex = findFirstIndexOfGreaterEqualElements( currentHead.keys, currentHead.startIndex, currentHead.cursor, endKey ); currentHead.cursor = newEndIndex; break; } } ListIterator fromTailIterator = chunks.listIterator(); while (fromTailIterator.hasNext()) { Chunk currentTail = fromTailIterator.next(); if (isLastElementIsLessThanKey(currentTail, startKey)) { freeChunk(currentTail); fromTailIterator.remove(); } else { int newStartIndex = findFirstIndexOfGreaterEqualElements( currentTail.keys, currentTail.startIndex, currentTail.cursor, startKey ); if (currentTail.startIndex != newStartIndex) { currentTail.startIndex = newStartIndex; currentTail.chunkSize = currentTail.cursor - currentTail.startIndex; } break; } } } /** * Clear all in specified boundaries. * Remove all items between startKey(inclusive) and endKey(exclusive) * * @param startKey * @param endKey */ synchronized void clear(long startKey, long endKey) { /* * [3, 4, 5, 9] -> [10, 13, 14, 15] -> [21, 24, 29, 30] -> [31] :: start layout * |5______________________________23| :: clear(5, 23) * [3, 4] -> [24, 29, 30] -> [31] :: result layout */ ListIterator fromHeadIterator = chunks.listIterator(chunks.size()); while (fromHeadIterator.hasPrevious()) { Chunk currentTail = fromHeadIterator.previous(); if (!isFirstElementIsEmptyOrGreaterEqualThanKey(currentTail, endKey)) { Chunk afterTailChunk = splitChunkOnTwoSeparateChunks(currentTail, endKey); if (afterTailChunk != null) { fromHeadIterator.add(afterTailChunk); break; } } } // now we should remove specified gap [startKey, endKey] while (fromHeadIterator.hasPrevious()) { Chunk afterGapHead = fromHeadIterator.previous(); if (isFirstElementIsEmptyOrGreaterEqualThanKey(afterGapHead, startKey)) { freeChunk(afterGapHead); fromHeadIterator.remove(); } else { int newEndIndex = findFirstIndexOfGreaterEqualElements( afterGapHead.keys, afterGapHead.startIndex, afterGapHead.cursor, startKey ); if (newEndIndex == afterGapHead.startIndex) { break; } if (afterGapHead.cursor != newEndIndex) { afterGapHead.cursor = newEndIndex; afterGapHead.chunkSize = afterGapHead.cursor - afterGapHead.startIndex; break; } } } } synchronized void clear() { chunks.clear(); } private Chunk splitChunkOnTwoSeparateChunks(Chunk chunk, long key) { /* * [1, 2, 3, 4, 5, 6, 7, 8] :: beforeSplit * |s--------chunk-------e| * * splitChunkOnTwoSeparateChunks(chunk, 5) * * [1, 2, 3, 4, 5, 6, 7, 8] :: afterSplit * |s--tail--e||s--head--e| */ int splitIndex = findFirstIndexOfGreaterEqualElements( chunk.keys, chunk.startIndex, chunk.cursor, key ); if (splitIndex == chunk.startIndex || splitIndex == chunk.cursor) { return null; } int newTailSize = splitIndex - chunk.startIndex; Chunk newTail = new Chunk(chunk.keys, chunk.values, chunk.startIndex, splitIndex, newTailSize); chunk.startIndex = splitIndex; chunk.chunkSize = chunk.chunkSize - newTailSize; return newTail; } private boolean isFirstElementIsEmptyOrGreaterEqualThanKey(Chunk chunk, long key) { return chunk.cursor == chunk.startIndex || chunk.keys[chunk.startIndex] >= key; } private boolean isLastElementIsLessThanKey(Chunk chunk, long key) { return chunk.cursor == chunk.startIndex || chunk.keys[chunk.cursor - 1] < key; } private int findFirstIndexOfGreaterEqualElements(long[] array, int startIndex, int endIndex, long minKey) { if (endIndex == startIndex || array[startIndex] >= minKey) { return startIndex; } int searchIndex = binarySearch(array, startIndex, endIndex, minKey); int realIndex; if (searchIndex < 0) { realIndex = -(searchIndex + 1); } else { realIndex = searchIndex; } return realIndex; } private static class Chunk { private final long[] keys; private final long[] values; private int chunkSize; // can differ from keys.length after half clear() private int startIndex = 0; private int cursor = 0; private Chunk(int chunkSize) { this.chunkSize = chunkSize; this.keys = new long[chunkSize]; this.values = new long[chunkSize]; } private Chunk(final long[] keys, final long[] values, final int startIndex, final int cursor, final int chunkSize) { this.keys = keys; this.values = values; this.startIndex = startIndex; this.cursor = cursor; this.chunkSize = chunkSize; } private void append(long key, long value) { keys[cursor] = key; values[cursor] = value; cursor++; } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Clock.java000066400000000000000000000027051315671014200261050ustar00rootroot00000000000000package com.codahale.metrics; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; /** * An abstraction for how time passes. It is passed to {@link Timer} to track timing. */ public abstract class Clock { /** * Returns the current time tick. * * @return time tick in nanoseconds */ public abstract long getTick(); /** * Returns the current time in milliseconds. * * @return time in milliseconds */ public long getTime() { return System.currentTimeMillis(); } private static final Clock DEFAULT = new UserTimeClock(); /** * The default clock to use. * * @return the default {@link Clock} instance * * @see Clock.UserTimeClock */ public static Clock defaultClock() { return DEFAULT; } /** * A clock implementation which returns the current time in epoch nanoseconds. */ public static class UserTimeClock extends Clock { @Override public long getTick() { return System.nanoTime(); } } /** * A clock implementation which returns the current thread's CPU time. */ public static class CpuTimeClock extends Clock { private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); @Override public long getTick() { return THREAD_MX_BEAN.getCurrentThreadCpuTime(); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ConsoleReporter.java000066400000000000000000000362241315671014200302020ustar00rootroot00000000000000package com.codahale.metrics; import java.io.PrintStream; import java.text.DateFormat; import java.util.*; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * A reporter which outputs measurements to a {@link PrintStream}, like {@code System.out}. */ public class ConsoleReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link ConsoleReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link ConsoleReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } /** * A builder for {@link ConsoleReporter} instances. Defaults to using the default locale and * time zone, writing to {@code System.out}, converting rates to events/second, converting * durations to milliseconds, and not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private PrintStream output; private Locale locale; private Clock clock; private TimeZone timeZone; private TimeUnit rateUnit; private TimeUnit durationUnit; private MetricFilter filter; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop; private Set disabledMetricAttributes; private Builder(MetricRegistry registry) { this.registry = registry; this.output = System.out; this.locale = Locale.getDefault(); this.clock = Clock.defaultClock(); this.timeZone = TimeZone.getDefault(); this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.filter = MetricFilter.ALL; this.executor = null; this.shutdownExecutorOnStop = true; disabledMetricAttributes = Collections.emptySet(); } /** * Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter. * Default value is true. * Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}. * * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter * @return {@code this} */ public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } /** * Specifies the executor to use while scheduling reporting of metrics. * Default value is null. * Null value leads to executor will be auto created on start. * * @param executor the executor to use while scheduling reporting of metrics. * @return {@code this} */ public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } /** * Write to the given {@link PrintStream}. * * @param output a {@link PrintStream} instance. * @return {@code this} */ public Builder outputTo(PrintStream output) { this.output = output; return this; } /** * Format numbers for the given {@link Locale}. * * @param locale a {@link Locale} * @return {@code this} */ public Builder formattedFor(Locale locale) { this.locale = locale; return this; } /** * Use the given {@link Clock} instance for the time. * * @param clock a {@link Clock} instance * @return {@code this} */ public Builder withClock(Clock clock) { this.clock = clock; return this; } /** * Use the given {@link TimeZone} for the time. * * @param timeZone a {@link TimeZone} * @return {@code this} */ public Builder formattedFor(TimeZone timeZone) { this.timeZone = timeZone; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } /** * Don't report the passed metric attributes for all metrics (e.g. "p999", "stddev" or "m15"). * See {@link MetricAttribute}. * * @param disabledMetricAttributes a {@link MetricFilter} * @return {@code this} */ public Builder disabledMetricAttributes(Set disabledMetricAttributes) { this.disabledMetricAttributes = disabledMetricAttributes; return this; } /** * Builds a {@link ConsoleReporter} with the given properties. * * @return a {@link ConsoleReporter} */ public ConsoleReporter build() { return new ConsoleReporter(registry, output, locale, clock, timeZone, rateUnit, durationUnit, filter, executor, shutdownExecutorOnStop, disabledMetricAttributes); } } private static final int CONSOLE_WIDTH = 80; private final PrintStream output; private final Locale locale; private final Clock clock; private final DateFormat dateFormat; private ConsoleReporter(MetricRegistry registry, PrintStream output, Locale locale, Clock clock, TimeZone timeZone, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, Set disabledMetricAttributes) { super(registry, "console-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop, disabledMetricAttributes); this.output = output; this.locale = locale; this.clock = clock; this.dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale); dateFormat.setTimeZone(timeZone); } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { final String dateTime = dateFormat.format(new Date(clock.getTime())); printWithBanner(dateTime, '='); output.println(); if (!gauges.isEmpty()) { printWithBanner("-- Gauges", '-'); for (Map.Entry entry : gauges.entrySet()) { output.println(entry.getKey()); printGauge(entry); } output.println(); } if (!counters.isEmpty()) { printWithBanner("-- Counters", '-'); for (Map.Entry entry : counters.entrySet()) { output.println(entry.getKey()); printCounter(entry); } output.println(); } if (!histograms.isEmpty()) { printWithBanner("-- Histograms", '-'); for (Map.Entry entry : histograms.entrySet()) { output.println(entry.getKey()); printHistogram(entry.getValue()); } output.println(); } if (!meters.isEmpty()) { printWithBanner("-- Meters", '-'); for (Map.Entry entry : meters.entrySet()) { output.println(entry.getKey()); printMeter(entry.getValue()); } output.println(); } if (!timers.isEmpty()) { printWithBanner("-- Timers", '-'); for (Map.Entry entry : timers.entrySet()) { output.println(entry.getKey()); printTimer(entry.getValue()); } output.println(); } output.println(); output.flush(); } private void printMeter(Meter meter) { printIfEnabled(MetricAttribute.COUNT, String.format(locale, " count = %d", meter.getCount())); printIfEnabled(MetricAttribute.MEAN_RATE, String.format(locale, " mean rate = %2.2f events/%s", convertRate(meter.getMeanRate()), getRateUnit())); printIfEnabled(MetricAttribute.M1_RATE, String.format(locale, " 1-minute rate = %2.2f events/%s", convertRate(meter.getOneMinuteRate()), getRateUnit())); printIfEnabled(MetricAttribute.M5_RATE, String.format(locale, " 5-minute rate = %2.2f events/%s", convertRate(meter.getFiveMinuteRate()), getRateUnit())); printIfEnabled(MetricAttribute.M15_RATE, String.format(locale, " 15-minute rate = %2.2f events/%s", convertRate(meter.getFifteenMinuteRate()), getRateUnit())); } private void printCounter(Map.Entry entry) { output.printf(locale, " count = %d%n", entry.getValue().getCount()); } private void printGauge(Map.Entry entry) { output.printf(locale, " value = %s%n", entry.getValue().getValue()); } private void printHistogram(Histogram histogram) { printIfEnabled(MetricAttribute.COUNT, String.format(locale, " count = %d", histogram.getCount())); Snapshot snapshot = histogram.getSnapshot(); printIfEnabled(MetricAttribute.MIN, String.format(locale, " min = %d", snapshot.getMin())); printIfEnabled(MetricAttribute.MAX, String.format(locale, " max = %d", snapshot.getMax())); printIfEnabled(MetricAttribute.MEAN, String.format(locale, " mean = %2.2f", snapshot.getMean())); printIfEnabled(MetricAttribute.STDDEV, String.format(locale, " stddev = %2.2f", snapshot.getStdDev())); printIfEnabled(MetricAttribute.P50, String.format(locale, " median = %2.2f", snapshot.getMedian())); printIfEnabled(MetricAttribute.P75, String.format(locale, " 75%% <= %2.2f", snapshot.get75thPercentile())); printIfEnabled(MetricAttribute.P95, String.format(locale, " 95%% <= %2.2f", snapshot.get95thPercentile())); printIfEnabled(MetricAttribute.P98, String.format(locale, " 98%% <= %2.2f", snapshot.get98thPercentile())); printIfEnabled(MetricAttribute.P99, String.format(locale, " 99%% <= %2.2f", snapshot.get99thPercentile())); printIfEnabled(MetricAttribute.P999, String.format(locale, " 99.9%% <= %2.2f", snapshot.get999thPercentile())); } private void printTimer(Timer timer) { final Snapshot snapshot = timer.getSnapshot(); printIfEnabled(MetricAttribute.COUNT, String.format(locale, " count = %d", timer.getCount())); printIfEnabled(MetricAttribute.MEAN_RATE, String.format(locale, " mean rate = %2.2f calls/%s", convertRate(timer.getMeanRate()), getRateUnit())); printIfEnabled(MetricAttribute.M1_RATE, String.format(locale, " 1-minute rate = %2.2f calls/%s", convertRate(timer.getOneMinuteRate()), getRateUnit())); printIfEnabled(MetricAttribute.M5_RATE, String.format(locale, " 5-minute rate = %2.2f calls/%s", convertRate(timer.getFiveMinuteRate()), getRateUnit())); printIfEnabled(MetricAttribute.M15_RATE, String.format(locale, " 15-minute rate = %2.2f calls/%s", convertRate(timer.getFifteenMinuteRate()), getRateUnit())); printIfEnabled(MetricAttribute.MIN, String.format(locale, " min = %2.2f %s", convertDuration(snapshot.getMin()), getDurationUnit())); printIfEnabled(MetricAttribute.MAX, String.format(locale, " max = %2.2f %s", convertDuration(snapshot.getMax()), getDurationUnit())); printIfEnabled(MetricAttribute.MEAN, String.format(locale, " mean = %2.2f %s", convertDuration(snapshot.getMean()), getDurationUnit())); printIfEnabled(MetricAttribute.STDDEV, String.format(locale, " stddev = %2.2f %s", convertDuration(snapshot.getStdDev()), getDurationUnit())); printIfEnabled(MetricAttribute.P50, String.format(locale, " median = %2.2f %s", convertDuration(snapshot.getMedian()), getDurationUnit())); printIfEnabled(MetricAttribute.P75, String.format(locale, " 75%% <= %2.2f %s", convertDuration(snapshot.get75thPercentile()), getDurationUnit())); printIfEnabled(MetricAttribute.P95, String.format(locale, " 95%% <= %2.2f %s", convertDuration(snapshot.get95thPercentile()), getDurationUnit())); printIfEnabled(MetricAttribute.P98, String.format(locale, " 98%% <= %2.2f %s", convertDuration(snapshot.get98thPercentile()), getDurationUnit())); printIfEnabled(MetricAttribute.P99, String.format(locale, " 99%% <= %2.2f %s", convertDuration(snapshot.get99thPercentile()), getDurationUnit())); printIfEnabled(MetricAttribute.P999, String.format(locale, " 99.9%% <= %2.2f %s", convertDuration(snapshot.get999thPercentile()), getDurationUnit())); } private void printWithBanner(String s, char c) { output.print(s); output.print(' '); for (int i = 0; i < (CONSOLE_WIDTH - s.length() - 1); i++) { output.print(c); } output.println(); } /** * Print only if the attribute is enabled * @param type Metric attribute * @param status Status to be logged */ private void printIfEnabled(MetricAttribute type, String status) { if(getDisabledMetricAttributes().contains(type)) { return; } output.println(status); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Counter.java000066400000000000000000000020111315671014200264570ustar00rootroot00000000000000package com.codahale.metrics; /** * An incrementing and decrementing counter metric. */ public class Counter implements Metric, Counting { private final LongAdderAdapter count; public Counter() { this.count = LongAdderProxy.create(); } /** * Increment the counter by one. */ public void inc() { inc(1); } /** * Increment the counter by {@code n}. * * @param n the amount by which the counter will be increased */ public void inc(long n) { count.add(n); } /** * Decrement the counter by one. */ public void dec() { dec(1); } /** * Decrement the counter by {@code n}. * * @param n the amount by which the counter will be decreased */ public void dec(long n) { count.add(-n); } /** * Returns the counter's current value. * * @return the counter's current value */ @Override public long getCount() { return count.sum(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Counting.java000066400000000000000000000003501315671014200266320ustar00rootroot00000000000000package com.codahale.metrics; /** * An interface for metric types which have counts. */ public interface Counting { /** * Returns the current count. * * @return the current count */ long getCount(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/CsvFileProvider.java000066400000000000000000000004111315671014200301100ustar00rootroot00000000000000package com.codahale.metrics; import java.io.File; /** * This interface allows a pluggable implementation of what file names * the {@link CsvReporter} will write to. */ public interface CsvFileProvider { File getFile(File directory, String metricName); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/CsvReporter.java000066400000000000000000000263551315671014200273370ustar00rootroot00000000000000package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * A reporter which creates a comma-separated values file of the measurements for each metric. */ public class CsvReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link CsvReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link CsvReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } /** * A builder for {@link CsvReporter} instances. Defaults to using the default locale, converting * rates to events/second, converting durations to milliseconds, and not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private Locale locale; private TimeUnit rateUnit; private TimeUnit durationUnit; private Clock clock; private MetricFilter filter; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop; private CsvFileProvider csvFileProvider; private Builder(MetricRegistry registry) { this.registry = registry; this.locale = Locale.getDefault(); this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.clock = Clock.defaultClock(); this.filter = MetricFilter.ALL; this.executor = null; this.shutdownExecutorOnStop = true; this.csvFileProvider = new FixedNameCsvFileProvider(); } /** * Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter. * Default value is true. * Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}. * * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter * @return {@code this} */ public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } /** * Specifies the executor to use while scheduling reporting of metrics. * Default value is null. * Null value leads to executor will be auto created on start. * * @param executor the executor to use while scheduling reporting of metrics. * @return {@code this} */ public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } /** * Format numbers for the given {@link Locale}. * * @param locale a {@link Locale} * @return {@code this} */ public Builder formatFor(Locale locale) { this.locale = locale; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Use the given {@link Clock} instance for the time. * * @param clock a {@link Clock} instance * @return {@code this} */ public Builder withClock(Clock clock) { this.clock = clock; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } public Builder withCsvFileProvider(CsvFileProvider csvFileProvider) { this.csvFileProvider = csvFileProvider; return this; } /** * Builds a {@link CsvReporter} with the given properties, writing {@code .csv} files to the * given directory. * * @param directory the directory in which the {@code .csv} files will be created * @return a {@link CsvReporter} */ public CsvReporter build(File directory) { return new CsvReporter(registry, directory, locale, rateUnit, durationUnit, clock, filter, executor, shutdownExecutorOnStop, csvFileProvider); } } private static final Logger LOGGER = LoggerFactory.getLogger(CsvReporter.class); private static final Charset UTF_8 = Charset.forName("UTF-8"); private final File directory; private final Locale locale; private final Clock clock; private final CsvFileProvider csvFileProvider; private CsvReporter(MetricRegistry registry, File directory, Locale locale, TimeUnit rateUnit, TimeUnit durationUnit, Clock clock, MetricFilter filter, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, CsvFileProvider csvFileProvider) { super(registry, "csv-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop); this.directory = directory; this.locale = locale; this.clock = clock; this.csvFileProvider = csvFileProvider; } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { final long timestamp = TimeUnit.MILLISECONDS.toSeconds(clock.getTime()); for (Map.Entry entry : gauges.entrySet()) { reportGauge(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry entry : counters.entrySet()) { reportCounter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry entry : histograms.entrySet()) { reportHistogram(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry entry : meters.entrySet()) { reportMeter(timestamp, entry.getKey(), entry.getValue()); } for (Map.Entry entry : timers.entrySet()) { reportTimer(timestamp, entry.getKey(), entry.getValue()); } } private void reportTimer(long timestamp, String name, Timer timer) { final Snapshot snapshot = timer.getSnapshot(); report(timestamp, name, "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,calls/%s,%s", timer.getCount(), convertDuration(snapshot.getMax()), convertDuration(snapshot.getMean()), convertDuration(snapshot.getMin()), convertDuration(snapshot.getStdDev()), convertDuration(snapshot.getMedian()), convertDuration(snapshot.get75thPercentile()), convertDuration(snapshot.get95thPercentile()), convertDuration(snapshot.get98thPercentile()), convertDuration(snapshot.get99thPercentile()), convertDuration(snapshot.get999thPercentile()), convertRate(timer.getMeanRate()), convertRate(timer.getOneMinuteRate()), convertRate(timer.getFiveMinuteRate()), convertRate(timer.getFifteenMinuteRate()), getRateUnit(), getDurationUnit()); } private void reportMeter(long timestamp, String name, Meter meter) { report(timestamp, name, "count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", "%d,%f,%f,%f,%f,events/%s", meter.getCount(), convertRate(meter.getMeanRate()), convertRate(meter.getOneMinuteRate()), convertRate(meter.getFiveMinuteRate()), convertRate(meter.getFifteenMinuteRate()), getRateUnit()); } private void reportHistogram(long timestamp, String name, Histogram histogram) { final Snapshot snapshot = histogram.getSnapshot(); report(timestamp, name, "count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", "%d,%d,%f,%d,%f,%f,%f,%f,%f,%f,%f", histogram.getCount(), snapshot.getMax(), snapshot.getMean(), snapshot.getMin(), snapshot.getStdDev(), snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), snapshot.get98thPercentile(), snapshot.get99thPercentile(), snapshot.get999thPercentile()); } private void reportCounter(long timestamp, String name, Counter counter) { report(timestamp, name, "count", "%d", counter.getCount()); } private void reportGauge(long timestamp, String name, Gauge gauge) { report(timestamp, name, "value", "%s", gauge.getValue()); } private void report(long timestamp, String name, String header, String line, Object... values) { try { final File file = csvFileProvider.getFile(directory, name); final boolean fileAlreadyExists = file.exists(); if (fileAlreadyExists || file.createNewFile()) { final PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file,true), UTF_8)); try { if (!fileAlreadyExists) { out.println("t," + header); } out.printf(locale, String.format(locale, "%d,%s%n", timestamp, line), values); } finally { out.close(); } } } catch (IOException e) { LOGGER.warn("Error writing to {}", name, e); } } protected String sanitize(String name) { return name; } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/DefaultObjectNameFactory.java000066400000000000000000000016071315671014200317160ustar00rootroot00000000000000package com.codahale.metrics; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultObjectNameFactory implements ObjectNameFactory { private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); @Override public ObjectName createName(String type, String domain, String name) { try { ObjectName objectName = new ObjectName(domain, "name", name); if (objectName.isPattern()) { objectName = new ObjectName(domain, "name", ObjectName.quote(name)); } return objectName; } catch (MalformedObjectNameException e) { try { return new ObjectName(domain, "name", ObjectName.quote(name)); } catch (MalformedObjectNameException e1) { LOGGER.warn("Unable to register {} {}", type, name, e1); throw new RuntimeException(e1); } } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/DerivativeGauge.java000066400000000000000000000015001315671014200301150ustar00rootroot00000000000000package com.codahale.metrics; /** * A gauge whose value is derived from the value of another gauge. * * @param the base gauge's value type * @param the derivative type */ public abstract class DerivativeGauge implements Gauge { private final Gauge base; /** * Creates a new derivative with the given base gauge. * * @param base the gauge from which to derive this gauge's value */ protected DerivativeGauge(Gauge base) { this.base = base; } @Override public T getValue() { return transform(base.getValue()); } /** * Transforms the value of the base gauge to the value of this gauge. * * @param value the value of the base gauge * @return this gauge's value */ protected abstract T transform(F value); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/EWMA.java000066400000000000000000000065521315671014200256070ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.TimeUnit; import static java.lang.Math.exp; /** * An exponentially-weighted moving average. * * @see UNIX Load Average Part 1: How * It Works * @see UNIX Load Average Part 2: Not * Your Average Average * @see EMA */ public class EWMA { private static final int INTERVAL = 5; private static final double SECONDS_PER_MINUTE = 60.0; private static final int ONE_MINUTE = 1; private static final int FIVE_MINUTES = 5; private static final int FIFTEEN_MINUTES = 15; private static final double M1_ALPHA = 1 - exp(-INTERVAL / SECONDS_PER_MINUTE / ONE_MINUTE); private static final double M5_ALPHA = 1 - exp(-INTERVAL / SECONDS_PER_MINUTE / FIVE_MINUTES); private static final double M15_ALPHA = 1 - exp(-INTERVAL / SECONDS_PER_MINUTE / FIFTEEN_MINUTES); private volatile boolean initialized = false; private volatile double rate = 0.0; private final LongAdderAdapter uncounted = LongAdderProxy.create(); private final double alpha, interval; /** * Creates a new EWMA which is equivalent to the UNIX one minute load average and which expects * to be ticked every 5 seconds. * * @return a one-minute EWMA */ public static EWMA oneMinuteEWMA() { return new EWMA(M1_ALPHA, INTERVAL, TimeUnit.SECONDS); } /** * Creates a new EWMA which is equivalent to the UNIX five minute load average and which expects * to be ticked every 5 seconds. * * @return a five-minute EWMA */ public static EWMA fiveMinuteEWMA() { return new EWMA(M5_ALPHA, INTERVAL, TimeUnit.SECONDS); } /** * Creates a new EWMA which is equivalent to the UNIX fifteen minute load average and which * expects to be ticked every 5 seconds. * * @return a fifteen-minute EWMA */ public static EWMA fifteenMinuteEWMA() { return new EWMA(M15_ALPHA, INTERVAL, TimeUnit.SECONDS); } /** * Create a new EWMA with a specific smoothing constant. * * @param alpha the smoothing constant * @param interval the expected tick interval * @param intervalUnit the time unit of the tick interval */ public EWMA(double alpha, long interval, TimeUnit intervalUnit) { this.interval = intervalUnit.toNanos(interval); this.alpha = alpha; } /** * Update the moving average with a new value. * * @param n the new value */ public void update(long n) { uncounted.add(n); } /** * Mark the passage of time and decay the current rate accordingly. */ public void tick() { final long count = uncounted.sumThenReset(); final double instantRate = count / interval; if (initialized) { rate += (alpha * (instantRate - rate)); } else { rate = instantRate; initialized = true; } } /** * Returns the rate in the given units of time. * * @param rateUnit the unit of time * @return the rate */ public double getRate(TimeUnit rateUnit) { return rate * (double) rateUnit.toNanos(1); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java000066400000000000000000000176151315671014200332600ustar00rootroot00000000000000package com.codahale.metrics; import java.util.ArrayList; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; import static java.lang.Math.exp; import static java.lang.Math.min; import com.codahale.metrics.WeightedSnapshot.WeightedSample; /** * An exponentially-decaying random reservoir of {@code long}s. Uses Cormode et al's * forward-decaying priority reservoir sampling method to produce a statistically representative * sampling reservoir, exponentially biased towards newer entries. * * @see * Cormode et al. Forward Decay: A Practical Time Decay Model for Streaming Systems. ICDE '09: * Proceedings of the 2009 IEEE International Conference on Data Engineering (2009) */ public class ExponentiallyDecayingReservoir implements Reservoir { private static final int DEFAULT_SIZE = 1028; private static final double DEFAULT_ALPHA = 0.015; private static final long RESCALE_THRESHOLD = TimeUnit.HOURS.toNanos(1); private final ConcurrentSkipListMap values; private final ReentrantReadWriteLock lock; private final double alpha; private final int size; private final AtomicLong count; private volatile long startTime; private final AtomicLong nextScaleTime; private final Clock clock; /** * Creates a new {@link ExponentiallyDecayingReservoir} of 1028 elements, which offers a 99.9% * confidence level with a 5% margin of error assuming a normal distribution, and an alpha * factor of 0.015, which heavily biases the reservoir to the past 5 minutes of measurements. */ public ExponentiallyDecayingReservoir() { this(DEFAULT_SIZE, DEFAULT_ALPHA); } /** * Creates a new {@link ExponentiallyDecayingReservoir}. * * @param size the number of samples to keep in the sampling reservoir * @param alpha the exponential decay factor; the higher this is, the more biased the reservoir * will be towards newer values */ public ExponentiallyDecayingReservoir(int size, double alpha) { this(size, alpha, Clock.defaultClock()); } /** * Creates a new {@link ExponentiallyDecayingReservoir}. * * @param size the number of samples to keep in the sampling reservoir * @param alpha the exponential decay factor; the higher this is, the more biased the reservoir * will be towards newer values * @param clock the clock used to timestamp samples and track rescaling */ public ExponentiallyDecayingReservoir(int size, double alpha, Clock clock) { this.values = new ConcurrentSkipListMap(); this.lock = new ReentrantReadWriteLock(); this.alpha = alpha; this.size = size; this.clock = clock; this.count = new AtomicLong(0); this.startTime = currentTimeInSeconds(); this.nextScaleTime = new AtomicLong(clock.getTick() + RESCALE_THRESHOLD); } @Override public int size() { return (int) min(size, count.get()); } @Override public void update(long value) { update(value, currentTimeInSeconds()); } /** * Adds an old value with a fixed timestamp to the reservoir. * * @param value the value to be added * @param timestamp the epoch timestamp of {@code value} in seconds */ public void update(long value, long timestamp) { rescaleIfNeeded(); lockForRegularUsage(); try { final double itemWeight = weight(timestamp - startTime); final WeightedSample sample = new WeightedSample(value, itemWeight); final double priority = itemWeight / ThreadLocalRandomProxy.current().nextDouble(); final long newCount = count.incrementAndGet(); if (newCount <= size) { values.put(priority, sample); } else { Double first = values.firstKey(); if (first < priority && values.putIfAbsent(priority, sample) == null) { // ensure we always remove an item while (values.remove(first) == null) { first = values.firstKey(); } } } } finally { unlockForRegularUsage(); } } private void rescaleIfNeeded() { final long now = clock.getTick(); final long next = nextScaleTime.get(); if (now >= next) { rescale(now, next); } } @Override public Snapshot getSnapshot() { rescaleIfNeeded(); lockForRegularUsage(); try { return new WeightedSnapshot(values.values()); } finally { unlockForRegularUsage(); } } private long currentTimeInSeconds() { return TimeUnit.MILLISECONDS.toSeconds(clock.getTime()); } private double weight(long t) { return exp(alpha * t); } /* "A common feature of the above techniques—indeed, the key technique that * allows us to track the decayed weights efficiently—is that they maintain * counts and other quantities based on g(ti − L), and only scale by g(t − L) * at query time. But while g(ti −L)/g(t−L) is guaranteed to lie between zero * and one, the intermediate values of g(ti − L) could become very large. For * polynomial functions, these values should not grow too large, and should be * effectively represented in practice by floating point values without loss of * precision. For exponential functions, these values could grow quite large as * new values of (ti − L) become large, and potentially exceed the capacity of * common floating point types. However, since the values stored by the * algorithms are linear combinations of g values (scaled sums), they can be * rescaled relative to a new landmark. That is, by the analysis of exponential * decay in Section III-A, the choice of L does not affect the final result. We * can therefore multiply each value based on L by a factor of exp(−α(L′ − L)), * and obtain the correct value as if we had instead computed relative to a new * landmark L′ (and then use this new L′ at query time). This can be done with * a linear pass over whatever data structure is being used." */ private void rescale(long now, long next) { lockForRescale(); try { if (nextScaleTime.compareAndSet(next, now + RESCALE_THRESHOLD)) { final long oldStartTime = startTime; this.startTime = currentTimeInSeconds(); final double scalingFactor = exp(-alpha * (startTime - oldStartTime)); if (Double.compare(scalingFactor, 0) == 0) { values.clear(); } else { final ArrayList keys = new ArrayList(values.keySet()); for (Double key : keys) { final WeightedSample sample = values.remove(key); final WeightedSample newSample = new WeightedSample(sample.value, sample.weight * scalingFactor); values.put(key * scalingFactor, newSample); } } // make sure the counter is in sync with the number of stored samples. count.set(values.size()); } } finally { unlockForRescale(); } } private void unlockForRescale() { lock.writeLock().unlock(); } private void lockForRescale() { lock.writeLock().lock(); } private void lockForRegularUsage() { lock.readLock().lock(); } private void unlockForRegularUsage() { lock.readLock().unlock(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/FixedNameCsvFileProvider.java000066400000000000000000000014251315671014200316770ustar00rootroot00000000000000package com.codahale.metrics; import java.io.File; /** * This implementation of the {@link CsvFileProvider} will always return the same name * for the same metric. This means the CSV file will grow indefinitely. */ public class FixedNameCsvFileProvider implements CsvFileProvider { @Override public File getFile(File directory, String metricName) { return new File(directory, sanitize(metricName) + ".csv"); } protected String sanitize(String metricName) { //Forward slash character is definitely illegal in both Windows and Linux //https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx final String sanitizedName = metricName.replaceFirst("^/","").replaceAll("/","."); return sanitizedName; } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Gauge.java000066400000000000000000000012051315671014200260740ustar00rootroot00000000000000package com.codahale.metrics; /** * A gauge metric is an instantaneous reading of a particular value. To instrument a queue's depth, * for example:
    *

    
     * final Queue<String> queue = new ConcurrentLinkedQueue<String>();
     * final Gauge<Integer> queueDepth = new Gauge<Integer>() {
     *     public Integer getValue() {
     *         return queue.size();
     *     }
     * };
     * 
    * * @param the type of the metric's value */ public interface Gauge extends Metric { /** * Returns the metric's current value. * * @return the metric's current value */ T getValue(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Histogram.java000066400000000000000000000024351315671014200270070ustar00rootroot00000000000000package com.codahale.metrics; /** * A metric which calculates the distribution of a value. * * @see Accurately computing running * variance */ public class Histogram implements Metric, Sampling, Counting { private final Reservoir reservoir; private final LongAdderAdapter count; /** * Creates a new {@link Histogram} with the given reservoir. * * @param reservoir the reservoir to create a histogram from */ public Histogram(Reservoir reservoir) { this.reservoir = reservoir; this.count = LongAdderProxy.create(); } /** * Adds a recorded value. * * @param value the length of the value */ public void update(int value) { update((long) value); } /** * Adds a recorded value. * * @param value the length of the value */ public void update(long value) { count.increment(); reservoir.update(value); } /** * Returns the number of values recorded. * * @return the number of values recorded */ @Override public long getCount() { return count.sum(); } @Override public Snapshot getSnapshot() { return reservoir.getSnapshot(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/InstrumentedExecutorService.java000066400000000000000000000145421315671014200325750ustar00rootroot00000000000000package com.codahale.metrics; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; /** * An {@link ExecutorService} that monitors the number of tasks submitted, running, * completed and also keeps a {@link Timer} for the task duration. *

    * It will register the metrics using the given (or auto-generated) name as classifier, e.g: * "your-executor-service.submitted", "your-executor-service.running", etc. */ public class InstrumentedExecutorService implements ExecutorService { private static final AtomicLong nameCounter = new AtomicLong(); private final ExecutorService delegate; private final Meter submitted; private final Counter running; private final Meter completed; private final Timer duration; /** * Wraps an {@link ExecutorService} uses an auto-generated default name. * * @param delegate {@link ExecutorService} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. */ public InstrumentedExecutorService(ExecutorService delegate, MetricRegistry registry) { this(delegate, registry, "instrumented-delegate-" + nameCounter.incrementAndGet()); } /** * Wraps an {@link ExecutorService} with an explicit name. * * @param delegate {@link ExecutorService} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. * @param name name for this executor service. */ public InstrumentedExecutorService(ExecutorService delegate, MetricRegistry registry, String name) { this.delegate = delegate; this.submitted = registry.meter(MetricRegistry.name(name, "submitted")); this.running = registry.counter(MetricRegistry.name(name, "running")); this.completed = registry.meter(MetricRegistry.name(name, "completed")); this.duration = registry.timer(MetricRegistry.name(name, "duration")); } /** * {@inheritDoc} */ @Override public void execute(Runnable runnable) { submitted.mark(); delegate.execute(new InstrumentedRunnable(runnable)); } /** * {@inheritDoc} */ @Override public Future submit(Runnable runnable) { submitted.mark(); return delegate.submit(new InstrumentedRunnable(runnable)); } /** * {@inheritDoc} */ @Override public Future submit(Runnable runnable, T result) { submitted.mark(); return delegate.submit(new InstrumentedRunnable(runnable), result); } /** * {@inheritDoc} */ @Override public Future submit(Callable task) { submitted.mark(); return delegate.submit(new InstrumentedCallable(task)); } /** * {@inheritDoc} */ @Override public List> invokeAll(Collection> tasks) throws InterruptedException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAll(instrumented); } /** * {@inheritDoc} */ @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAll(instrumented, timeout, unit); } /** * {@inheritDoc} */ @Override public T invokeAny(Collection> tasks) throws ExecutionException, InterruptedException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAny(instrumented); } /** * {@inheritDoc} */ @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAny(instrumented, timeout, unit); } private Collection> instrument(Collection> tasks) { final List> instrumented = new ArrayList>(tasks.size()); for (Callable task : tasks) { instrumented.add(new InstrumentedCallable(task)); } return instrumented; } @Override public void shutdown() { delegate.shutdown(); } @Override public List shutdownNow() { return delegate.shutdownNow(); } @Override public boolean isShutdown() { return delegate.isShutdown(); } @Override public boolean isTerminated() { return delegate.isTerminated(); } @Override public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException { return delegate.awaitTermination(l, timeUnit); } private class InstrumentedRunnable implements Runnable { private final Runnable task; InstrumentedRunnable(Runnable task) { this.task = task; } @Override public void run() { running.inc(); final Timer.Context context = duration.time(); try { task.run(); } finally { context.stop(); running.dec(); completed.mark(); } } } private class InstrumentedCallable implements Callable { private final Callable callable; InstrumentedCallable(Callable callable) { this.callable = callable; } @Override public T call() throws Exception { running.inc(); final Timer.Context context = duration.time(); try { return callable.call(); } finally { context.stop(); running.dec(); completed.mark(); } } } } InstrumentedScheduledExecutorService.java000066400000000000000000000220021315671014200343250ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/main/java/com/codahale/metricspackage com.codahale.metrics; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; /** * An {@link ScheduledExecutorService} that monitors the number of tasks submitted, running, * completed and also keeps a {@link Timer} for the task duration. *

    * It will register the metrics using the given (or auto-generated) name as classifier, e.g: * "your-executor-service.submitted", "your-executor-service.running", etc. */ public class InstrumentedScheduledExecutorService implements ScheduledExecutorService { private static final AtomicLong nameCounter = new AtomicLong(); private final ScheduledExecutorService delegate; private final Meter submitted; private final Counter running; private final Meter completed; private final Timer duration; private final Meter scheduledOnce; private final Meter scheduledRepetitively; private final Counter scheduledOverrun; private final Histogram percentOfPeriod; /** * Wraps an {@link ScheduledExecutorService} uses an auto-generated default name. * * @param delegate {@link ScheduledExecutorService} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. */ public InstrumentedScheduledExecutorService(ScheduledExecutorService delegate, MetricRegistry registry) { this(delegate, registry, "instrumented-scheduled-executor-service-" + nameCounter.incrementAndGet()); } /** * Wraps an {@link ScheduledExecutorService} with an explicit name. * * @param delegate {@link ScheduledExecutorService} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. * @param name name for this executor service. */ public InstrumentedScheduledExecutorService(ScheduledExecutorService delegate, MetricRegistry registry, String name) { this.delegate = delegate; this.submitted = registry.meter(MetricRegistry.name(name, "submitted")); this.running = registry.counter(MetricRegistry.name(name, "running")); this.completed = registry.meter(MetricRegistry.name(name, "completed")); this.duration = registry.timer(MetricRegistry.name(name, "duration")); this.scheduledOnce = registry.meter(MetricRegistry.name(name, "scheduled.once")); this.scheduledRepetitively = registry.meter(MetricRegistry.name(name, "scheduled.repetitively")); this.scheduledOverrun = registry.counter(MetricRegistry.name(name, "scheduled.overrun")); this.percentOfPeriod = registry.histogram(MetricRegistry.name(name, "scheduled.percent-of-period")); } /** * {@inheritDoc} */ @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { scheduledOnce.mark(); return delegate.schedule(new InstrumentedRunnable(command), delay, unit); } /** * {@inheritDoc} */ @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { scheduledOnce.mark(); return delegate.schedule(new InstrumentedCallable(callable), delay, unit); } /** * {@inheritDoc} */ @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { scheduledRepetitively.mark(); return delegate.scheduleAtFixedRate(new InstrumentedPeriodicRunnable(command, period, unit), initialDelay, period, unit); } /** * {@inheritDoc} */ @Override public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { scheduledRepetitively.mark(); return delegate.scheduleWithFixedDelay(new InstrumentedRunnable(command), initialDelay, delay, unit); } /** * {@inheritDoc} */ @Override public void shutdown() { delegate.shutdown(); } /** * {@inheritDoc} */ @Override public List shutdownNow() { return delegate.shutdownNow(); } /** * {@inheritDoc} */ @Override public boolean isShutdown() { return delegate.isShutdown(); } /** * {@inheritDoc} */ @Override public boolean isTerminated() { return delegate.isTerminated(); } /** * {@inheritDoc} */ @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return delegate.awaitTermination(timeout, unit); } /** * {@inheritDoc} */ @Override public Future submit(Callable task) { submitted.mark(); return delegate.submit(new InstrumentedCallable(task)); } /** * {@inheritDoc} */ @Override public Future submit(Runnable task, T result) { submitted.mark(); return delegate.submit(new InstrumentedRunnable(task), result); } /** * {@inheritDoc} */ @Override public Future submit(Runnable task) { submitted.mark(); return delegate.submit(new InstrumentedRunnable(task)); } /** * {@inheritDoc} */ @Override public List> invokeAll(Collection> tasks) throws InterruptedException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAll(instrumented); } /** * {@inheritDoc} */ @Override public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAll(instrumented, timeout, unit); } /** * {@inheritDoc} */ @Override public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAny(instrumented); } /** * {@inheritDoc} */ @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { submitted.mark(tasks.size()); Collection> instrumented = instrument(tasks); return delegate.invokeAny(instrumented, timeout, unit); } private Collection> instrument(Collection> tasks) { final List> instrumented = new ArrayList>(tasks.size()); for (Callable task : tasks) { instrumented.add(new InstrumentedCallable(task)); } return instrumented; } /** * {@inheritDoc} */ @Override public void execute(Runnable command) { submitted.mark(); delegate.execute(new InstrumentedRunnable(command)); } private class InstrumentedRunnable implements Runnable { private final Runnable command; InstrumentedRunnable(Runnable command) { this.command = command; } @Override public void run() { running.inc(); final Timer.Context context = duration.time(); try { command.run(); } finally { context.stop(); running.dec(); completed.mark(); } } } private class InstrumentedPeriodicRunnable implements Runnable { private final Runnable command; private final long periodInNanos; InstrumentedPeriodicRunnable(Runnable command, long period, TimeUnit unit) { this.command = command; this.periodInNanos = unit.toNanos(period); } @Override public void run() { running.inc(); final Timer.Context context = duration.time(); try { command.run(); } finally { final long elapsed = context.stop(); running.dec(); completed.mark(); if (elapsed > periodInNanos) { scheduledOverrun.inc(); } percentOfPeriod.update((100L * elapsed) / periodInNanos); } } } private class InstrumentedCallable implements Callable { private final Callable task; InstrumentedCallable(Callable task) { this.task = task; } @Override public T call() throws Exception { running.inc(); final Timer.Context context = duration.time(); try { return task.call(); } finally { context.stop(); running.dec(); completed.mark(); } } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/InstrumentedThreadFactory.java000066400000000000000000000046321315671014200322140ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; /** * A {@link ThreadFactory} that monitors the number of threads created, running and terminated. *

    * It will register the metrics using the given (or auto-generated) name as classifier, e.g: * "your-thread-delegate.created", "your-thread-delegate.running", etc. */ public class InstrumentedThreadFactory implements ThreadFactory { private static final AtomicLong nameCounter = new AtomicLong(); private final ThreadFactory delegate; private final Meter created; private final Counter running; private final Meter terminated; /** * Wraps a {@link ThreadFactory}, uses a default auto-generated name. * * @param delegate {@link ThreadFactory} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. */ public InstrumentedThreadFactory(ThreadFactory delegate, MetricRegistry registry) { this(delegate, registry, "instrumented-thread-delegate-" + nameCounter.incrementAndGet()); } /** * Wraps a {@link ThreadFactory} with an explicit name. * * @param delegate {@link ThreadFactory} to wrap. * @param registry {@link MetricRegistry} that will contain the metrics. * @param name name for this delegate. */ public InstrumentedThreadFactory(ThreadFactory delegate, MetricRegistry registry, String name) { this.delegate = delegate; this.created = registry.meter(MetricRegistry.name(name, "created")); this.running = registry.counter(MetricRegistry.name(name, "running")); this.terminated = registry.meter(MetricRegistry.name(name, "terminated")); } /** * {@inheritDoc} */ @Override public Thread newThread(Runnable runnable) { Runnable wrappedRunnable = new InstrumentedRunnable(runnable); Thread thread = delegate.newThread(wrappedRunnable); created.mark(); return thread; } private class InstrumentedRunnable implements Runnable { private final Runnable task; InstrumentedRunnable(Runnable task) { this.task = task; } @Override public void run() { running.inc(); try { task.run(); } finally { running.dec(); terminated.mark(); } } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/JmxAttributeGauge.java000066400000000000000000000037511315671014200304470ustar00rootroot00000000000000package com.codahale.metrics; import java.io.IOException; import javax.management.JMException; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.Set; /** * A {@link Gauge} implementation which queries an {@link MBeanServerConnection} for an attribute of an object. */ public class JmxAttributeGauge implements Gauge { private final MBeanServerConnection mBeanServerConn; private final ObjectName objectName; private final String attributeName; /** * Creates a new JmxAttributeGauge. * * @param objectName the name of the object * @param attributeName the name of the object's attribute */ public JmxAttributeGauge(ObjectName objectName, String attributeName) { this(ManagementFactory.getPlatformMBeanServer(), objectName, attributeName); } /** * Creates a new JmxAttributeGauge. * * @param mBeanServerConn the {@link MBeanServerConnection} * @param objectName the name of the object * @param attributeName the name of the object's attribute */ public JmxAttributeGauge(MBeanServerConnection mBeanServerConn, ObjectName objectName, String attributeName) { this.mBeanServerConn = mBeanServerConn; this.objectName = objectName; this.attributeName = attributeName; } @Override public Object getValue() { try { return mBeanServerConn.getAttribute(getObjectName(), attributeName); } catch (IOException e) { return null; } catch (JMException e) { return null; } } private ObjectName getObjectName() throws IOException { if (objectName.isPattern()) { Set foundNames = mBeanServerConn.queryNames(objectName, null); if (foundNames.size() == 1) { return foundNames.iterator().next(); } } return objectName; } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/JmxReporter.java000066400000000000000000000560561315671014200273430ustar00rootroot00000000000000package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.*; import java.io.Closeable; import java.lang.management.ManagementFactory; import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * A reporter which listens for new metrics and exposes them as namespaced MBeans. */ public class JmxReporter implements Reporter, Closeable { /** * Returns a new {@link Builder} for {@link JmxReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link JmxReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } /** * A builder for {@link JmxReporter} instances. Defaults to using the default MBean server and * not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private MBeanServer mBeanServer; private TimeUnit rateUnit; private TimeUnit durationUnit; private ObjectNameFactory objectNameFactory; private MetricFilter filter = MetricFilter.ALL; private String domain; private Map specificDurationUnits; private Map specificRateUnits; private Builder(MetricRegistry registry) { this.registry = registry; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.domain = "metrics"; this.objectNameFactory = new DefaultObjectNameFactory(); this.specificDurationUnits = Collections.emptyMap(); this.specificRateUnits = Collections.emptyMap(); } /** * Register MBeans with the given {@link MBeanServer}. * * @param mBeanServer an {@link MBeanServer} * @return {@code this} */ public Builder registerWith(MBeanServer mBeanServer) { this.mBeanServer = mBeanServer; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } public Builder createsObjectNamesWith(ObjectNameFactory onFactory) { if(onFactory == null) { throw new IllegalArgumentException("null objectNameFactory"); } this.objectNameFactory = onFactory; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } public Builder inDomain(String domain) { this.domain = domain; return this; } /** * Use specific {@link TimeUnit}s for the duration of the metrics with these names. * * @param specificDurationUnits a map of metric names and specific {@link TimeUnit}s * @return {@code this} */ public Builder specificDurationUnits(Map specificDurationUnits) { this.specificDurationUnits = Collections.unmodifiableMap(specificDurationUnits); return this; } /** * Use specific {@link TimeUnit}s for the rate of the metrics with these names. * * @param specificRateUnits a map of metric names and specific {@link TimeUnit}s * @return {@code this} */ public Builder specificRateUnits(Map specificRateUnits) { this.specificRateUnits = Collections.unmodifiableMap(specificRateUnits); return this; } /** * Builds a {@link JmxReporter} with the given properties. * * @return a {@link JmxReporter} */ public JmxReporter build() { final MetricTimeUnits timeUnits = new MetricTimeUnits(rateUnit, durationUnit, specificRateUnits, specificDurationUnits); if (mBeanServer==null) { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } return new JmxReporter(mBeanServer, domain, registry, filter, timeUnits, objectNameFactory); } } private static final Logger LOGGER = LoggerFactory.getLogger(JmxReporter.class); // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface MetricMBean { ObjectName objectName(); } // CHECKSTYLE:ON private abstract static class AbstractBean implements MetricMBean { private final ObjectName objectName; AbstractBean(ObjectName objectName) { this.objectName = objectName; } @Override public ObjectName objectName() { return objectName; } } // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface JmxGaugeMBean extends MetricMBean { Object getValue(); } // CHECKSTYLE:ON private static class JmxGauge extends AbstractBean implements JmxGaugeMBean { private final Gauge metric; private JmxGauge(Gauge metric, ObjectName objectName) { super(objectName); this.metric = metric; } @Override public Object getValue() { return metric.getValue(); } } // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface JmxCounterMBean extends MetricMBean { long getCount(); } // CHECKSTYLE:ON private static class JmxCounter extends AbstractBean implements JmxCounterMBean { private final Counter metric; private JmxCounter(Counter metric, ObjectName objectName) { super(objectName); this.metric = metric; } @Override public long getCount() { return metric.getCount(); } } // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface JmxHistogramMBean extends MetricMBean { long getCount(); long getMin(); long getMax(); double getMean(); double getStdDev(); double get50thPercentile(); double get75thPercentile(); double get95thPercentile(); double get98thPercentile(); double get99thPercentile(); double get999thPercentile(); long[] values(); long getSnapshotSize(); } // CHECKSTYLE:ON private static class JmxHistogram implements JmxHistogramMBean { private final ObjectName objectName; private final Histogram metric; private JmxHistogram(Histogram metric, ObjectName objectName) { this.metric = metric; this.objectName = objectName; } @Override public ObjectName objectName() { return objectName; } @Override public double get50thPercentile() { return metric.getSnapshot().getMedian(); } @Override public long getCount() { return metric.getCount(); } @Override public long getMin() { return metric.getSnapshot().getMin(); } @Override public long getMax() { return metric.getSnapshot().getMax(); } @Override public double getMean() { return metric.getSnapshot().getMean(); } @Override public double getStdDev() { return metric.getSnapshot().getStdDev(); } @Override public double get75thPercentile() { return metric.getSnapshot().get75thPercentile(); } @Override public double get95thPercentile() { return metric.getSnapshot().get95thPercentile(); } @Override public double get98thPercentile() { return metric.getSnapshot().get98thPercentile(); } @Override public double get99thPercentile() { return metric.getSnapshot().get99thPercentile(); } @Override public double get999thPercentile() { return metric.getSnapshot().get999thPercentile(); } @Override public long[] values() { return metric.getSnapshot().getValues(); } public long getSnapshotSize() { return metric.getSnapshot().size(); } } //CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface JmxMeterMBean extends MetricMBean { long getCount(); double getMeanRate(); double getOneMinuteRate(); double getFiveMinuteRate(); double getFifteenMinuteRate(); String getRateUnit(); } //CHECKSTYLE:ON private static class JmxMeter extends AbstractBean implements JmxMeterMBean { private final Metered metric; private final double rateFactor; private final String rateUnit; private JmxMeter(Metered metric, ObjectName objectName, TimeUnit rateUnit) { super(objectName); this.metric = metric; this.rateFactor = rateUnit.toSeconds(1); this.rateUnit = ("events/" + calculateRateUnit(rateUnit)).intern(); } @Override public long getCount() { return metric.getCount(); } @Override public double getMeanRate() { return metric.getMeanRate() * rateFactor; } @Override public double getOneMinuteRate() { return metric.getOneMinuteRate() * rateFactor; } @Override public double getFiveMinuteRate() { return metric.getFiveMinuteRate() * rateFactor; } @Override public double getFifteenMinuteRate() { return metric.getFifteenMinuteRate() * rateFactor; } @Override public String getRateUnit() { return rateUnit; } private String calculateRateUnit(TimeUnit unit) { final String s = unit.toString().toLowerCase(Locale.US); return s.substring(0, s.length() - 1); } } // CHECKSTYLE:OFF @SuppressWarnings("UnusedDeclaration") public interface JmxTimerMBean extends JmxMeterMBean { double getMin(); double getMax(); double getMean(); double getStdDev(); double get50thPercentile(); double get75thPercentile(); double get95thPercentile(); double get98thPercentile(); double get99thPercentile(); double get999thPercentile(); long[] values(); String getDurationUnit(); } // CHECKSTYLE:ON static class JmxTimer extends JmxMeter implements JmxTimerMBean { private final Timer metric; private final double durationFactor; private final String durationUnit; private JmxTimer(Timer metric, ObjectName objectName, TimeUnit rateUnit, TimeUnit durationUnit) { super(metric, objectName, rateUnit); this.metric = metric; this.durationFactor = 1.0 / durationUnit.toNanos(1); this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); } @Override public double get50thPercentile() { return metric.getSnapshot().getMedian() * durationFactor; } @Override public double getMin() { return metric.getSnapshot().getMin() * durationFactor; } @Override public double getMax() { return metric.getSnapshot().getMax() * durationFactor; } @Override public double getMean() { return metric.getSnapshot().getMean() * durationFactor; } @Override public double getStdDev() { return metric.getSnapshot().getStdDev() * durationFactor; } @Override public double get75thPercentile() { return metric.getSnapshot().get75thPercentile() * durationFactor; } @Override public double get95thPercentile() { return metric.getSnapshot().get95thPercentile() * durationFactor; } @Override public double get98thPercentile() { return metric.getSnapshot().get98thPercentile() * durationFactor; } @Override public double get99thPercentile() { return metric.getSnapshot().get99thPercentile() * durationFactor; } @Override public double get999thPercentile() { return metric.getSnapshot().get999thPercentile() * durationFactor; } @Override public long[] values() { return metric.getSnapshot().getValues(); } @Override public String getDurationUnit() { return durationUnit; } } private static class JmxListener implements MetricRegistryListener { private final String name; private final MBeanServer mBeanServer; private final MetricFilter filter; private final MetricTimeUnits timeUnits; private final Map registered; private final ObjectNameFactory objectNameFactory; private JmxListener(MBeanServer mBeanServer, String name, MetricFilter filter, MetricTimeUnits timeUnits, ObjectNameFactory objectNameFactory) { this.mBeanServer = mBeanServer; this.name = name; this.filter = filter; this.timeUnits = timeUnits; this.registered = new ConcurrentHashMap(); this.objectNameFactory = objectNameFactory; } private void registerMBean(Object mBean, ObjectName objectName) throws InstanceAlreadyExistsException, JMException { ObjectInstance objectInstance = mBeanServer.registerMBean(mBean, objectName); if (objectInstance != null) { // the websphere mbeanserver rewrites the objectname to include // cell, node & server info // make sure we capture the new objectName for unregistration registered.put(objectName, objectInstance.getObjectName()); } else { registered.put(objectName, objectName); } } private void unregisterMBean(ObjectName originalObjectName) throws InstanceNotFoundException, MBeanRegistrationException { ObjectName storedObjectName = registered.remove(originalObjectName); if (storedObjectName != null) { mBeanServer.unregisterMBean(storedObjectName); } else { mBeanServer.unregisterMBean(originalObjectName); } } @Override public void onGaugeAdded(String name, Gauge gauge) { try { if (filter.matches(name, gauge)) { final ObjectName objectName = createName("gauges", name); registerMBean(new JmxGauge(gauge, objectName), objectName); } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register gauge", e); } catch (JMException e) { LOGGER.warn("Unable to register gauge", e); } } @Override public void onGaugeRemoved(String name) { try { final ObjectName objectName = createName("gauges", name); unregisterMBean(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister gauge", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister gauge", e); } } @Override public void onCounterAdded(String name, Counter counter) { try { if (filter.matches(name, counter)) { final ObjectName objectName = createName("counters", name); registerMBean(new JmxCounter(counter, objectName), objectName); } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register counter", e); } catch (JMException e) { LOGGER.warn("Unable to register counter", e); } } @Override public void onCounterRemoved(String name) { try { final ObjectName objectName = createName("counters", name); unregisterMBean(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister counter", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister counter", e); } } @Override public void onHistogramAdded(String name, Histogram histogram) { try { if (filter.matches(name, histogram)) { final ObjectName objectName = createName("histograms", name); registerMBean(new JmxHistogram(histogram, objectName), objectName); } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register histogram", e); } catch (JMException e) { LOGGER.warn("Unable to register histogram", e); } } @Override public void onHistogramRemoved(String name) { try { final ObjectName objectName = createName("histograms", name); unregisterMBean(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister histogram", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister histogram", e); } } @Override public void onMeterAdded(String name, Meter meter) { try { if (filter.matches(name, meter)) { final ObjectName objectName = createName("meters", name); registerMBean(new JmxMeter(meter, objectName, timeUnits.rateFor(name)), objectName); } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register meter", e); } catch (JMException e) { LOGGER.warn("Unable to register meter", e); } } @Override public void onMeterRemoved(String name) { try { final ObjectName objectName = createName("meters", name); unregisterMBean(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister meter", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister meter", e); } } @Override public void onTimerAdded(String name, Timer timer) { try { if (filter.matches(name, timer)) { final ObjectName objectName = createName("timers", name); registerMBean(new JmxTimer(timer, objectName, timeUnits.rateFor(name), timeUnits.durationFor(name)), objectName); } } catch (InstanceAlreadyExistsException e) { LOGGER.debug("Unable to register timer", e); } catch (JMException e) { LOGGER.warn("Unable to register timer", e); } } @Override public void onTimerRemoved(String name) { try { final ObjectName objectName = createName("timers", name); unregisterMBean(objectName); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister timer", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister timer", e); } } private ObjectName createName(String type, String name) { return objectNameFactory.createName(type, this.name, name); } void unregisterAll() { for (ObjectName name : registered.keySet()) { try { unregisterMBean(name); } catch (InstanceNotFoundException e) { LOGGER.debug("Unable to unregister metric", e); } catch (MBeanRegistrationException e) { LOGGER.warn("Unable to unregister metric", e); } } registered.clear(); } } private static class MetricTimeUnits { private final TimeUnit defaultRate; private final TimeUnit defaultDuration; private final Map rateOverrides; private final Map durationOverrides; MetricTimeUnits(TimeUnit defaultRate, TimeUnit defaultDuration, Map rateOverrides, Map durationOverrides) { this.defaultRate = defaultRate; this.defaultDuration = defaultDuration; this.rateOverrides = rateOverrides; this.durationOverrides = durationOverrides; } public TimeUnit durationFor(String name) { return durationOverrides.containsKey(name) ? durationOverrides.get(name) : defaultDuration; } public TimeUnit rateFor(String name) { return rateOverrides.containsKey(name) ? rateOverrides.get(name) : defaultRate; } } private final MetricRegistry registry; private final JmxListener listener; private JmxReporter(MBeanServer mBeanServer, String domain, MetricRegistry registry, MetricFilter filter, MetricTimeUnits timeUnits, ObjectNameFactory objectNameFactory) { this.registry = registry; this.listener = new JmxListener(mBeanServer, domain, filter, timeUnits, objectNameFactory); } /** * Starts the reporter. */ public void start() { registry.addListener(listener); } /** * Stops the reporter. */ public void stop() { registry.removeListener(listener); listener.unregisterAll(); } /** * Stops the reporter. */ @Override public void close() { stop(); } /** * Visible for testing */ ObjectNameFactory getObjectNameFactory() { return listener.objectNameFactory; } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/JvmAttributeGaugeSet.java000066400000000000000000000034561315671014200311230ustar00rootroot00000000000000package com.codahale.metrics; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * A set of gauges for the JVM name, vendor, and uptime. */ public class JvmAttributeGaugeSet implements MetricSet { private final RuntimeMXBean runtime; /** * Creates a new set of gauges. */ public JvmAttributeGaugeSet() { this(ManagementFactory.getRuntimeMXBean()); } /** * Creates a new set of gauges with the given {@link RuntimeMXBean}. * @param runtime JVM management interface with access to system properties */ public JvmAttributeGaugeSet(RuntimeMXBean runtime) { this.runtime = runtime; } @Override public Map getMetrics() { final Map gauges = new HashMap(); gauges.put("name", new Gauge() { @Override public String getValue() { return runtime.getName(); } }); gauges.put("vendor", new Gauge() { @Override public String getValue() { return String.format(Locale.US, "%s %s %s (%s)", runtime.getVmVendor(), runtime.getVmName(), runtime.getVmVersion(), runtime.getSpecVersion()); } }); gauges.put("uptime", new Gauge() { @Override public Long getValue() { return runtime.getUptime(); } }); return Collections.unmodifiableMap(gauges); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/LongAdder.java000066400000000000000000000134641315671014200267150ustar00rootroot00000000000000/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ * * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.14&view=markup */ package com.codahale.metrics; import java.io.Serializable; import java.util.concurrent.atomic.AtomicLong; // CHECKSTYLE:OFF /** * One or more variables that together maintain an initially zero {@code long} sum. When updates * (method {@link #add}) are contended across threads, the set of variables may grow dynamically to * reduce contention. Method {@link #sum} (or, equivalently, {@link #longValue}) returns the current * total combined across the variables maintaining the sum. *

    *

    This class is usually preferable to {@link AtomicLong} when multiple threads update a common * sum that is used for purposes such as collecting statistics, not for fine-grained synchronization * control. Under low update contention, the two classes have similar characteristics. But under * high contention, expected throughput of this class is significantly higher, at the expense of * higher space consumption. *

    *

    This class extends {@link Number}, but does not define methods such as {@code * equals}, {@code hashCode} and {@code compareTo} because instances are expected to be mutated, and * so are not useful as collection keys. *

    *

    jsr166e note: This class is targeted to be placed in java.util.concurrent.atomic. * * @author Doug Lea * @since 1.8 */ @SuppressWarnings("all") class LongAdder extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; /** * Version of plus for use in retryUpdate */ final long fn(long v, long x) { return v + x; } /** * Creates a new adder with initial sum of zero. */ LongAdder() { } /** * Adds the given value. * * @param x the value to add */ public void add(long x) { Cell[] as; long b, v; HashCode hc; Cell a; int n; if ((as = cells) != null || !casBase(b = base, b + x)) { boolean uncontended = true; int h = (hc = threadHashCode.get()).code; if (as == null || (n = as.length) < 1 || (a = as[(n - 1) & h]) == null || !(uncontended = a.cas(v = a.value, v + x))) retryUpdate(x, hc, uncontended); } } /** * Equivalent to {@code add(1)}. */ public void increment() { add(1L); } /** * Equivalent to {@code add(-1)}. */ public void decrement() { add(-1L); } /** * Returns the current sum. The returned value is NOT an atomic snapshot; invocation * in the absence of concurrent updates returns an accurate result, but concurrent updates that * occur while the sum is being calculated might not be incorporated. * * @return the sum */ public long sum() { long sum = base; Cell[] as = cells; if (as != null) { int n = as.length; for (int i = 0; i < n; ++i) { Cell a = as[i]; if (a != null) sum += a.value; } } return sum; } /** * Resets variables maintaining the sum to zero. This method may be a useful alternative to * creating a new adder, but is only effective if there are no concurrent updates. Because this * method is intrinsically racy, it should only be used when it is known that no threads are * concurrently updating. */ public void reset() { internalReset(0L); } /** * Equivalent in effect to {@link #sum} followed by {@link #reset}. This method may apply for * example during quiescent points between multithreaded computations. If there are updates * concurrent with this method, the returned value is not guaranteed to be the final * value occurring before the reset. * * @return the sum */ public long sumThenReset() { long sum = base; Cell[] as = cells; base = 0L; if (as != null) { int n = as.length; for (int i = 0; i < n; ++i) { Cell a = as[i]; if (a != null) { sum += a.value; a.value = 0L; } } } return sum; } /** * Returns the String representation of the {@link #sum}. * * @return the String representation of the {@link #sum} */ public String toString() { return Long.toString(sum()); } /** * Equivalent to {@link #sum}. * * @return the sum */ public long longValue() { return sum(); } /** * Returns the {@link #sum} as an {@code int} after a narrowing primitive conversion. */ public int intValue() { return (int) sum(); } /** * Returns the {@link #sum} as a {@code float} after a widening primitive conversion. */ public float floatValue() { return (float) sum(); } /** * Returns the {@link #sum} as a {@code double} after a widening primitive conversion. */ public double doubleValue() { return (double) sum(); } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeLong(sum()); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); busy = 0; cells = null; base = s.readLong(); } } // CHECKSTYLE:ON metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/LongAdderAdapter.java000066400000000000000000000004611315671014200302070ustar00rootroot00000000000000package com.codahale.metrics; /** * Interface which exposes the LongAdder functionality. Allows different * LongAdder implementations to coexist together. */ interface LongAdderAdapter { void add(long x); long sum(); void increment(); void decrement(); long sumThenReset(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/LongAdderProxy.java000066400000000000000000000060211315671014200277460ustar00rootroot00000000000000package com.codahale.metrics; /** * Proxy for creating long adders depending on the runtime. By default it tries to * the JDK's implementation and fallbacks to the internal one if the JDK doesn't provide * any. The JDK's LongAdder and the internal one don't have a common interface, therefore * we adapten them to {@link InternalLongAdderProvider}, which serves as a common interface for * long adders. */ class LongAdderProxy { private interface Provider { LongAdderAdapter get(); } /** * To avoid NoClassDefFoundError during loading {@link LongAdderProxy} */ private static class JdkProvider implements Provider { @Override public LongAdderAdapter get() { return new LongAdderAdapter() { private final java.util.concurrent.atomic.LongAdder longAdder = new java.util.concurrent.atomic.LongAdder(); @Override public void add(long x) { longAdder.add(x); } @Override public long sum() { return longAdder.sum(); } @Override public void increment() { longAdder.increment(); } @Override public void decrement() { longAdder.decrement(); } @Override public long sumThenReset() { return longAdder.sumThenReset(); } }; } } /** * Backed by the internal LongAdder */ private static class InternalLongAdderProvider implements Provider { @Override public LongAdderAdapter get() { return new LongAdderAdapter() { private final LongAdder longAdder = new LongAdder(); @Override public void add(long x) { longAdder.add(x); } @Override public long sum() { return longAdder.sum(); } @Override public void increment() { longAdder.increment(); } @Override public void decrement() { longAdder.decrement(); } @Override public long sumThenReset() { return longAdder.sumThenReset(); } }; } } private static final Provider INSTANCE = getLongAdderProvider(); private static Provider getLongAdderProvider() { try { final JdkProvider jdkProvider = new JdkProvider(); jdkProvider.get(); // To trigger a possible `NoClassDefFoundError` exception return jdkProvider; } catch (Throwable e) { return new InternalLongAdderProvider(); } } public static LongAdderAdapter create() { return INSTANCE.get(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Meter.java000066400000000000000000000055611315671014200261310ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * A meter metric which measures mean throughput and one-, five-, and fifteen-minute * exponentially-weighted moving average throughputs. * * @see EWMA */ public class Meter implements Metered { private static final long TICK_INTERVAL = TimeUnit.SECONDS.toNanos(5); private final EWMA m1Rate = EWMA.oneMinuteEWMA(); private final EWMA m5Rate = EWMA.fiveMinuteEWMA(); private final EWMA m15Rate = EWMA.fifteenMinuteEWMA(); private final LongAdderAdapter count = LongAdderProxy.create(); private final long startTime; private final AtomicLong lastTick; private final Clock clock; /** * Creates a new {@link Meter}. */ public Meter() { this(Clock.defaultClock()); } /** * Creates a new {@link Meter}. * * @param clock the clock to use for the meter ticks */ public Meter(Clock clock) { this.clock = clock; this.startTime = this.clock.getTick(); this.lastTick = new AtomicLong(startTime); } /** * Mark the occurrence of an event. */ public void mark() { mark(1); } /** * Mark the occurrence of a given number of events. * * @param n the number of events */ public void mark(long n) { tickIfNecessary(); count.add(n); m1Rate.update(n); m5Rate.update(n); m15Rate.update(n); } private void tickIfNecessary() { final long oldTick = lastTick.get(); final long newTick = clock.getTick(); final long age = newTick - oldTick; if (age > TICK_INTERVAL) { final long newIntervalStartTick = newTick - age % TICK_INTERVAL; if (lastTick.compareAndSet(oldTick, newIntervalStartTick)) { final long requiredTicks = age / TICK_INTERVAL; for (long i = 0; i < requiredTicks; i++) { m1Rate.tick(); m5Rate.tick(); m15Rate.tick(); } } } } @Override public long getCount() { return count.sum(); } @Override public double getFifteenMinuteRate() { tickIfNecessary(); return m15Rate.getRate(TimeUnit.SECONDS); } @Override public double getFiveMinuteRate() { tickIfNecessary(); return m5Rate.getRate(TimeUnit.SECONDS); } @Override public double getMeanRate() { if (getCount() == 0) { return 0.0; } else { final double elapsed = (clock.getTick() - startTime); return getCount() / elapsed * TimeUnit.SECONDS.toNanos(1); } } @Override public double getOneMinuteRate() { tickIfNecessary(); return m1Rate.getRate(TimeUnit.SECONDS); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Metered.java000066400000000000000000000037221315671014200264370ustar00rootroot00000000000000package com.codahale.metrics; /** * An object which maintains mean and exponentially-weighted rate. */ public interface Metered extends Metric, Counting { /** * Returns the number of events which have been marked. * * @return the number of events which have been marked */ long getCount(); /** * Returns the fifteen-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created. *

    * This rate has the same exponential decay factor as the fifteen-minute load average in the * {@code top} Unix command. * * @return the fifteen-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created */ double getFifteenMinuteRate(); /** * Returns the five-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created. *

    * This rate has the same exponential decay factor as the five-minute load average in the {@code * top} Unix command. * * @return the five-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created */ double getFiveMinuteRate(); /** * Returns the mean rate at which events have occurred since the meter was created. * * @return the mean rate at which events have occurred since the meter was created */ double getMeanRate(); /** * Returns the one-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created. *

    * This rate has the same exponential decay factor as the one-minute load average in the {@code * top} Unix command. * * @return the one-minute exponentially-weighted moving average rate at which events have * occurred since the meter was created */ double getOneMinuteRate(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Metric.java000066400000000000000000000001751315671014200262740ustar00rootroot00000000000000package com.codahale.metrics; /** * A tag interface to indicate that a class is a metric. */ public interface Metric { } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/MetricAttribute.java000066400000000000000000000011051315671014200301520ustar00rootroot00000000000000package com.codahale.metrics; /** * Represents attributes of metrics which can be reported. */ public enum MetricAttribute { MAX("max"), MEAN("mean"), MIN("min"), STDDEV("stddev"), P50("p50"), P75("p75"), P95("p95"), P98("p98"), P99("p99"), P999("p999"), COUNT("count"), M1_RATE("m1_rate"), M5_RATE("m5_rate"), M15_RATE("m15_rate"), MEAN_RATE("mean_rate"); private final String code; MetricAttribute(String code) { this.code = code; } public String getCode() { return code; } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/MetricFilter.java000066400000000000000000000012761315671014200274450ustar00rootroot00000000000000package com.codahale.metrics; /** * A filter used to determine whether or not a metric should be reported, among other things. */ public interface MetricFilter { /** * Matches all metrics, regardless of type or name. */ MetricFilter ALL = new MetricFilter() { @Override public boolean matches(String name, Metric metric) { return true; } }; /** * Returns {@code true} if the metric matches the filter; {@code false} otherwise. * * @param name the metric's name * @param metric the metric * @return {@code true} if the metric matches the filter */ boolean matches(String name, Metric metric); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/MetricRegistry.java000066400000000000000000000456261315671014200300370ustar00rootroot00000000000000package com.codahale.metrics; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; /** * A registry of metric instances. */ public class MetricRegistry implements MetricSet { /** * Concatenates elements to form a dotted name, eliding any null values or empty strings. * * @param name the first element of the name * @param names the remaining elements of the name * @return {@code name} and {@code names} concatenated by periods */ public static String name(String name, String... names) { final StringBuilder builder = new StringBuilder(); append(builder, name); if (names != null) { for (String s : names) { append(builder, s); } } return builder.toString(); } /** * Concatenates a class name and elements to form a dotted name, eliding any null values or * empty strings. * * @param klass the first element of the name * @param names the remaining elements of the name * @return {@code klass} and {@code names} concatenated by periods */ public static String name(Class klass, String... names) { return name(klass.getName(), names); } private static void append(StringBuilder builder, String part) { if (part != null && !part.isEmpty()) { if (builder.length() > 0) { builder.append('.'); } builder.append(part); } } private final ConcurrentMap metrics; private final List listeners; /** * Creates a new {@link MetricRegistry}. */ public MetricRegistry() { this.metrics = buildMap(); this.listeners = new CopyOnWriteArrayList(); } /** * Creates a new {@link ConcurrentMap} implementation for use inside the registry. Override this * to create a {@link MetricRegistry} with space- or time-bounded metric lifecycles, for * example. * * @return a new {@link ConcurrentMap} */ protected ConcurrentMap buildMap() { return new ConcurrentHashMap(); } /** * Given a {@link Metric}, registers it under the given name. * * @param name the name of the metric * @param metric the metric * @param the type of the metric * @return {@code metric} * @throws IllegalArgumentException if the name is already registered */ @SuppressWarnings("unchecked") public T register(String name, T metric) throws IllegalArgumentException { if (metric instanceof MetricSet) { registerAll(name, (MetricSet) metric); } else { final Metric existing = metrics.putIfAbsent(name, metric); if (existing == null) { onMetricAdded(name, metric); } else { throw new IllegalArgumentException("A metric named " + name + " already exists"); } } return metric; } /** * Given a metric set, registers them. * * @param metrics a set of metrics * @throws IllegalArgumentException if any of the names are already registered */ public void registerAll(MetricSet metrics) throws IllegalArgumentException { registerAll(null, metrics); } /** * Return the {@link Counter} registered under this name; or create and register * a new {@link Counter} if none is registered. * * @param name the name of the metric * @return a new or pre-existing {@link Counter} */ public Counter counter(String name) { return getOrAdd(name, MetricBuilder.COUNTERS); } /** * Return the {@link Counter} registered under this name; or create and register * a new {@link Counter} using the provided MetricSupplier if none is registered. * * @param name the name of the metric * @param supplier a MetricSupplier that can be used to manufacture a counter. * @return a new or pre-existing {@link Counter} */ public Counter counter(String name, final MetricSupplier supplier) { return getOrAdd(name, new MetricBuilder() { @Override public Counter newMetric() { return supplier.newMetric(); } @Override public boolean isInstance(Metric metric) { return Counter.class.isInstance(metric); } }); } /** * Return the {@link Histogram} registered under this name; or create and register * a new {@link Histogram} if none is registered. * * @param name the name of the metric * @return a new or pre-existing {@link Histogram} */ public Histogram histogram(String name) { return getOrAdd(name, MetricBuilder.HISTOGRAMS); } /** * Return the {@link Histogram} registered under this name; or create and register * a new {@link Histogram} using the provided MetricSupplier if none is registered. * * @param name the name of the metric * @param supplier a MetricSupplier that can be used to manufacture a histogram * @return a new or pre-existing {@link Histogram} */ public Histogram histogram(String name, final MetricSupplier supplier) { return getOrAdd(name, new MetricBuilder() { @Override public Histogram newMetric() { return supplier.newMetric(); } @Override public boolean isInstance(Metric metric) { return Histogram.class.isInstance(metric); } }); } /** * Return the {@link Meter} registered under this name; or create and register * a new {@link Meter} if none is registered. * * @param name the name of the metric * @return a new or pre-existing {@link Meter} */ public Meter meter(String name) { return getOrAdd(name, MetricBuilder.METERS); } /** * Return the {@link Meter} registered under this name; or create and register * a new {@link Meter} using the provided MetricSupplier if none is registered. * * @param name the name of the metric * @param supplier a MetricSupplier that can be used to manufacture a Meter * @return a new or pre-existing {@link Meter} */ public Meter meter(String name, final MetricSupplier supplier) { return getOrAdd(name, new MetricBuilder() { @Override public Meter newMetric() { return supplier.newMetric(); } @Override public boolean isInstance(Metric metric) { return Meter.class.isInstance(metric); } }); } /** * Return the {@link Timer} registered under this name; or create and register * a new {@link Timer} if none is registered. * * @param name the name of the metric * @return a new or pre-existing {@link Timer} */ public Timer timer(String name) { return getOrAdd(name, MetricBuilder.TIMERS); } /** * Return the {@link Timer} registered under this name; or create and register * a new {@link Timer} using the provided MetricSupplier if none is registered. * * @param name the name of the metric * @param supplier a MetricSupplier that can be used to manufacture a Timer * @return a new or pre-existing {@link Timer} */ public Timer timer(String name, final MetricSupplier supplier) { return getOrAdd(name, new MetricBuilder() { @Override public Timer newMetric() { return supplier.newMetric(); } @Override public boolean isInstance(Metric metric) { return Timer.class.isInstance(metric); } }); } /** * Return the {@link Gauge} registered under this name; or create and register * a new {@link Gauge} using the provided MetricSupplier if none is registered. * * @param name the name of the metric * @param supplier a MetricSupplier that can be used to manufacture a Gauge * @return a new or pre-existing {@link Gauge} */ public Gauge gauge(String name, final MetricSupplier supplier) { return getOrAdd(name, new MetricBuilder() { @Override public Gauge newMetric() { return supplier.newMetric(); } @Override public boolean isInstance(Metric metric) { return Gauge.class.isInstance(metric); } }); } /** * Removes the metric with the given name. * * @param name the name of the metric * @return whether or not the metric was removed */ public boolean remove(String name) { final Metric metric = metrics.remove(name); if (metric != null) { onMetricRemoved(name, metric); return true; } return false; } /** * Removes all metrics which match the given filter. * * @param filter a filter */ public void removeMatching(MetricFilter filter) { for (Map.Entry entry : metrics.entrySet()) { if (filter.matches(entry.getKey(), entry.getValue())) { remove(entry.getKey()); } } } /** * Adds a {@link MetricRegistryListener} to a collection of listeners that will be notified on * metric creation. Listeners will be notified in the order in which they are added. *

    * N.B.: The listener will be notified of all existing metrics when it first registers. * * @param listener the listener that will be notified */ public void addListener(MetricRegistryListener listener) { listeners.add(listener); for (Map.Entry entry : metrics.entrySet()) { notifyListenerOfAddedMetric(listener, entry.getValue(), entry.getKey()); } } /** * Removes a {@link MetricRegistryListener} from this registry's collection of listeners. * * @param listener the listener that will be removed */ public void removeListener(MetricRegistryListener listener) { listeners.remove(listener); } /** * Returns a set of the names of all the metrics in the registry. * * @return the names of all the metrics */ public SortedSet getNames() { return Collections.unmodifiableSortedSet(new TreeSet(metrics.keySet())); } /** * Returns a map of all the gauges in the registry and their names. * * @return all the gauges in the registry */ public SortedMap getGauges() { return getGauges(MetricFilter.ALL); } /** * Returns a map of all the gauges in the registry and their names which match the given filter. * * @param filter the metric filter to match * @return all the gauges in the registry */ public SortedMap getGauges(MetricFilter filter) { return getMetrics(Gauge.class, filter); } /** * Returns a map of all the counters in the registry and their names. * * @return all the counters in the registry */ public SortedMap getCounters() { return getCounters(MetricFilter.ALL); } /** * Returns a map of all the counters in the registry and their names which match the given * filter. * * @param filter the metric filter to match * @return all the counters in the registry */ public SortedMap getCounters(MetricFilter filter) { return getMetrics(Counter.class, filter); } /** * Returns a map of all the histograms in the registry and their names. * * @return all the histograms in the registry */ public SortedMap getHistograms() { return getHistograms(MetricFilter.ALL); } /** * Returns a map of all the histograms in the registry and their names which match the given * filter. * * @param filter the metric filter to match * @return all the histograms in the registry */ public SortedMap getHistograms(MetricFilter filter) { return getMetrics(Histogram.class, filter); } /** * Returns a map of all the meters in the registry and their names. * * @return all the meters in the registry */ public SortedMap getMeters() { return getMeters(MetricFilter.ALL); } /** * Returns a map of all the meters in the registry and their names which match the given filter. * * @param filter the metric filter to match * @return all the meters in the registry */ public SortedMap getMeters(MetricFilter filter) { return getMetrics(Meter.class, filter); } /** * Returns a map of all the timers in the registry and their names. * * @return all the timers in the registry */ public SortedMap getTimers() { return getTimers(MetricFilter.ALL); } /** * Returns a map of all the timers in the registry and their names which match the given filter. * * @param filter the metric filter to match * @return all the timers in the registry */ public SortedMap getTimers(MetricFilter filter) { return getMetrics(Timer.class, filter); } @SuppressWarnings("unchecked") private T getOrAdd(String name, MetricBuilder builder) { final Metric metric = metrics.get(name); if (builder.isInstance(metric)) { return (T) metric; } else if (metric == null) { try { return register(name, builder.newMetric()); } catch (IllegalArgumentException e) { final Metric added = metrics.get(name); if (builder.isInstance(added)) { return (T) added; } } } throw new IllegalArgumentException(name + " is already used for a different type of metric"); } @SuppressWarnings("unchecked") private SortedMap getMetrics(Class klass, MetricFilter filter) { final TreeMap timers = new TreeMap(); for (Map.Entry entry : metrics.entrySet()) { if (klass.isInstance(entry.getValue()) && filter.matches(entry.getKey(), entry.getValue())) { timers.put(entry.getKey(), (T) entry.getValue()); } } return Collections.unmodifiableSortedMap(timers); } private void onMetricAdded(String name, Metric metric) { for (MetricRegistryListener listener : listeners) { notifyListenerOfAddedMetric(listener, metric, name); } } private void notifyListenerOfAddedMetric(MetricRegistryListener listener, Metric metric, String name) { if (metric instanceof Gauge) { listener.onGaugeAdded(name, (Gauge) metric); } else if (metric instanceof Counter) { listener.onCounterAdded(name, (Counter) metric); } else if (metric instanceof Histogram) { listener.onHistogramAdded(name, (Histogram) metric); } else if (metric instanceof Meter) { listener.onMeterAdded(name, (Meter) metric); } else if (metric instanceof Timer) { listener.onTimerAdded(name, (Timer) metric); } else { throw new IllegalArgumentException("Unknown metric type: " + metric.getClass()); } } private void onMetricRemoved(String name, Metric metric) { for (MetricRegistryListener listener : listeners) { notifyListenerOfRemovedMetric(name, metric, listener); } } private void notifyListenerOfRemovedMetric(String name, Metric metric, MetricRegistryListener listener) { if (metric instanceof Gauge) { listener.onGaugeRemoved(name); } else if (metric instanceof Counter) { listener.onCounterRemoved(name); } else if (metric instanceof Histogram) { listener.onHistogramRemoved(name); } else if (metric instanceof Meter) { listener.onMeterRemoved(name); } else if (metric instanceof Timer) { listener.onTimerRemoved(name); } else { throw new IllegalArgumentException("Unknown metric type: " + metric.getClass()); } } private void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException { for (Map.Entry entry : metrics.getMetrics().entrySet()) { if (entry.getValue() instanceof MetricSet) { registerAll(name(prefix, entry.getKey()), (MetricSet) entry.getValue()); } else { register(name(prefix, entry.getKey()), entry.getValue()); } } } @Override public Map getMetrics() { return Collections.unmodifiableMap(metrics); } public interface MetricSupplier { T newMetric(); } /** * A quick and easy way of capturing the notion of default metrics. */ private interface MetricBuilder { MetricBuilder COUNTERS = new MetricBuilder() { @Override public Counter newMetric() { return new Counter(); } @Override public boolean isInstance(Metric metric) { return Counter.class.isInstance(metric); } }; MetricBuilder HISTOGRAMS = new MetricBuilder() { @Override public Histogram newMetric() { return new Histogram(new ExponentiallyDecayingReservoir()); } @Override public boolean isInstance(Metric metric) { return Histogram.class.isInstance(metric); } }; MetricBuilder METERS = new MetricBuilder() { @Override public Meter newMetric() { return new Meter(); } @Override public boolean isInstance(Metric metric) { return Meter.class.isInstance(metric); } }; MetricBuilder TIMERS = new MetricBuilder() { @Override public Timer newMetric() { return new Timer(); } @Override public boolean isInstance(Metric metric) { return Timer.class.isInstance(metric); } }; T newMetric(); boolean isInstance(Metric metric); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/MetricRegistryListener.java000066400000000000000000000060601315671014200315320ustar00rootroot00000000000000package com.codahale.metrics; import java.util.EventListener; /** * Listeners for events from the registry. Listeners must be thread-safe. */ public interface MetricRegistryListener extends EventListener { /** * A no-op implementation of {@link MetricRegistryListener}. */ abstract class Base implements MetricRegistryListener { @Override public void onGaugeAdded(String name, Gauge gauge) { } @Override public void onGaugeRemoved(String name) { } @Override public void onCounterAdded(String name, Counter counter) { } @Override public void onCounterRemoved(String name) { } @Override public void onHistogramAdded(String name, Histogram histogram) { } @Override public void onHistogramRemoved(String name) { } @Override public void onMeterAdded(String name, Meter meter) { } @Override public void onMeterRemoved(String name) { } @Override public void onTimerAdded(String name, Timer timer) { } @Override public void onTimerRemoved(String name) { } } /** * Called when a {@link Gauge} is added to the registry. * * @param name the gauge's name * @param gauge the gauge */ void onGaugeAdded(String name, Gauge gauge); /** * Called when a {@link Gauge} is removed from the registry. * * @param name the gauge's name */ void onGaugeRemoved(String name); /** * Called when a {@link Counter} is added to the registry. * * @param name the counter's name * @param counter the counter */ void onCounterAdded(String name, Counter counter); /** * Called when a {@link Counter} is removed from the registry. * * @param name the counter's name */ void onCounterRemoved(String name); /** * Called when a {@link Histogram} is added to the registry. * * @param name the histogram's name * @param histogram the histogram */ void onHistogramAdded(String name, Histogram histogram); /** * Called when a {@link Histogram} is removed from the registry. * * @param name the histogram's name */ void onHistogramRemoved(String name); /** * Called when a {@link Meter} is added to the registry. * * @param name the meter's name * @param meter the meter */ void onMeterAdded(String name, Meter meter); /** * Called when a {@link Meter} is removed from the registry. * * @param name the meter's name */ void onMeterRemoved(String name); /** * Called when a {@link Timer} is added to the registry. * * @param name the timer's name * @param timer the timer */ void onTimerAdded(String name, Timer timer); /** * Called when a {@link Timer} is removed from the registry. * * @param name the timer's name */ void onTimerRemoved(String name); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/MetricSet.java000066400000000000000000000004711315671014200267470ustar00rootroot00000000000000package com.codahale.metrics; import java.util.Map; /** * A set of named metrics. * * @see MetricRegistry#registerAll(MetricSet) */ public interface MetricSet extends Metric { /** * A map of metric names to metrics. * * @return the metrics */ Map getMetrics(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ObjectNameFactory.java000066400000000000000000000002551315671014200304070ustar00rootroot00000000000000package com.codahale.metrics; import javax.management.ObjectName; public interface ObjectNameFactory { ObjectName createName(String type, String domain, String name); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/RatioGauge.java000066400000000000000000000035701315671014200271020ustar00rootroot00000000000000package com.codahale.metrics; import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; /** * A gauge which measures the ratio of one value to another. *

    * If the denominator is zero, not a number, or infinite, the resulting ratio is not a number. */ public abstract class RatioGauge implements Gauge { /** * A ratio of one quantity to another. */ public static class Ratio { /** * Creates a new ratio with the given numerator and denominator. * * @param numerator the numerator of the ratio * @param denominator the denominator of the ratio * @return {@code numerator:denominator} */ public static Ratio of(double numerator, double denominator) { return new Ratio(numerator, denominator); } private final double numerator; private final double denominator; private Ratio(double numerator, double denominator) { this.numerator = numerator; this.denominator = denominator; } /** * Returns the ratio, which is either a {@code double} between 0 and 1 (inclusive) or * {@code NaN}. * * @return the ratio */ public double getValue() { final double d = denominator; if (isNaN(d) || isInfinite(d) || d == 0) { return Double.NaN; } return numerator / d; } @Override public String toString() { return numerator + ":" + denominator; } } /** * Returns the {@link Ratio} which is the gauge's current value. * * @return the {@link Ratio} which is the gauge's current value */ protected abstract Ratio getRatio(); @Override public Double getValue() { return getRatio().getValue(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Reporter.java000066400000000000000000000002001315671014200266400ustar00rootroot00000000000000package com.codahale.metrics; /* * A tag interface to indicate that a class is a Reporter. */ public interface Reporter { } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Reservoir.java000066400000000000000000000010641315671014200270270ustar00rootroot00000000000000package com.codahale.metrics; /** * A statistically representative reservoir of a data stream. */ public interface Reservoir { /** * Returns the number of values recorded. * * @return the number of values recorded */ int size(); /** * Adds a new recorded value to the reservoir. * * @param value a new recorded value */ void update(long value); /** * Returns a snapshot of the reservoir's values. * * @return a snapshot of the reservoir's values */ Snapshot getSnapshot(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Sampling.java000066400000000000000000000003541315671014200266220ustar00rootroot00000000000000package com.codahale.metrics; /** * An object which samples values. */ public interface Sampling { /** * Returns a snapshot of the values. * * @return a snapshot of the values */ Snapshot getSnapshot(); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ScheduledReporter.java000066400000000000000000000261461315671014200305020ustar00rootroot00000000000000package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.util.Collections; import java.util.Locale; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** * The abstract base class for all scheduled reporters (i.e., reporters which process a registry's * metrics periodically). * * @see ConsoleReporter * @see CsvReporter * @see Slf4jReporter */ public abstract class ScheduledReporter implements Closeable, Reporter { private static final Logger LOG = LoggerFactory.getLogger(ScheduledReporter.class); /** * A simple named thread factory. */ @SuppressWarnings("NullableProblems") private static class NamedThreadFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; private NamedThreadFactory(String name) { final SecurityManager s = System.getSecurityManager(); this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = "metrics-" + name + "-thread-"; } @Override public Thread newThread(Runnable r) { final Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); t.setDaemon(true); if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } } private static final AtomicInteger FACTORY_ID = new AtomicInteger(); private final MetricRegistry registry; private final ScheduledExecutorService executor; private final boolean shutdownExecutorOnStop; private final Set disabledMetricAttributes; private ScheduledFuture scheduledFuture; private final MetricFilter filter; private final long durationFactor; private final String durationUnit; private final long rateFactor; private final String rateUnit; /** * Creates a new {@link ScheduledReporter} instance. * * @param registry the {@link com.codahale.metrics.MetricRegistry} containing the metrics this * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report * @param rateUnit a unit of time * @param durationUnit a unit of time */ protected ScheduledReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit) { this(registry, name, filter, rateUnit, durationUnit, createDefaultExecutor(name)); } /** * Creates a new {@link ScheduledReporter} instance. * * @param registry the {@link com.codahale.metrics.MetricRegistry} containing the metrics this * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report * @param executor the executor to use while scheduling reporting of metrics. */ protected ScheduledReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, ScheduledExecutorService executor) { this(registry, name, filter, rateUnit, durationUnit, executor, true); } /** * Creates a new {@link ScheduledReporter} instance. * * @param registry the {@link com.codahale.metrics.MetricRegistry} containing the metrics this * reporter will report * @param name the reporter's name * @param filter the filter for which metrics to report * @param executor the executor to use while scheduling reporting of metrics. * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter */ protected ScheduledReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, ScheduledExecutorService executor, boolean shutdownExecutorOnStop) { this(registry, name, filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop, Collections.emptySet()); } protected ScheduledReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, Set disabledMetricAttributes) { this.registry = registry; this.filter = filter; this.executor = executor == null? createDefaultExecutor(name) : executor; this.shutdownExecutorOnStop = shutdownExecutorOnStop; this.rateFactor = rateUnit.toSeconds(1); this.rateUnit = calculateRateUnit(rateUnit); this.durationFactor = durationUnit.toNanos(1); this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); this.disabledMetricAttributes = disabledMetricAttributes != null ? disabledMetricAttributes : Collections.emptySet(); } /** * Starts the reporter polling at the given period. * * @param period the amount of time between polls * @param unit the unit for {@code period} */ public void start(long period, TimeUnit unit) { start(period, period, unit); } /** * Starts the reporter polling at the given period. * * @param initialDelay the time to delay the first execution * @param period the amount of time between polls * @param unit the unit for {@code period} */ synchronized public void start(long initialDelay, long period, TimeUnit unit) { if (this.scheduledFuture != null) { throw new IllegalArgumentException("Reporter already started"); } this.scheduledFuture = executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { report(); } catch (Throwable ex) { LOG.error("Exception thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex); } } }, initialDelay, period, unit); } /** * Stops the reporter and if shutdownExecutorOnStop is true then shuts down its thread of execution. * * Uses the shutdown pattern from http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html */ public void stop() { if (shutdownExecutorOnStop) { executor.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!executor.awaitTermination(1, TimeUnit.SECONDS)) { executor.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!executor.awaitTermination(1, TimeUnit.SECONDS)) { System.err.println(getClass().getSimpleName() + ": ScheduledExecutorService did not terminate"); } } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted executor.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } else { // The external manager(like JEE container) responsible for lifecycle of executor synchronized (this) { if (this.scheduledFuture == null) { // was never started return; } if (this.scheduledFuture.isCancelled()) { // already cancelled return; } // just cancel the scheduledFuture and exit this.scheduledFuture.cancel(false); } } } /** * Stops the reporter and shuts down its thread of execution. */ @Override public void close() { stop(); } /** * Report the current values of all metrics in the registry. */ public void report() { synchronized (this) { report(registry.getGauges(filter), registry.getCounters(filter), registry.getHistograms(filter), registry.getMeters(filter), registry.getTimers(filter)); } } /** * Called periodically by the polling thread. Subclasses should report all the given metrics. * * @param gauges all of the gauges in the registry * @param counters all of the counters in the registry * @param histograms all of the histograms in the registry * @param meters all of the meters in the registry * @param timers all of the timers in the registry */ public abstract void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers); protected String getRateUnit() { return rateUnit; } protected String getDurationUnit() { return durationUnit; } protected double convertDuration(double duration) { return duration / durationFactor; } protected double convertRate(double rate) { return rate * rateFactor; } protected boolean isShutdownExecutorOnStop() { return shutdownExecutorOnStop; } protected Set getDisabledMetricAttributes() { return disabledMetricAttributes; } private String calculateRateUnit(TimeUnit unit) { final String s = unit.toString().toLowerCase(Locale.US); return s.substring(0, s.length() - 1); } private static ScheduledExecutorService createDefaultExecutor(String name) { return Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name + '-' + FACTORY_ID.incrementAndGet())); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/SharedMetricRegistries.java000066400000000000000000000066541315671014200314740ustar00rootroot00000000000000package com.codahale.metrics; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; /** * A map of shared, named metric registries. */ public class SharedMetricRegistries { private static final ConcurrentMap REGISTRIES = new ConcurrentHashMap(); private static AtomicReference defaultRegistryName = new AtomicReference(); /* Visible for testing */ static void setDefaultRegistryName(AtomicReference defaultRegistryName) { SharedMetricRegistries.defaultRegistryName = defaultRegistryName; } private SharedMetricRegistries() { /* singleton */ } public static void clear() { REGISTRIES.clear(); } public static Set names() { return REGISTRIES.keySet(); } public static void remove(String key) { REGISTRIES.remove(key); } public static MetricRegistry add(String name, MetricRegistry registry) { return REGISTRIES.putIfAbsent(name, registry); } public static MetricRegistry getOrCreate(String name) { final MetricRegistry existing = REGISTRIES.get(name); if (existing == null) { final MetricRegistry created = new MetricRegistry(); final MetricRegistry raced = add(name, created); if (raced == null) { return created; } return raced; } return existing; } /** * Creates a new registry and sets it as the default one under the provided name. * * @param name the registry name * @return the default registry * @throws IllegalStateException if the name has already been set */ public synchronized static MetricRegistry setDefault(String name) { final MetricRegistry registry = getOrCreate(name); return setDefault(name, registry); } /** * Sets the provided registry as the default one under the provided name * * @param name the default registry name * @param metricRegistry the default registry * @throws IllegalStateException if the default registry has already been set */ public static MetricRegistry setDefault(String name, MetricRegistry metricRegistry) { if (defaultRegistryName.compareAndSet(null, name)) { add(name, metricRegistry); return metricRegistry; } throw new IllegalStateException("Default metric registry name is already set."); } /** * Gets the name of the default registry, if it has been set * * @return the default registry * @throws IllegalStateException if the default has not been set */ public static MetricRegistry getDefault() { MetricRegistry metricRegistry = tryGetDefault(); if (metricRegistry == null) { throw new IllegalStateException("Default registry name has not been set."); } return metricRegistry; } /** * Same as {@link #getDefault()} except returns null when the default registry has not been set. * * @return the default registry or null */ public static MetricRegistry tryGetDefault() { final String name = defaultRegistryName.get(); if (name != null) { return getOrCreate(name); } else { return null; } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Slf4jReporter.java000066400000000000000000000343551315671014200275650ustar00rootroot00000000000000package com.codahale.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import java.util.Map.Entry; import java.util.SortedMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * A reporter class for logging metrics values to a SLF4J {@link Logger} periodically, similar to * {@link ConsoleReporter} or {@link CsvReporter}, but using the SLF4J framework instead. It also * supports specifying a {@link Marker} instance that can be used by custom appenders and filters * for the bound logging toolkit to further process metrics reports. */ public class Slf4jReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link Slf4jReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link Slf4jReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } public enum LoggingLevel {TRACE, DEBUG, INFO, WARN, ERROR} /** * A builder for {@link Slf4jReporter} instances. Defaults to logging to {@code metrics}, not * using a marker, converting rates to events/second, converting durations to milliseconds, and * not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private Logger logger; private LoggingLevel loggingLevel; private Marker marker; private String prefix; private TimeUnit rateUnit; private TimeUnit durationUnit; private MetricFilter filter; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop; private Builder(MetricRegistry registry) { this.registry = registry; this.logger = LoggerFactory.getLogger("metrics"); this.marker = null; this.prefix = ""; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.filter = MetricFilter.ALL; this.loggingLevel = LoggingLevel.INFO; this.executor = null; this.shutdownExecutorOnStop = true; } /** * Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter. * Default value is true. * Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}. * * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter * @return {@code this} */ public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } /** * Specifies the executor to use while scheduling reporting of metrics. * Default value is null. * Null value leads to executor will be auto created on start. * * @param executor the executor to use while scheduling reporting of metrics. * @return {@code this} */ public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } /** * Log metrics to the given logger. * * @param logger an SLF4J {@link Logger} * @return {@code this} */ public Builder outputTo(Logger logger) { this.logger = logger; return this; } /** * Mark all logged metrics with the given marker. * * @param marker an SLF4J {@link Marker} * @return {@code this} */ public Builder markWith(Marker marker) { this.marker = marker; return this; } /** * Prefix all metric names with the given string. * * @param prefix the prefix for all metric names * @return {@code this} */ public Builder prefixedWith(String prefix) { this.prefix = prefix; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } /** * Use Logging Level when reporting. * * @param loggingLevel a (@link Slf4jReporter.LoggingLevel} * @return {@code this} */ public Builder withLoggingLevel(LoggingLevel loggingLevel) { this.loggingLevel = loggingLevel; return this; } /** * Builds a {@link Slf4jReporter} with the given properties. * * @return a {@link Slf4jReporter} */ public Slf4jReporter build() { LoggerProxy loggerProxy; switch (loggingLevel) { case TRACE: loggerProxy = new TraceLoggerProxy(logger); break; case INFO: loggerProxy = new InfoLoggerProxy(logger); break; case WARN: loggerProxy = new WarnLoggerProxy(logger); break; case ERROR: loggerProxy = new ErrorLoggerProxy(logger); break; default: case DEBUG: loggerProxy = new DebugLoggerProxy(logger); break; } return new Slf4jReporter(registry, loggerProxy, marker, prefix, rateUnit, durationUnit, filter, executor, shutdownExecutorOnStop); } } private final LoggerProxy loggerProxy; private final Marker marker; private final String prefix; private Slf4jReporter(MetricRegistry registry, LoggerProxy loggerProxy, Marker marker, String prefix, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter, ScheduledExecutorService executor, boolean shutdownExecutorOnStop) { super(registry, "logger-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop); this.loggerProxy = loggerProxy; this.marker = marker; this.prefix = prefix; } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { if (loggerProxy.isEnabled(marker)) { for (Entry entry : gauges.entrySet()) { logGauge(entry.getKey(), entry.getValue()); } for (Entry entry : counters.entrySet()) { logCounter(entry.getKey(), entry.getValue()); } for (Entry entry : histograms.entrySet()) { logHistogram(entry.getKey(), entry.getValue()); } for (Entry entry : meters.entrySet()) { logMeter(entry.getKey(), entry.getValue()); } for (Entry entry : timers.entrySet()) { logTimer(entry.getKey(), entry.getValue()); } } } private void logTimer(String name, Timer timer) { final Snapshot snapshot = timer.getSnapshot(); loggerProxy.log(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, " + "p75={}, p95={}, p98={}, p99={}, p999={}, mean_rate={}, m1={}, m5={}, " + "m15={}, rate_unit={}, duration_unit={}", "TIMER", prefix(name), timer.getCount(), convertDuration(snapshot.getMin()), convertDuration(snapshot.getMax()), convertDuration(snapshot.getMean()), convertDuration(snapshot.getStdDev()), convertDuration(snapshot.getMedian()), convertDuration(snapshot.get75thPercentile()), convertDuration(snapshot.get95thPercentile()), convertDuration(snapshot.get98thPercentile()), convertDuration(snapshot.get99thPercentile()), convertDuration(snapshot.get999thPercentile()), convertRate(timer.getMeanRate()), convertRate(timer.getOneMinuteRate()), convertRate(timer.getFiveMinuteRate()), convertRate(timer.getFifteenMinuteRate()), getRateUnit(), getDurationUnit()); } private void logMeter(String name, Meter meter) { loggerProxy.log(marker, "type={}, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", "METER", prefix(name), meter.getCount(), convertRate(meter.getMeanRate()), convertRate(meter.getOneMinuteRate()), convertRate(meter.getFiveMinuteRate()), convertRate(meter.getFifteenMinuteRate()), getRateUnit()); } private void logHistogram(String name, Histogram histogram) { final Snapshot snapshot = histogram.getSnapshot(); loggerProxy.log(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, " + "median={}, p75={}, p95={}, p98={}, p99={}, p999={}", "HISTOGRAM", prefix(name), histogram.getCount(), snapshot.getMin(), snapshot.getMax(), snapshot.getMean(), snapshot.getStdDev(), snapshot.getMedian(), snapshot.get75thPercentile(), snapshot.get95thPercentile(), snapshot.get98thPercentile(), snapshot.get99thPercentile(), snapshot.get999thPercentile()); } private void logCounter(String name, Counter counter) { loggerProxy.log(marker, "type={}, name={}, count={}", "COUNTER", prefix(name), counter.getCount()); } private void logGauge(String name, Gauge gauge) { loggerProxy.log(marker, "type={}, name={}, value={}", "GAUGE", prefix(name), gauge.getValue()); } @Override protected String getRateUnit() { return "events/" + super.getRateUnit(); } private String prefix(String... components) { return MetricRegistry.name(prefix, components); } /* private class to allow logger configuration */ static abstract class LoggerProxy { protected final Logger logger; public LoggerProxy(Logger logger) { this.logger = logger; } abstract void log(Marker marker, String format, Object... arguments); abstract boolean isEnabled(Marker marker); } /* private class to allow logger configuration */ private static class DebugLoggerProxy extends LoggerProxy { public DebugLoggerProxy(Logger logger) { super(logger); } @Override public void log(Marker marker, String format, Object... arguments) { logger.debug(marker, format, arguments); } @Override public boolean isEnabled(Marker marker) { return logger.isDebugEnabled(marker); } } /* private class to allow logger configuration */ private static class TraceLoggerProxy extends LoggerProxy { public TraceLoggerProxy(Logger logger) { super(logger); } @Override public void log(Marker marker, String format, Object... arguments) { logger.trace(marker, format, arguments); } @Override public boolean isEnabled(Marker marker) { return logger.isTraceEnabled(marker); } } /* private class to allow logger configuration */ private static class InfoLoggerProxy extends LoggerProxy { public InfoLoggerProxy(Logger logger) { super(logger); } @Override public void log(Marker marker, String format, Object... arguments) { logger.info(marker, format, arguments); } @Override public boolean isEnabled(Marker marker) { return logger.isInfoEnabled(marker); } } /* private class to allow logger configuration */ private static class WarnLoggerProxy extends LoggerProxy { public WarnLoggerProxy(Logger logger) { super(logger); } @Override public void log(Marker marker, String format, Object... arguments) { logger.warn(marker, format, arguments); } @Override public boolean isEnabled(Marker marker) { return logger.isWarnEnabled(marker); } } /* private class to allow logger configuration */ private static class ErrorLoggerProxy extends LoggerProxy { public ErrorLoggerProxy(Logger logger) { super(logger); } @Override public void log(Marker marker, String format, Object... arguments) { logger.error(marker, format, arguments); } @Override public boolean isEnabled(Marker marker) { return logger.isErrorEnabled(marker); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/SlidingTimeWindowArrayReservoir.java000066400000000000000000000066041315671014200333540ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * A {@link Reservoir} implementation backed by a sliding window that stores only the measurements made * in the last {@code N} seconds (or other time unit). */ public class SlidingTimeWindowArrayReservoir implements Reservoir { // allow for this many duplicate ticks before overwriting measurements private static final long COLLISION_BUFFER = 256L; // only trim on updating once every N private static final long TRIM_THRESHOLD = 256L; private static final long CLEAR_BUFFER = TimeUnit.HOURS.toNanos(1) * COLLISION_BUFFER; private final Clock clock; private final ChunkedAssociativeLongArray measurements; private final long window; private final AtomicLong lastTick; private final AtomicLong count; private final long startTick; /** * Creates a new {@link SlidingTimeWindowArrayReservoir} with the given window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} */ public SlidingTimeWindowArrayReservoir(long window, TimeUnit windowUnit) { this(window, windowUnit, Clock.defaultClock()); } /** * Creates a new {@link SlidingTimeWindowArrayReservoir} with the given clock and window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} * @param clock the {@link Clock} to use */ public SlidingTimeWindowArrayReservoir(long window, TimeUnit windowUnit, Clock clock) { this.startTick = clock.getTick(); this.clock = clock; this.measurements = new ChunkedAssociativeLongArray(); this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; this.lastTick = new AtomicLong((clock.getTick() - startTick) * COLLISION_BUFFER); this.count = new AtomicLong(); } @Override public int size() { trim(); return measurements.size(); } @Override public void update(long value) { long newTick; do { if (count.incrementAndGet() % TRIM_THRESHOLD == 0L) { trim(); } long lastTick = this.lastTick.get(); newTick = getTick(); boolean longOverflow = newTick < lastTick; if (longOverflow) { measurements.clear(); } } while (!measurements.put(newTick, value)); } @Override public Snapshot getSnapshot() { trim(); return new UniformSnapshot(measurements.values()); } private long getTick() { for (; ; ) { final long oldTick = lastTick.get(); final long tick = (clock.getTick() - startTick) * COLLISION_BUFFER; // ensure the tick is strictly incrementing even if there are duplicate ticks final long newTick = tick - oldTick > 0L ? tick : oldTick + 1L; if (lastTick.compareAndSet(oldTick, newTick)) { return newTick; } } } void trim() { final long now = getTick(); final long windowStart = now - window; final long windowEnd = now + CLEAR_BUFFER; if (windowStart < windowEnd) { measurements.trim(windowStart, windowEnd); } else { measurements.clear(windowEnd, windowStart); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/SlidingTimeWindowReservoir.java000066400000000000000000000063101315671014200323470ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * A {@link Reservoir} implementation backed by a sliding window that stores only the measurements made * in the last {@code N} seconds (or other time unit). */ public class SlidingTimeWindowReservoir implements Reservoir { // allow for this many duplicate ticks before overwriting measurements private static final int COLLISION_BUFFER = 256; // only trim on updating once every N private static final int TRIM_THRESHOLD = 256; // offsets the front of the time window for the purposes of clearing the buffer in trim private static final long CLEAR_BUFFER = TimeUnit.HOURS.toNanos(1) * COLLISION_BUFFER; private final Clock clock; private final ConcurrentSkipListMap measurements; private final long window; private final AtomicLong lastTick; private final AtomicLong count; /** * Creates a new {@link SlidingTimeWindowReservoir} with the given window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} */ public SlidingTimeWindowReservoir(long window, TimeUnit windowUnit) { this(window, windowUnit, Clock.defaultClock()); } /** * Creates a new {@link SlidingTimeWindowReservoir} with the given clock and window of time. * * @param window the window of time * @param windowUnit the unit of {@code window} * @param clock the {@link Clock} to use */ public SlidingTimeWindowReservoir(long window, TimeUnit windowUnit, Clock clock) { this.clock = clock; this.measurements = new ConcurrentSkipListMap(); this.window = windowUnit.toNanos(window) * COLLISION_BUFFER; this.lastTick = new AtomicLong(clock.getTick() * COLLISION_BUFFER); this.count = new AtomicLong(); } @Override public int size() { trim(); return measurements.size(); } @Override public void update(long value) { if (count.incrementAndGet() % TRIM_THRESHOLD == 0) { trim(); } measurements.put(getTick(), value); } @Override public Snapshot getSnapshot() { trim(); return new UniformSnapshot(measurements.values()); } private long getTick() { for (; ; ) { final long oldTick = lastTick.get(); final long tick = clock.getTick() * COLLISION_BUFFER; // ensure the tick is strictly incrementing even if there are duplicate ticks final long newTick = tick - oldTick > 0 ? tick : oldTick + 1; if (lastTick.compareAndSet(oldTick, newTick)) { return newTick; } } } private void trim() { final long now = getTick(); final long windowStart = now - window; final long windowEnd = now + CLEAR_BUFFER; if (windowStart < windowEnd) { measurements.headMap(windowStart).clear(); measurements.tailMap(windowEnd).clear(); } else { measurements.subMap(windowEnd, windowStart).clear(); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/SlidingWindowReservoir.java000066400000000000000000000022151315671014200315300ustar00rootroot00000000000000package com.codahale.metrics; import static java.lang.Math.min; /** * A {@link Reservoir} implementation backed by a sliding window that stores the last {@code N} * measurements. */ public class SlidingWindowReservoir implements Reservoir { private final long[] measurements; private long count; /** * Creates a new {@link SlidingWindowReservoir} which stores the last {@code size} measurements. * * @param size the number of measurements to store */ public SlidingWindowReservoir(int size) { this.measurements = new long[size]; this.count = 0; } @Override public synchronized int size() { return (int) min(count, measurements.length); } @Override public synchronized void update(long value) { measurements[(int) (count++ % measurements.length)] = value; } @Override public Snapshot getSnapshot() { final long[] values = new long[size()]; for (int i = 0; i < values.length; i++) { synchronized (this) { values[i] = measurements[i]; } } return new UniformSnapshot(values); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Snapshot.java000066400000000000000000000053351315671014200266530ustar00rootroot00000000000000package com.codahale.metrics; import java.io.OutputStream; /** * A statistical snapshot of a {@link Snapshot}. */ public abstract class Snapshot { /** * Returns the value at the given quantile. * * @param quantile a given quantile, in {@code [0..1]} * @return the value in the distribution at {@code quantile} */ public abstract double getValue(double quantile); /** * Returns the entire set of values in the snapshot. * * @return the entire set of values */ public abstract long[] getValues(); /** * Returns the number of values in the snapshot. * * @return the number of values */ public abstract int size(); /** * Returns the median value in the distribution. * * @return the median value */ public double getMedian() { return getValue(0.5); } /** * Returns the value at the 75th percentile in the distribution. * * @return the value at the 75th percentile */ public double get75thPercentile() { return getValue(0.75); } /** * Returns the value at the 95th percentile in the distribution. * * @return the value at the 95th percentile */ public double get95thPercentile() { return getValue(0.95); } /** * Returns the value at the 98th percentile in the distribution. * * @return the value at the 98th percentile */ public double get98thPercentile() { return getValue(0.98); } /** * Returns the value at the 99th percentile in the distribution. * * @return the value at the 99th percentile */ public double get99thPercentile() { return getValue(0.99); } /** * Returns the value at the 99.9th percentile in the distribution. * * @return the value at the 99.9th percentile */ public double get999thPercentile() { return getValue(0.999); } /** * Returns the highest value in the snapshot. * * @return the highest value */ public abstract long getMax(); /** * Returns the arithmetic mean of the values in the snapshot. * * @return the arithmetic mean */ public abstract double getMean(); /** * Returns the lowest value in the snapshot. * * @return the lowest value */ public abstract long getMin(); /** * Returns the standard deviation of the values in the snapshot. * * @return the standard value */ public abstract double getStdDev(); /** * Writes the values of the snapshot to the given stream. * * @param output an output stream */ public abstract void dump(OutputStream output); } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Striped64.java000066400000000000000000000272161315671014200266420ustar00rootroot00000000000000/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ * * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.8&view=markup */ package com.codahale.metrics; import java.util.Random; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicLongFieldUpdater; // CHECKSTYLE:OFF /** * A package-local class holding common representation and mechanics for classes supporting dynamic * striping on 64bit values. The class extends Number so that concrete subclasses must publicly do * so. */ @SuppressWarnings("all") abstract class Striped64 extends Number { /* * This class maintains a lazily-initialized table of atomically * updated variables, plus an extra "base" field. The table size * is a power of two. Indexing uses masked per-thread hash codes. * Nearly all declarations in this class are package-private, * accessed directly by subclasses. * * Table entries are of class Cell; a variant of AtomicLong padded * to reduce cache contention on most processors. Padding is * overkill for most Atomics because they are usually irregularly * scattered in memory and thus don't interfere much with each * other. But Atomic objects residing in arrays will tend to be * placed adjacent to each other, and so will most often share * cache lines (with a huge negative performance impact) without * this precaution. * * In part because Cells are relatively large, we avoid creating * them until they are needed. When there is no contention, all * updates are made to the base field. Upon first contention (a * failed CAS on base update), the table is initialized to size 2. * The table size is doubled upon further contention until * reaching the nearest power of two greater than or equal to the * number of CPUS. Table slots remain empty (null) until they are * needed. * * A single spinlock ("busy") is used for initializing and * resizing the table, as well as populating slots with new Cells. * There is no need for a blocking lock; when the lock is not * available, threads try other slots (or the base). During these * retries, there is increased contention and reduced locality, * which is still better than alternatives. * * Per-thread hash codes are initialized to random values. * Contention and/or table collisions are indicated by failed * CASes when performing an update operation (see method * retryUpdate). Upon a collision, if the table size is less than * the capacity, it is doubled in size unless some other thread * holds the lock. If a hashed slot is empty, and lock is * available, a new Cell is created. Otherwise, if the slot * exists, a CAS is tried. Retries proceed by "double hashing", * using a secondary hash (Marsaglia XorShift) to try to find a * free slot. * * The table size is capped because, when there are more threads * than CPUs, supposing that each thread were bound to a CPU, * there would exist a perfect hash function mapping threads to * slots that eliminates collisions. When we reach capacity, we * search for this mapping by randomly varying the hash codes of * colliding threads. Because search is random, and collisions * only become known via CAS failures, convergence can be slow, * and because threads are typically not bound to CPUS forever, * may not occur at all. However, despite these limitations, * observed contention rates are typically low in these cases. * * It is possible for a Cell to become unused when threads that * once hashed to it terminate, as well as in the case where * doubling the table causes no thread to hash to it under * expanded mask. We do not try to detect or remove such cells, * under the assumption that for long-running instances, observed * contention levels will recur, so the cells will eventually be * needed again; and for short-lived ones, it does not matter. */ /** * Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed * between pads, hoping that the JVM doesn't reorder them. *

    * JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were * provided. */ static final class Cell { volatile long p0, p1, p2, p3, p4, p5, p6; volatile long value; volatile long q0, q1, q2, q3, q4, q5, q6; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { return valueUpdater.compareAndSet(this, cmp, val); } private static final AtomicLongFieldUpdater valueUpdater = AtomicLongFieldUpdater.newUpdater(Cell.class, "value"); } /** * Holder for the thread-local hash code. The code is initially random, but may be set to a * different value upon collisions. */ static final class HashCode { static final Random rng = new Random(); int code; HashCode() { int h = rng.nextInt(); // Avoid zero to allow xorShift rehash code = (h == 0) ? 1 : h; } } /** * The corresponding ThreadLocal class */ static final class ThreadHashCode extends ThreadLocal { public HashCode initialValue() { return new HashCode(); } } static final AtomicLongFieldUpdater baseUpdater = AtomicLongFieldUpdater.newUpdater(Striped64.class, "base"); static final AtomicIntegerFieldUpdater busyUpdater = AtomicIntegerFieldUpdater.newUpdater(Striped64.class, "busy"); /** * Static per-thread hash codes. Shared across all instances to reduce ThreadLocal pollution and * because adjustments due to collisions in one table are likely to be appropriate for others. */ static final ThreadHashCode threadHashCode = new ThreadHashCode(); /** * Number of CPUS, to place bound on table size */ static final int NCPU = Runtime.getRuntime().availableProcessors(); /** * Table of cells. When non-null, size is a power of 2. */ transient volatile Cell[] cells; /** * Base value, used mainly when there is no contention, but also as a fallback during table * initialization races. Updated via CAS. */ transient volatile long base; /** * Spinlock (locked via CAS) used when resizing and/or creating Cells. */ transient volatile int busy; /** * Package-private default constructor */ Striped64() { } /** * CASes the base field. */ final boolean casBase(long cmp, long val) { return baseUpdater.compareAndSet(this, cmp, val); } /** * CASes the busy field from 0 to 1 to acquire lock. */ final boolean casBusy() { return busyUpdater.compareAndSet(this, 0, 1); } /** * Computes the function of current and new value. Subclasses should open-code this update * function for most uses, but the virtualized form is needed within retryUpdate. * * @param currentValue the current value (of either base or a cell) * @param newValue the argument from a user update call * @return result of the update function */ abstract long fn(long currentValue, long newValue); /** * Handles cases of updates involving initialization, resizing, creating new Cells, and/or * contention. See above for explanation. This method suffers the usual non-modularity problems * of optimistic retry code, relying on rechecked sets of reads. * * @param x the value * @param hc the hash code holder * @param wasUncontended false if CAS failed before call */ final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { int h = hc.code; boolean collide = false; // True if last slot nonempty for (; ; ) { Cell[] as; Cell a; int n; long v; if ((as = cells) != null && (n = as.length) > 0) { if ((a = as[(n - 1) & h]) == null) { if (busy == 0) { // Try to attach new Cell Cell r = new Cell(x); // Optimistically create if (busy == 0 && casBusy()) { boolean created = false; try { // Recheck under lock Cell[] rs; int m, j; if ((rs = cells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { rs[j] = r; created = true; } } finally { busy = 0; } if (created) break; continue; // Slot is now non-empty } } collide = false; } else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash else if (a.cas(v = a.value, fn(v, x))) break; else if (n >= NCPU || cells != as) collide = false; // At max size or stale else if (!collide) collide = true; else if (busy == 0 && casBusy()) { try { if (cells == as) { // Expand table unless stale Cell[] rs = new Cell[n << 1]; for (int i = 0; i < n; ++i) rs[i] = as[i]; cells = rs; } } finally { busy = 0; } collide = false; continue; // Retry with expanded table } h ^= h << 13; // Rehash h ^= h >>> 17; h ^= h << 5; } else if (busy == 0 && cells == as && casBusy()) { boolean init = false; try { // Initialize table if (cells == as) { Cell[] rs = new Cell[2]; rs[h & 1] = new Cell(x); cells = rs; init = true; } } finally { busy = 0; } if (init) break; } else if (casBase(v = base, fn(v, x))) break; // Fall back on using base } hc.code = h; // Record index for next time } /** * Sets base and all cells to the given value. */ final void internalReset(long initialValue) { Cell[] as = cells; base = initialValue; if (as != null) { int n = as.length; for (int i = 0; i < n; ++i) { Cell a = as[i]; if (a != null) a.value = initialValue; } } } } // CHECKSTYLE:ON metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ThreadLocalRandom.java000066400000000000000000000134301315671014200303720ustar00rootroot00000000000000/* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ * * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ThreadLocalRandom.java?view=markup */ package com.codahale.metrics; import java.util.Random; // CHECKSTYLE:OFF /** * Copied directly from the JSR-166 project. */ @SuppressWarnings("all") class ThreadLocalRandom extends Random { // same constants as Random, but must be redeclared because private private static final long multiplier = 0x5DEECE66DL; private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1; /** * The random seed. We can't use super.seed. */ private long rnd; /** * Initialization flag to permit calls to setSeed to succeed only while executing the Random * constructor. We can't allow others since it would cause setting seed in one part of a * program to unintentionally impact other usages by the thread. */ boolean initialized; // Padding to help avoid memory contention among seed updates in // different TLRs in the common case that they are located near // each other. private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; /** * The actual ThreadLocal */ private static final ThreadLocal localRandom = new ThreadLocal() { protected ThreadLocalRandom initialValue() { return new ThreadLocalRandom(); } }; /** * Constructor called only by localRandom.initialValue. */ ThreadLocalRandom() { super(); initialized = true; } /** * Returns the current thread's {@code ThreadLocalRandom}. * * @return the current thread's {@code ThreadLocalRandom} */ public static ThreadLocalRandom current() { return localRandom.get(); } /** * Throws {@code UnsupportedOperationException}. Setting seeds in this generator is not * supported. * * @throws UnsupportedOperationException always */ public void setSeed(long seed) { if (initialized) throw new UnsupportedOperationException(); rnd = (seed ^ multiplier) & mask; } protected int next(int bits) { rnd = (rnd * multiplier + addend) & mask; return (int) (rnd >>> (48 - bits)); } /** * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) * and bound (exclusive). * * @param least the least value returned * @param bound the upper bound (exclusive) * @return the next value * @throws IllegalArgumentException if least greater than or equal to bound */ public int nextInt(int least, int bound) { if (least >= bound) throw new IllegalArgumentException(); return nextInt(bound - least) + least; } /** * Returns a pseudorandom, uniformly distributed value between 0 (inclusive) and the specified * value (exclusive). * * @param n the bound on the random number to be returned. Must be positive. * @return the next value * @throws IllegalArgumentException if n is not positive */ public long nextLong(long n) { if (n <= 0) throw new IllegalArgumentException("n must be positive"); // Divide n by two until small enough for nextInt. On each // iteration (at most 31 of them but usually much less), // randomly choose both whether to include high bit in result // (offset) and whether to continue with the lower vs upper // half (which makes a difference only if odd). long offset = 0; while (n >= Integer.MAX_VALUE) { final int bits = next(2); final long half = n >>> 1; final long nextn = ((bits & 2) == 0) ? half : n - half; if ((bits & 1) == 0) offset += n - nextn; n = nextn; } return offset + nextInt((int) n); } /** * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) * and bound (exclusive). * * @param least the least value returned * @param bound the upper bound (exclusive) * @return the next value * @throws IllegalArgumentException if least greater than or equal to bound */ public long nextLong(long least, long bound) { if (least >= bound) throw new IllegalArgumentException(); return nextLong(bound - least) + least; } /** * Returns a pseudorandom, uniformly distributed {@code double} value between 0 (inclusive) and * the specified value (exclusive). * * @param n the bound on the random number to be returned. Must be positive. * @return the next value * @throws IllegalArgumentException if n is not positive */ public double nextDouble(double n) { if (n <= 0) throw new IllegalArgumentException("n must be positive"); return nextDouble() * n; } /** * Returns a pseudorandom, uniformly distributed value between the given least value (inclusive) * and bound (exclusive). * * @param least the least value returned * @param bound the upper bound (exclusive) * @return the next value * @throws IllegalArgumentException if least greater than or equal to bound */ public double nextDouble(double least, double bound) { if (least >= bound) throw new IllegalArgumentException(); return nextDouble() * (bound - least) + least; } private static final long serialVersionUID = -5851777807851030925L; } // CHECKSTYLE:ON metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/ThreadLocalRandomProxy.java000066400000000000000000000025221315671014200314340ustar00rootroot00000000000000package com.codahale.metrics; import java.util.Random; /** * Proxy for creating thread local {@link Random} instances depending on the runtime. * By default it tries to use the JDK's implementation and fallbacks to the internal * one if the JDK doesn't provide any. */ class ThreadLocalRandomProxy { private interface Provider { Random current(); } /** * To avoid NoClassDefFoundError during loading {@link ThreadLocalRandomProxy} */ private static class JdkProvider implements Provider { @Override public Random current() { return java.util.concurrent.ThreadLocalRandom.current(); } } private static class InternalProvider implements Provider { @Override public Random current() { return ThreadLocalRandom.current(); } } private static final Provider INSTANCE = getThreadLocalProvider(); private static Provider getThreadLocalProvider() { try { final JdkProvider jdkProvider = new JdkProvider(); jdkProvider.current(); // To make sure that ThreadLocalRandom actually exists in the JDK return jdkProvider; } catch (Throwable e) { return new InternalProvider(); } } public static Random current() { return INSTANCE.current(); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/Timer.java000066400000000000000000000110471315671014200261310ustar00rootroot00000000000000package com.codahale.metrics; import java.io.Closeable; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; /** * A timer metric which aggregates timing durations and provides duration statistics, plus * throughput statistics via {@link Meter}. */ public class Timer implements Metered, Sampling { /** * A timing context. * * @see Timer#time() */ public static class Context implements Closeable { private final Timer timer; private final Clock clock; private final long startTime; private Context(Timer timer, Clock clock) { this.timer = timer; this.clock = clock; this.startTime = clock.getTick(); } /** * Updates the timer with the difference between current and start time. Call to this method will * not reset the start time. Multiple calls result in multiple updates. * @return the elapsed time in nanoseconds */ public long stop() { final long elapsed = clock.getTick() - startTime; timer.update(elapsed, TimeUnit.NANOSECONDS); return elapsed; } /** Equivalent to calling {@link #stop()}. */ @Override public void close() { stop(); } } private final Meter meter; private final Histogram histogram; private final Clock clock; /** * Creates a new {@link Timer} using an {@link ExponentiallyDecayingReservoir} and the default * {@link Clock}. */ public Timer() { this(new ExponentiallyDecayingReservoir()); } /** * Creates a new {@link Timer} that uses the given {@link Reservoir}. * * @param reservoir the {@link Reservoir} implementation the timer should use */ public Timer(Reservoir reservoir) { this(reservoir, Clock.defaultClock()); } /** * Creates a new {@link Timer} that uses the given {@link Reservoir} and {@link Clock}. * * @param reservoir the {@link Reservoir} implementation the timer should use * @param clock the {@link Clock} implementation the timer should use */ public Timer(Reservoir reservoir, Clock clock) { this.meter = new Meter(clock); this.clock = clock; this.histogram = new Histogram(reservoir); } /** * Adds a recorded duration. * * @param duration the length of the duration * @param unit the scale unit of {@code duration} */ public void update(long duration, TimeUnit unit) { update(unit.toNanos(duration)); } /** * Times and records the duration of event. * * @param event a {@link Callable} whose {@link Callable#call()} method implements a process * whose duration should be timed * @param the type of the value returned by {@code event} * @return the value returned by {@code event} * @throws Exception if {@code event} throws an {@link Exception} */ public T time(Callable event) throws Exception { final long startTime = clock.getTick(); try { return event.call(); } finally { update(clock.getTick() - startTime); } } /** * Times and records the duration of event. * * @param event a {@link Runnable} whose {@link Runnable#run()} method implements a process * whose duration should be timed */ public void time(Runnable event) { final long startTime = clock.getTick(); try { event.run(); } finally { update(clock.getTick() - startTime); } } /** * Returns a new {@link Context}. * * @return a new {@link Context} * @see Context */ public Context time() { return new Context(this, clock); } @Override public long getCount() { return histogram.getCount(); } @Override public double getFifteenMinuteRate() { return meter.getFifteenMinuteRate(); } @Override public double getFiveMinuteRate() { return meter.getFiveMinuteRate(); } @Override public double getMeanRate() { return meter.getMeanRate(); } @Override public double getOneMinuteRate() { return meter.getOneMinuteRate(); } @Override public Snapshot getSnapshot() { return histogram.getSnapshot(); } private void update(long duration) { if (duration >= 0) { histogram.update(duration); meter.mark(); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/UniformReservoir.java000066400000000000000000000050251315671014200303700ustar00rootroot00000000000000package com.codahale.metrics; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; /** * A random sampling reservoir of a stream of {@code long}s. Uses Vitter's Algorithm R to produce a * statistically representative sample. * * @see Random Sampling with a Reservoir */ public class UniformReservoir implements Reservoir { private static final int DEFAULT_SIZE = 1028; private static final int BITS_PER_LONG = 63; private final AtomicLong count = new AtomicLong(); private final AtomicLongArray values; /** * Creates a new {@link UniformReservoir} of 1028 elements, which offers a 99.9% confidence level * with a 5% margin of error assuming a normal distribution. */ public UniformReservoir() { this(DEFAULT_SIZE); } /** * Creates a new {@link UniformReservoir}. * * @param size the number of samples to keep in the sampling reservoir */ public UniformReservoir(int size) { this.values = new AtomicLongArray(size); for (int i = 0; i < values.length(); i++) { values.set(i, 0); } count.set(0); } @Override public int size() { final long c = count.get(); if (c > values.length()) { return values.length(); } return (int) c; } @Override public void update(long value) { final long c = count.incrementAndGet(); if (c <= values.length()) { values.set((int) c - 1, value); } else { final long r = nextLong(c); if (r < values.length()) { values.set((int) r, value); } } } /** * Get a pseudo-random long uniformly between 0 and n-1. Stolen from * {@link java.util.Random#nextInt()}. * * @param n the bound * @return a value select randomly from the range {@code [0..n)}. */ private static long nextLong(long n) { long bits, val; do { bits = ThreadLocalRandomProxy.current().nextLong() & (~(1L << BITS_PER_LONG)); val = bits % n; } while (bits - val + (n - 1) < 0L); return val; } @Override public Snapshot getSnapshot() { final int s = size(); long[] copy = new long[s]; for (int i = 0; i < s; i++) { copy[i] = values.get(i); } return new UniformSnapshot(copy); } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/UniformSnapshot.java000066400000000000000000000106211315671014200302050ustar00rootroot00000000000000package com.codahale.metrics; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collection; import static java.lang.Math.floor; /** * A statistical snapshot of a {@link UniformSnapshot}. */ public class UniformSnapshot extends Snapshot { private static final Charset UTF_8 = Charset.forName("UTF-8"); private final long[] values; /** * Create a new {@link Snapshot} with the given values. * * @param values an unordered set of values in the reservoir */ public UniformSnapshot(Collection values) { final Object[] copy = values.toArray(); this.values = new long[copy.length]; for (int i = 0; i < copy.length; i++) { this.values[i] = (Long) copy[i]; } Arrays.sort(this.values); } /** * Create a new {@link Snapshot} with the given values. * * @param values an unordered set of values in the reservoir that can be used by this class directly */ public UniformSnapshot(long[] values) { this.values = Arrays.copyOf(values, values.length); Arrays.sort(this.values); } /** * Returns the value at the given quantile. * * @param quantile a given quantile, in {@code [0..1]} * @return the value in the distribution at {@code quantile} */ @Override public double getValue(double quantile) { if (quantile < 0.0 || quantile > 1.0 || Double.isNaN( quantile )) { throw new IllegalArgumentException(quantile + " is not in [0..1]"); } if (values.length == 0) { return 0.0; } final double pos = quantile * (values.length + 1); final int index = (int) pos; if (index < 1) { return values[0]; } if (index >= values.length) { return values[values.length - 1]; } final double lower = values[index - 1]; final double upper = values[index]; return lower + (pos - floor(pos)) * (upper - lower); } /** * Returns the number of values in the snapshot. * * @return the number of values */ @Override public int size() { return values.length; } /** * Returns the entire set of values in the snapshot. * * @return the entire set of values */ @Override public long[] getValues() { return Arrays.copyOf(values, values.length); } /** * Returns the highest value in the snapshot. * * @return the highest value */ @Override public long getMax() { if (values.length == 0) { return 0; } return values[values.length - 1]; } /** * Returns the lowest value in the snapshot. * * @return the lowest value */ @Override public long getMin() { if (values.length == 0) { return 0; } return values[0]; } /** * Returns the arithmetic mean of the values in the snapshot. * * @return the arithmetic mean */ @Override public double getMean() { if (values.length == 0) { return 0; } double sum = 0; for (long value : values) { sum += value; } return sum / values.length; } /** * Returns the standard deviation of the values in the snapshot. * * @return the standard deviation value */ @Override public double getStdDev() { // two-pass algorithm for variance, avoids numeric overflow if (values.length <= 1) { return 0; } final double mean = getMean(); double sum = 0; for (long value : values) { final double diff = value - mean; sum += diff * diff; } final double variance = sum / (values.length - 1); return Math.sqrt(variance); } /** * Writes the values of the snapshot to the given stream. * * @param output an output stream */ @Override public void dump(OutputStream output) { final PrintWriter out = new PrintWriter(new OutputStreamWriter(output, UTF_8)); try { for (long value : values) { out.printf("%d%n", value); } } finally { out.close(); } } } metrics-3.2.5/metrics-core/src/main/java/com/codahale/metrics/WeightedSnapshot.java000066400000000000000000000123471315671014200303350ustar00rootroot00000000000000package com.codahale.metrics; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; /** * A statistical snapshot of a {@link WeightedSnapshot}. */ public class WeightedSnapshot extends Snapshot { /** * A single sample item with value and its weights for {@link WeightedSnapshot}. */ public static class WeightedSample { public final long value; public final double weight; public WeightedSample(long value, double weight) { this.value = value; this.weight = weight; } } private static final Charset UTF_8 = Charset.forName("UTF-8"); private final long[] values; private final double[] normWeights; private final double[] quantiles; /** * Create a new {@link Snapshot} with the given values. * * @param values an unordered set of values in the reservoir */ public WeightedSnapshot(Collection values) { final WeightedSample[] copy = values.toArray( new WeightedSample[]{} ); Arrays.sort(copy, new Comparator() { @Override public int compare(WeightedSample o1, WeightedSample o2) { if (o1.value > o2.value) return 1; if (o1.value < o2.value) return -1; return 0; } } ); this.values = new long[copy.length]; this.normWeights = new double[copy.length]; this.quantiles = new double[copy.length]; double sumWeight = 0; for (WeightedSample sample : copy) { sumWeight += sample.weight; } for (int i = 0; i < copy.length; i++) { this.values[i] = copy[i].value; this.normWeights[i] = copy[i].weight / sumWeight; } for (int i = 1; i < copy.length; i++) { this.quantiles[i] = this.quantiles[i - 1] + this.normWeights[i - 1]; } } /** * Returns the value at the given quantile. * * @param quantile a given quantile, in {@code [0..1]} * @return the value in the distribution at {@code quantile} */ @Override public double getValue(double quantile) { if (quantile < 0.0 || quantile > 1.0 || Double.isNaN( quantile )) { throw new IllegalArgumentException(quantile + " is not in [0..1]"); } if (values.length == 0) { return 0.0; } int posx = Arrays.binarySearch(quantiles, quantile); if (posx < 0) posx = ((-posx) - 1) - 1; if (posx < 1) { return values[0]; } if (posx >= values.length) { return values[values.length - 1]; } return values[(int) posx]; } /** * Returns the number of values in the snapshot. * * @return the number of values */ @Override public int size() { return values.length; } /** * Returns the entire set of values in the snapshot. * * @return the entire set of values */ @Override public long[] getValues() { return Arrays.copyOf(values, values.length); } /** * Returns the highest value in the snapshot. * * @return the highest value */ @Override public long getMax() { if (values.length == 0) { return 0; } return values[values.length - 1]; } /** * Returns the lowest value in the snapshot. * * @return the lowest value */ @Override public long getMin() { if (values.length == 0) { return 0; } return values[0]; } /** * Returns the weighted arithmetic mean of the values in the snapshot. * * @return the weighted arithmetic mean */ @Override public double getMean() { if (values.length == 0) { return 0; } double sum = 0; for (int i = 0; i < values.length; i++) { sum += values[i] * normWeights[i]; } return sum; } /** * Returns the weighted standard deviation of the values in the snapshot. * * @return the weighted standard deviation value */ @Override public double getStdDev() { // two-pass algorithm for variance, avoids numeric overflow if (values.length <= 1) { return 0; } final double mean = getMean(); double variance = 0; for (int i = 0; i < values.length; i++) { final double diff = values[i] - mean; variance += normWeights[i] * diff*diff; } return Math.sqrt(variance); } /** * Writes the values of the snapshot to the given stream. * * @param output an output stream */ @Override public void dump(OutputStream output) { final PrintWriter out = new PrintWriter(new OutputStreamWriter(output, UTF_8)); try { for (long value : values) { out.printf("%d%n", value); } } finally { out.close(); } } } metrics-3.2.5/metrics-core/src/test/000077500000000000000000000000001315671014200173315ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/000077500000000000000000000000001315671014200202525ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/000077500000000000000000000000001315671014200210305ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/000077500000000000000000000000001315671014200225705ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200242365ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/CachedGaugeTest.java000066400000000000000000000020501315671014200300560ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; public class CachedGaugeTest { private final AtomicInteger value = new AtomicInteger(0); private final Gauge gauge = new CachedGauge(100, TimeUnit.MILLISECONDS) { @Override protected Integer loadValue() { return value.incrementAndGet(); } }; @Test public void cachesTheValueForTheGivenPeriod() throws Exception { assertThat(gauge.getValue()) .isEqualTo(1); assertThat(gauge.getValue()) .isEqualTo(1); } @Test public void reloadsTheCachedValueAfterTheGivenPeriod() throws Exception { assertThat(gauge.getValue()) .isEqualTo(1); Thread.sleep(150); assertThat(gauge.getValue()) .isEqualTo(2); assertThat(gauge.getValue()) .isEqualTo(2); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/ChunkedAssociativeLongArrayTest.java000066400000000000000000000040221315671014200333320ustar00rootroot00000000000000package com.codahale.metrics; import static org.assertj.core.api.BDDAssertions.then; import org.junit.Test; public class ChunkedAssociativeLongArrayTest { @Test public void testClear() { ChunkedAssociativeLongArray array = new ChunkedAssociativeLongArray(3); array.put(-3, 3); array.put(-2, 1); array.put(0, 5); array.put(3, 0); array.put(9, 8); array.put(15, 0); array.put(19, 5); array.put(21, 5); array.put(34, -9); array.put(109, 5); then(array.out()) .isEqualTo("[(-3: 3) (-2: 1) (0: 5) ]->[(3: 0) (9: 8) (15: 0) ]->[(19: 5) (21: 5) (34: -9) ]->[(109: 5) ]"); then(array.values()) .isEqualTo(new long[]{3, 1, 5, 0, 8, 0, 5, 5, -9, 5}); then(array.size()) .isEqualTo(10); array.clear(-2, 20); then(array.out()) .isEqualTo("[(-3: 3) ]->[(21: 5) (34: -9) ]->[(109: 5) ]"); then(array.values()) .isEqualTo(new long[]{3, 5, -9, 5}); then(array.size()) .isEqualTo(4); } @Test public void testTrim() { ChunkedAssociativeLongArray array = new ChunkedAssociativeLongArray(3); array.put(-3, 3); array.put(-2, 1); array.put(0, 5); array.put(3, 0); array.put(9, 8); array.put(15, 0); array.put(19, 5); array.put(21, 5); array.put(34, -9); array.put(109, 5); then(array.out()) .isEqualTo("[(-3: 3) (-2: 1) (0: 5) ]->[(3: 0) (9: 8) (15: 0) ]->[(19: 5) (21: 5) (34: -9) ]->[(109: 5) ]"); then(array.values()) .isEqualTo(new long[]{3, 1, 5, 0, 8, 0, 5, 5, -9, 5}); then(array.size()) .isEqualTo(10); array.trim(-2, 20); then(array.out()) .isEqualTo("[(-2: 1) (0: 5) ]->[(3: 0) (9: 8) (15: 0) ]->[(19: 5) ]"); then(array.values()) .isEqualTo(new long[]{1, 5, 0, 8, 0, 5}); then(array.size()) .isEqualTo(6); } }metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/ClockTest.java000066400000000000000000000024241315671014200267760ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.lang.management.ManagementFactory; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; public class ClockTest { @Test public void cpuTimeClock() throws Exception { final Clock.CpuTimeClock clock = new Clock.CpuTimeClock(); assertThat((double) clock.getTime()) .isEqualTo(System.currentTimeMillis(), offset(100.0)); assertThat((double) clock.getTick()) .isEqualTo(ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime(), offset(1000000.0)); } @Test public void userTimeClock() throws Exception { final Clock.UserTimeClock clock = new Clock.UserTimeClock(); assertThat((double) clock.getTime()) .isEqualTo(System.currentTimeMillis(), offset(100.0)); assertThat((double) clock.getTick()) .isEqualTo(System.nanoTime(), offset(100000.0)); } @Test public void defaultsToUserTime() throws Exception { assertThat(Clock.defaultClock()) .isInstanceOf(Clock.UserTimeClock.class); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/ConsoleReporterTest.java000066400000000000000000000443441315671014200310770ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.Locale; import java.util.TimeZone; import java.util.SortedMap; import java.util.TreeMap; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ConsoleReporterTest { private final Locale locale = Locale.US; private final TimeZone timeZone = TimeZone.getTimeZone("PST"); private final MetricRegistry registry = mock(MetricRegistry.class); private final Clock clock = mock(Clock.class); private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); private final PrintStream output = new PrintStream(bytes); private final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .outputTo(output) .formattedFor(locale) .withClock(clock) .formattedFor(timeZone) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(); @Before public void setUp() throws Exception { when(clock.getTime()).thenReturn(1363568676000L); } @Test public void reportsGaugeValues() throws Exception { final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(1); reporter.report(map("gauge", gauge), this.map(), this.map(), this.map(), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Gauges ----------------------------------------------------------------------", "gauge", " value = 1", "", "" )); } @Test public void reportsCounterValues() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); reporter.report(this.map(), map("test.counter", counter), this.map(), this.map(), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Counters --------------------------------------------------------------------", "test.counter", " count = 100", "", "" )); } @Test public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Histograms ------------------------------------------------------------------", "test.histogram", " count = 1", " min = 4", " max = 2", " mean = 3.00", " stddev = 5.00", " median = 6.00", " 75% <= 7.00", " 95% <= 8.00", " 98% <= 9.00", " 99% <= 10.00", " 99.9% <= 11.00", "", "" )); } @Test public void reportsMeterValues() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); reporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Meters ----------------------------------------------------------------------", "test.meter", " count = 1", " mean rate = 2.00 events/second", " 1-minute rate = 3.00 events/second", " 5-minute rate = 4.00 events/second", " 15-minute rate = 5.00 events/second", "", "" )); } @Test public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS .toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Timers ----------------------------------------------------------------------", "test.another.timer", " count = 1", " mean rate = 2.00 calls/second", " 1-minute rate = 3.00 calls/second", " 5-minute rate = 4.00 calls/second", " 15-minute rate = 5.00 calls/second", " min = 300.00 milliseconds", " max = 100.00 milliseconds", " mean = 200.00 milliseconds", " stddev = 400.00 milliseconds", " median = 500.00 milliseconds", " 75% <= 600.00 milliseconds", " 95% <= 700.00 milliseconds", " 98% <= 800.00 milliseconds", " 99% <= 900.00 milliseconds", " 99.9% <= 1000.00 milliseconds", "", "" )); } @Test public void reportMeterWithDisabledAttributes() throws Exception { Set disabledMetricAttributes = EnumSet.of(MetricAttribute.M15_RATE, MetricAttribute.M5_RATE, MetricAttribute.COUNT); final ConsoleReporter customReporter = ConsoleReporter.forRegistry(registry) .outputTo(output) .formattedFor(locale) .withClock(clock) .formattedFor(timeZone) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(disabledMetricAttributes) .build(); final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); customReporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Meters ----------------------------------------------------------------------", "test.meter", " mean rate = 2.00 events/second", " 1-minute rate = 3.00 events/second", "", "" )); } @Test public void reportTimerWithDisabledAttributes() throws Exception { Set disabledMetricAttributes = EnumSet.of(MetricAttribute.P50, MetricAttribute.P999, MetricAttribute.M5_RATE, MetricAttribute.MAX); final ConsoleReporter customReporter = ConsoleReporter.forRegistry(registry) .outputTo(output) .formattedFor(locale) .withClock(clock) .formattedFor(timeZone) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(disabledMetricAttributes) .build(); final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS .toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); customReporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Timers ----------------------------------------------------------------------", "test.another.timer", " count = 1", " mean rate = 2.00 calls/second", " 1-minute rate = 3.00 calls/second", " 15-minute rate = 5.00 calls/second", " min = 300.00 milliseconds", " mean = 200.00 milliseconds", " stddev = 400.00 milliseconds", " 75% <= 600.00 milliseconds", " 95% <= 700.00 milliseconds", " 98% <= 800.00 milliseconds", " 99% <= 900.00 milliseconds", "", "" )); } @Test public void reportHistogramWithDisabledAttributes() throws Exception { Set disabledMetricAttributes = EnumSet.of(MetricAttribute.MIN, MetricAttribute.MAX, MetricAttribute.STDDEV, MetricAttribute.P95); final ConsoleReporter customReporter = ConsoleReporter.forRegistry(registry) .outputTo(output) .formattedFor(locale) .withClock(clock) .formattedFor(timeZone) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(disabledMetricAttributes) .build(); final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); customReporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); assertThat(consoleOutput()) .isEqualTo(lines( "3/17/13 6:04:36 PM =============================================================", "", "-- Histograms ------------------------------------------------------------------", "test.histogram", " count = 1", " mean = 3.00", " median = 6.00", " 75% <= 7.00", " 98% <= 9.00", " 99% <= 10.00", " 99.9% <= 11.00", "", "" )); } private String lines(String... lines) { final StringBuilder builder = new StringBuilder(); for (String line : lines) { builder.append(line).append(String.format("%n")); } return builder.toString(); } private String consoleOutput() throws UnsupportedEncodingException { return bytes.toString("UTF-8"); } private SortedMap map() { return new TreeMap(); } private SortedMap map(String name, T metric) { final TreeMap map = new TreeMap(); map.put(name, metric); return map; } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/CounterTest.java000066400000000000000000000025341315671014200273640ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class CounterTest { private final Counter counter = new Counter(); @Test public void startsAtZero() throws Exception { assertThat(counter.getCount()) .isZero(); } @Test public void incrementsByOne() throws Exception { counter.inc(); assertThat(counter.getCount()) .isEqualTo(1); } @Test public void incrementsByAnArbitraryDelta() throws Exception { counter.inc(12); assertThat(counter.getCount()) .isEqualTo(12); } @Test public void decrementsByOne() throws Exception { counter.dec(); assertThat(counter.getCount()) .isEqualTo(-1); } @Test public void decrementsByAnArbitraryDelta() throws Exception { counter.dec(12); assertThat(counter.getCount()) .isEqualTo(-12); } @Test public void incrementByNegativeDelta() throws Exception { counter.inc(-12); assertThat(counter.getCount()) .isEqualTo(-12); } @Test public void decrementByNegativeDelta() throws Exception { counter.dec(-12); assertThat(counter.getCount()) .isEqualTo(12); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/CsvReporterTest.java000066400000000000000000000202471315671014200302240ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Locale; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; public class CsvReporterTest { @Rule public final TemporaryFolder folder = new TemporaryFolder(); private final MetricRegistry registry = mock(MetricRegistry.class); private final Clock clock = mock(Clock.class); private File dataDirectory; private CsvReporter reporter; @Before public void setUp() throws Exception { when(clock.getTime()).thenReturn(19910191000L); this.dataDirectory = folder.newFolder(); this.reporter = CsvReporter.forRegistry(registry) .formatFor(Locale.US) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .withClock(clock) .filter(MetricFilter.ALL) .build(dataDirectory); } @Test public void reportsGaugeValues() throws Exception { final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(1); reporter.report(map("gauge", gauge), this.map(), this.map(), this.map(), this.map()); assertThat(fileContents("gauge.csv")) .isEqualTo(csv( "t,value", "19910191,1" )); } @Test public void reportsCounterValues() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); reporter.report(this.map(), map("test.counter", counter), this.map(), this.map(), this.map()); assertThat(fileContents("test.counter.csv")) .isEqualTo(csv( "t,count", "19910191,100" )); } @Test public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); assertThat(fileContents("test.histogram.csv")) .isEqualTo(csv( "t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999", "19910191,1,2,3.000000,4,5.000000,6.000000,7.000000,8.000000,9.000000,10.000000,11.000000" )); } @Test public void reportsMeterValues() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); reporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); assertThat(fileContents("test.meter.csv")) .isEqualTo(csv( "t,count,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit", "19910191,1,2.000000,3.000000,4.000000,5.000000,events/second" )); } @Test public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); assertThat(fileContents("test.another.timer.csv")) .isEqualTo(csv( "t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit", "19910191,1,100.000000,200.000000,300.000000,400.000000,500.000000,600.000000,700.000000,800.000000,900.000000,1000.000000,2.000000,3.000000,4.000000,5.000000,calls/second,milliseconds" )); } @Test public void testCsvFileProviderIsUsed() { CsvFileProvider fileProvider = mock(CsvFileProvider.class); when(fileProvider.getFile(dataDirectory, "gauge")).thenReturn(new File(dataDirectory, "guage.csv")); CsvReporter reporter = CsvReporter.forRegistry(registry) .withCsvFileProvider(fileProvider) .build(dataDirectory); final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(1); reporter.report(map("gauge", gauge), this.map(), this.map(), this.map(), this.map()); verify(fileProvider).getFile(dataDirectory, "gauge"); } private String csv(String... lines) { final StringBuilder builder = new StringBuilder(); for (String line : lines) { builder.append(line).append(String.format("%n")); } return builder.toString(); } private String fileContents(String filename) throws IOException { return new String(Files.readAllBytes(new File(dataDirectory, filename).toPath())); } private SortedMap map() { return new TreeMap(); } private SortedMap map(String name, T metric) { final TreeMap map = new TreeMap(); map.put(name, metric); return map; } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/DefaultObjectNameFactoryTest.java000066400000000000000000000013431315671014200326060ustar00rootroot00000000000000package com.codahale.metrics; import static org.assertj.core.api.Assertions.assertThat; import javax.management.ObjectName; import org.junit.Test; public class DefaultObjectNameFactoryTest { @Test public void createsObjectNameWithDomainInInput() { DefaultObjectNameFactory f = new DefaultObjectNameFactory(); ObjectName on = f.createName("type", "com.domain", "something.with.dots"); assertThat(on.getDomain()).isEqualTo("com.domain"); } @Test public void createsObjectNameWithNameAsKeyPropertyName() { DefaultObjectNameFactory f = new DefaultObjectNameFactory(); ObjectName on = f.createName("type", "com.domain", "something.with.dots"); assertThat(on.getKeyProperty("name")).isEqualTo("something.with.dots"); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/DerivativeGaugeTest.java000066400000000000000000000012351315671014200310150ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class DerivativeGaugeTest { private final Gauge gauge1 = new Gauge() { @Override public String getValue() { return "woo"; } }; private final Gauge gauge2 = new DerivativeGauge(gauge1) { @Override protected Integer transform(String value) { return value.length(); } }; @Test public void returnsATransformedValue() throws Exception { assertThat(gauge2.getValue()) .isEqualTo(3); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/EWMATest.java000066400000000000000000000147751315671014200265100ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; public class EWMATest { @Test public void aOneMinuteEWMAWithAValueOfThree() throws Exception { final EWMA ewma = EWMA.oneMinuteEWMA(); ewma.update(3); ewma.tick(); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.08120117, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.02987224, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.01098938, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00404277, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00148725, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00054713, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00020128, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00007405, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00002724, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00001002, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000369, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000136, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000050, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.00000018, offset(0.000001)); } @Test public void aFiveMinuteEWMAWithAValueOfThree() throws Exception { final EWMA ewma = EWMA.fiveMinuteEWMA(); ewma.update(3); ewma.tick(); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.49123845, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.40219203, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.32928698, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.26959738, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.18071653, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.14795818, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.12113791, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.09917933, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.08120117, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.06648190, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.05443077, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.04456415, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.03648604, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.02987224, offset(0.000001)); } @Test public void aFifteenMinuteEWMAWithAValueOfThree() throws Exception { final EWMA ewma = EWMA.fifteenMinuteEWMA(); ewma.update(3); ewma.tick(); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.6, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.56130419, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.52510399, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.49123845, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.45955700, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.42991879, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.40219203, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.37625345, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.35198773, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.32928698, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.30805027, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.28818318, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.26959738, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.25221023, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.23594443, offset(0.000001)); elapseMinute(ewma); assertThat(ewma.getRate(TimeUnit.SECONDS)).isEqualTo(0.22072766, offset(0.000001)); } private void elapseMinute(EWMA ewma) { for (int i = 1; i <= 12; i++) { ewma.tick(); } } } ExponentiallyDecayingReservoirTest.java000066400000000000000000000314021315671014200340620ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/metricspackage com.codahale.metrics; import org.junit.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; public class ExponentiallyDecayingReservoirTest { @Test public void aReservoirOf100OutOf1000Elements() throws Exception { final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(100, 0.99); for (int i = 0; i < 1000; i++) { reservoir.update(i); } assertThat(reservoir.size()) .isEqualTo(100); final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(100); assertAllValuesBetween(reservoir, 0, 1000); } @Test public void aReservoirOf100OutOf10Elements() throws Exception { final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(100, 0.99); for (int i = 0; i < 10; i++) { reservoir.update(i); } final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(10); assertThat(snapshot.size()) .isEqualTo(10); assertAllValuesBetween(reservoir, 0, 10); } @Test public void aHeavilyBiasedReservoirOf100OutOf1000Elements() throws Exception { final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(1000, 0.01); for (int i = 0; i < 100; i++) { reservoir.update(i); } assertThat(reservoir.size()) .isEqualTo(100); final Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.size()) .isEqualTo(100); assertAllValuesBetween(reservoir, 0, 100); } @Test public void longPeriodsOfInactivityShouldNotCorruptSamplingState() { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(10, 0.015, clock); // add 1000 values at a rate of 10 values/second for (int i = 0; i < 1000; i++) { reservoir.update(1000 + i); clock.addMillis(100); } assertThat(reservoir.getSnapshot().size()) .isEqualTo(10); assertAllValuesBetween(reservoir, 1000, 2000); // wait for 15 hours and add another value. // this should trigger a rescale. Note that the number of samples will be reduced to 2 // because of the very small scaling factor that will make all existing priorities equal to // zero after rescale. clock.addHours(15); reservoir.update(2000); assertThat(reservoir.getSnapshot().size()) .isEqualTo(1); assertAllValuesBetween(reservoir, 1000, 3000); // add 1000 values at a rate of 10 values/second for (int i = 0; i < 1000; i++) { reservoir.update(3000 + i); clock.addMillis(100); } assertThat(reservoir.getSnapshot().size()) .isEqualTo(10); assertAllValuesBetween(reservoir, 3000, 4000); } @Test public void longPeriodsOfInactivity_fetchShouldResample() { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(10, 0.015, clock); // add 1000 values at a rate of 10 values/second for (int i = 0; i < 1000; i++) { reservoir.update(1000 + i); clock.addMillis(100); } assertThat(reservoir.getSnapshot().size()) .isEqualTo(10); assertAllValuesBetween(reservoir, 1000, 2000); // wait for 15 hours and add another value. // this should trigger a rescale. Note that the number of samples will be reduced to 2 // because of the very small scaling factor that will make all existing priorities equal to // zero after rescale. clock.addHours(20); Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.getMax()).isEqualTo(0); assertThat(snapshot.getMean()).isEqualTo(0); assertThat(snapshot.getMedian()).isEqualTo(0); assertThat(snapshot.size()).isEqualTo(0); } @Test public void emptyReservoirSnapshot_shouldReturnZeroForAllValues() { final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(100, 0.015, new ManualClock()); Snapshot snapshot = reservoir.getSnapshot(); assertThat(snapshot.getMax()).isEqualTo(0); assertThat(snapshot.getMean()).isEqualTo(0); assertThat(snapshot.getMedian()).isEqualTo(0); assertThat(snapshot.size()).isEqualTo(0); } @Test public void multipleUpdatesAfterlongPeriodsOfInactivityShouldNotCorruptSamplingState () throws Exception { // This test illustrates the potential race condition in rescale that // can lead to a corrupt state. Note that while this test uses updates // exclusively to trigger the race condition, two concurrent updates // may be made much more likely to trigger this behavior if executed // while another thread is constructing a snapshot of the reservoir; // that thread then holds the read lock when the two competing updates // are executed and the race condition's window is substantially // expanded. // Run the test several times. for (int attempt=0; attempt < 10; attempt++) { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(10, 0.015, clock); // Various atomics used to communicate between this thread and the // thread created below. final AtomicBoolean running = new AtomicBoolean(true); final AtomicInteger threadUpdates = new AtomicInteger(0); final AtomicInteger testUpdates = new AtomicInteger(0); final Thread thread = new Thread(new Runnable() { @Override public void run() { int previous = 0; while (running.get()) { // Wait for the test thread to update it's counter // before updaing the reservoir. int next; while (previous >= (next = testUpdates.get())) ; // spin lock previous = next; // Update the reservoir. This needs to occur at the // same time as the test thread's update. reservoir.update(1000); // Signal the main thread; allows the next update // attempt to begin. threadUpdates.incrementAndGet(); } } }); thread.start(); int sum = 0; int previous = -1; for (int i = 0; i < 100; i++) { // Wait for 24 hours before attempting the next concurrent // update. The delay here needs to be sufficiently long to // overflow if an update attempt is allowed to add a value to // the reservoir without rescaling. Note that: // e(alpha*(15*60*60)) =~ 10^351 >> Double.MAX_VALUE =~ 1.8*10^308. clock.addHours(15); // Signal the other thread; asynchronously updates the reservoir. testUpdates.incrementAndGet(); // Delay a variable length of time. Without a delay here this // thread is heavily favored and the race condition is almost // never observed. for (int j = 0; j < i; j++) sum += j; // Competing reservoir update. reservoir.update(1000); // Wait for the other thread to finish it's update. int next; while (previous >= (next = threadUpdates.get())) ; // spin lock previous = next; } // Terminate the thread. running.set(false); testUpdates.incrementAndGet(); thread.join(); // Test failures will result in normWeights that are not finite; // checking the mean value here is sufficient. assertThat(reservoir.getSnapshot().getMean()).isBetween(0.0, Double.MAX_VALUE); // Check the value of sum; should prevent the JVM from optimizing // out the delay loop entirely. assertThat(sum).isEqualTo(161700); } } @Test public void spotLift() { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(1000, 0.015, clock); final int valuesRatePerMinute = 10; final int valuesIntervalMillis = (int) (TimeUnit.MINUTES.toMillis(1) / valuesRatePerMinute); // mode 1: steady regime for 120 minutes for (int i = 0; i < 120*valuesRatePerMinute; i++) { reservoir.update(177); clock.addMillis(valuesIntervalMillis); } // switching to mode 2: 10 minutes more with the same rate, but larger value for (int i = 0; i < 10*valuesRatePerMinute; i++) { reservoir.update(9999); clock.addMillis(valuesIntervalMillis); } // expect that quantiles should be more about mode 2 after 10 minutes assertThat(reservoir.getSnapshot().getMedian()) .isEqualTo(9999); } @Test public void spotFall() { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(1000, 0.015, clock); final int valuesRatePerMinute = 10; final int valuesIntervalMillis = (int) (TimeUnit.MINUTES.toMillis(1) / valuesRatePerMinute); // mode 1: steady regime for 120 minutes for (int i = 0; i < 120*valuesRatePerMinute; i++) { reservoir.update(9998); clock.addMillis(valuesIntervalMillis); } // switching to mode 2: 10 minutes more with the same rate, but smaller value for (int i = 0; i < 10*valuesRatePerMinute; i++) { reservoir.update(178); clock.addMillis(valuesIntervalMillis); } // expect that quantiles should be more about mode 2 after 10 minutes assertThat(reservoir.getSnapshot().get95thPercentile()) .isEqualTo(178); } @Test public void quantiliesShouldBeBasedOnWeights() { final ManualClock clock = new ManualClock(); final ExponentiallyDecayingReservoir reservoir = new ExponentiallyDecayingReservoir(1000, 0.015, clock); for (int i = 0; i < 40; i++) { reservoir.update(177); } clock.addSeconds(120); for (int i = 0; i < 10; i++) { reservoir.update(9999); } assertThat(reservoir.getSnapshot().size()) .isEqualTo(50); // the first added 40 items (177) have weights 1 // the next added 10 items (9999) have weights ~6 // so, it's 40 vs 60 distribution, not 40 vs 10 assertThat(reservoir.getSnapshot().getMedian()) .isEqualTo(9999); assertThat(reservoir.getSnapshot().get75thPercentile()) .isEqualTo(9999); } private static void assertAllValuesBetween(ExponentiallyDecayingReservoir reservoir, double min, double max) { for (double i : reservoir.getSnapshot().getValues()) { assertThat(i) .isLessThan(max) .isGreaterThanOrEqualTo(min); } } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/FixedNameCsvFileProviderTest.java000066400000000000000000000021531315671014200325710ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; import static org.assertj.core.api.Assertions.assertThat; public class FixedNameCsvFileProviderTest { @Rule public final TemporaryFolder folder = new TemporaryFolder(); private File dataDirectory; @Before public void setUp() throws Exception { this.dataDirectory = folder.newFolder(); } @Test public void testGetFile() { FixedNameCsvFileProvider provider = new FixedNameCsvFileProvider(); File file = provider.getFile(dataDirectory, "test"); assertThat(file.getParentFile()).isEqualTo(dataDirectory); assertThat(file.getName()).isEqualTo("test.csv"); } @Test public void testGetFileSanitize() { FixedNameCsvFileProvider provider = new FixedNameCsvFileProvider(); File file = provider.getFile(dataDirectory, "/myfake/uri"); assertThat(file.getParentFile()).isEqualTo(dataDirectory); assertThat(file.getName()).isEqualTo("myfake.uri.csv"); } }metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/HistogramTest.java000066400000000000000000000017361315671014200277050ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; public class HistogramTest { private final Reservoir reservoir = mock(Reservoir.class); private final Histogram histogram = new Histogram(reservoir); @Test public void updatesTheCountOnUpdates() throws Exception { assertThat(histogram.getCount()) .isZero(); histogram.update(1); assertThat(histogram.getCount()) .isEqualTo(1); } @Test public void returnsTheSnapshotFromTheReservoir() throws Exception { final Snapshot snapshot = mock(Snapshot.class); when(reservoir.getSnapshot()).thenReturn(snapshot); assertThat(histogram.getSnapshot()) .isEqualTo(snapshot); } @Test public void updatesTheReservoir() throws Exception { histogram.update(1); verify(reservoir).update(1); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/InstrumentedExecutorServiceTest.java000066400000000000000000000044001315671014200334600ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.After; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedExecutorServiceTest { private static final Logger LOGGER = LoggerFactory.getLogger(InstrumentedExecutorServiceTest.class); private final ExecutorService executor = Executors.newCachedThreadPool(); private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedExecutorService instrumentedExecutorService = new InstrumentedExecutorService(executor, registry, "xs"); @Test public void reportsTasksInformation() throws Exception { final Meter submitted = registry.meter("xs.submitted"); final Counter running = registry.counter("xs.running"); final Meter completed = registry.meter("xs.completed"); final Timer duration = registry.timer("xs.duration"); assertThat(submitted.getCount()).isEqualTo(0); assertThat(running.getCount()).isEqualTo(0); assertThat(completed.getCount()).isEqualTo(0); assertThat(duration.getCount()).isEqualTo(0); Future theFuture = instrumentedExecutorService.submit(new Runnable() { public void run() { assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isEqualTo(1); assertThat(completed.getCount()).isEqualTo(0); assertThat(duration.getCount()).isEqualTo(0); } }); theFuture.get(); assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isEqualTo(0); assertThat(completed.getCount()).isEqualTo(1); assertThat(duration.getCount()).isEqualTo(1); assertThat(duration.getSnapshot().size()).isEqualTo(1); } @After public void tearDown() throws Exception { instrumentedExecutorService.shutdown(); if (!instrumentedExecutorService.awaitTermination(2, TimeUnit.SECONDS)) { LOGGER.error("InstrumentedExecutorService did not terminate."); } } } InstrumentedScheduledExecutorServiceTest.java000066400000000000000000000276531315671014200352410ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/metricspackage com.codahale.metrics; import org.junit.After; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedScheduledExecutorServiceTest { private static final Logger LOGGER = LoggerFactory.getLogger(InstrumentedScheduledExecutorServiceTest.class); private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedScheduledExecutorService instrumentedScheduledExecutor = new InstrumentedScheduledExecutorService(scheduledExecutor, registry, "xs"); final Meter submitted = registry.meter("xs.submitted"); final Counter running = registry.counter("xs.running"); final Meter completed = registry.meter("xs.completed"); final Timer duration = registry.timer("xs.duration"); final Meter scheduledOnce = registry.meter("xs.scheduled.once"); final Meter scheduledRepetitively = registry.meter("xs.scheduled.repetitively"); final Counter scheduledOverrun = registry.counter("xs.scheduled.overrun"); final Histogram percentOfPeriod = registry.histogram("xs.scheduled.percent-of-period"); @Test public void testSubmitRunnable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); Future theFuture = instrumentedScheduledExecutor.submit(new Runnable() { public void run() { assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isEqualTo(1); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } }); theFuture.get(); assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isEqualTo(1); assertThat(duration.getCount()).isEqualTo(1); assertThat(duration.getSnapshot().size()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } @Test public void testScheduleRunnable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); ScheduledFuture theFuture = instrumentedScheduledExecutor.schedule(new Runnable() { public void run() { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isEqualTo(1); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isEqualTo(1); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } }, 10L, TimeUnit.MILLISECONDS); theFuture.get(); assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isEqualTo(1); assertThat(duration.getCount()).isEqualTo(1); assertThat(duration.getSnapshot().size()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isEqualTo(1); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } @Test public void testSubmitCallable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); final Object obj = new Object(); Future theFuture = instrumentedScheduledExecutor.submit(new Callable() { public Object call() { assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isEqualTo(1); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); return obj; } }); assertThat(theFuture.get()).isEqualTo(obj); assertThat(submitted.getCount()).isEqualTo(1); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isEqualTo(1); assertThat(duration.getCount()).isEqualTo(1); assertThat(duration.getSnapshot().size()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } @Test public void testScheduleCallable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); final Object obj = new Object(); ScheduledFuture theFuture = instrumentedScheduledExecutor.schedule(new Callable() { public Object call() { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isEqualTo(1); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isEqualTo(1); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); return obj; } }, 10L, TimeUnit.MILLISECONDS); assertThat(theFuture.get()).isEqualTo(obj); assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isEqualTo(1); assertThat(duration.getCount()).isEqualTo(1); assertThat(duration.getSnapshot().size()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isEqualTo(1); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); } @Test public void testScheduleFixedRateCallable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); ScheduledFuture theFuture = instrumentedScheduledExecutor.scheduleAtFixedRate(new Runnable() { public void run() { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isEqualTo(0); assertThat(scheduledRepetitively.getCount()).isEqualTo(1); try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } return; } }, 10L, 10L, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS.sleep(100); theFuture.cancel(true); TimeUnit.MILLISECONDS.sleep(100); assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isNotEqualTo(0); assertThat(duration.getCount()).isNotEqualTo(0); assertThat(duration.getSnapshot().size()).isNotEqualTo(0); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isEqualTo(1); assertThat(scheduledOverrun.getCount()).isNotEqualTo(0); assertThat(percentOfPeriod.getCount()).isNotEqualTo(0); } @Test public void testScheduleFixedDelayCallable() throws Exception { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isZero(); assertThat(duration.getCount()).isZero(); assertThat(scheduledOnce.getCount()).isZero(); assertThat(scheduledRepetitively.getCount()).isZero(); assertThat(scheduledOverrun.getCount()).isZero(); assertThat(percentOfPeriod.getCount()).isZero(); ScheduledFuture theFuture = instrumentedScheduledExecutor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isEqualTo(1); assertThat(scheduledOnce.getCount()).isEqualTo(0); assertThat(scheduledRepetitively.getCount()).isEqualTo(1); try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } return; } }, 10L, 10L, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS.sleep(100); theFuture.cancel(true); TimeUnit.MILLISECONDS.sleep(100); assertThat(submitted.getCount()).isZero(); assertThat(running.getCount()).isZero(); assertThat(completed.getCount()).isNotEqualTo(0); assertThat(duration.getCount()).isNotEqualTo(0); assertThat(duration.getSnapshot().size()).isNotEqualTo(0); } @After public void tearDown() throws Exception { instrumentedScheduledExecutor.shutdown(); if (!instrumentedScheduledExecutor.awaitTermination(2, TimeUnit.SECONDS)) { LOGGER.error("InstrumentedScheduledExecutorService did not terminate."); } } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/InstrumentedThreadFactoryTest.java000066400000000000000000000064611315671014200331110ustar00rootroot00000000000000package com.codahale.metrics; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; public class InstrumentedThreadFactoryTest { private static final int THREAD_COUNT = 10; private final ThreadFactory factory = Executors.defaultThreadFactory(); private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedThreadFactory instrumentedFactory = new InstrumentedThreadFactory(factory, registry, "factory"); private final ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT, instrumentedFactory); /** * Tests all parts of the InstrumentedThreadFactory except for termination since that * is currently difficult to do without race conditions. * * TODO: Try not using real threads in a unit test? */ @Test public void reportsThreadInformation() throws Exception { final CountDownLatch latch = new CountDownLatch(THREAD_COUNT); final Object lock = new Object(); final AtomicInteger interrupted = new AtomicInteger(); /* * Implements a runnable that notifies a latch after locking 'lock'. * This asserts that all threads have to enter the critical block before the * testing thread notifies all. * * We have to do this to guarantee that the thread pool has 10 LIVE threads * before we check the 'created' Meter. */ Runnable fastOne = new Runnable() { @Override public void run() { synchronized (lock) { latch.countDown(); try { lock.wait(); } catch (InterruptedException e) { interrupted.incrementAndGet(); } } } }; Meter created = registry.meter("factory.created"); Meter terminated = registry.meter("factory.terminated"); assertThat(created.getCount()).isEqualTo(0); assertThat(terminated.getCount()).isEqualTo(0); // generate demand so the executor service creates the threads through our factory. for (int i = 0; i < THREAD_COUNT + 1; i++) { executor.submit(fastOne); } latch.await(1, TimeUnit.SECONDS); synchronized (lock) { // wake up all threads. lock.notifyAll(); } assertThat(created.getCount()).isEqualTo(10); assertThat(terminated.getCount()).isEqualTo(0); // terminate all threads in the executor service. executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); // assert that all threads from the factory have been terminated. // TODO: Remove this? // There is no guarantee that all threads have entered the block where they are // counted as terminated by this time. // assertThat(terminated.getCount()).isEqualTo(10); // Check that none of the threads were interrupted. assertThat(interrupted.get()).isEqualTo(0); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/JmxAttributeGaugeTest.java000066400000000000000000000064431315671014200313430ustar00rootroot00000000000000package com.codahale.metrics; import static org.assertj.core.api.Assertions.assertThat; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectInstance; import javax.management.ObjectName; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class JmxAttributeGaugeTest { private static MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); private static List registeredMBeans = new ArrayList(); public interface JmxTestMBean { Long getValue(); } private static class JmxTest implements JmxTestMBean { @Override public Long getValue() { return Long.MAX_VALUE; } } @BeforeClass public static void setUp() throws Exception { registerMBean(new ObjectName("JmxAttributeGaugeTest:type=test,name=test1")); registerMBean(new ObjectName("JmxAttributeGaugeTest:type=test,name=test2")); } @AfterClass public static void tearDown() { for (ObjectName objectName : registeredMBeans) { try { mBeanServer.unregisterMBean(objectName); } catch (Exception e) { // ignore } } } @Test public void returnsJmxAttribute() throws Exception { ObjectName objectName = new ObjectName("java.lang:type=ClassLoading"); JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "LoadedClassCount"); assertThat(gauge.getValue()).isInstanceOf(Integer.class); assertThat((Integer) gauge.getValue()).isGreaterThan(0); } @Test public void returnsNullIfAttributeDoesNotExist() throws Exception { ObjectName objectName = new ObjectName("java.lang:type=ClassLoading"); JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "DoesNotExist"); assertThat(gauge.getValue()).isNull(); } @Test public void returnsNullIfMBeanNotFound() throws Exception { ObjectName objectName = new ObjectName("foo.bar:type=NoSuchMBean"); JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "LoadedClassCount"); assertThat(gauge.getValue()).isNull(); } @Test public void returnsAttributeForObjectNamePattern() throws Exception { ObjectName objectName = new ObjectName("JmxAttributeGaugeTest:name=test1,*"); JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "Value"); assertThat(gauge.getValue()).isInstanceOf(Long.class); assertThat((Long) gauge.getValue()).isEqualTo(Long.MAX_VALUE); } @Test public void returnsNullIfObjectNamePatternAmbiguous() throws Exception { ObjectName objectName = new ObjectName("JmxAttributeGaugeTest:type=test,*"); JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "Value"); assertThat(gauge.getValue()).isNull(); } private static void registerMBean(ObjectName objectName) throws JMException { ObjectInstance objectInstance = mBeanServer.registerMBean(new JmxTest(), objectName); registeredMBeans.add(objectInstance.getObjectName()); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/JmxReporterTest.java000066400000000000000000000315271315671014200302320ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.After; import org.junit.Before; import org.junit.Test; import javax.management.*; import java.lang.management.ManagementFactory; import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; public class JmxReporterTest { private final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); private final String name = UUID.randomUUID().toString().replaceAll("[{\\-}]", ""); private final MetricRegistry registry = new MetricRegistry(); private final JmxReporter reporter = JmxReporter.forRegistry(registry) .registerWith(mBeanServer) .inDomain(name) .convertDurationsTo(TimeUnit.MILLISECONDS) .convertRatesTo(TimeUnit.SECONDS) .filter(MetricFilter.ALL) .build(); private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); private final Histogram histogram = mock(Histogram.class); private final Meter meter = mock(Meter.class); private final Timer timer = mock(Timer.class); private final ObjectNameFactory mockObjectNameFactory = mock(ObjectNameFactory.class); private final ObjectNameFactory concreteObjectNameFactory = reporter.getObjectNameFactory(); @Before public void setUp() throws Exception { when(gauge.getValue()).thenReturn(1); when(counter.getCount()).thenReturn(100L); when(histogram.getCount()).thenReturn(1L); final Snapshot hSnapshot = mock(Snapshot.class); when(hSnapshot.getMax()).thenReturn(2L); when(hSnapshot.getMean()).thenReturn(3.0); when(hSnapshot.getMin()).thenReturn(4L); when(hSnapshot.getStdDev()).thenReturn(5.0); when(hSnapshot.getMedian()).thenReturn(6.0); when(hSnapshot.get75thPercentile()).thenReturn(7.0); when(hSnapshot.get95thPercentile()).thenReturn(8.0); when(hSnapshot.get98thPercentile()).thenReturn(9.0); when(hSnapshot.get99thPercentile()).thenReturn(10.0); when(hSnapshot.get999thPercentile()).thenReturn(11.0); when(hSnapshot.size()).thenReturn(1); when(histogram.getSnapshot()).thenReturn(hSnapshot); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot tSnapshot = mock(Snapshot.class); when(tSnapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(tSnapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(tSnapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(tSnapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(tSnapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(tSnapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(tSnapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(tSnapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(tSnapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(tSnapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); when(tSnapshot.size()).thenReturn(1); when(timer.getSnapshot()).thenReturn(tSnapshot); registry.register("gauge", gauge); registry.register("test.counter", counter); registry.register("test.histogram", histogram); registry.register("test.meter", meter); registry.register("test.another.timer", timer); reporter.start(); } @After public void tearDown() throws Exception { reporter.stop(); } @Test public void registersMBeansForMetricObjectsUsingProvidedObjectNameFactory() throws Exception { ObjectName n = new ObjectName(name + ":name=dummy"); try { String widgetName = "something"; when(mockObjectNameFactory.createName(any(String.class), any(String.class), any(String.class))).thenReturn(n); Gauge aGauge = mock(Gauge.class); when(aGauge.getValue()).thenReturn(1); JmxReporter reporter = JmxReporter.forRegistry(registry) .registerWith(mBeanServer) .inDomain(name) .createsObjectNamesWith(mockObjectNameFactory) .build(); registry.register(widgetName, aGauge); reporter.start(); verify(mockObjectNameFactory).createName(eq("gauges"), any(String.class), eq("something")); //verifyNoMoreInteractions(mockObjectNameFactory); } finally { reporter.stop(); if(mBeanServer.isRegistered(n)) { mBeanServer.unregisterMBean(n); } } } @Test public void registersMBeansForGauges() throws Exception { final AttributeList attributes = getAttributes("gauge", "Value"); assertThat(values(attributes)) .contains(entry("Value", 1)); } @Test public void registersMBeansForCounters() throws Exception { final AttributeList attributes = getAttributes("test.counter", "Count"); assertThat(values(attributes)) .contains(entry("Count", 100L)); } @Test public void registersMBeansForHistograms() throws Exception { final AttributeList attributes = getAttributes("test.histogram", "Count", "Max", "Mean", "Min", "StdDev", "50thPercentile", "75thPercentile", "95thPercentile", "98thPercentile", "99thPercentile", "999thPercentile", "SnapshotSize"); assertThat(values(attributes)) .contains(entry("Count", 1L)) .contains(entry("Max", 2L)) .contains(entry("Mean", 3.0)) .contains(entry("Min", 4L)) .contains(entry("StdDev", 5.0)) .contains(entry("50thPercentile", 6.0)) .contains(entry("75thPercentile", 7.0)) .contains(entry("95thPercentile", 8.0)) .contains(entry("98thPercentile", 9.0)) .contains(entry("99thPercentile", 10.0)) .contains(entry("999thPercentile", 11.0)) .contains(entry("SnapshotSize", 1L)) ; } @Test public void registersMBeansForMeters() throws Exception { final AttributeList attributes = getAttributes("test.meter", "Count", "MeanRate", "OneMinuteRate", "FiveMinuteRate", "FifteenMinuteRate", "RateUnit"); assertThat(values(attributes)) .contains(entry("Count", 1L)) .contains(entry("MeanRate", 2.0)) .contains(entry("OneMinuteRate", 3.0)) .contains(entry("FiveMinuteRate", 4.0)) .contains(entry("FifteenMinuteRate", 5.0)) .contains(entry("RateUnit", "events/second")); } @Test public void registersMBeansForTimers() throws Exception { final AttributeList attributes = getAttributes("test.another.timer", "Count", "MeanRate", "OneMinuteRate", "FiveMinuteRate", "FifteenMinuteRate", "Max", "Mean", "Min", "StdDev", "50thPercentile", "75thPercentile", "95thPercentile", "98thPercentile", "99thPercentile", "999thPercentile", "RateUnit", "DurationUnit"); assertThat(values(attributes)) .contains(entry("Count", 1L)) .contains(entry("MeanRate", 2.0)) .contains(entry("OneMinuteRate", 3.0)) .contains(entry("FiveMinuteRate", 4.0)) .contains(entry("FifteenMinuteRate", 5.0)) .contains(entry("Max", 100.0)) .contains(entry("Mean", 200.0)) .contains(entry("Min", 300.0)) .contains(entry("StdDev", 400.0)) .contains(entry("50thPercentile", 500.0)) .contains(entry("75thPercentile", 600.0)) .contains(entry("95thPercentile", 700.0)) .contains(entry("98thPercentile", 800.0)) .contains(entry("99thPercentile", 900.0)) .contains(entry("999thPercentile", 1000.0)) .contains(entry("RateUnit", "events/second")) .contains(entry("DurationUnit", "milliseconds")); } @Test public void cleansUpAfterItselfWhenStopped() throws Exception { reporter.stop(); try { getAttributes("gauge", "Value"); failBecauseExceptionWasNotThrown(InstanceNotFoundException.class); } catch (InstanceNotFoundException e) { } } @Test public void objectNameModifyingMBeanServer() throws Exception { MBeanServer mockedMBeanServer = mock(MBeanServer.class); // overwrite the objectName when(mockedMBeanServer.registerMBean(any(Object.class), any(ObjectName.class))).thenReturn(new ObjectInstance("DOMAIN:key=value","className")); MetricRegistry testRegistry = new MetricRegistry(); JmxReporter testJmxReporter = JmxReporter.forRegistry(testRegistry) .registerWith(mockedMBeanServer) .inDomain(name) .build(); testJmxReporter.start(); // should trigger a registerMBean testRegistry.timer("test"); // should trigger an unregisterMBean with the overwritten objectName = "DOMAIN:key=value" testJmxReporter.stop(); verify(mockedMBeanServer).unregisterMBean(new ObjectName("DOMAIN:key=value")); } @Test public void testJmxMetricNameWithAsterisk() { MetricRegistry metricRegistry = new MetricRegistry(); JmxReporter.forRegistry(metricRegistry).build().start(); metricRegistry.counter("test*"); } private AttributeList getAttributes(String name, String... attributeNames) throws JMException { ObjectName n = concreteObjectNameFactory.createName("only-for-logging-error", this.name, name); return mBeanServer.getAttributes(n, attributeNames); } private SortedMap values(AttributeList attributes) { final TreeMap values = new TreeMap(); for (Object o : attributes) { final Attribute attribute = (Attribute) o; values.put(attribute.getName(), attribute.getValue()); } return values; } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/JvmAttributeGaugeSetTest.java000066400000000000000000000040341315671014200320070ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Test; import java.lang.management.RuntimeMXBean; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class JvmAttributeGaugeSetTest { private final RuntimeMXBean runtime = mock(RuntimeMXBean.class); private final JvmAttributeGaugeSet gauges = new JvmAttributeGaugeSet(runtime); @Before public void setUp() throws Exception { when(runtime.getName()).thenReturn("9928@example.com"); when(runtime.getVmVendor()).thenReturn("Oracle Corporation"); when(runtime.getVmName()).thenReturn("Java HotSpot(TM) 64-Bit Server VM"); when(runtime.getVmVersion()).thenReturn("23.7-b01"); when(runtime.getSpecVersion()).thenReturn("1.7"); when(runtime.getUptime()).thenReturn(100L); } @Test public void hasASetOfGauges() throws Exception { assertThat(gauges.getMetrics().keySet()) .containsOnly("vendor", "name", "uptime"); } @Test public void hasAGaugeForTheJVMName() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("name"); assertThat(gauge.getValue()) .isEqualTo("9928@example.com"); } @Test public void hasAGaugeForTheJVMVendor() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("vendor"); assertThat(gauge.getValue()) .isEqualTo("Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.7-b01 (1.7)"); } @Test public void hasAGaugeForTheJVMUptime() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("uptime"); assertThat(gauge.getValue()) .isEqualTo(100L); } @Test public void autoDiscoversTheRuntimeBean() throws Exception { final Gauge gauge = (Gauge) new JvmAttributeGaugeSet().getMetrics().get("uptime"); assertThat((Long) gauge.getValue()) .isPositive(); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/ManualClock.java000066400000000000000000000014601315671014200272730ustar00rootroot00000000000000package com.codahale.metrics; import java.util.concurrent.TimeUnit; public class ManualClock extends Clock { long ticksInNanos = 0; public synchronized void addNanos(long nanos) { ticksInNanos += nanos; } public synchronized void addSeconds(long seconds) { ticksInNanos += TimeUnit.SECONDS.toNanos(seconds); } public synchronized void addMillis(long millis) { ticksInNanos += TimeUnit.MILLISECONDS.toNanos(millis); } public synchronized void addHours(long hours) { ticksInNanos += TimeUnit.HOURS.toNanos(hours); } @Override public synchronized long getTick() { return ticksInNanos; } @Override public synchronized long getTime() { return TimeUnit.NANOSECONDS.toMillis(ticksInNanos); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/MeterApproximationTest.java000066400000000000000000000047761315671014200316060ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; @RunWith(value = Parameterized.class) public class MeterApproximationTest { @Parameters public static Collection ratesPerMinute() { Object[][] data = new Object[][] { { 15 }, { 60 }, { 600 }, { 6000 } }; return Arrays.asList(data); } private final long ratePerMinute; public MeterApproximationTest(long ratePerMinute) { this.ratePerMinute = ratePerMinute; } @Test public void controlMeter1MinuteMeanApproximation() throws Exception { final Meter meter = simulateMetronome( 62934, TimeUnit.MILLISECONDS, 3, TimeUnit.MINUTES); assertThat(meter.getOneMinuteRate()*60.0) .isEqualTo(ratePerMinute, offset(0.1*ratePerMinute)); } @Test public void controlMeter5MinuteMeanApproximation() throws Exception { final Meter meter = simulateMetronome( 62934, TimeUnit.MILLISECONDS, 13, TimeUnit.MINUTES); assertThat(meter.getFiveMinuteRate()*60.0) .isEqualTo(ratePerMinute, offset(0.1*ratePerMinute)); } @Test public void controlMeter15MinuteMeanApproximation() throws Exception { final Meter meter = simulateMetronome( 62934, TimeUnit.MILLISECONDS, 38, TimeUnit.MINUTES); assertThat(meter.getFifteenMinuteRate()*60.0) .isEqualTo(ratePerMinute, offset(0.1*ratePerMinute)); } private Meter simulateMetronome( long introDelay, TimeUnit introDelayUnit, long duration, TimeUnit durationUnit) { final ManualClock clock = new ManualClock(); final Meter meter = new Meter(clock); clock.addNanos(introDelayUnit.toNanos(introDelay)); final long endTick = clock.getTick() + durationUnit.toNanos(duration); final long marksIntervalInNanos = TimeUnit.MINUTES.toNanos(1) / ratePerMinute; while (clock.getTick() <= endTick) { clock.addNanos(marksIntervalInNanos); meter.mark(); } return meter; } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/MeterTest.java000066400000000000000000000031241315671014200270150ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Test; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MeterTest { private final Clock clock = mock(Clock.class); private final Meter meter = new Meter(clock); @Before public void setUp() throws Exception { when(clock.getTick()).thenReturn(0L, TimeUnit.SECONDS.toNanos(10)); } @Test public void startsOutWithNoRatesOrCount() throws Exception { assertThat(meter.getCount()) .isZero(); assertThat(meter.getMeanRate()) .isEqualTo(0.0, offset(0.001)); assertThat(meter.getOneMinuteRate()) .isEqualTo(0.0, offset(0.001)); assertThat(meter.getFiveMinuteRate()) .isEqualTo(0.0, offset(0.001)); assertThat(meter.getFifteenMinuteRate()) .isEqualTo(0.0, offset(0.001)); } @Test public void marksEventsAndUpdatesRatesAndCount() throws Exception { meter.mark(); meter.mark(2); assertThat(meter.getMeanRate()) .isEqualTo(0.3, offset(0.001)); assertThat(meter.getOneMinuteRate()) .isEqualTo(0.1840, offset(0.001)); assertThat(meter.getFiveMinuteRate()) .isEqualTo(0.1966, offset(0.001)); assertThat(meter.getFifteenMinuteRate()) .isEqualTo(0.1988, offset(0.001)); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/MetricFilterTest.java000066400000000000000000000005571315671014200303410ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; public class MetricFilterTest { @Test public void theAllFilterMatchesAllMetrics() throws Exception { assertThat(MetricFilter.ALL.matches("", mock(Metric.class))) .isTrue(); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryListenerTest.java000066400000000000000000000032221315671014200324220ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyZeroInteractions; public class MetricRegistryListenerTest { private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); private final Histogram histogram = mock(Histogram.class); private final Meter meter = mock(Meter.class); private final Timer timer = mock(Timer.class); private final MetricRegistryListener listener = new MetricRegistryListener.Base() { }; @Test public void noOpsOnGaugeAdded() throws Exception { listener.onGaugeAdded("blah", gauge); verifyZeroInteractions(gauge); } @Test public void noOpsOnCounterAdded() throws Exception { listener.onCounterAdded("blah", counter); verifyZeroInteractions(counter); } @Test public void noOpsOnHistogramAdded() throws Exception { listener.onHistogramAdded("blah", histogram); verifyZeroInteractions(histogram); } @Test public void noOpsOnMeterAdded() throws Exception { listener.onMeterAdded("blah", meter); verifyZeroInteractions(meter); } @Test public void noOpsOnTimerAdded() throws Exception { listener.onTimerAdded("blah", timer); verifyZeroInteractions(timer); } @Test public void doesNotExplodeWhenMetricsAreRemoved() throws Exception { listener.onGaugeRemoved("blah"); listener.onCounterRemoved("blah"); listener.onHistogramRemoved("blah"); listener.onMeterRemoved("blah"); listener.onTimerRemoved("blah"); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/MetricRegistryTest.java000066400000000000000000000353771315671014200307340ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Test; import java.util.HashMap; import java.util.Map; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.Mockito.*; public class MetricRegistryTest { private final MetricRegistryListener listener = mock(MetricRegistryListener.class); private final MetricRegistry registry = new MetricRegistry(); @SuppressWarnings("unchecked") private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); private final Histogram histogram = mock(Histogram.class); private final Meter meter = mock(Meter.class); private final Timer timer = mock(Timer.class); @Before public void setUp() throws Exception { registry.addListener(listener); } @Test public void registeringAGaugeTriggersANotification() throws Exception { assertThat(registry.register("thing", gauge)) .isEqualTo(gauge); verify(listener).onGaugeAdded("thing", gauge); } @Test public void removingAGaugeTriggersANotification() throws Exception { registry.register("thing", gauge); assertThat(registry.remove("thing")) .isTrue(); verify(listener).onGaugeRemoved("thing"); } @Test public void registeringACounterTriggersANotification() throws Exception { assertThat(registry.register("thing", counter)) .isEqualTo(counter); verify(listener).onCounterAdded("thing", counter); } @Test public void accessingACounterRegistersAndReusesTheCounter() throws Exception { final Counter counter1 = registry.counter("thing"); final Counter counter2 = registry.counter("thing"); assertThat(counter1) .isSameAs(counter2); verify(listener).onCounterAdded("thing", counter1); } @Test public void accessingACustomCounterRegistersAndReusesTheCounter() throws Exception { final MetricRegistry.MetricSupplier supplier = new MetricRegistry.MetricSupplier() { @Override public Counter newMetric() { return counter; } }; final Counter counter1 = registry.counter("thing", supplier); final Counter counter2 = registry.counter("thing", supplier); assertThat(counter1) .isSameAs(counter2); verify(listener).onCounterAdded("thing", counter1); } @Test public void removingACounterTriggersANotification() throws Exception { registry.register("thing", counter); assertThat(registry.remove("thing")) .isTrue(); verify(listener).onCounterRemoved("thing"); } @Test public void registeringAHistogramTriggersANotification() throws Exception { assertThat(registry.register("thing", histogram)) .isEqualTo(histogram); verify(listener).onHistogramAdded("thing", histogram); } @Test public void accessingAHistogramRegistersAndReusesIt() throws Exception { final Histogram histogram1 = registry.histogram("thing"); final Histogram histogram2 = registry.histogram("thing"); assertThat(histogram1) .isSameAs(histogram2); verify(listener).onHistogramAdded("thing", histogram1); } @Test public void accessingACustomHistogramRegistersAndReusesIt() throws Exception { final MetricRegistry.MetricSupplier supplier = new MetricRegistry.MetricSupplier() { @Override public Histogram newMetric() { return histogram; } }; final Histogram histogram1 = registry.histogram("thing", supplier); final Histogram histogram2 = registry.histogram("thing", supplier); assertThat(histogram1) .isSameAs(histogram2); verify(listener).onHistogramAdded("thing", histogram1); } @Test public void removingAHistogramTriggersANotification() throws Exception { registry.register("thing", histogram); assertThat(registry.remove("thing")) .isTrue(); verify(listener).onHistogramRemoved("thing"); } @Test public void registeringAMeterTriggersANotification() throws Exception { assertThat(registry.register("thing", meter)) .isEqualTo(meter); verify(listener).onMeterAdded("thing", meter); } @Test public void accessingAMeterRegistersAndReusesIt() throws Exception { final Meter meter1 = registry.meter("thing"); final Meter meter2 = registry.meter("thing"); assertThat(meter1) .isSameAs(meter2); verify(listener).onMeterAdded("thing", meter1); } @Test public void accessingACustomMeterRegistersAndReusesIt() throws Exception { final MetricRegistry.MetricSupplier supplier = new MetricRegistry.MetricSupplier() { @Override public Meter newMetric() { return meter; } }; final Meter meter1 = registry.meter("thing", supplier); final Meter meter2 = registry.meter("thing", supplier); assertThat(meter1) .isSameAs(meter2); verify(listener).onMeterAdded("thing", meter1); } @Test public void removingAMeterTriggersANotification() throws Exception { registry.register("thing", meter); assertThat(registry.remove("thing")) .isTrue(); verify(listener).onMeterRemoved("thing"); } @Test public void registeringATimerTriggersANotification() throws Exception { assertThat(registry.register("thing", timer)) .isEqualTo(timer); verify(listener).onTimerAdded("thing", timer); } @Test public void accessingATimerRegistersAndReusesIt() throws Exception { final Timer timer1 = registry.timer("thing"); final Timer timer2 = registry.timer("thing"); assertThat(timer1) .isSameAs(timer2); verify(listener).onTimerAdded("thing", timer1); } @Test public void accessingACustomTimerRegistersAndReusesIt() throws Exception { final MetricRegistry.MetricSupplier supplier = new MetricRegistry.MetricSupplier() { @Override public Timer newMetric() { return timer; } }; final Timer timer1 = registry.timer("thing", supplier); final Timer timer2 = registry.timer("thing", supplier); assertThat(timer1) .isSameAs(timer2); verify(listener).onTimerAdded("thing", timer1); } @Test public void removingATimerTriggersANotification() throws Exception { registry.register("thing", timer); assertThat(registry.remove("thing")) .isTrue(); verify(listener).onTimerRemoved("thing"); } @Test public void accessingACustomGaugeRegistersAndReusesIt() throws Exception { final MetricRegistry.MetricSupplier supplier = new MetricRegistry.MetricSupplier() { @Override public Gauge newMetric() { return gauge; } }; final Gauge gauge1 = registry.gauge("thing", supplier); final Gauge gauge2 = registry.gauge("thing", supplier); assertThat(gauge1) .isSameAs(gauge2); verify(listener).onGaugeAdded("thing", gauge1); } @Test public void addingAListenerWithExistingMetricsCatchesItUp() throws Exception { registry.register("gauge", gauge); registry.register("counter", counter); registry.register("histogram", histogram); registry.register("meter", meter); registry.register("timer", timer); final MetricRegistryListener other = mock(MetricRegistryListener.class); registry.addListener(other); verify(other).onGaugeAdded("gauge", gauge); verify(other).onCounterAdded("counter", counter); verify(other).onHistogramAdded("histogram", histogram); verify(other).onMeterAdded("meter", meter); verify(other).onTimerAdded("timer", timer); } @Test public void aRemovedListenerDoesNotReceiveUpdates() throws Exception { registry.register("gauge", gauge); registry.removeListener(listener); registry.register("gauge2", gauge); verify(listener, never()).onGaugeAdded("gauge2", gauge); } @Test public void hasAMapOfRegisteredGauges() throws Exception { registry.register("gauge", gauge); assertThat(registry.getGauges()) .contains(entry("gauge", gauge)); } @Test public void hasAMapOfRegisteredCounters() throws Exception { registry.register("counter", counter); assertThat(registry.getCounters()) .contains(entry("counter", counter)); } @Test public void hasAMapOfRegisteredHistograms() throws Exception { registry.register("histogram", histogram); assertThat(registry.getHistograms()) .contains(entry("histogram", histogram)); } @Test public void hasAMapOfRegisteredMeters() throws Exception { registry.register("meter", meter); assertThat(registry.getMeters()) .contains(entry("meter", meter)); } @Test public void hasAMapOfRegisteredTimers() throws Exception { registry.register("timer", timer); assertThat(registry.getTimers()) .contains(entry("timer", timer)); } @Test public void hasASetOfRegisteredMetricNames() throws Exception { registry.register("gauge", gauge); registry.register("counter", counter); registry.register("histogram", histogram); registry.register("meter", meter); registry.register("timer", timer); assertThat(registry.getNames()) .containsOnly("gauge", "counter", "histogram", "meter", "timer"); } @Test public void registersMultipleMetrics() throws Exception { final MetricSet metrics = new MetricSet() { @Override public Map getMetrics() { final Map metrics = new HashMap(); metrics.put("gauge", gauge); metrics.put("counter", counter); return metrics; } }; registry.registerAll(metrics); assertThat(registry.getNames()) .containsOnly("gauge", "counter"); } @Test public void registersMultipleMetricsWithAPrefix() throws Exception { final MetricSet metrics = new MetricSet() { @Override public Map getMetrics() { final Map metrics = new HashMap(); metrics.put("gauge", gauge); metrics.put("counter", counter); return metrics; } }; registry.register("my", metrics); assertThat(registry.getNames()) .containsOnly("my.gauge", "my.counter"); } @Test public void registersRecursiveMetricSets() throws Exception { final MetricSet inner = new MetricSet() { @Override public Map getMetrics() { final Map metrics = new HashMap(); metrics.put("gauge", gauge); return metrics; } }; final MetricSet outer = new MetricSet() { @Override public Map getMetrics() { final Map metrics = new HashMap(); metrics.put("inner", inner); metrics.put("counter", counter); return metrics; } }; registry.register("my", outer); assertThat(registry.getNames()) .containsOnly("my.inner.gauge", "my.counter"); } @Test public void registersMetricsFromAnotherRegistry() throws Exception { MetricRegistry other = new MetricRegistry(); other.register("gauge", gauge); registry.register("nested", other); assertThat(registry.getNames()).containsOnly("nested.gauge"); } @Test public void concatenatesStringsToFormADottedName() throws Exception { assertThat(name("one", "two", "three")) .isEqualTo("one.two.three"); } @Test @SuppressWarnings("NullArgumentToVariableArgMethod") public void elidesNullValuesFromNamesWhenOnlyOneNullPassedIn() throws Exception { assertThat(name("one", (String)null)) .isEqualTo("one"); } @Test public void elidesNullValuesFromNamesWhenManyNullsPassedIn() throws Exception { assertThat(name("one", null, null)) .isEqualTo("one"); } @Test public void elidesNullValuesFromNamesWhenNullAndNotNullPassedIn() throws Exception { assertThat(name("one", null, "three")) .isEqualTo("one.three"); } @Test public void elidesEmptyStringsFromNames() throws Exception { assertThat(name("one", "", "three")) .isEqualTo("one.three"); } @Test public void concatenatesClassNamesWithStringsToFormADottedName() throws Exception { assertThat(name(MetricRegistryTest.class, "one", "two")) .isEqualTo("com.codahale.metrics.MetricRegistryTest.one.two"); } @Test public void concatenatesClassesWithoutCanonicalNamesWithStrings() throws Exception { final Gauge g = new Gauge() { @Override public String getValue() { return null; } }; assertThat(name(g.getClass(), "one", "two")) .isEqualTo("com.codahale.metrics.MetricRegistryTest$10.one.two"); } @Test public void removesMetricsMatchingAFilter() throws Exception { registry.timer("timer-1"); registry.timer("timer-2"); registry.histogram("histogram-1"); assertThat(registry.getNames()) .contains("timer-1", "timer-2", "histogram-1"); registry.removeMatching(new MetricFilter() { @Override public boolean matches(String name, Metric metric) { return name.endsWith("1"); } }); assertThat(registry.getNames()) .doesNotContain("timer-1", "histogram-1"); assertThat(registry.getNames()) .contains("timer-2"); verify(listener).onTimerRemoved("timer-1"); verify(listener).onHistogramRemoved("histogram-1"); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/RatioGaugeTest.java000066400000000000000000000033331315671014200277720ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class RatioGaugeTest { @Test public void ratiosAreHumanReadable() throws Exception { final RatioGauge.Ratio ratio = RatioGauge.Ratio.of(100, 200); assertThat(ratio.toString()) .isEqualTo("100.0:200.0"); } @Test public void calculatesTheRatioOfTheNumeratorToTheDenominator() throws Exception { final RatioGauge regular = new RatioGauge() { @Override protected Ratio getRatio() { return RatioGauge.Ratio.of(2, 4); } }; assertThat(regular.getValue()) .isEqualTo(0.5); } @Test public void handlesDivideByZeroIssues() throws Exception { final RatioGauge divByZero = new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(100, 0); } }; assertThat(divByZero.getValue()) .isNaN(); } @Test public void handlesInfiniteDenominators() throws Exception { final RatioGauge infinite = new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(10, Double.POSITIVE_INFINITY); } }; assertThat(infinite.getValue()) .isNaN(); } @Test public void handlesNaNDenominators() throws Exception { final RatioGauge nan = new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(10, Double.NaN); } }; assertThat(nan.getValue()) .isNaN(); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/ScheduledReporterTest.java000066400000000000000000000201511315671014200313630ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; public class ScheduledReporterTest { private final Gauge gauge = mock(Gauge.class); private final Counter counter = mock(Counter.class); private final Histogram histogram = mock(Histogram.class); private final Meter meter = mock(Meter.class); private final Timer timer = mock(Timer.class); private final ScheduledExecutorService mockExecutor = mock(ScheduledExecutorService.class); private final ScheduledExecutorService customExecutor = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService externalExecutor = Executors.newSingleThreadScheduledExecutor(); private final MetricRegistry registry = new MetricRegistry(); private final ScheduledReporter reporter = spy( new DummyReporter(registry, "example", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS) ); private final ScheduledReporter reporterWithNullExecutor = spy( new DummyReporter(registry, "example", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, null) ); private final ScheduledReporter reporterWithCustomMockExecutor = new DummyReporter(registry, "example", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, mockExecutor); private final ScheduledReporter reporterWithCustomExecutor = new DummyReporter(registry, "example", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, customExecutor); private final DummyReporter reporterWithExternallyManagedExecutor = new DummyReporter(registry, "example", MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, externalExecutor, false); private final ScheduledReporter[] reporters = new ScheduledReporter[] {reporter, reporterWithCustomExecutor, reporterWithExternallyManagedExecutor}; @Before public void setUp() throws Exception { registry.register("gauge", gauge); registry.register("counter", counter); registry.register("histogram", histogram); registry.register("meter", meter); registry.register("timer", timer); } @After public void tearDown() throws Exception { customExecutor.shutdown(); externalExecutor.shutdown(); reporter.stop(); reporterWithNullExecutor.stop(); } @Test public void pollsPeriodically() throws Exception { reporter.start(200, TimeUnit.MILLISECONDS); Thread.sleep(500); verify(reporter, times(2)).report( map("gauge", gauge), map("counter", counter), map("histogram", histogram), map("meter", meter), map("timer", timer) ); } @Test public void shouldUsePeriodAsInitialDelayIfNotSpecifiedOtherwise() throws Exception { reporterWithCustomMockExecutor.start(200, TimeUnit.MILLISECONDS); verify(mockExecutor, times(1)).scheduleAtFixedRate( any(Runnable.class), eq(200L), eq(200L), eq(TimeUnit.MILLISECONDS) ); } @Test public void shouldStartWithSpecifiedInitialDelay() throws Exception { reporterWithCustomMockExecutor.start(350, 100, TimeUnit.MILLISECONDS); verify(mockExecutor).scheduleAtFixedRate( any(Runnable.class), eq(350L), eq(100L), eq(TimeUnit.MILLISECONDS) ); } @Test public void shouldAutoCreateExecutorWhenItNull() throws Exception { reporterWithNullExecutor.start(200, TimeUnit.MILLISECONDS); Thread.sleep(500); verify(reporterWithNullExecutor, times(2)).report( map("gauge", gauge), map("counter", counter), map("histogram", histogram), map("meter", meter), map("timer", timer) ); } @Test(expected = IllegalArgumentException.class) public void shouldDisallowToStartReportingMultiple() throws Exception { reporter.start(200, TimeUnit.MILLISECONDS); reporter.start(200, TimeUnit.MILLISECONDS); } @Test(expected = IllegalArgumentException.class) public void shouldDisallowToStartReportingMultipleTimesOnCustomExecutor() throws Exception { reporterWithCustomExecutor.start(200, TimeUnit.MILLISECONDS); reporterWithCustomExecutor.start(200, TimeUnit.MILLISECONDS); } @Test(expected = IllegalArgumentException.class) public void shouldDisallowToStartReportingMultipleTimesOnExternallyManagedExecutor() throws Exception { reporterWithExternallyManagedExecutor.start(200, TimeUnit.MILLISECONDS); reporterWithExternallyManagedExecutor.start(200, TimeUnit.MILLISECONDS); } @Test public void shouldNotFailOnStopIfReporterWasNotStared() { for (ScheduledReporter reporter : reporters) { reporter.stop(); } } @Test public void shouldNotFailWhenStoppingMultipleTimes() { for (ScheduledReporter reporter : reporters) { reporter.start(200, TimeUnit.MILLISECONDS); reporter.stop(); reporter.stop(); reporter.stop(); } } @Test public void shouldShutdownExecutorOnStopByDefault() { reporterWithCustomExecutor.start(200, TimeUnit.MILLISECONDS); reporterWithCustomExecutor.stop(); assertTrue(customExecutor.isTerminated()); } @Test public void shouldNotShutdownExternallyManagedExecutorOnStop() { reporterWithExternallyManagedExecutor.start(200, TimeUnit.MILLISECONDS); reporterWithExternallyManagedExecutor.stop(); assertFalse(mockExecutor.isTerminated()); assertFalse(mockExecutor.isShutdown()); } @Test public void shouldCancelScheduledFutureWhenStoppingWithExternallyManagedExecutor() throws InterruptedException, ExecutionException, TimeoutException { // configure very frequency rate of execution reporterWithExternallyManagedExecutor.start(1, TimeUnit.MILLISECONDS); reporterWithExternallyManagedExecutor.stop(); Thread.sleep(100); // executionCount should not increase when scheduled future is canceled properly int executionCount = reporterWithExternallyManagedExecutor.executionCount.get(); Thread.sleep(500); assertEquals(executionCount, reporterWithExternallyManagedExecutor.executionCount.get()); } @Test public void shouldConvertDurationToMillisecondsPrecisely() { assertEquals(2.0E-5, reporter.convertDuration(20), 0.0); } private SortedMap map(String name, T value) { final SortedMap map = new TreeMap(); map.put(name, value); return map; } private static class DummyReporter extends ScheduledReporter { private AtomicInteger executionCount = new AtomicInteger(); public DummyReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit) { super(registry, name, filter, rateUnit, durationUnit); } public DummyReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, ScheduledExecutorService executor) { super(registry, name, filter, rateUnit, durationUnit, executor); } public DummyReporter(MetricRegistry registry, String name, MetricFilter filter, TimeUnit rateUnit, TimeUnit durationUnit, ScheduledExecutorService executor, boolean shutdownExecutorOnStop) { super(registry, name, filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop); } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { executionCount.incrementAndGet(); // nothing doing! } } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/SharedMetricRegistriesTest.java000066400000000000000000000063241315671014200323610ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.atomic.AtomicReference; public class SharedMetricRegistriesTest { @Before public void setUp() throws Exception { SharedMetricRegistries.setDefaultRegistryName(new AtomicReference()); SharedMetricRegistries.clear(); } @Test public void memorizesRegistriesByName() throws Exception { final MetricRegistry one = SharedMetricRegistries.getOrCreate("one"); final MetricRegistry two = SharedMetricRegistries.getOrCreate("one"); assertThat(one) .isSameAs(two); } @Test public void hasASetOfNames() throws Exception { SharedMetricRegistries.getOrCreate("one"); assertThat(SharedMetricRegistries.names()) .containsOnly("one"); } @Test public void removesRegistries() throws Exception { final MetricRegistry one = SharedMetricRegistries.getOrCreate("one"); SharedMetricRegistries.remove("one"); assertThat(SharedMetricRegistries.names()) .isEmpty(); final MetricRegistry two = SharedMetricRegistries.getOrCreate("one"); assertThat(two) .isNotSameAs(one); } @Test public void clearsRegistries() throws Exception { SharedMetricRegistries.getOrCreate("one"); SharedMetricRegistries.getOrCreate("two"); SharedMetricRegistries.clear(); assertThat(SharedMetricRegistries.names()) .isEmpty(); } @Test public void errorsWhenDefaultUnset() throws Exception { try { SharedMetricRegistries.getDefault(); } catch (final Exception e) { assertThat(e).isInstanceOf(IllegalStateException.class); assertThat(e.getMessage()).isEqualTo("Default registry name has not been set."); } } @Test public void createsDefaultRegistries() throws Exception { final String defaultName = "default"; final MetricRegistry registry = SharedMetricRegistries.setDefault(defaultName); assertThat(registry).isNotNull(); assertThat(SharedMetricRegistries.getDefault()).isEqualTo(registry); assertThat(SharedMetricRegistries.getOrCreate(defaultName)).isEqualTo(registry); } @Test public void errorsWhenDefaultAlreadySet() throws Exception { try { SharedMetricRegistries.setDefault("foobah"); SharedMetricRegistries.setDefault("borg"); } catch (final Exception e) { assertThat(e).isInstanceOf(IllegalStateException.class); assertThat(e.getMessage()).isEqualTo("Default metric registry name is already set."); } } @Test public void setsDefaultExistingRegistries() throws Exception { final String defaultName = "default"; final MetricRegistry registry = new MetricRegistry(); assertThat(SharedMetricRegistries.setDefault(defaultName, registry)).isEqualTo(registry); assertThat(SharedMetricRegistries.getDefault()).isEqualTo(registry); assertThat(SharedMetricRegistries.getOrCreate(defaultName)).isEqualTo(registry); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/Slf4jReporterTest.java000066400000000000000000000327221315671014200304540ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.Marker; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; public class Slf4jReporterTest { private final Logger logger = mock(Logger.class); private final Marker marker = mock(Marker.class); private final MetricRegistry registry = mock(MetricRegistry.class); private final Slf4jReporter infoReporter = Slf4jReporter.forRegistry(registry) .outputTo(logger) .markWith(marker) .prefixedWith("prefix") .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .withLoggingLevel(Slf4jReporter.LoggingLevel.INFO) .filter(MetricFilter.ALL) .build(); private final Slf4jReporter errorReporter = Slf4jReporter.forRegistry(registry) .outputTo(logger) .markWith(marker) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .withLoggingLevel(Slf4jReporter.LoggingLevel.ERROR) .filter(MetricFilter.ALL) .build(); @Test public void reportsGaugeValuesAtError() throws Exception { when(logger.isErrorEnabled(marker)).thenReturn(true); errorReporter.report(map("gauge", gauge("value")), this.map(), this.map(), this.map(), this.map()); verify(logger).error(marker, "type={}, name={}, value={}", new Object[]{"GAUGE", "gauge", "value"}); } @Test public void reportsCounterValuesAtError() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); when(logger.isErrorEnabled(marker)).thenReturn(true); errorReporter.report(this.map(), map("test.counter", counter), this.map(), this.map(), this.map()); verify(logger).error(marker, "type={}, name={}, count={}", new Object[]{"COUNTER", "test.counter", 100L}); } @Test public void reportsHistogramValuesAtError() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); when(logger.isErrorEnabled(marker)).thenReturn(true); errorReporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); verify(logger).error(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p99={}, p999={}", "HISTOGRAM", "test.histogram", 1L, 4L, 2L, 3.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0); } @Test public void reportsMeterValuesAtError() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); when(logger.isErrorEnabled(marker)).thenReturn(true); errorReporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); verify(logger).error(marker, "type={}, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", "METER", "test.meter", 1L, 2.0, 3.0, 4.0, 5.0, "events/second"); } @Test public void reportsTimerValuesAtError() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS .toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); when(logger.isErrorEnabled(marker)).thenReturn(true); errorReporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); verify(logger).error(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p99={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}, duration_unit={}", "TIMER", "test.another.timer", 1L, 300.0, 100.0, 200.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 2.0, 3.0, 4.0, 5.0, "events/second", "milliseconds"); } @Test public void reportsGaugeValues() throws Exception { when(logger.isInfoEnabled(marker)).thenReturn(true); infoReporter.report(map("gauge", gauge("value")), this.map(), this.map(), this.map(), this.map()); verify(logger).info(marker, "type={}, name={}, value={}", new Object[]{"GAUGE", "prefix.gauge", "value"}); } @Test public void reportsCounterValues() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); when(logger.isInfoEnabled(marker)).thenReturn(true); infoReporter.report(this.map(), map("test.counter", counter), this.map(), this.map(), this.map()); verify(logger).info(marker, "type={}, name={}, count={}", new Object[]{"COUNTER", "prefix.test.counter", 100L}); } @Test public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); when(logger.isInfoEnabled(marker)).thenReturn(true); infoReporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); verify(logger).info(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p99={}, p999={}", "HISTOGRAM", "prefix.test.histogram", 1L, 4L, 2L, 3.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0); } @Test public void reportsMeterValues() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); when(logger.isInfoEnabled(marker)).thenReturn(true); infoReporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); verify(logger).info(marker, "type={}, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}", "METER", "prefix.test.meter", 1L, 2.0, 3.0, 4.0, 5.0, "events/second"); } @Test public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS .toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); when(logger.isInfoEnabled(marker)).thenReturn(true); infoReporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); verify(logger).info(marker, "type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, p75={}, p95={}, p98={}, p99={}, p999={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}, duration_unit={}", "TIMER", "prefix.test.another.timer", 1L, 300.0, 100.0, 200.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0, 2.0, 3.0, 4.0, 5.0, "events/second", "milliseconds"); } private SortedMap map() { return new TreeMap(); } private SortedMap map(String name, T metric) { final TreeMap map = new TreeMap(); map.put(name, metric); return map; } private Gauge gauge(T value) { final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(value); return gauge; } } SlidingTimeWindowArrayReservoirTest.java000066400000000000000000000143261315671014200341700ustar00rootroot00000000000000metrics-3.2.5/metrics-core/src/test/java/com/codahale/metricspackage com.codahale.metrics; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static java.util.concurrent.TimeUnit.NANOSECONDS; import org.junit.Test; import java.util.Arrays; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @SuppressWarnings("Duplicates") public class SlidingTimeWindowArrayReservoirTest { @Test public void storesMeasurementsWithDuplicateTicks() throws Exception { final Clock clock = mock(Clock.class); final SlidingTimeWindowArrayReservoir reservoir = new SlidingTimeWindowArrayReservoir(10, NANOSECONDS, clock); when(clock.getTick()).thenReturn(20L); reservoir.update(1); reservoir.update(2); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(1, 2); } @Test public void boundsMeasurementsToATimeWindow() throws Exception { final Clock clock = mock(Clock.class); final SlidingTimeWindowArrayReservoir reservoir = new SlidingTimeWindowArrayReservoir(10, NANOSECONDS, clock); when(clock.getTick()).thenReturn(0L); reservoir.update(1); when(clock.getTick()).thenReturn(5L); reservoir.update(2); when(clock.getTick()).thenReturn(10L); reservoir.update(3); when(clock.getTick()).thenReturn(15L); reservoir.update(4); when(clock.getTick()).thenReturn(20L); reservoir.update(5); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(4, 5); } @Test public void comparisonResultsTest() { int cycles = 1000000; long time = (Long.MAX_VALUE / 256) - (long) (cycles * 0.5); ManualClock manualClock = new ManualClock(); manualClock.addNanos(time); int window = 300; Random random = new Random(ThreadLocalRandom.current().nextInt()); SlidingTimeWindowReservoir treeReservoir = new SlidingTimeWindowReservoir(window, NANOSECONDS, manualClock); SlidingTimeWindowArrayReservoir arrayReservoir = new SlidingTimeWindowArrayReservoir(window, NANOSECONDS, manualClock); for (int i = 0; i < cycles; i++) { manualClock.addNanos(1); treeReservoir.update(i); arrayReservoir.update(i); if (random.nextDouble() < 0.01) { long[] treeValues = treeReservoir.getSnapshot().getValues(); long[] arrValues = arrayReservoir.getSnapshot().getValues(); assertThat(arrValues).isEqualTo(treeValues); } if (random.nextDouble() < 0.05) { assertThat(arrayReservoir.size()).isEqualTo(treeReservoir.size()); } } } @Test public void testGetTickOverflow() { final Random random = new Random(0); final int window = 128; AtomicLong counter = new AtomicLong(0L); // Note: 'threshold' defines the number of updates submitted to the reservoir after overflowing for (int threshold : Arrays.asList(0, 1, 2, 127, 128, 129, 255, 256, 257)) { // Note: 'updatePerTick' defines the number of updates submitted to the reservoir between each tick for (int updatesPerTick : Arrays.asList(1, 2, 127, 128, 129, 255, 256, 257)) { //logger.info("Executing test: threshold={}, updatesPerTick={}", threshold, updatesPerTick); // Set the clock to overflow in (2*window+1)ns final ManualClock clock = new ManualClock(); clock.addNanos(Long.MAX_VALUE / 256 - 2 * window - clock.getTick()); assertThat(clock.getTick() * 256).isGreaterThan(0); // Create the reservoir final SlidingTimeWindowArrayReservoir reservoir = new SlidingTimeWindowArrayReservoir(window, NANOSECONDS, clock); int updatesAfterThreshold = 0; while (true) { // Update the reservoir for (int i = 0; i < updatesPerTick; i++) { long l = counter.incrementAndGet(); reservoir.update(l); } // Randomly check the reservoir size if (random.nextDouble() < 0.1) { assertThat(reservoir.size()) .as("Bad reservoir size with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isLessThanOrEqualTo(window * 256); } // Update the clock clock.addNanos(1); // If the clock has overflowed start counting updates if ((clock.getTick() * 256) < 0) { if (updatesAfterThreshold++ >= threshold) { break; } } } // Check the final reservoir size assertThat(reservoir.size()) .as("Bad final reservoir size with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isLessThanOrEqualTo(window * 256); // Advance the clock far enough to clear the reservoir. Note that here the window only loosely defines // the reservoir window; when updatesPerTick is greater than 128 the sliding window will always be well // ahead of the current clock time, and advances in getTick while in trim (called randomly above from // size and every 256 updates). Until the clock "catches up" advancing the clock will have no effect on // the reservoir, and reservoir.size() will merely move the window forward 1/256th of a ns - as such, an // arbitrary increment of 1s here was used instead to advance the clock well beyond any updates recorded // above. clock.addSeconds(1); // The reservoir should now be empty assertThat(reservoir.size()) .as("Bad reservoir size after delay with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isEqualTo(0); } } } }metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/SlidingTimeWindowReservoirTest.java000066400000000000000000000115071315671014200332460ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.util.Arrays; import java.util.Random; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class SlidingTimeWindowReservoirTest { @Test public void storesMeasurementsWithDuplicateTicks() throws Exception { final Clock clock = mock(Clock.class); final SlidingTimeWindowReservoir reservoir = new SlidingTimeWindowReservoir(10, NANOSECONDS, clock); when(clock.getTick()).thenReturn(20L); reservoir.update(1); reservoir.update(2); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(1, 2); } @Test public void boundsMeasurementsToATimeWindow() throws Exception { final Clock clock = mock(Clock.class); final SlidingTimeWindowReservoir reservoir = new SlidingTimeWindowReservoir(10, NANOSECONDS, clock); when(clock.getTick()).thenReturn(0L); reservoir.update(1); when(clock.getTick()).thenReturn(5L); reservoir.update(2); when(clock.getTick()).thenReturn(10L); reservoir.update(3); when(clock.getTick()).thenReturn(15L); reservoir.update(4); when(clock.getTick()).thenReturn(20L); reservoir.update(5); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(4, 5); } @Test public void testGetTickOverflow () { final Random random = new Random(0); final int window = 128; // Note: 'threshold' defines the number of updates submitted to the reservoir after overflowing for (int threshold : Arrays.asList(0, 1, 2, 127, 128, 129, 255, 256, 257)) { // Note: 'updatePerTick' defines the number of updates submitted to the reservoir between each tick for (int updatesPerTick : Arrays.asList(1, 2, 127, 128, 129, 255, 256, 257)) { //logger.info("Executing test: threshold={}, updatesPerTick={}", threshold, updatesPerTick); // Set the clock to overflow in (2*window+1)ns final ManualClock clock = new ManualClock(); clock.addNanos(Long.MAX_VALUE/256 - 2*window - clock.getTick()); assertThat(clock.getTick() * 256).isGreaterThan(0); // Create the reservoir final SlidingTimeWindowReservoir reservoir = new SlidingTimeWindowReservoir(window, NANOSECONDS, clock); int updatesAfterThreshold = 0; while (true) { // Update the reservoir for (int i = 0; i < updatesPerTick; i++) reservoir.update(0); // Randomly check the reservoir size if (random.nextDouble() < 0.1) { assertThat(reservoir.size()) .as("Bad reservoir size with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isLessThanOrEqualTo(window * 256); } // Update the clock clock.addNanos(1); // If the clock has overflowed start counting updates if ((clock.getTick() * 256) < 0) { if (updatesAfterThreshold++ >= threshold) break; } } // Check the final reservoir size assertThat(reservoir.size()) .as("Bad final reservoir size with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isLessThanOrEqualTo(window * 256); // Advance the clock far enough to clear the reservoir. Note that here the window only loosely defines // the reservoir window; when updatesPerTick is greater than 128 the sliding window will always be well // ahead of the current clock time, and advances in getTick while in trim (called randomly above from // size and every 256 updates). Until the clock "catches up" advancing the clock will have no effect on // the reservoir, and reservoir.size() will merely move the window forward 1/256th of a ns - as such, an // arbitrary increment of 1s here was used instead to advance the clock well beyond any updates recorded // above. clock.addSeconds(1); // The reservoir should now be empty assertThat(reservoir.size()) .as("Bad reservoir size after delay with: threshold=%d, updatesPerTick=%d", threshold, updatesPerTick) .isEqualTo(0); } } } }metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/SlidingWindowReservoirTest.java000066400000000000000000000014141315671014200324230ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class SlidingWindowReservoirTest { private final SlidingWindowReservoir reservoir = new SlidingWindowReservoir(3); @Test public void handlesSmallDataStreams() throws Exception { reservoir.update(1); reservoir.update(2); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(1, 2); } @Test public void onlyKeepsTheMostRecentFromBigDataStreams() throws Exception { reservoir.update(1); reservoir.update(2); reservoir.update(3); reservoir.update(4); assertThat(reservoir.getSnapshot().getValues()) .containsOnly(2, 3, 4); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/TimerTest.java000066400000000000000000000060641315671014200270270ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.mockito.Mockito.*; public class TimerTest { private final Reservoir reservoir = mock(Reservoir.class); private final Clock clock = new Clock() { // a mock clock that increments its ticker by 50msec per call private long val = 0; @Override public long getTick() { return val += 50000000; } }; private final Timer timer = new Timer(reservoir, clock); @Test public void hasRates() throws Exception { assertThat(timer.getCount()) .isZero(); assertThat(timer.getMeanRate()) .isEqualTo(0.0, offset(0.001)); assertThat(timer.getOneMinuteRate()) .isEqualTo(0.0, offset(0.001)); assertThat(timer.getFiveMinuteRate()) .isEqualTo(0.0, offset(0.001)); assertThat(timer.getFifteenMinuteRate()) .isEqualTo(0.0, offset(0.001)); } @Test public void updatesTheCountOnUpdates() throws Exception { assertThat(timer.getCount()) .isZero(); timer.update(1, TimeUnit.SECONDS); assertThat(timer.getCount()) .isEqualTo(1); } @Test public void timesCallableInstances() throws Exception { final String value = timer.time(new Callable() { @Override public String call() throws Exception { return "one"; } }); assertThat(timer.getCount()) .isEqualTo(1); assertThat(value) .isEqualTo("one"); verify(reservoir).update(50000000); } @Test public void timesRunnableInstances() throws Exception { final AtomicBoolean called = new AtomicBoolean(); timer.time(new Runnable() { @Override public void run() { called.set(true); } }); assertThat(timer.getCount()) .isEqualTo(1); assertThat(called.get()) .isTrue(); verify(reservoir).update(50000000); } @Test public void timesContexts() throws Exception { timer.time().stop(); assertThat(timer.getCount()) .isEqualTo(1); verify(reservoir).update(50000000); } @Test public void returnsTheSnapshotFromTheReservoir() throws Exception { final Snapshot snapshot = mock(Snapshot.class); when(reservoir.getSnapshot()).thenReturn(snapshot); assertThat(timer.getSnapshot()) .isEqualTo(snapshot); } @Test public void ignoresNegativeValues() throws Exception { timer.update(-1, TimeUnit.SECONDS); assertThat(timer.getCount()) .isZero(); verifyZeroInteractions(reservoir); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/UniformReservoirTest.java000066400000000000000000000014401315671014200312600ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class UniformReservoirTest { @Test @SuppressWarnings("unchecked") public void aReservoirOf100OutOf1000Elements() throws Exception { final UniformReservoir reservoir = new UniformReservoir(100); for (int i = 0; i < 1000; i++) { reservoir.update(i); } final Snapshot snapshot = reservoir.getSnapshot(); assertThat(reservoir.size()) .isEqualTo(100); assertThat(snapshot.size()) .isEqualTo(100); for (double i : snapshot.getValues()) { assertThat(i) .isLessThan(1000) .isGreaterThanOrEqualTo(0); } } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/UniformSnapshotTest.java000066400000000000000000000141721315671014200311050ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.util.Random; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class UniformSnapshotTest { private final Snapshot snapshot = new UniformSnapshot(new long[]{5, 1, 2, 3, 4}); @Test public void smallQuantilesAreTheFirstValue() throws Exception { assertThat(snapshot.getValue(0.0)) .isEqualTo(1, offset(0.1)); } @Test public void bigQuantilesAreTheLastValue() throws Exception { assertThat(snapshot.getValue(1.0)) .isEqualTo(5, offset(0.1)); } @Test(expected = IllegalArgumentException.class) public void disallowsNotANumberQuantile() { snapshot.getValue( Double.NaN ); } @Test(expected = IllegalArgumentException.class) public void disallowsNegativeQuantile() { snapshot.getValue( -0.5 ); } @Test(expected = IllegalArgumentException.class) public void disallowsQuantileOverOne() { snapshot.getValue( 1.5 ); } @Test public void hasAMedian() throws Exception { assertThat(snapshot.getMedian()).isEqualTo(3, offset(0.1)); } @Test public void hasAp75() throws Exception { assertThat(snapshot.get75thPercentile()).isEqualTo(4.5, offset(0.1)); } @Test public void hasAp95() throws Exception { assertThat(snapshot.get95thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp98() throws Exception { assertThat(snapshot.get98thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp99() throws Exception { assertThat(snapshot.get99thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp999() throws Exception { assertThat(snapshot.get999thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasValues() throws Exception { assertThat(snapshot.getValues()) .containsOnly(1, 2, 3, 4, 5); } @Test public void hasASize() throws Exception { assertThat(snapshot.size()) .isEqualTo(5); } @Test public void canAlsoBeCreatedFromACollectionOfLongs() throws Exception { final Snapshot other = new UniformSnapshot(asList(5L, 1L, 2L, 3L, 4L)); assertThat(other.getValues()) .containsOnly(1, 2, 3, 4, 5); } @Test public void correctlyCreatedFromCollectionWithWeakIterator() throws Exception { final ConcurrentSkipListSet values = new ConcurrentSkipListSet(); // Create a latch to make sure that the background thread has started and // pushed some data to the collection. final CountDownLatch latch = new CountDownLatch(10); final Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { final Random random = new Random(); // Update the collection in the loop to trigger a potential `ArrayOutOfBoundException` // and verify that the snapshot doesn't make assumptions about the size of the iterator. while (!Thread.currentThread().isInterrupted()) { values.add(random.nextLong()); latch.countDown(); } } }); backgroundThread.start(); try { latch.await(5, TimeUnit.SECONDS); assertThat(latch.getCount()).isEqualTo(0); // Create a snapshot while the collection is being updated. final Snapshot snapshot = new UniformSnapshot(values); assertThat(snapshot.getValues().length).isGreaterThanOrEqualTo(10); } finally { backgroundThread.interrupt(); } } @Test public void dumpsToAStream() throws Exception { final ByteArrayOutputStream output = new ByteArrayOutputStream(); snapshot.dump(output); assertThat(output.toString()) .isEqualTo(String.format("1%n2%n3%n4%n5%n")); } @Test public void calculatesTheMinimumValue() throws Exception { assertThat(snapshot.getMin()) .isEqualTo(1); } @Test public void calculatesTheMaximumValue() throws Exception { assertThat(snapshot.getMax()) .isEqualTo(5); } @Test public void calculatesTheMeanValue() throws Exception { assertThat(snapshot.getMean()) .isEqualTo(3.0); } @Test public void calculatesTheStdDev() throws Exception { assertThat(snapshot.getStdDev()) .isEqualTo(1.5811, offset(0.0001)); } @Test public void calculatesAMinOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new UniformSnapshot(new long[]{ }); assertThat(emptySnapshot.getMin()) .isZero(); } @Test public void calculatesAMaxOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new UniformSnapshot(new long[]{ }); assertThat(emptySnapshot.getMax()) .isZero(); } @Test public void calculatesAMeanOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new UniformSnapshot(new long[]{ }); assertThat(emptySnapshot.getMean()) .isZero(); } @Test public void calculatesAStdDevOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new UniformSnapshot(new long[]{ }); assertThat(emptySnapshot.getStdDev()) .isZero(); } @Test public void calculatesAStdDevOfZeroForASingletonSnapshot() throws Exception { final Snapshot singleItemSnapshot = new UniformSnapshot(new long[]{ 1 }); assertThat(singleItemSnapshot.getStdDev()) .isZero(); } } metrics-3.2.5/metrics-core/src/test/java/com/codahale/metrics/WeightedSnapshotTest.java000066400000000000000000000147071315671014200312320ustar00rootroot00000000000000package com.codahale.metrics; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; import com.codahale.metrics.WeightedSnapshot.WeightedSample; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; public class WeightedSnapshotTest { static public ArrayList WeightedArray(long[] values, double[] weights) { if (values.length != weights.length) { throw new IllegalArgumentException("Mismatched lengths: " + values.length + " vs " + weights.length); } final ArrayList samples = new ArrayList(); for (int i = 0; i < values.length; i++) { samples.add(new WeightedSnapshot.WeightedSample(values[i], weights[i])); } return samples; } private final Snapshot snapshot = new WeightedSnapshot( WeightedArray(new long[]{5, 1, 2, 3, 4}, new double[]{1, 2, 3, 2, 2}) ); @Test public void smallQuantilesAreTheFirstValue() throws Exception { assertThat(snapshot.getValue(0.0)) .isEqualTo(1.0, offset(0.1)); } @Test public void bigQuantilesAreTheLastValue() throws Exception { assertThat(snapshot.getValue(1.0)) .isEqualTo(5.0, offset(0.1)); } @Test(expected = IllegalArgumentException.class) public void disallowsNotANumberQuantile() { snapshot.getValue( Double.NaN ); } @Test(expected = IllegalArgumentException.class) public void disallowsNegativeQuantile() { snapshot.getValue( -0.5 ); } @Test(expected = IllegalArgumentException.class) public void disallowsQuantileOverOne() { snapshot.getValue( 1.5 ); } @Test public void hasAMedian() throws Exception { assertThat(snapshot.getMedian()).isEqualTo(3.0, offset(0.1)); } @Test public void hasAp75() throws Exception { assertThat(snapshot.get75thPercentile()).isEqualTo(4.0, offset(0.1)); } @Test public void hasAp95() throws Exception { assertThat(snapshot.get95thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp98() throws Exception { assertThat(snapshot.get98thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp99() throws Exception { assertThat(snapshot.get99thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasAp999() throws Exception { assertThat(snapshot.get999thPercentile()).isEqualTo(5.0, offset(0.1)); } @Test public void hasValues() throws Exception { assertThat(snapshot.getValues()) .containsOnly(1, 2, 3, 4, 5); } @Test public void hasASize() throws Exception { assertThat(snapshot.size()) .isEqualTo(5); } @Test public void worksWithUnderestimatedCollections() throws Exception { final List items = spy(WeightedArray(new long[]{5, 1, 2, 3, 4}, new double[]{1, 2, 3, 2, 2})); when(items.size()).thenReturn(4, 5); final Snapshot other = new WeightedSnapshot(items); assertThat(other.getValues()) .containsOnly(1, 2, 3, 4, 5); } @Test public void worksWithOverestimatedCollections() throws Exception { final List items = spy(WeightedArray(new long[]{5, 1, 2, 3, 4}, new double[]{1, 2, 3, 2, 2})); when(items.size()).thenReturn(6, 5); final Snapshot other = new WeightedSnapshot(items); assertThat(other.getValues()) .containsOnly(1, 2, 3, 4, 5); } @Test public void dumpsToAStream() throws Exception { final ByteArrayOutputStream output = new ByteArrayOutputStream(); snapshot.dump(output); assertThat(output.toString()) .isEqualTo(String.format("1%n2%n3%n4%n5%n")); } @Test public void calculatesTheMinimumValue() throws Exception { assertThat(snapshot.getMin()) .isEqualTo(1); } @Test public void calculatesTheMaximumValue() throws Exception { assertThat(snapshot.getMax()) .isEqualTo(5); } @Test public void calculatesTheMeanValue() throws Exception { assertThat(snapshot.getMean()) .isEqualTo(2.7); } @Test public void calculatesTheStdDev() throws Exception { assertThat(snapshot.getStdDev()) .isEqualTo(1.2688, offset(0.0001)); } @Test public void calculatesAMinOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new WeightedSnapshot( WeightedArray(new long[]{}, new double[]{}) ); assertThat(emptySnapshot.getMin()) .isZero(); } @Test public void calculatesAMaxOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new WeightedSnapshot( WeightedArray(new long[]{}, new double[]{}) ); assertThat(emptySnapshot.getMax()) .isZero(); } @Test public void calculatesAMeanOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new WeightedSnapshot( WeightedArray(new long[]{}, new double[]{}) ); assertThat(emptySnapshot.getMean()) .isZero(); } @Test public void calculatesAStdDevOfZeroForAnEmptySnapshot() throws Exception { final Snapshot emptySnapshot = new WeightedSnapshot( WeightedArray(new long[]{}, new double[]{}) ); assertThat(emptySnapshot.getStdDev()) .isZero(); } @Test public void calculatesAStdDevOfZeroForASingletonSnapshot() throws Exception { final Snapshot singleItemSnapshot = new WeightedSnapshot( WeightedArray(new long[]{ 1 }, new double[]{ 1.0 }) ); assertThat(singleItemSnapshot.getStdDev()) .isZero(); } @Test public void expectNoOverflowForLowWeights() throws Exception { final Snapshot scatteredSnapshot = new WeightedSnapshot( WeightedArray( new long[]{ 1, 2, 3 }, new double[]{ Double.MIN_VALUE, Double.MIN_VALUE, Double.MIN_VALUE } ) ); assertThat(scatteredSnapshot.getMean()) .isEqualTo(2); } } metrics-3.2.5/metrics-ehcache/000077500000000000000000000000001315671014200162135ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/pom.xml000066400000000000000000000024351315671014200175340ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-ehcache Metrics Integration for Ehcache bundle An Ehcache wrapper providing Metrics instrumentation of caches. io.dropwizard.metrics metrics-core ${project.version} net.sf.ehcache ehcache 2.8.3 org.slf4j slf4j-api metrics-3.2.5/metrics-ehcache/src/000077500000000000000000000000001315671014200170025ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/000077500000000000000000000000001315671014200177265ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/000077500000000000000000000000001315671014200206475ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/com/000077500000000000000000000000001315671014200214255ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/com/codahale/000077500000000000000000000000001315671014200231655ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200246335ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/com/codahale/metrics/ehcache/000077500000000000000000000000001315671014200262135ustar00rootroot00000000000000InstrumentedCacheDecoratorFactory.java000066400000000000000000000017361315671014200356060ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/main/java/com/codahale/metrics/ehcachepackage com.codahale.metrics.ehcache; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import net.sf.ehcache.Ehcache; import net.sf.ehcache.constructs.CacheDecoratorFactory; import java.util.Properties; public class InstrumentedCacheDecoratorFactory extends CacheDecoratorFactory { @Override public Ehcache createDecoratedEhcache(Ehcache cache, Properties properties) { final String name = properties.getProperty("metric-registry-name"); final MetricRegistry registry = SharedMetricRegistries.getOrCreate(name); return InstrumentedEhcache.instrument(registry, cache); } @Override public Ehcache createDefaultDecoratedEhcache(Ehcache cache, Properties properties) { final String name = properties.getProperty("metric-registry-name"); final MetricRegistry registry = SharedMetricRegistries.getOrCreate(name); return InstrumentedEhcache.instrument(registry, cache); } } metrics-3.2.5/metrics-ehcache/src/main/java/com/codahale/metrics/ehcache/InstrumentedEhcache.java000066400000000000000000000267321315671014200330120ustar00rootroot00000000000000package com.codahale.metrics.ehcache; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import net.sf.ehcache.constructs.EhcacheDecoratorAdapter; import net.sf.ehcache.statistics.StatisticsGateway; import java.io.Serializable; import static com.codahale.metrics.MetricRegistry.name; /** * An instrumented {@link Ehcache} instance. */ public class InstrumentedEhcache extends EhcacheDecoratorAdapter { /** * Instruments the given {@link Ehcache} instance with get and put timers * and a set of gauges for Ehcache's built-in statistics: *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Ehcache timered metrics
    {@code hits}The number of times a requested item was found in the * cache.
    {@code in-memory-hits}Number of times a requested item was found in the memory * store.
    {@code off-heap-hits}Number of times a requested item was found in the off-heap * store.
    {@code on-disk-hits}Number of times a requested item was found in the disk * store.
    {@code misses}Number of times a requested item was not found in the * cache.
    {@code in-memory-misses}Number of times a requested item was not found in the memory * store.
    {@code off-heap-misses}Number of times a requested item was not found in the * off-heap store.
    {@code on-disk-misses}Number of times a requested item was not found in the disk * store.
    {@code objects}Number of elements stored in the cache.
    {@code in-memory-objects}Number of objects in the memory store.
    {@code off-heap-objects}Number of objects in the off-heap store.
    {@code on-disk-objects}Number of objects in the disk store.
    {@code mean-get-time}The average get time. Because ehcache support JDK1.4.2, each * get time uses {@link System#currentTimeMillis()}, rather than * nanoseconds. The accuracy is thus limited.
    {@code mean-search-time}The average execution time (in milliseconds) within the last * sample period.
    {@code eviction-count}The number of cache evictions, since the cache was created, * or statistics were cleared.
    {@code searches-per-second}The number of search executions that have completed in the * last second.
    {@code accuracy}A human readable description of the accuracy setting. One of * "None", "Best Effort" or "Guaranteed".
    * * N.B.: This enables Ehcache's sampling statistics with an accuracy * level of "none." * * @param cache an {@link Ehcache} instance * @param registry a {@link MetricRegistry} * @return an instrumented decorator for {@code cache} * @see StatisticsGateway */ public static Ehcache instrument(MetricRegistry registry, final Ehcache cache) { final String prefix = name(cache.getClass(), cache.getName()); registry.register(name(prefix, "hits"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().cacheHitCount(); } }); registry.register(name(prefix, "in-memory-hits"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localHeapHitCount(); } }); registry.register(name(prefix, "off-heap-hits"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localOffHeapHitCount(); } }); registry.register(name(prefix, "on-disk-hits"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localDiskHitCount(); } }); registry.register(name(prefix, "misses"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().cacheMissCount(); } }); registry.register(name(prefix, "in-memory-misses"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localHeapMissCount(); } }); registry.register(name(prefix, "off-heap-misses"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localOffHeapMissCount(); } }); registry.register(name(prefix, "on-disk-misses"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().localDiskMissCount(); } }); registry.register(name(prefix, "objects"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().getSize(); } }); registry.register(name(prefix, "in-memory-objects"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().getLocalHeapSize(); } }); registry.register(name(prefix, "off-heap-objects"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().getLocalOffHeapSize(); } }); registry.register(name(prefix, "on-disk-objects"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().getLocalDiskSize(); } }); registry.register(name(prefix, "mean-get-time"), new Gauge() { @Override public Double getValue() { return cache.getStatistics().cacheGetOperation().latency().average().value(); } }); registry.register(name(prefix, "mean-search-time"), new Gauge() { @Override public Double getValue() { return cache.getStatistics().cacheSearchOperation().latency().average().value(); } }); registry.register(name(prefix, "eviction-count"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().cacheEvictionOperation().count().value(); } }); registry.register(name(prefix, "searches-per-second"), new Gauge() { @Override public Double getValue() { return cache.getStatistics().cacheSearchOperation().rate().value(); } }); registry.register(name(prefix, "writer-queue-size"), new Gauge() { @Override public Long getValue() { return cache.getStatistics().getWriterQueueLength(); } }); return new InstrumentedEhcache(registry, cache); } private final Timer getTimer, putTimer; private InstrumentedEhcache(MetricRegistry registry, Ehcache cache) { super(cache); this.getTimer = registry.timer(name(cache.getClass(), cache.getName(), "gets")); this.putTimer = registry.timer(name(cache.getClass(), cache.getName(), "puts")); } @Override public Element get(Object key) throws IllegalStateException, CacheException { final Timer.Context ctx = getTimer.time(); try { return underlyingCache.get(key); } finally { ctx.stop(); } } @Override public Element get(Serializable key) throws IllegalStateException, CacheException { final Timer.Context ctx = getTimer.time(); try { return underlyingCache.get(key); } finally { ctx.stop(); } } @Override public void put(Element element) throws IllegalArgumentException, IllegalStateException, CacheException { final Timer.Context ctx = putTimer.time(); try { underlyingCache.put(element); } finally { ctx.stop(); } } @Override public void put(Element element, boolean doNotNotifyCacheReplicators) throws IllegalArgumentException, IllegalStateException, CacheException { final Timer.Context ctx = putTimer.time(); try { underlyingCache.put(element, doNotNotifyCacheReplicators); } finally { ctx.stop(); } } @Override public Element putIfAbsent(Element element) throws NullPointerException { final Timer.Context ctx = putTimer.time(); try { return underlyingCache.putIfAbsent(element); } finally { ctx.stop(); } } } metrics-3.2.5/metrics-ehcache/src/test/000077500000000000000000000000001315671014200177615ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/000077500000000000000000000000001315671014200207025ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/000077500000000000000000000000001315671014200214605ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/codahale/000077500000000000000000000000001315671014200232205ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200246665ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/codahale/metrics/ehcache/000077500000000000000000000000001315671014200262465ustar00rootroot00000000000000InstrumentedCacheDecoratorFactoryTest.java000066400000000000000000000026421315671014200364760ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/codahale/metrics/ehcachepackage com.codahale.metrics.ehcache; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Test; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assume.assumeThat; public class InstrumentedCacheDecoratorFactoryTest { private static final CacheManager MANAGER = CacheManager.create(); private MetricRegistry registry; private Ehcache cache; @Before public void setUp() throws Exception { this.cache = MANAGER.getEhcache("test-config"); assumeThat(cache, is(CoreMatchers.notNullValue())); this.registry = SharedMetricRegistries.getOrCreate("cache-metrics"); } @Test public void measuresGets() throws Exception { cache.get("woo"); assertThat(registry.timer(name(Cache.class, "test-config", "gets")).getCount()) .isEqualTo(1); } @Test public void measuresPuts() throws Exception { cache.put(new Element("woo", "whee")); assertThat(registry.timer(name(Cache.class, "test-config", "puts")).getCount()) .isEqualTo(1); } } InstrumentedEhcacheTest.java000066400000000000000000000024671315671014200336250ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/java/com/codahale/metrics/ehcachepackage com.codahale.metrics.ehcache; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import net.sf.ehcache.config.CacheConfiguration; import org.junit.Before; import org.junit.Test; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedEhcacheTest { private static final CacheManager MANAGER = CacheManager.create(); private final MetricRegistry registry = new MetricRegistry(); private Ehcache cache; @Before public void setUp() throws Exception { final Cache c = new Cache(new CacheConfiguration("test", 100)); MANAGER.addCache(c); this.cache = InstrumentedEhcache.instrument(registry, c); } @Test public void measuresGetsAndPuts() throws Exception { cache.get("woo"); cache.put(new Element("woo", "whee")); final Timer gets = registry.timer(name(Cache.class, "test", "gets")); assertThat(gets.getCount()) .isEqualTo(1); final Timer puts = registry.timer(name(Cache.class, "test", "puts")); assertThat(puts.getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-ehcache/src/test/resources/000077500000000000000000000000001315671014200217735ustar00rootroot00000000000000metrics-3.2.5/metrics-ehcache/src/test/resources/ehcache.xml000066400000000000000000000016571315671014200241060ustar00rootroot00000000000000 metrics-3.2.5/metrics-ganglia/000077500000000000000000000000001315671014200162355ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/pom.xml000066400000000000000000000021351315671014200175530ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-ganglia Ganglia Integration for Metrics bundle A reporter for Metrics which announces measurements to a Ganglia cluster. io.dropwizard.metrics metrics-core ${project.version} info.ganglia.gmetric4j gmetric4j 1.0.7 metrics-3.2.5/metrics-ganglia/src/000077500000000000000000000000001315671014200170245ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/000077500000000000000000000000001315671014200177505ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/000077500000000000000000000000001315671014200206715ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/com/000077500000000000000000000000001315671014200214475ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/com/codahale/000077500000000000000000000000001315671014200232075ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200246555ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/com/codahale/metrics/ganglia/000077500000000000000000000000001315671014200262575ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/main/java/com/codahale/metrics/ganglia/GangliaReporter.java000066400000000000000000000406141315671014200322140ustar00rootroot00000000000000package com.codahale.metrics.ganglia; import com.codahale.metrics.*; import com.codahale.metrics.MetricAttribute; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetricSlope; import info.ganglia.gmetric4j.gmetric.GMetricType; import info.ganglia.gmetric4j.gmetric.GangliaException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import static com.codahale.metrics.MetricRegistry.name; import static com.codahale.metrics.MetricAttribute.*; /** * A reporter which announces metric values to a Ganglia cluster. * * @see Ganglia Monitoring System */ public class GangliaReporter extends ScheduledReporter { private static final Pattern SLASHES = Pattern.compile("\\\\"); /** * Returns a new {@link Builder} for {@link GangliaReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link GangliaReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } /** * A builder for {@link GangliaReporter} instances. Defaults to using a {@code tmax} of {@code 60}, * a {@code dmax} of {@code 0}, converting rates to events/second, converting durations to * milliseconds, and not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private String prefix; private int tMax; private int dMax; private TimeUnit rateUnit; private TimeUnit durationUnit; private MetricFilter filter; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop; private Set disabledMetricAttributes = Collections.emptySet(); private Builder(MetricRegistry registry) { this.registry = registry; this.tMax = 60; this.dMax = 0; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.filter = MetricFilter.ALL; this.executor = null; this.shutdownExecutorOnStop = true; } /** * Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter. * Default value is true. * Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}. * * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter * @return {@code this} */ public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } /** * Specifies the executor to use while scheduling reporting of metrics. * Default value is null. * Null value leads to executor will be auto created on start. * * @param executor the executor to use while scheduling reporting of metrics. * @return {@code this} */ public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } /** * Use the given {@code tmax} value when announcing metrics. * * @param tMax the desired gmond {@code tmax} value * @return {@code this} */ public Builder withTMax(int tMax) { this.tMax = tMax; return this; } /** * Prefix all metric names with the given string. * * @param prefix the prefix for all metric names * @return {@code this} */ public Builder prefixedWith(String prefix) { this.prefix = prefix; return this; } /** * Use the given {@code dmax} value when announcing metrics. * * @param dMax the desired gmond {@code dmax} value * @return {@code this} */ public Builder withDMax(int dMax) { this.dMax = dMax; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } /** * Don't report the passed metric attributes for all metrics (e.g. "p999", "stddev" or "m15"). * See {@link MetricAttribute}. * * @param disabledMetricAttributes a {@link MetricFilter} * @return {@code this} */ public Builder disabledMetricAttributes(Set disabledMetricAttributes) { this.disabledMetricAttributes = disabledMetricAttributes; return this; } /** * Builds a {@link GangliaReporter} with the given properties, announcing metrics to the * given {@link GMetric} client. * * @param gmetric the client to use for announcing metrics * @return a {@link GangliaReporter} */ public GangliaReporter build(GMetric gmetric) { return new GangliaReporter(registry, gmetric, null, prefix, tMax, dMax, rateUnit, durationUnit, filter, executor, shutdownExecutorOnStop, disabledMetricAttributes); } /** * Builds a {@link GangliaReporter} with the given properties, announcing metrics to the * given {@link GMetric} client. * * @param gmetrics the clients to use for announcing metrics * @return a {@link GangliaReporter} */ public GangliaReporter build(GMetric... gmetrics) { return new GangliaReporter(registry, null, gmetrics, prefix, tMax, dMax, rateUnit, durationUnit, filter, executor, shutdownExecutorOnStop , disabledMetricAttributes); } } private static final Logger LOGGER = LoggerFactory.getLogger(GangliaReporter.class); private final GMetric gmetric; private final GMetric[] gmetrics; private final String prefix; private final int tMax; private final int dMax; private GangliaReporter(MetricRegistry registry, GMetric gmetric, GMetric[] gmetrics, String prefix, int tMax, int dMax, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, Set disabledMetricAttributes) { super(registry, "ganglia-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop, disabledMetricAttributes); this.gmetric = gmetric; this.gmetrics = gmetrics; this.prefix = prefix; this.tMax = tMax; this.dMax = dMax; } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { for (Map.Entry entry : gauges.entrySet()) { reportGauge(entry.getKey(), entry.getValue()); } for (Map.Entry entry : counters.entrySet()) { reportCounter(entry.getKey(), entry.getValue()); } for (Map.Entry entry : histograms.entrySet()) { reportHistogram(entry.getKey(), entry.getValue()); } for (Map.Entry entry : meters.entrySet()) { reportMeter(entry.getKey(), entry.getValue()); } for (Map.Entry entry : timers.entrySet()) { reportTimer(entry.getKey(), entry.getValue()); } } private void reportTimer(String name, Timer timer) { final String sanitizedName = escapeSlashes(name); final String group = group(name); try { final Snapshot snapshot = timer.getSnapshot(); announceIfEnabled(MAX, sanitizedName, group, convertDuration(snapshot.getMax()), getDurationUnit()); announceIfEnabled(MEAN, sanitizedName, group, convertDuration(snapshot.getMean()), getDurationUnit()); announceIfEnabled(MIN, sanitizedName, group, convertDuration(snapshot.getMin()), getDurationUnit()); announceIfEnabled(STDDEV, sanitizedName, group, convertDuration(snapshot.getStdDev()), getDurationUnit()); announceIfEnabled(P50, sanitizedName, group, convertDuration(snapshot.getMedian()), getDurationUnit()); announceIfEnabled(P75, sanitizedName, group, convertDuration(snapshot.get75thPercentile()), getDurationUnit()); announceIfEnabled(P95, sanitizedName, group, convertDuration(snapshot.get95thPercentile()), getDurationUnit()); announceIfEnabled(P98, sanitizedName, group, convertDuration(snapshot.get98thPercentile()), getDurationUnit()); announceIfEnabled(P99, sanitizedName, group, convertDuration(snapshot.get99thPercentile()), getDurationUnit()); announceIfEnabled(P999, sanitizedName, group, convertDuration(snapshot.get999thPercentile()), getDurationUnit()); reportMetered(sanitizedName, timer, group, "calls"); } catch (GangliaException e) { LOGGER.warn("Unable to report timer {}", sanitizedName, e); } } private void reportMeter(String name, Meter meter) { final String sanitizedName = escapeSlashes(name); final String group = group(name); try { reportMetered(sanitizedName, meter, group, "events"); } catch (GangliaException e) { LOGGER.warn("Unable to report meter {}", name, e); } } private void reportMetered(String name, Metered meter, String group, String eventName) throws GangliaException { final String unit = eventName + '/' + getRateUnit(); announceIfEnabled(COUNT, name, group, meter.getCount(), eventName); announceIfEnabled(M1_RATE, name, group, convertRate(meter.getOneMinuteRate()), unit); announceIfEnabled(M5_RATE, name, group, convertRate(meter.getFiveMinuteRate()), unit); announceIfEnabled(M15_RATE, name, group, convertRate(meter.getFifteenMinuteRate()), unit); announceIfEnabled(MEAN_RATE, name, group, convertRate(meter.getMeanRate()), unit); } private void reportHistogram(String name, Histogram histogram) { final String sanitizedName = escapeSlashes(name); final String group = group(name); try { final Snapshot snapshot = histogram.getSnapshot(); announceIfEnabled(COUNT, sanitizedName, group, histogram.getCount(), ""); announceIfEnabled(MAX, sanitizedName, group, snapshot.getMax(), ""); announceIfEnabled(MEAN, sanitizedName, group, snapshot.getMean(), ""); announceIfEnabled(MIN, sanitizedName, group, snapshot.getMin(), ""); announceIfEnabled(STDDEV, sanitizedName, group, snapshot.getStdDev(), ""); announceIfEnabled(P50, sanitizedName, group, snapshot.getMedian(), ""); announceIfEnabled(P75, sanitizedName, group, snapshot.get75thPercentile(), ""); announceIfEnabled(P95, sanitizedName, group, snapshot.get95thPercentile(), ""); announceIfEnabled(P98, sanitizedName, group, snapshot.get98thPercentile(), ""); announceIfEnabled(P99, sanitizedName, group, snapshot.get99thPercentile(), ""); announceIfEnabled(P999, sanitizedName, group, snapshot.get999thPercentile(), ""); } catch (GangliaException e) { LOGGER.warn("Unable to report histogram {}", sanitizedName, e); } } private void reportCounter(String name, Counter counter) { final String sanitizedName = escapeSlashes(name); final String group = group(name); try { announce(prefix(sanitizedName, COUNT.getCode()), group, Long.toString(counter.getCount()), GMetricType.DOUBLE, ""); } catch (GangliaException e) { LOGGER.warn("Unable to report counter {}", name, e); } } private void reportGauge(String name, Gauge gauge) { final String sanitizedName = escapeSlashes(name); final String group = group(name); final Object obj = gauge.getValue(); final String value = String.valueOf(obj); final GMetricType type = detectType(obj); try { announce(name(prefix, sanitizedName), group, value, type, ""); } catch (GangliaException e) { LOGGER.warn("Unable to report gauge {}", name, e); } } private static final double MIN_VAL = 1E-300; private void announceIfEnabled(MetricAttribute metricAttribute, String metricName, String group, double value, String units) throws GangliaException { if (getDisabledMetricAttributes().contains(metricAttribute)) { return; } final String string = Math.abs(value) < MIN_VAL ? "0" : Double.toString(value); announce(prefix(metricName, metricAttribute.getCode()), group, string, GMetricType.DOUBLE, units); } private void announceIfEnabled(MetricAttribute metricAttribute, String metricName, String group, long value, String units) throws GangliaException { if (getDisabledMetricAttributes().contains(metricAttribute)) { return; } announce(prefix(metricName, metricAttribute.getCode()), group, Long.toString(value), GMetricType.DOUBLE, units); } private void announce(String name, String group, String value, GMetricType type, String units) throws GangliaException { if (gmetric != null) { gmetric.announce(name, value, type, units, GMetricSlope.BOTH, tMax, dMax, group); } else { for (GMetric gmetric : gmetrics) { gmetric.announce(name, value, type, units, GMetricSlope.BOTH, tMax, dMax, group); } } } private GMetricType detectType(Object o) { if (o instanceof Float) { return GMetricType.FLOAT; } else if (o instanceof Double) { return GMetricType.DOUBLE; } else if (o instanceof Byte) { return GMetricType.INT8; } else if (o instanceof Short) { return GMetricType.INT16; } else if (o instanceof Integer) { return GMetricType.INT32; } else if (o instanceof Long) { return GMetricType.DOUBLE; } return GMetricType.STRING; } private String group(String name) { final int i = name.lastIndexOf('.'); if (i < 0) { return ""; } return name.substring(0, i); } private String prefix(String name, String n) { return name(prefix, name, n); } // ganglia metric names can't contain slashes. private String escapeSlashes(String name) { return SLASHES.matcher(name).replaceAll("_"); } } metrics-3.2.5/metrics-ganglia/src/test/000077500000000000000000000000001315671014200200035ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/000077500000000000000000000000001315671014200207245ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/com/000077500000000000000000000000001315671014200215025ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/com/codahale/000077500000000000000000000000001315671014200232425ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200247105ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/com/codahale/metrics/ganglia/000077500000000000000000000000001315671014200263125ustar00rootroot00000000000000metrics-3.2.5/metrics-ganglia/src/test/java/com/codahale/metrics/ganglia/GangliaReporterTest.java000066400000000000000000000360621315671014200331110ustar00rootroot00000000000000package com.codahale.metrics.ganglia; import com.codahale.metrics.*; import info.ganglia.gmetric4j.gmetric.GMetric; import info.ganglia.gmetric4j.gmetric.GMetricSlope; import info.ganglia.gmetric4j.gmetric.GMetricType; import org.junit.Test; import java.util.EnumSet; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; public class GangliaReporterTest { private final GMetric ganglia = mock(GMetric.class); private final MetricRegistry registry = mock(MetricRegistry.class); private final GangliaReporter reporter = GangliaReporter.forRegistry(registry) .prefixedWith("m") .withTMax(60) .withDMax(0) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(ganglia); @Test public void reportsStringGaugeValues() throws Exception { reporter.report(map("gauge", gauge("value")), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "value", GMetricType.STRING, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void escapeSlashesInMetricNames() throws Exception { reporter.report(map("gauge_with\\slashes", gauge("value")), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge_with_slashes", "value", GMetricType.STRING, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsByteGaugeValues() throws Exception { reporter.report(map("gauge", gauge((byte) 1)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1", GMetricType.INT8, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsShortGaugeValues() throws Exception { reporter.report(map("gauge", gauge((short) 1)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1", GMetricType.INT16, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsIntegerGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1", GMetricType.INT32, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsLongGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1L)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsFloatGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1.0f)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1.0", GMetricType.FLOAT, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsDoubleGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1.0)), this.map(), this.map(), this.map(), this.map()); verify(ganglia).announce("m.gauge", "1.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, ""); verifyNoMoreInteractions(ganglia); } @Test public void reportsCounterValues() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); reporter.report(this.map(), map("test.counter", counter), this.map(), this.map(), this.map()); verify(ganglia).announce("m.test.counter.count", "100", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verifyNoMoreInteractions(ganglia); } @Test public void reportsHistogramValues() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), map("test.histogram", histogram), this.map(), this.map()); verify(ganglia).announce("m.test.histogram.count", "1", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.max", "2", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.mean", "3.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.min", "4", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.stddev", "5.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p50", "6.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p75", "7.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p95", "8.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p98", "9.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p99", "10.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.histogram.p999", "11.0", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verifyNoMoreInteractions(ganglia); } @Test public void reportsMeterValues() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); reporter.report(this.map(), this.map(), this.map(), map("test.meter", meter), this.map()); verify(ganglia).announce("m.test.meter.count", "1", GMetricType.DOUBLE, "events", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.mean_rate", "2.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.m1_rate", "3.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.m5_rate", "4.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.m15_rate", "5.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verifyNoMoreInteractions(ganglia); } @Test public void reportsTimerValues() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), this.map(), this.map(), map("test.another.timer", timer)); verify(ganglia).announce("m.test.another.timer.max", "100.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.mean", "200.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.min", "300.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.stddev", "400.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p50", "500.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p75", "600.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p95", "700.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p98", "800.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p99", "900.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.p999", "1000.0", GMetricType.DOUBLE, "milliseconds", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.count", "1", GMetricType.DOUBLE, "calls", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.mean_rate", "2.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.m1_rate", "3.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.m5_rate", "4.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); verify(ganglia).announce("m.test.another.timer.m15_rate", "5.0", GMetricType.DOUBLE, "calls/second", GMetricSlope.BOTH, 60, 0, "test.another"); verifyNoMoreInteractions(ganglia); } @Test public void disabledMetricAttributes() throws Exception { final Meter meter = mock(Meter.class); final Counter counter = mock(Counter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(3.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(5.0); when(counter.getCount()).thenReturn(1L); GangliaReporter reporter = GangliaReporter.forRegistry(registry) .prefixedWith("m") .withTMax(60) .withDMax(0) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(EnumSet.of(MetricAttribute.COUNT, MetricAttribute.MEAN_RATE, MetricAttribute.M15_RATE)) .build(ganglia); reporter.report(this.map(), map("test.counter", counter), this.map(), map("test.meter", meter), this.map()); verify(ganglia).announce("m.test.counter.count", "1", GMetricType.DOUBLE, "", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.m1_rate", "3.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verify(ganglia).announce("m.test.meter.m5_rate", "4.0", GMetricType.DOUBLE, "events/second", GMetricSlope.BOTH, 60, 0, "test"); verifyNoMoreInteractions(ganglia); reporter.close(); } private SortedMap map() { return new TreeMap(); } private SortedMap map(String name, T metric) { final TreeMap map = new TreeMap(); map.put(name, metric); return map; } private Gauge gauge(T value) { final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(value); return gauge; } } metrics-3.2.5/metrics-graphite/000077500000000000000000000000001315671014200164365ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/pom.xml000066400000000000000000000025341315671014200177570ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-graphite Graphite Integration for Metrics bundle A reporter for Metrics which announces measurements to a Graphite server. io.dropwizard.metrics metrics-core ${project.version} com.rabbitmq amqp-client ${rabbitmq.version} true org.python jython-standalone 2.5.3 test metrics-3.2.5/metrics-graphite/src/000077500000000000000000000000001315671014200172255ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/000077500000000000000000000000001315671014200201515ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/000077500000000000000000000000001315671014200210725ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/com/000077500000000000000000000000001315671014200216505ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/000077500000000000000000000000001315671014200234105ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200250565ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/000077500000000000000000000000001315671014200266615ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/Graphite.java000066400000000000000000000132431315671014200312720ustar00rootroot00000000000000package com.codahale.metrics.graphite; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.SocketFactory; import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.nio.charset.Charset; /** * A client to a Carbon server via TCP. */ public class Graphite implements GraphiteSender { // this may be optimistic about Carbon/Graphite private static final Charset UTF_8 = Charset.forName("UTF-8"); private final String hostname; private final int port; private final InetSocketAddress address; private final SocketFactory socketFactory; private final Charset charset; private Socket socket; private Writer writer; private int failures; private static final Logger LOGGER = LoggerFactory.getLogger(Graphite.class); /** * Creates a new client which connects to the given address using the default * {@link SocketFactory}. * * @param hostname The hostname of the Carbon server * @param port The port of the Carbon server */ public Graphite(String hostname, int port) { this(hostname, port, SocketFactory.getDefault()); } /** * Creates a new client which connects to the given address and socket factory. * * @param hostname The hostname of the Carbon server * @param port The port of the Carbon server * @param socketFactory the socket factory */ public Graphite(String hostname, int port, SocketFactory socketFactory) { this(hostname, port, socketFactory, UTF_8); } /** * Creates a new client which connects to the given address and socket factory using the given * character set. * * @param hostname The hostname of the Carbon server * @param port The port of the Carbon server * @param socketFactory the socket factory * @param charset the character set used by the server */ public Graphite(String hostname, int port, SocketFactory socketFactory, Charset charset) { this.hostname = hostname; this.port = port; this.address = null; this.socketFactory = socketFactory; this.charset = charset; } /** * Creates a new client which connects to the given address using the default * {@link SocketFactory}. * * @param address the address of the Carbon server */ public Graphite(InetSocketAddress address) { this(address, SocketFactory.getDefault()); } /** * Creates a new client which connects to the given address and socket factory. * * @param address the address of the Carbon server * @param socketFactory the socket factory */ public Graphite(InetSocketAddress address, SocketFactory socketFactory) { this(address, socketFactory, UTF_8); } /** * Creates a new client which connects to the given address and socket factory using the given * character set. * * @param address the address of the Carbon server * @param socketFactory the socket factory * @param charset the character set used by the server */ public Graphite(InetSocketAddress address, SocketFactory socketFactory, Charset charset) { this.hostname = null; this.port = -1; this.address = address; this.socketFactory = socketFactory; this.charset = charset; } @Override public void connect() throws IllegalStateException, IOException { if (isConnected()) { throw new IllegalStateException("Already connected"); } InetSocketAddress address = this.address; if (address == null) { address = new InetSocketAddress(hostname, port); } if (address.getAddress() == null) { // retry lookup, just in case the DNS changed address = new InetSocketAddress(address.getHostName(),address.getPort()); if (address.getAddress() == null) { throw new UnknownHostException(address.getHostName()); } } this.socket = socketFactory.createSocket(address.getAddress(), address.getPort()); this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), charset)); } @Override public boolean isConnected() { return socket != null && socket.isConnected() && !socket.isClosed(); } @Override public void send(String name, String value, long timestamp) throws IOException { try { writer.write(sanitize(name)); writer.write(' '); writer.write(sanitize(value)); writer.write(' '); writer.write(Long.toString(timestamp)); writer.write('\n'); this.failures = 0; } catch (IOException e) { failures++; throw e; } } @Override public int getFailures() { return failures; } @Override public void flush() throws IOException { if (writer != null) { writer.flush(); } } @Override public void close() throws IOException { try { if (writer != null) { writer.close(); } } catch (IOException ex) { LOGGER.debug("Error closing writer", ex); } finally { this.writer = null; } try { if (socket != null) { socket.close(); } } catch (IOException ex) { LOGGER.debug("Error closing socket", ex); } finally { this.socket = null; } } protected String sanitize(String s) { return GraphiteSanitize.sanitize(s); } } metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteRabbitMQ.java000066400000000000000000000132351315671014200326550ustar00rootroot00000000000000package com.codahale.metrics.graphite; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DefaultSocketConfigurator; import java.io.IOException; import java.net.Socket; import java.nio.charset.Charset; import java.util.concurrent.TimeoutException; /** * A rabbit-mq client to a Carbon server. */ public class GraphiteRabbitMQ implements GraphiteSender { private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Integer DEFAULT_RABBIT_CONNECTION_TIMEOUT_MS = 500; private static final Integer DEFAULT_RABBIT_SOCKET_TIMEOUT_MS = 5000; private static final Integer DEFAULT_RABBIT_REQUESTED_HEARTBEAT_SEC = 10; private ConnectionFactory connectionFactory; private Connection connection; private Channel channel; private String exchange; private int failures; /** * Creates a new client with a given a {@link com.rabbitmq.client.ConnectionFactory} and an amqp exchange * * @param connectionFactory the {@link com.rabbitmq.client.ConnectionFactory} used to establish connection and publish to graphite server * @param exchange the amqp exchange */ public GraphiteRabbitMQ(final ConnectionFactory connectionFactory, final String exchange) { this.connectionFactory = connectionFactory; this.exchange = exchange; } /** * Creates a new client given connection details * * @param rabbitHost the rabbitmq server host * @param rabbitPort the rabbitmq server port * @param rabbitUsername the rabbitmq server username * @param rabbitPassword the rabbitmq server password * @param exchange the amqp exchange */ public GraphiteRabbitMQ( final String rabbitHost, final Integer rabbitPort, final String rabbitUsername, final String rabbitPassword, final String exchange) { this(rabbitHost, rabbitPort, rabbitUsername, rabbitPassword, exchange, DEFAULT_RABBIT_CONNECTION_TIMEOUT_MS, DEFAULT_RABBIT_SOCKET_TIMEOUT_MS, DEFAULT_RABBIT_REQUESTED_HEARTBEAT_SEC); } /** * Creates a new client given connection details * * @param rabbitHost the rabbitmq server host * @param rabbitPort the rabbitmq server port * @param rabbitUsername the rabbitmq server username * @param rabbitPassword the rabbitmq server password * @param exchange the amqp exchange * @param rabbitConnectionTimeoutMS the connection timeout in milliseconds * @param rabbitSocketTimeoutMS the socket timeout in milliseconds * @param rabbitRequestedHeartbeatInSeconds the hearthbeat in seconds */ public GraphiteRabbitMQ( final String rabbitHost, final Integer rabbitPort, final String rabbitUsername, final String rabbitPassword, final String exchange, final Integer rabbitConnectionTimeoutMS, final Integer rabbitSocketTimeoutMS, final Integer rabbitRequestedHeartbeatInSeconds) { this.exchange = exchange; this.connectionFactory = new ConnectionFactory(); connectionFactory.setSocketConfigurator(new DefaultSocketConfigurator() { @Override public void configure(Socket socket) throws IOException { super.configure(socket); socket.setSoTimeout(rabbitSocketTimeoutMS); } }); connectionFactory.setConnectionTimeout(rabbitConnectionTimeoutMS); connectionFactory.setRequestedHeartbeat(rabbitRequestedHeartbeatInSeconds); connectionFactory.setHost(rabbitHost); connectionFactory.setPort(rabbitPort); connectionFactory.setUsername(rabbitUsername); connectionFactory.setPassword(rabbitPassword); } @Override public void connect() throws IllegalStateException, IOException { if (isConnected()) { throw new IllegalStateException("Already connected"); } try { connection = connectionFactory.newConnection(); } catch (TimeoutException e) { throw new IllegalStateException(e); } channel = connection.createChannel(); } @Override public boolean isConnected() { return connection != null && connection.isOpen(); } @Override public void send(String name, String value, long timestamp) throws IOException { try { final String sanitizedName = sanitize(name); final String sanitizedValue = sanitize(value); final String message = new StringBuilder() .append(sanitizedName).append(' ') .append(sanitizedValue).append(' ') .append(Long.toString(timestamp)).append('\n').toString(); channel.basicPublish(exchange, sanitizedName, null, message.getBytes(UTF_8)); } catch (IOException e) { failures++; throw e; } } @Override public void flush() throws IOException { // Nothing to do } @Override public void close() throws IOException { if (connection != null) { connection.close(); } } @Override public int getFailures() { return failures; } public String sanitize(String s) { return GraphiteSanitize.sanitize(s); } } metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteReporter.java000066400000000000000000000363441315671014200330240ustar00rootroot00000000000000package com.codahale.metrics.graphite; import com.codahale.metrics.*; import com.codahale.metrics.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricAttribute.*; /** * A reporter which publishes metric values to a Graphite server. * * @see Graphite - Scalable Realtime Graphing */ public class GraphiteReporter extends ScheduledReporter { /** * Returns a new {@link Builder} for {@link GraphiteReporter}. * * @param registry the registry to report * @return a {@link Builder} instance for a {@link GraphiteReporter} */ public static Builder forRegistry(MetricRegistry registry) { return new Builder(registry); } /** * A builder for {@link GraphiteReporter} instances. Defaults to not using a prefix, using the * default clock, converting rates to events/second, converting durations to milliseconds, and * not filtering metrics. */ public static class Builder { private final MetricRegistry registry; private Clock clock; private String prefix; private TimeUnit rateUnit; private TimeUnit durationUnit; private MetricFilter filter; private ScheduledExecutorService executor; private boolean shutdownExecutorOnStop; private Set disabledMetricAttributes; private Builder(MetricRegistry registry) { this.registry = registry; this.clock = Clock.defaultClock(); this.prefix = null; this.rateUnit = TimeUnit.SECONDS; this.durationUnit = TimeUnit.MILLISECONDS; this.filter = MetricFilter.ALL; this.executor = null; this.shutdownExecutorOnStop = true; this.disabledMetricAttributes = Collections.emptySet(); } /** * Specifies whether or not, the executor (used for reporting) will be stopped with same time with reporter. * Default value is true. * Setting this parameter to false, has the sense in combining with providing external managed executor via {@link #scheduleOn(ScheduledExecutorService)}. * * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter * @return {@code this} */ public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop) { this.shutdownExecutorOnStop = shutdownExecutorOnStop; return this; } /** * Specifies the executor to use while scheduling reporting of metrics. * Default value is null. * Null value leads to executor will be auto created on start. * * @param executor the executor to use while scheduling reporting of metrics. * @return {@code this} */ public Builder scheduleOn(ScheduledExecutorService executor) { this.executor = executor; return this; } /** * Use the given {@link Clock} instance for the time. * * @param clock a {@link Clock} instance * @return {@code this} */ public Builder withClock(Clock clock) { this.clock = clock; return this; } /** * Prefix all metric names with the given string. * * @param prefix the prefix for all metric names * @return {@code this} */ public Builder prefixedWith(String prefix) { this.prefix = prefix; return this; } /** * Convert rates to the given time unit. * * @param rateUnit a unit of time * @return {@code this} */ public Builder convertRatesTo(TimeUnit rateUnit) { this.rateUnit = rateUnit; return this; } /** * Convert durations to the given time unit. * * @param durationUnit a unit of time * @return {@code this} */ public Builder convertDurationsTo(TimeUnit durationUnit) { this.durationUnit = durationUnit; return this; } /** * Only report metrics which match the given filter. * * @param filter a {@link MetricFilter} * @return {@code this} */ public Builder filter(MetricFilter filter) { this.filter = filter; return this; } /** * Don't report the passed metric attributes for all metrics (e.g. "p999", "stddev" or "m15"). * See {@link MetricAttribute}. * * @param disabledMetricAttributes a {@link MetricFilter} * @return {@code this} */ public Builder disabledMetricAttributes(Set disabledMetricAttributes) { this.disabledMetricAttributes = disabledMetricAttributes; return this; } /** * Builds a {@link GraphiteReporter} with the given properties, sending metrics using the * given {@link GraphiteSender}. * * Present for binary compatibility * * @param graphite a {@link Graphite} * @return a {@link GraphiteReporter} */ public GraphiteReporter build(Graphite graphite) { return build((GraphiteSender) graphite); } /** * Builds a {@link GraphiteReporter} with the given properties, sending metrics using the * given {@link GraphiteSender}. * * @param graphite a {@link GraphiteSender} * @return a {@link GraphiteReporter} */ public GraphiteReporter build(GraphiteSender graphite) { return new GraphiteReporter(registry, graphite, clock, prefix, rateUnit, durationUnit, filter, executor, shutdownExecutorOnStop, disabledMetricAttributes); } } private static final Logger LOGGER = LoggerFactory.getLogger(GraphiteReporter.class); private final GraphiteSender graphite; private final Clock clock; private final String prefix; /** * Creates a new {@link GraphiteReporter} instance. * * @param registry the {@link MetricRegistry} containing the metrics this * reporter will report * @param graphite the {@link GraphiteSender} which is responsible for sending metrics to a Carbon server * via a transport protocol * @param clock the instance of the time. Use {@link Clock#defaultClock()} for the default * @param prefix the prefix of all metric names (may be null) * @param rateUnit the time unit of in which rates will be converted * @param durationUnit the time unit of in which durations will be converted * @param filter the filter for which metrics to report * @param executor the executor to use while scheduling reporting of metrics (may be null). * @param shutdownExecutorOnStop if true, then executor will be stopped in same time with this reporter */ protected GraphiteReporter(MetricRegistry registry, GraphiteSender graphite, Clock clock, String prefix, TimeUnit rateUnit, TimeUnit durationUnit, MetricFilter filter, ScheduledExecutorService executor, boolean shutdownExecutorOnStop, Set disabledMetricAttributes) { super(registry, "graphite-reporter", filter, rateUnit, durationUnit, executor, shutdownExecutorOnStop, disabledMetricAttributes); this.graphite = graphite; this.clock = clock; this.prefix = prefix; } @Override public void report(SortedMap gauges, SortedMap counters, SortedMap histograms, SortedMap meters, SortedMap timers) { final long timestamp = clock.getTime() / 1000; // oh it'd be lovely to use Java 7 here try { graphite.connect(); for (Map.Entry entry : gauges.entrySet()) { reportGauge(entry.getKey(), entry.getValue(), timestamp); } for (Map.Entry entry : counters.entrySet()) { reportCounter(entry.getKey(), entry.getValue(), timestamp); } for (Map.Entry entry : histograms.entrySet()) { reportHistogram(entry.getKey(), entry.getValue(), timestamp); } for (Map.Entry entry : meters.entrySet()) { reportMetered(entry.getKey(), entry.getValue(), timestamp); } for (Map.Entry entry : timers.entrySet()) { reportTimer(entry.getKey(), entry.getValue(), timestamp); } graphite.flush(); } catch (IOException e) { LOGGER.warn("Unable to report to Graphite", graphite, e); } finally { try { graphite.close(); } catch (IOException e1) { LOGGER.warn("Error closing Graphite", graphite, e1); } } } @Override public void stop() { try { super.stop(); } finally { try { graphite.close(); } catch (IOException e) { LOGGER.debug("Error disconnecting from Graphite", graphite, e); } } } private void reportTimer(String name, Timer timer, long timestamp) throws IOException { final Snapshot snapshot = timer.getSnapshot(); sendIfEnabled(MAX, name, convertDuration(snapshot.getMax()), timestamp); sendIfEnabled(MEAN, name, convertDuration(snapshot.getMean()), timestamp); sendIfEnabled(MIN, name, convertDuration(snapshot.getMin()), timestamp); sendIfEnabled(STDDEV, name, convertDuration(snapshot.getStdDev()), timestamp); sendIfEnabled(P50, name, convertDuration(snapshot.getMedian()), timestamp); sendIfEnabled(P75, name, convertDuration(snapshot.get75thPercentile()), timestamp); sendIfEnabled(P95, name, convertDuration(snapshot.get95thPercentile()), timestamp); sendIfEnabled(P98, name, convertDuration(snapshot.get98thPercentile()), timestamp); sendIfEnabled(P99, name, convertDuration(snapshot.get99thPercentile()), timestamp); sendIfEnabled(P999, name, convertDuration(snapshot.get999thPercentile()), timestamp); reportMetered(name, timer, timestamp); } private void reportMetered(String name, Metered meter, long timestamp) throws IOException { sendIfEnabled(COUNT, name, meter.getCount(), timestamp); sendIfEnabled(M1_RATE, name, convertRate(meter.getOneMinuteRate()), timestamp); sendIfEnabled(M5_RATE, name, convertRate(meter.getFiveMinuteRate()), timestamp); sendIfEnabled(M15_RATE, name, convertRate(meter.getFifteenMinuteRate()), timestamp); sendIfEnabled(MEAN_RATE, name, convertRate(meter.getMeanRate()), timestamp); } private void reportHistogram(String name, Histogram histogram, long timestamp) throws IOException { final Snapshot snapshot = histogram.getSnapshot(); sendIfEnabled(COUNT, name, histogram.getCount(), timestamp); sendIfEnabled(MAX, name, snapshot.getMax(), timestamp); sendIfEnabled(MEAN, name, snapshot.getMean(), timestamp); sendIfEnabled(MIN, name, snapshot.getMin(), timestamp); sendIfEnabled(STDDEV, name, snapshot.getStdDev(), timestamp); sendIfEnabled(P50, name, snapshot.getMedian(), timestamp); sendIfEnabled(P75, name, snapshot.get75thPercentile(), timestamp); sendIfEnabled(P95, name, snapshot.get95thPercentile(), timestamp); sendIfEnabled(P98, name, snapshot.get98thPercentile(), timestamp); sendIfEnabled(P99, name, snapshot.get99thPercentile(), timestamp); sendIfEnabled(P999, name, snapshot.get999thPercentile(), timestamp); } private void sendIfEnabled(MetricAttribute type, String name, double value, long timestamp) throws IOException { if (getDisabledMetricAttributes().contains(type)){ return; } graphite.send(prefix(name, type.getCode()), format(value), timestamp); } private void sendIfEnabled(MetricAttribute type, String name, long value, long timestamp) throws IOException { if (getDisabledMetricAttributes().contains(type)){ return; } graphite.send(prefix(name, type.getCode()), format(value), timestamp); } private void reportCounter(String name, Counter counter, long timestamp) throws IOException { graphite.send(prefix(name, COUNT.getCode()), format(counter.getCount()), timestamp); } private void reportGauge(String name, Gauge gauge, long timestamp) throws IOException { final String value = format(gauge.getValue()); if (value != null) { graphite.send(prefix(name), value, timestamp); } } private String format(Object o) { if (o instanceof Float) { return format(((Float) o).doubleValue()); } else if (o instanceof Double) { return format(((Double) o).doubleValue()); } else if (o instanceof Byte) { return format(((Byte) o).longValue()); } else if (o instanceof Short) { return format(((Short) o).longValue()); } else if (o instanceof Integer) { return format(((Integer) o).longValue()); } else if (o instanceof Long) { return format(((Long) o).longValue()); } else if (o instanceof BigInteger) { return format(((BigInteger) o).doubleValue()); } else if (o instanceof BigDecimal) { return format(((BigDecimal) o).doubleValue()); } else if (o instanceof Boolean) { return format(((Boolean) o) ? 1 : 0); } return null; } private String prefix(String... components) { return MetricRegistry.name(prefix, components); } private String format(long n) { return Long.toString(n); } protected String format(double v) { // the Carbon plaintext format is pretty underspecified, but it seems like it just wants // US-formatted digits return String.format(Locale.US, "%2.2f", v); } } metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteSanitize.java000066400000000000000000000006701315671014200330010ustar00rootroot00000000000000package com.codahale.metrics.graphite; import java.util.regex.Pattern; class GraphiteSanitize { private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); private static final String DASH = "-"; /** * Trims the string and replaces all whitespace characters with the provided symbol */ static String sanitize(String string) { return WHITESPACE.matcher(string.trim()).replaceAll(DASH); } } metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteSender.java000066400000000000000000000020601315671014200324260ustar00rootroot00000000000000package com.codahale.metrics.graphite; import java.io.Closeable; import java.io.IOException; public interface GraphiteSender extends Closeable{ /** * Connects to the server. * * @throws IllegalStateException if the client is already connected * @throws IOException if there is an error connecting */ public void connect() throws IllegalStateException, IOException; /** * Sends the given measurement to the server. * * @param name the name of the metric * @param value the value of the metric * @param timestamp the timestamp of the metric * @throws IOException if there was an error sending the metric */ public void send(String name, String value, long timestamp) throws IOException; /** * Flushes buffer, if applicable * * @throws IOException */ void flush() throws IOException; /** * Returns true if ready to send data */ boolean isConnected(); /** * Returns the number of failed writes to the server. * * @return the number of failed writes to the server */ public int getFailures(); }metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/GraphiteUDP.java000066400000000000000000000063211315671014200316420ustar00rootroot00000000000000package com.codahale.metrics.graphite; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.charset.Charset; /** * A client to a Carbon server using unconnected UDP */ public class GraphiteUDP implements GraphiteSender { private static final Charset UTF_8 = Charset.forName("UTF-8"); private final String hostname; private final int port; private InetSocketAddress address; private DatagramChannel datagramChannel = null; private int failures; /** * Creates a new client which sends data to given address using UDP * * @param hostname The hostname of the Carbon server * @param port The port of the Carbon server */ public GraphiteUDP(String hostname, int port) { this.hostname = hostname; this.port = port; this.address = null; } /** * Creates a new client which sends data to given address using UDP * * @param address the address of the Carbon server */ public GraphiteUDP(InetSocketAddress address) { this.hostname = null; this.port = -1; this.address = address; } @Override public void connect() throws IllegalStateException, IOException { if (isConnected()) { throw new IllegalStateException("Already connected"); } // Resolve hostname if (hostname != null) { address = new InetSocketAddress(hostname, port); } datagramChannel = DatagramChannel.open(); } @Override public boolean isConnected() { return datagramChannel != null && !datagramChannel.socket().isClosed(); } @Override public void send(String name, String value, long timestamp) throws IOException { try { StringBuilder buf = new StringBuilder(); buf.append(sanitize(name)); buf.append(' '); buf.append(sanitize(value)); buf.append(' '); buf.append(Long.toString(timestamp)); buf.append('\n'); String str = buf.toString(); ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes(UTF_8)); datagramChannel.send(byteBuffer, address); this.failures = 0; } catch (IOException e) { failures++; throw e; } } @Override public int getFailures() { return failures; } @Override public void flush() throws IOException { // Nothing to do } @Override public void close() throws IOException { if (datagramChannel != null) { try { datagramChannel.close(); } finally { datagramChannel = null; } } } protected String sanitize(String s) { return GraphiteSanitize.sanitize(s); } DatagramChannel getDatagramChannel() { return datagramChannel; } void setDatagramChannel(DatagramChannel datagramChannel) { this.datagramChannel = datagramChannel; } InetSocketAddress getAddress() { return address; } void setAddress(InetSocketAddress address) { this.address = address; } } metrics-3.2.5/metrics-graphite/src/main/java/com/codahale/metrics/graphite/PickledGraphite.java000066400000000000000000000265171315671014200325760ustar00rootroot00000000000000package com.codahale.metrics.graphite; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.SocketFactory; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; /** * A client to a Carbon server that sends all metrics after they have been pickled in configurable sized batches */ public class PickledGraphite implements GraphiteSender { private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Logger LOGGER = LoggerFactory.getLogger(PickledGraphite.class); private final static int DEFAULT_BATCH_SIZE = 100; private int batchSize; // graphite expects a python-pickled list of nested tuples. private List metrics = new LinkedList(); private final String hostname; private final int port; private final InetSocketAddress address; private final SocketFactory socketFactory; private final Charset charset; private Socket socket; private Writer writer; private int failures; /** * Creates a new client which connects to the given address using the default {@link SocketFactory}. This defaults * to a batchSize of 100 * * @param address * the address of the Carbon server */ public PickledGraphite(InetSocketAddress address) { this(address, DEFAULT_BATCH_SIZE); } /** * Creates a new client which connects to the given address using the default {@link SocketFactory}. * * @param address * the address of the Carbon server * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(InetSocketAddress address, int batchSize) { this(address, SocketFactory.getDefault(), batchSize); } /** * Creates a new client which connects to the given address and socket factory. * * @param address * the address of the Carbon server * @param socketFactory * the socket factory * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(InetSocketAddress address, SocketFactory socketFactory, int batchSize) { this(address, socketFactory, UTF_8, batchSize); } /** * Creates a new client which connects to the given address and socket factory using the given character set. * * @param address * the address of the Carbon server * @param socketFactory * the socket factory * @param charset * the character set used by the server * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(InetSocketAddress address, SocketFactory socketFactory, Charset charset, int batchSize) { this.address = address; this.hostname = null; this.port = -1; this.socketFactory = socketFactory; this.charset = charset; this.batchSize = batchSize; } /** * Creates a new client which connects to the given address using the default {@link SocketFactory}. This defaults * to a batchSize of 100 * * @param hostname * the hostname of the Carbon server * @param port * the port of the Carbon server */ public PickledGraphite(String hostname, int port) { this(hostname, port, DEFAULT_BATCH_SIZE); } /** * Creates a new client which connects to the given address using the default {@link SocketFactory}. * * @param hostname * the hostname of the Carbon server * @param port * the port of the Carbon server * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(String hostname, int port, int batchSize) { this(hostname, port, SocketFactory.getDefault(), batchSize); } /** * Creates a new client which connects to the given address and socket factory. * * @param hostname * the hostname of the Carbon server * @param port * the port of the Carbon server * @param socketFactory * the socket factory * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(String hostname, int port, SocketFactory socketFactory, int batchSize) { this(hostname, port, socketFactory, UTF_8, batchSize); } /** * Creates a new client which connects to the given address and socket factory using the given character set. * * @param hostname * the hostname of the Carbon server * @param port * the port of the Carbon server * @param socketFactory * the socket factory * @param charset * the character set used by the server * @param batchSize * how many metrics are bundled into a single pickle request to graphite */ public PickledGraphite(String hostname, int port, SocketFactory socketFactory, Charset charset, int batchSize) { this.address = null; this.hostname = hostname; this.port = port; this.socketFactory = socketFactory; this.charset = charset; this.batchSize = batchSize; } @Override public void connect() throws IllegalStateException, IOException { if (isConnected()) { throw new IllegalStateException("Already connected"); } InetSocketAddress address = this.address; if (address == null) { address = new InetSocketAddress(hostname, port); } if (address.getAddress() == null) { throw new UnknownHostException(address.getHostName()); } this.socket = socketFactory.createSocket(address.getAddress(), address.getPort()); this.writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), charset)); } @Override public boolean isConnected() { return socket != null && socket.isConnected() && !socket.isClosed(); } /** * Convert the metric to a python tuple of the form: *

    * (timestamp, (name, value)) *

    * And add it to the list of metrics. If we reach the batch size, write them out. * * @param name * the name of the metric * @param value * the value of the metric * @param timestamp * the timestamp of the metric * @throws IOException * if there was an error sending the metric */ @Override public void send(String name, String value, long timestamp) throws IOException { metrics.add(new MetricTuple(sanitize(name), timestamp, sanitize(value))); if (metrics.size() >= batchSize) { writeMetrics(); } } @Override public void flush() throws IOException { writeMetrics(); if (writer != null) { writer.flush(); } } @Override public void close() throws IOException { try { flush(); if (writer != null) { writer.close(); } } catch (IOException ex) { if (socket != null) { socket.close(); } } finally { this.socket = null; this.writer = null; } } @Override public int getFailures() { return failures; } /** * 1. Run the pickler script to package all the pending metrics into a single message * 2. Send the message to graphite * 3. Clear out the list of metrics */ private void writeMetrics() throws IOException { if (metrics.size() > 0) { try { byte[] payload = pickleMetrics(metrics); byte[] header = ByteBuffer.allocate(4).putInt(payload.length).array(); @SuppressWarnings("resource") OutputStream outputStream = socket.getOutputStream(); outputStream.write(header); outputStream.write(payload); outputStream.flush(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Wrote {} metrics", metrics.size()); } } catch (IOException e) { this.failures++; throw e; } finally { // if there was an error, we might miss some data. for now, drop those on the floor and // try to keep going. metrics.clear(); } } } /** * Minimally necessary pickle opcodes. */ private final char MARK = '(', STOP = '.', LONG = 'L', STRING = 'S', APPEND = 'a', LIST = 'l', TUPLE = 't', QUOTE = '\'', LF = '\n'; /** * See: http://readthedocs.org/docs/graphite/en/1.0/feeding-carbon.html * * @throws IOException */ byte[] pickleMetrics(List metrics) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(metrics.size() * 75); // Extremely rough estimate of 75 bytes per message Writer pickled = new OutputStreamWriter(out, charset); pickled.append(MARK); pickled.append(LIST); for (MetricTuple tuple : metrics) { // start the outer tuple pickled.append(MARK); // the metric name is a string. pickled.append(STRING); // the single quotes are to match python's repr("abcd") pickled.append(QUOTE); pickled.append(tuple.name); pickled.append(QUOTE); pickled.append(LF); // start the inner tuple pickled.append(MARK); // timestamp is a long pickled.append(LONG); pickled.append(Long.toString(tuple.timestamp)); // the trailing L is to match python's repr(long(1234)) pickled.append(LONG); pickled.append(LF); // and the value is a string. pickled.append(STRING); pickled.append(QUOTE); pickled.append(tuple.value); pickled.append(QUOTE); pickled.append(LF); pickled.append(TUPLE); // inner close pickled.append(TUPLE); // outer close pickled.append(APPEND); } // every pickle ends with STOP pickled.append(STOP); pickled.flush(); return out.toByteArray(); } static class MetricTuple { String name; long timestamp; String value; MetricTuple(String name, long timestamp, String value) { this.name = name; this.timestamp = timestamp; this.value = value; } } protected String sanitize(String s) { return GraphiteSanitize.sanitize(s); } } metrics-3.2.5/metrics-graphite/src/test/000077500000000000000000000000001315671014200202045ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/000077500000000000000000000000001315671014200211255ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/com/000077500000000000000000000000001315671014200217035ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/000077500000000000000000000000001315671014200234435ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200251115ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/000077500000000000000000000000001315671014200267145ustar00rootroot00000000000000metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteRabbitMQTest.java000077500000000000000000000104161315671014200335510ustar00rootroot00000000000000package com.codahale.metrics.graphite; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.net.UnknownHostException; import java.nio.charset.Charset; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; import static org.mockito.Mockito.*; public class GraphiteRabbitMQTest { private final ConnectionFactory connectionFactory = mock(ConnectionFactory.class); private final Connection connection = mock(Connection.class); private final Channel channel = mock(Channel.class); private final ConnectionFactory bogusConnectionFactory = mock(ConnectionFactory.class); private final Connection bogusConnection = mock(Connection.class); private final Channel bogusChannel = mock(Channel.class); private final GraphiteRabbitMQ graphite = new GraphiteRabbitMQ(connectionFactory, "graphite"); private static final Charset UTF_8 = Charset.forName("UTF-8"); @Before public void setUp() throws Exception { when(connectionFactory.newConnection()).thenReturn(connection); when(connection.createChannel()).thenReturn(channel); when(connection.isOpen()).thenReturn(true); when(bogusConnectionFactory.newConnection()).thenReturn(bogusConnection); when(bogusConnection.createChannel()).thenReturn(bogusChannel); doThrow(new IOException()) .when(bogusChannel) .basicPublish(anyString(), anyString(), any(AMQP.BasicProperties.class), any(byte[].class)); } @Test public void shouldConnectToGraphiteServer() throws Exception { graphite.connect(); verify(connectionFactory, atMost(1)).newConnection(); verify(connection, atMost(1)).createChannel(); } @Test public void measuresFailures() throws Exception { final GraphiteRabbitMQ graphite = new GraphiteRabbitMQ(bogusConnectionFactory, "graphite"); graphite.connect(); try { graphite.send("name", "value", 0); failBecauseExceptionWasNotThrown(IOException.class); } catch (IOException e) { assertThat(graphite.getFailures()).isEqualTo(1); } } @Test public void shouldDisconnectsFromGraphiteServer() throws Exception { graphite.connect(); graphite.close(); verify(connection).close(); } @Test public void shouldNotConnectToGraphiteServerMoreThenOnce() throws Exception { graphite.connect(); try { graphite.connect(); failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException e) { assertThat(e.getMessage()).isEqualTo("Already connected"); } } @Test public void shouldSendMetricsToGraphiteServer() throws Exception { graphite.connect(); graphite.send("name", "value", 100); String expectedMessage = "name value 100\n"; verify(channel, times(1)).basicPublish("graphite", "name", null, expectedMessage.getBytes(UTF_8)); assertThat(graphite.getFailures()).isZero(); } @Test public void shouldSanitizeAndSendMetricsToGraphiteServer() throws Exception { graphite.connect(); graphite.send("name to sanitize", "value to sanitize", 100); String expectedMessage = "name-to-sanitize value-to-sanitize 100\n"; verify(channel, times(1)).basicPublish("graphite", "name-to-sanitize", null, expectedMessage.getBytes(UTF_8)); assertThat(graphite.getFailures()).isZero(); } @Test public void shouldFailWhenGraphiteHostUnavailable() throws Exception { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("some-unknown-host"); GraphiteRabbitMQ unavailableGraphite = new GraphiteRabbitMQ(connectionFactory, "graphite"); try { unavailableGraphite.connect(); failBecauseExceptionWasNotThrown(UnknownHostException.class); } catch (Exception e) { assertThat(e.getMessage()) .isEqualTo("some-unknown-host"); } } } metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteReporterTest.java000066400000000000000000000470471315671014200337210ustar00rootroot00000000000000package com.codahale.metrics.graphite; import com.codahale.metrics.*; import com.codahale.metrics.Timer; import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import java.net.UnknownHostException; import java.util.Locale; import java.util.Collections; import java.util.EnumSet; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; public class GraphiteReporterTest { private final long timestamp = 1000198; private final Clock clock = mock(Clock.class); private final Graphite graphite = mock(Graphite.class); private final MetricRegistry registry = mock(MetricRegistry.class); private final GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) .withClock(clock) .prefixedWith("prefix") .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(Collections.emptySet()) .build(graphite); private final GraphiteReporter minuteRateReporter = GraphiteReporter .forRegistry(registry) .withClock(clock) .prefixedWith("prefix") .convertRatesTo(TimeUnit.MINUTES) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(Collections.emptySet()) .build(graphite); @Before public void setUp() throws Exception { when(clock.getTime()).thenReturn(timestamp * 1000); } @Test public void doesNotReportStringGaugeValues() throws Exception { reporter.report(map("gauge", gauge("value")), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite, never()).send("prefix.gauge", "value", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsByteGaugeValues() throws Exception { reporter.report(map("gauge", gauge((byte) 1)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsShortGaugeValues() throws Exception { reporter.report(map("gauge", gauge((short) 1)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsIntegerGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsLongGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1L)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsFloatGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1.1f)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1.10", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsDoubleGaugeValues() throws Exception { reporter.report(map("gauge", gauge(1.1)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1.10", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsDoubleGaugeValuesWithCustomFormat() throws Exception { final GraphiteReporter graphiteReporter = new GraphiteReporter(registry, graphite, clock, "prefix", TimeUnit.SECONDS, TimeUnit.MICROSECONDS, MetricFilter.ALL, null, false, Collections.emptySet()){ @Override protected String format(double v) { return String.format(Locale.US, "%4.4f", v); } }; graphiteReporter.report(map("gauge", gauge(1.13574)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1.1357", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsBooleanGaugeValues() throws Exception { reporter.report(map("gauge", gauge(true)), this.map(), this.map(), this.map(), this.map()); reporter.report(map("gauge", gauge(false)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "1", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.gauge", "0", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsCounters() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); reporter.report(this.map(), this.map("counter", counter), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.counter.count", "100", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsHistograms() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(histogram.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), this.map("histogram", histogram), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.histogram.count", "1", timestamp); inOrder.verify(graphite).send("prefix.histogram.max", "2", timestamp); inOrder.verify(graphite).send("prefix.histogram.mean", "3.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.min", "4", timestamp); inOrder.verify(graphite).send("prefix.histogram.stddev", "5.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p50", "6.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p75", "7.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p95", "8.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p98", "9.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p99", "10.00", timestamp); inOrder.verify(graphite).send("prefix.histogram.p999", "11.00", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsMeters() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getOneMinuteRate()).thenReturn(2.0); when(meter.getFiveMinuteRate()).thenReturn(3.0); when(meter.getFifteenMinuteRate()).thenReturn(4.0); when(meter.getMeanRate()).thenReturn(5.0); reporter.report(this.map(), this.map(), this.map(), this.map("meter", meter), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.meter.count", "1", timestamp); inOrder.verify(graphite).send("prefix.meter.m1_rate", "2.00", timestamp); inOrder.verify(graphite).send("prefix.meter.m5_rate", "3.00", timestamp); inOrder.verify(graphite).send("prefix.meter.m15_rate", "4.00", timestamp); inOrder.verify(graphite).send("prefix.meter.mean_rate", "5.00", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsMetersInMinutes() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getOneMinuteRate()).thenReturn(2.0); when(meter.getFiveMinuteRate()).thenReturn(3.0); when(meter.getFifteenMinuteRate()).thenReturn(4.0); when(meter.getMeanRate()).thenReturn(5.0); minuteRateReporter.report(this.map(), this.map(), this.map(), this.map("meter", meter), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.meter.count", "1", timestamp); inOrder.verify(graphite).send("prefix.meter.m1_rate", "120.00", timestamp); inOrder.verify(graphite).send("prefix.meter.m5_rate", "180.00", timestamp); inOrder.verify(graphite).send("prefix.meter.m15_rate", "240.00", timestamp); inOrder.verify(graphite).send("prefix.meter.mean_rate", "300.00", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void reportsTimers() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS .toNanos(1000)); when(timer.getSnapshot()).thenReturn(snapshot); reporter.report(this.map(), this.map(), this.map(), this.map(), map("timer", timer)); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.timer.max", "100.00", timestamp); inOrder.verify(graphite).send("prefix.timer.mean", "200.00", timestamp); inOrder.verify(graphite).send("prefix.timer.min", "300.00", timestamp); inOrder.verify(graphite).send("prefix.timer.stddev", "400.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p50", "500.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p75", "600.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p95", "700.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p98", "800.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p99", "900.00", timestamp); inOrder.verify(graphite).send("prefix.timer.p999", "1000.00", timestamp); inOrder.verify(graphite).send("prefix.timer.count", "1", timestamp); inOrder.verify(graphite).send("prefix.timer.m1_rate", "3.00", timestamp); inOrder.verify(graphite).send("prefix.timer.m5_rate", "4.00", timestamp); inOrder.verify(graphite).send("prefix.timer.m15_rate", "5.00", timestamp); inOrder.verify(graphite).send("prefix.timer.mean_rate", "2.00", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); reporter.close(); } @Test public void closesConnectionIfGraphiteIsUnavailable() throws Exception { doThrow(new UnknownHostException("UNKNOWN-HOST")).when(graphite).connect(); reporter.report(map("gauge", gauge(1)), this.map(), this.map(), this.map(), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void closesConnectionOnReporterStop() throws Exception { reporter.stop(); verify(graphite).close(); verifyNoMoreInteractions(graphite); } @Test public void disabledMetricsAttribute() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getOneMinuteRate()).thenReturn(2.0); when(meter.getFiveMinuteRate()).thenReturn(3.0); when(meter.getFifteenMinuteRate()).thenReturn(4.0); when(meter.getMeanRate()).thenReturn(5.0); final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(11L); Set disabledMetricAttributes = EnumSet.of(MetricAttribute.M15_RATE, MetricAttribute.M5_RATE); GraphiteReporter reporterWithdisabledMetricAttributes = GraphiteReporter.forRegistry(registry) .withClock(clock) .prefixedWith("prefix") .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .disabledMetricAttributes(disabledMetricAttributes) .build(graphite); reporterWithdisabledMetricAttributes.report(this.map(), this.map("counter", counter), this.map(), this.map("meter", meter), this.map()); final InOrder inOrder = inOrder(graphite); inOrder.verify(graphite).connect(); inOrder.verify(graphite).send("prefix.counter.count", "11", timestamp); inOrder.verify(graphite).send("prefix.meter.count", "1", timestamp); inOrder.verify(graphite).send("prefix.meter.m1_rate", "2.00", timestamp); inOrder.verify(graphite).send("prefix.meter.mean_rate", "5.00", timestamp); inOrder.verify(graphite).flush(); inOrder.verify(graphite).close(); verifyNoMoreInteractions(graphite); } private SortedMap map() { return new TreeMap(); } private SortedMap map(String name, T metric) { final TreeMap map = new TreeMap(); map.put(name, metric); return map; } private Gauge gauge(T value) { final Gauge gauge = mock(Gauge.class); when(gauge.getValue()).thenReturn(value); return gauge; } } metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteSanitizeTest.java000066400000000000000000000027211315671014200336730ustar00rootroot00000000000000package com.codahale.metrics.graphite; import org.assertj.core.api.SoftAssertions; import org.junit.Test; public class GraphiteSanitizeTest { @Test public void sanitizeGraphiteValues() { SoftAssertions softly = new SoftAssertions(); softly.assertThat(GraphiteSanitize.sanitize("Foo Bar")).isEqualTo("Foo-Bar"); softly.assertThat(GraphiteSanitize.sanitize(" Foo Bar ")).isEqualTo("Foo-Bar"); softly.assertThat(GraphiteSanitize.sanitize(" Foo Bar")).isEqualTo("Foo-Bar"); softly.assertThat(GraphiteSanitize.sanitize("Foo Bar ")).isEqualTo("Foo-Bar"); softly.assertThat(GraphiteSanitize.sanitize(" Foo Bar ")).isEqualTo("Foo-Bar"); softly.assertThat(GraphiteSanitize.sanitize("Foo@Bar")).isEqualTo("Foo@Bar"); softly.assertThat(GraphiteSanitize.sanitize("Foó Bar")).isEqualTo("Foó-Bar"); softly.assertThat(GraphiteSanitize.sanitize("||ó/.")).isEqualTo("||ó/."); softly.assertThat(GraphiteSanitize.sanitize("${Foo:Bar:baz}")).isEqualTo("${Foo:Bar:baz}"); softly.assertThat(GraphiteSanitize.sanitize("St. Foo's of Bar")).isEqualTo("St.-Foo's-of-Bar"); softly.assertThat(GraphiteSanitize.sanitize("(Foo and (Bar and (Baz)))")).isEqualTo("(Foo-and-(Bar-and-(Baz)))"); softly.assertThat(GraphiteSanitize.sanitize("Foo.bar.baz")).isEqualTo("Foo.bar.baz"); softly.assertThat(GraphiteSanitize.sanitize("FooBar")).isEqualTo("FooBar"); softly.assertAll(); } } metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteTest.java000066400000000000000000000126451315671014200321720ustar00rootroot00000000000000package com.codahale.metrics.graphite; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import javax.net.SocketFactory; import java.io.ByteArrayOutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicBoolean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.*; public class GraphiteTest { private final String host = "example.com"; private final int port = 1234; private final SocketFactory socketFactory = mock(SocketFactory.class); private final InetSocketAddress address = new InetSocketAddress(host, port); private final Socket socket = mock(Socket.class); private final ByteArrayOutputStream output = spy(new ByteArrayOutputStream()); private Graphite graphite; @Before public void setUp() throws Exception { final AtomicBoolean connected = new AtomicBoolean(true); final AtomicBoolean closed = new AtomicBoolean(false); when(socket.isConnected()).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { return connected.get(); } }); when(socket.isClosed()).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { return closed.get(); } }); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { connected.set(false); closed.set(true); return null; } }).when(socket).close(); when(socket.getOutputStream()).thenReturn(output); // Mock behavior of socket.getOutputStream().close() calling socket.close(); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { invocation.callRealMethod(); socket.close(); return null; } }).when(output).close(); when(socketFactory.createSocket(any(InetAddress.class), anyInt())).thenReturn(socket); } @Test public void connectsToGraphiteWithInetSocketAddress() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); verify(socketFactory).createSocket(address.getAddress(), address.getPort()); } @Test public void connectsToGraphiteWithHostAndPort() throws Exception { graphite = new Graphite(host, port, socketFactory); graphite.connect(); verify(socketFactory).createSocket(address.getAddress(), port); } @Test public void measuresFailures() throws Exception { graphite = new Graphite(address, socketFactory); assertThat(graphite.getFailures()) .isZero(); } @Test public void disconnectsFromGraphite() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); graphite.close(); verify(socket, times(2)).close(); } @Test public void doesNotAllowDoubleConnections() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); try { graphite.connect(); failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException e) { assertThat(e.getMessage()) .isEqualTo("Already connected"); } } @Test public void writesValuesToGraphite() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); graphite.send("name", "value", 100); graphite.close(); assertThat(output.toString()) .isEqualTo("name value 100\n"); } @Test public void sanitizesNames() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); graphite.send("name woo", "value", 100); graphite.close(); assertThat(output.toString()) .isEqualTo("name-woo value 100\n"); } @Test public void sanitizesValues() throws Exception { graphite = new Graphite(address, socketFactory); graphite.connect(); graphite.send("name", "value woo", 100); graphite.close(); assertThat(output.toString()) .isEqualTo("name value-woo 100\n"); } @Test public void notifiesIfGraphiteIsUnavailable() throws Exception { final String unavailableHost = "unknown-host-10el6m7yg56ge7dmcom"; InetSocketAddress unavailableAddress = new InetSocketAddress(unavailableHost, 1234); Graphite unavailableGraphite = new Graphite(unavailableAddress, socketFactory); try { unavailableGraphite.connect(); failBecauseExceptionWasNotThrown(UnknownHostException.class); } catch (Exception e) { assertThat(e.getMessage()) .isEqualTo(unavailableHost); } } } metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/GraphiteUDPTest.java000066400000000000000000000024471315671014200325420ustar00rootroot00000000000000package com.codahale.metrics.graphite; import org.junit.Test; import org.mockito.Mockito; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; public class GraphiteUDPTest { private final String host = "example.com"; private final int port = 1234; private GraphiteUDP graphiteUDP; @Test public void connects() throws Exception { graphiteUDP = new GraphiteUDP(host, port); graphiteUDP.connect(); assertThat(graphiteUDP.getDatagramChannel()).isNotNull(); assertThat(graphiteUDP.getAddress()).isEqualTo(new InetSocketAddress(host, port)); graphiteUDP.close(); } @Test public void writesValue() throws Exception { graphiteUDP = new GraphiteUDP(host, port); DatagramChannel mockDatagramChannel = Mockito.mock(DatagramChannel.class); graphiteUDP.setDatagramChannel(mockDatagramChannel); graphiteUDP.setAddress(new InetSocketAddress(host, port)); graphiteUDP.send("name woo", "value", 100); verify(mockDatagramChannel).send(ByteBuffer.wrap("name-woo value 100\n".getBytes("UTF-8")), new InetSocketAddress(host, port)); } }metrics-3.2.5/metrics-graphite/src/test/java/com/codahale/metrics/graphite/PickledGraphiteTest.java000066400000000000000000000153551315671014200334670ustar00rootroot00000000000000package com.codahale.metrics.graphite; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.*; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.python.core.PyList; import org.python.core.PyTuple; import javax.net.SocketFactory; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.SimpleBindings; import java.io.ByteArrayOutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.Charset; import java.util.concurrent.atomic.AtomicBoolean; public class PickledGraphiteTest { private final SocketFactory socketFactory = mock(SocketFactory.class); private final InetSocketAddress address = new InetSocketAddress("example.com", 1234); private final PickledGraphite graphite = new PickledGraphite(address, socketFactory, Charset.forName("UTF-8"), 2); private final Socket socket = mock(Socket.class); private final ByteArrayOutputStream output = spy(new ByteArrayOutputStream()); // Pulls apart the pickled payload. This skips ahead 4 characters to safely ignore // the header (length) private static final String UNPICKLER_SCRIPT = "import cPickle\n" + "import struct\n" + "format = '!L'\n" + "headerLength = struct.calcsize(format)\n" + "payloadLength, = struct.unpack(format, payload[:headerLength])\n" + "batchLength = headerLength + payloadLength.intValue()\n" + "metrics = cPickle.loads(payload[headerLength:batchLength])\n"; private CompiledScript unpickleScript; @Before public void setUp() throws Exception { final AtomicBoolean connected = new AtomicBoolean(true); final AtomicBoolean closed = new AtomicBoolean(false); when(socket.isConnected()).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { return connected.get(); } }); when(socket.isClosed()).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { return closed.get(); } }); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { connected.set(false); closed.set(true); return null; } }).when(socket).close(); when(socket.getOutputStream()).thenReturn(output); // Mock behavior of socket.getOutputStream().close() calling socket.close(); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { invocation.callRealMethod(); socket.close(); return null; } }).when(output).close(); when(socketFactory.createSocket(any(InetAddress.class), anyInt())).thenReturn(socket); ScriptEngine engine = new ScriptEngineManager().getEngineByName("python"); Compilable compilable = (Compilable) engine; unpickleScript = compilable.compile(UNPICKLER_SCRIPT); } @Test public void disconnectsFromGraphite() throws Exception { graphite.connect(); graphite.close(); verify(socket).close(); } @Test public void writesValuesToGraphite() throws Exception { graphite.connect(); graphite.send("name", "value", 100); graphite.close(); assertThat(unpickleOutput()) .isEqualTo("name value 100\n"); } @Test public void writesFullBatch() throws Exception { graphite.connect(); graphite.send("name", "value", 100); graphite.send("name", "value2", 100); graphite.close(); assertThat(unpickleOutput()) .isEqualTo("name value 100\nname value2 100\n"); } @Test public void writesPastFullBatch() throws Exception { graphite.connect(); graphite.send("name", "value", 100); graphite.send("name", "value2", 100); graphite.send("name", "value3", 100); graphite.close(); assertThat(unpickleOutput()) .isEqualTo("name value 100\nname value2 100\nname value3 100\n"); } @Test public void sanitizesNames() throws Exception { graphite.connect(); graphite.send("name woo", "value", 100); graphite.close(); assertThat(unpickleOutput()) .isEqualTo("name-woo value 100\n"); } @Test public void sanitizesValues() throws Exception { graphite.connect(); graphite.send("name", "value woo", 100); graphite.close(); assertThat(unpickleOutput()) .isEqualTo("name value-woo 100\n"); } @Test public void doesNotAllowDoubleConnections() throws Exception { graphite.connect(); try { graphite.connect(); failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException e) { assertThat(e.getMessage()) .isEqualTo("Already connected"); } } String unpickleOutput() throws Exception { StringBuilder results = new StringBuilder(); // the charset is important. if the GraphitePickleReporter and this test // don't agree, the header is not always correctly unpacked. String payload = output.toString("UTF-8"); PyList result = new PyList(); int nextIndex = 0; while (nextIndex < payload.length()) { Bindings bindings = new SimpleBindings(); bindings.put("payload", payload.substring(nextIndex)); unpickleScript.eval(bindings); result.addAll(result.size(), (PyList) bindings.get("metrics")); nextIndex += (Integer) bindings.get("batchLength"); } for (Object aResult : result) { PyTuple datapoint = (PyTuple) aResult; String name = datapoint.get(0).toString(); PyTuple valueTuple = (PyTuple) datapoint.get(1); Object timestamp = valueTuple.get(0); Object value = valueTuple.get(1); results.append(name).append(" ").append(value).append(" ").append(timestamp).append("\n"); } return results.toString(); } } metrics-3.2.5/metrics-healthchecks/000077500000000000000000000000001315671014200172615ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/pom.xml000066400000000000000000000020451315671014200205770ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-healthchecks Metrics Health Checks bundle An addition to Metrics which provides the ability to run application-specific health checks, allowing you to check your application's heath in production. io.dropwizard.metrics metrics-jvm ${project.version} true metrics-3.2.5/metrics-healthchecks/src/000077500000000000000000000000001315671014200200505ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/000077500000000000000000000000001315671014200207745ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/000077500000000000000000000000001315671014200217155ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/000077500000000000000000000000001315671014200224735ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/000077500000000000000000000000001315671014200242335ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200257015ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/000077500000000000000000000000001315671014200271465ustar00rootroot00000000000000AsyncHealthCheckDecorator.java000066400000000000000000000041621315671014200347410ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import com.codahale.metrics.health.annotation.Async; /** * A health check decorator to manage asynchronous executions. */ class AsyncHealthCheckDecorator extends HealthCheck implements Runnable { private static final String NO_RESULT_YET_MESSAGE = "Waiting for first asynchronous check result."; private final HealthCheck healthCheck; private final ScheduledFuture future; private volatile Result result; AsyncHealthCheckDecorator(HealthCheck healthCheck, ScheduledExecutorService executorService) { check(healthCheck != null, "healthCheck cannot be null"); check(executorService != null, "executorService cannot be null"); Async async = (Async) healthCheck.getClass().getAnnotation(Async.class); check(async != null, "healthCheck must contain Async annotation"); check(async.period() > 0, "period cannot be less than or equal to zero"); check(async.initialDelay() >= 0, "initialDelay cannot be less than zero"); this.healthCheck = healthCheck; result = Async.InitialState.HEALTHY.equals(async.initialState()) ? Result.healthy(NO_RESULT_YET_MESSAGE) : Result.unhealthy(NO_RESULT_YET_MESSAGE); if (Async.ScheduleType.FIXED_RATE.equals(async.scheduleType())) { future = executorService.scheduleAtFixedRate(this, async.initialDelay(), async.period(), async.unit()); } else { future = executorService.scheduleWithFixedDelay(this, async.initialDelay(), async.period(), async.unit()); } } @Override public void run() { result = healthCheck.execute(); } @Override protected Result check() throws Exception { return result; } boolean tearDown() { return future.cancel(true); } HealthCheck getHealthCheck() { return healthCheck; } private void check(boolean expression, String message) { if (!expression) { throw new IllegalArgumentException(message); } } } metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/HealthCheck.java000066400000000000000000000257271315671014200321710ustar00rootroot00000000000000package com.codahale.metrics.health; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * A health check for a component of your application. */ public abstract class HealthCheck { /** * The result of a {@link HealthCheck} being run. It can be healthy (with an optional message and optional details) * or unhealthy (with either an error message or a thrown exception and optional details). */ public static class Result { private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; private static final int PRIME = 31; /** * Returns a healthy {@link Result} with no additional message. * * @return a healthy {@link Result} with no additional message */ public static Result healthy() { return new Result(true, null, null); } /** * Returns a healthy {@link Result} with an additional message. * * @param message an informative message * @return a healthy {@link Result} with an additional message */ public static Result healthy(String message) { return new Result(true, message, null); } /** * Returns a healthy {@link Result} with a formatted message. *

    * Message formatting follows the same rules as {@link String#format(String, Object...)}. * * @param message a message format * @param args the arguments apply to the message format * @return a healthy {@link Result} with an additional message * @see String#format(String, Object...) */ public static Result healthy(String message, Object... args) { return healthy(String.format(message, args)); } /** * Returns an unhealthy {@link Result} with the given message. * * @param message an informative message describing how the health check failed * @return an unhealthy {@link Result} with the given message */ public static Result unhealthy(String message) { return new Result(false, message, null); } /** * Returns an unhealthy {@link Result} with a formatted message. *

    * Message formatting follows the same rules as {@link String#format(String, Object...)}. * * @param message a message format * @param args the arguments apply to the message format * @return an unhealthy {@link Result} with an additional message * @see String#format(String, Object...) */ public static Result unhealthy(String message, Object... args) { return unhealthy(String.format(message, args)); } /** * Returns an unhealthy {@link Result} with the given error. * * @param error an exception thrown during the health check * @return an unhealthy {@link Result} with the given {@code error} */ public static Result unhealthy(Throwable error) { return new Result(false, error.getMessage(), error); } /** * Returns a new {@link ResultBuilder} * * @return the {@link ResultBuilder} */ public static ResultBuilder builder() { return new ResultBuilder(); } private final boolean healthy; private final String message; private final Throwable error; private final Map details; private final String timestamp; private Result(boolean isHealthy, String message, Throwable error) { this(isHealthy, message, error, null); } private Result(ResultBuilder builder) { this(builder.healthy, builder.message, builder.error, builder.details); } private Result(boolean isHealthy, String message, Throwable error, Map details) { this.healthy = isHealthy; this.message = message; this.error = error; this.details = details == null ? null : Collections.unmodifiableMap(details); timestamp = new SimpleDateFormat(DATE_FORMAT_PATTERN).format(new Date()); } /** * Returns {@code true} if the result indicates the component is healthy; {@code false} * otherwise. * * @return {@code true} if the result indicates the component is healthy */ public boolean isHealthy() { return healthy; } /** * Returns any additional message for the result, or {@code null} if the result has no * message. * * @return any additional message for the result, or {@code null} */ public String getMessage() { return message; } /** * Returns any exception for the result, or {@code null} if the result has no exception. * * @return any exception for the result, or {@code null} */ public Throwable getError() { return error; } /** * Returns the timestamp when the result was created. * @return a formatted timestamp */ public String getTimestamp() { return timestamp; } public Map getDetails() { return details; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final Result result = (Result) o; return healthy == result.healthy && !(error != null ? !error.equals(result.error) : result.error != null) && !(message != null ? !message.equals(result.message) : result.message != null) && !(timestamp != null ? !timestamp.equals(result.timestamp) : result.timestamp != null); } @Override public int hashCode() { int result = (healthy ? 1 : 0); result = PRIME * result + (message != null ? message.hashCode() : 0); result = PRIME * result + (error != null ? error.hashCode() : 0); result = PRIME * result + (timestamp != null ? timestamp.hashCode() : 0); return result; } @Override public String toString() { final StringBuilder builder = new StringBuilder("Result{isHealthy="); builder.append(healthy); if (message != null) { builder.append(", message=").append(message); } if (error != null) { builder.append(", error=").append(error); } builder.append(", timestamp=").append(timestamp); if (details != null) { Iterator> it = details.entrySet().iterator(); while (it.hasNext()) { builder.append(", "); Map.Entry e = it.next(); builder.append(e.getKey()) .append("=") .append(String.valueOf(e.getValue())); } } builder.append('}'); return builder.toString(); } } /** * This a convenient builder for an {@link HealthCheck.Result}. It can be health (with optional message and detail) * or unhealthy (with optional message, error and detail) */ public static class ResultBuilder { private boolean healthy; private String message; private Throwable error; private Map details; protected ResultBuilder() { this.healthy = true; this.details = new LinkedHashMap(); } /** * Configure an healthy result * * @return */ public ResultBuilder healthy() { this.healthy = true; return this; } /** * Configure an unhealthy result * * @return */ public ResultBuilder unhealthy() { this.healthy = false; return this; } /** * Configure an unhealthy result with an {@code error} * * @param error the error * @return */ public ResultBuilder unhealthy(Throwable error) { this.error = error; return this.unhealthy().withMessage(error.getMessage()); } /** * Set an optional message * * @param message an informative message * @return this builder with the given {@code message} */ public ResultBuilder withMessage(String message) { this.message = message; return this; } /** * Set an optional formatted message *

    * Message formatting follows the same rules as {@link String#format(String, Object...)}. * * @param message a message format * @param args the arguments apply to the message format * @return this builder with the given formatted {@code message} * @see String#format(String, Object...) */ public ResultBuilder withMessage(String message, Object... args) { return withMessage(String.format(message, args)); } /** * Add an optional detail * * @param key a key for this detail * @param data an object representing the detail data * @return this builder with the given detail added */ public ResultBuilder withDetail(String key, Object data) { if (this.details == null) { this.details = new LinkedHashMap(); } this.details.put(key, data); return this; } public Result build() { return new Result(this); } } /** * Perform a check of the application component. * * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy {@link * Result} with a descriptive error message or exception * @throws Exception if there is an unhandled error during the health check; this will result in * a failed health check */ protected abstract Result check() throws Exception; /** * Executes the health check, catching and handling any exceptions raised by {@link #check()}. * * @return if the component is healthy, a healthy {@link Result}; otherwise, an unhealthy {@link * Result} with a descriptive error message or exception */ public Result execute() { try { return check(); } catch (Exception e) { return Result.unhealthy(e); } } } HealthCheckRegistry.java000066400000000000000000000245221315671014200336330ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import static com.codahale.metrics.health.HealthCheck.Result; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codahale.metrics.health.annotation.Async; /** * A registry for health checks. */ public class HealthCheckRegistry { private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckRegistry.class); private static final int ASYNC_EXECUTOR_POOL_SIZE = 2; private final ConcurrentMap healthChecks; private final List listeners; private final ScheduledExecutorService asyncExecutorService; private final Object lock = new Object(); /** * Creates a new {@link HealthCheckRegistry}. */ public HealthCheckRegistry() { this(ASYNC_EXECUTOR_POOL_SIZE); } /** * Creates a new {@link HealthCheckRegistry}. * * @param asyncExecutorPoolSize core pool size for async health check executions */ public HealthCheckRegistry(int asyncExecutorPoolSize) { this(createExecutorService(asyncExecutorPoolSize)); } /** * Creates a new {@link HealthCheckRegistry}. * * @param asyncExecutorService executor service for async health check executions */ public HealthCheckRegistry(ScheduledExecutorService asyncExecutorService) { this.healthChecks = new ConcurrentHashMap(); this.listeners = new CopyOnWriteArrayList(); this.asyncExecutorService = asyncExecutorService; } /** * Adds a {@link HealthCheckRegistryListener} to a collection of listeners that will be notified on health check * registration. Listeners will be notified in the order in which they are added. The listener will be notified of all * existing health checks when it first registers. * * @param listener listener to add */ public void addListener(HealthCheckRegistryListener listener) { listeners.add(listener); for (Map.Entry entry : healthChecks.entrySet()) { listener.onHealthCheckAdded(entry.getKey(), entry.getValue()); } } /** * Removes a {@link HealthCheckRegistryListener} from this registry's collection of listeners. * * @param listener listener to remove */ public void removeListener(HealthCheckRegistryListener listener) { listeners.remove(listener); } /** * Registers an application {@link HealthCheck}. * * @param name the name of the health check * @param healthCheck the {@link HealthCheck} instance */ public void register(String name, HealthCheck healthCheck) { HealthCheck registered = null; synchronized (lock) { if (!healthChecks.containsKey(name)) { registered = healthCheck; if (healthCheck.getClass().isAnnotationPresent(Async.class)) { registered = new AsyncHealthCheckDecorator(healthCheck, asyncExecutorService); } healthChecks.put(name, registered); } } if (registered != null) { onHealthCheckAdded(name, registered); } } /** * Unregisters the application {@link HealthCheck} with the given name. * * @param name the name of the {@link HealthCheck} instance */ public void unregister(String name) { HealthCheck healthCheck = null; synchronized (lock) { healthCheck = healthChecks.remove(name); if (healthCheck instanceof AsyncHealthCheckDecorator) { ((AsyncHealthCheckDecorator) healthCheck).tearDown(); } } if (healthCheck != null) { onHealthCheckRemoved(name, healthCheck); } } /** * Returns a set of the names of all registered health checks. * * @return the names of all registered health checks */ public SortedSet getNames() { return Collections.unmodifiableSortedSet(new TreeSet(healthChecks.keySet())); } /** * Runs the health check with the given name. * * @param name the health check's name * @return the result of the health check * @throws NoSuchElementException if there is no health check with the given name */ public HealthCheck.Result runHealthCheck(String name) throws NoSuchElementException { final HealthCheck healthCheck = healthChecks.get(name); if (healthCheck == null) { throw new NoSuchElementException("No health check named " + name + " exists"); } return healthCheck.execute(); } /** * Runs the registered health checks and returns a map of the results. * * @return a map of the health check results */ public SortedMap runHealthChecks() { final SortedMap results = new TreeMap(); for (Map.Entry entry : healthChecks.entrySet()) { final Result result = entry.getValue().execute(); results.put(entry.getKey(), result); } return Collections.unmodifiableSortedMap(results); } /** * Runs the registered health checks in parallel and returns a map of the results. * * @param executor object to launch and track health checks progress * @return a map of the health check results */ public SortedMap runHealthChecks(ExecutorService executor) { final Map> futures = new HashMap>(); for (final Map.Entry entry : healthChecks.entrySet()) { futures.put(entry.getKey(), executor.submit(new Callable() { @Override public Result call() throws Exception { return entry.getValue().execute(); } })); } final SortedMap results = new TreeMap(); for (Map.Entry> entry : futures.entrySet()) { try { results.put(entry.getKey(), entry.getValue().get()); } catch (Exception e) { LOGGER.warn("Error executing health check {}", entry.getKey(), e); results.put(entry.getKey(), HealthCheck.Result.unhealthy(e)); } } return Collections.unmodifiableSortedMap(results); } private void onHealthCheckAdded(String name, HealthCheck healthCheck) { for (HealthCheckRegistryListener listener : listeners) { listener.onHealthCheckAdded(name, healthCheck); } } private void onHealthCheckRemoved(String name, HealthCheck healthCheck) { for (HealthCheckRegistryListener listener : listeners) { listener.onHealthCheckRemoved(name, healthCheck); } } /** * Shuts down the scheduled executor for async health checks */ public void shutdown() { asyncExecutorService.shutdown(); // Disable new health checks from being submitted try { // Give some time to the current healtch checks to finish gracefully if (!asyncExecutorService.awaitTermination(1, TimeUnit.SECONDS)) { asyncExecutorService.shutdownNow(); } } catch (InterruptedException ie) { asyncExecutorService.shutdownNow(); Thread.currentThread().interrupt(); } } private static ScheduledExecutorService createExecutorService(int corePoolSize) { ScheduledExecutorService asyncExecutorService = Executors.newScheduledThreadPool(corePoolSize, new NamedThreadFactory("healthcheck-async-executor-")); try { Method method = asyncExecutorService.getClass().getMethod("setRemoveOnCancelPolicy", Boolean.TYPE); method.invoke(asyncExecutorService, true); } catch (NoSuchMethodException e) { logSetExecutorCancellationPolicyFailure(e); } catch (IllegalAccessException e) { logSetExecutorCancellationPolicyFailure(e); } catch (InvocationTargetException e) { logSetExecutorCancellationPolicyFailure(e); } return asyncExecutorService; } private static void logSetExecutorCancellationPolicyFailure(Exception e) { LOGGER.warn("Tried but failed to set executor cancellation policy to remove on cancel which has been introduced " + "in Java 7. This could result in a memory leak if many asynchronous health checks are registered and " + "removed because cancellation does not actually remove them from the executor.", e); } private static class NamedThreadFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; NamedThreadFactory(String namePrefix) { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = namePrefix; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); t.setDaemon(true); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } } } HealthCheckRegistryListener.java000066400000000000000000000013401315671014200353320ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import java.util.EventListener; /** * A listener contract for {@link HealthCheckRegistry} events. */ public interface HealthCheckRegistryListener extends EventListener { /** * Called when a new {@link HealthCheck} is added to the registry. * * @param name the name of the health check * @param healthCheck the health check */ void onHealthCheckAdded(String name, HealthCheck healthCheck); /** * Called when a {@link HealthCheck} is removed from the registry. * * @param name the name of the health check * @param healthCheck the health check */ void onHealthCheckRemoved(String name, HealthCheck healthCheck); } SharedHealthCheckRegistries.java000066400000000000000000000070541315671014200352730ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; /** * A map of shared, named health registries. */ public class SharedHealthCheckRegistries { private static final ConcurrentMap REGISTRIES = new ConcurrentHashMap(); private static AtomicReference defaultRegistryName = new AtomicReference(); /* Visible for testing */ static void setDefaultRegistryName(AtomicReference defaultRegistryName) { SharedHealthCheckRegistries.defaultRegistryName = defaultRegistryName; } private SharedHealthCheckRegistries() { /* singleton */ } public static void clear() { REGISTRIES.clear(); } public static Set names() { return REGISTRIES.keySet(); } public static void remove(String key) { REGISTRIES.remove(key); } public static HealthCheckRegistry add(String name, HealthCheckRegistry registry) { return REGISTRIES.putIfAbsent(name, registry); } public static HealthCheckRegistry getOrCreate(String name) { final HealthCheckRegistry existing = REGISTRIES.get(name); if (existing == null) { final HealthCheckRegistry created = new HealthCheckRegistry(); final HealthCheckRegistry raced = add(name, created); if (raced == null) { return created; } return raced; } return existing; } /** * Creates a new registry and sets it as the default one under the provided name. * * @param name the registry name * @return the default registry * @throws IllegalStateException if the name has already been set */ public synchronized static HealthCheckRegistry setDefault(String name) { final HealthCheckRegistry registry = getOrCreate(name); return setDefault(name, registry); } /** * Sets the provided registry as the default one under the provided name * * @param name the default registry name * @param healthCheckRegistry the default registry * @throws IllegalStateException if the default registry has already been set */ public static HealthCheckRegistry setDefault(String name, HealthCheckRegistry healthCheckRegistry) { if (defaultRegistryName.compareAndSet(null, name)) { add(name, healthCheckRegistry); return healthCheckRegistry; } throw new IllegalStateException("Default health check registry is already set."); } /** * Gets the name of the default registry, if it has been set * * @return the default registry * @throws IllegalStateException if the default has not been set */ public static HealthCheckRegistry getDefault() { final HealthCheckRegistry healthCheckRegistry = tryGetDefault(); if (healthCheckRegistry != null) { return healthCheckRegistry; } throw new IllegalStateException("Default registry name has not been set."); } /** * Same as {@link #getDefault()} except returns null when the default registry has not been set. * * @return the default registry or null */ public static HealthCheckRegistry tryGetDefault() { final String name = defaultRegistryName.get(); if (name != null) { return getOrCreate(name); } return null; } } metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/annotation/000077500000000000000000000000001315671014200313205ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/annotation/Async.java000066400000000000000000000026161315671014200332450ustar00rootroot00000000000000package com.codahale.metrics.health.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.concurrent.TimeUnit; /** * An annotation for marking asynchronous health check execution. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Async { /** * Enum representing the initial health states. */ public enum InitialState { HEALTHY, UNHEALTHY; } /** * Enum representing the possible schedule types. */ public enum ScheduleType { FIXED_RATE, FIXED_DELAY; } /** * Period between executions. * * @return period */ long period(); /** * Scheduling type of asynchronous executions. * * @return schedule type */ ScheduleType scheduleType() default ScheduleType.FIXED_RATE; /** * Initial delay of first execution. * * @return initial delay */ long initialDelay() default 0; /** * Time unit of initial delay and period. * * @return time unit */ TimeUnit unit() default TimeUnit.SECONDS; /** * Initial health state until first asynchronous execution completes. * * @return initial health state */ InitialState initialState() default InitialState.HEALTHY; } metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/jvm/000077500000000000000000000000001315671014200277425ustar00rootroot00000000000000ThreadDeadlockHealthCheck.java000066400000000000000000000017751315671014200354620ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/main/java/com/codahale/metrics/health/jvmpackage com.codahale.metrics.health.jvm; import com.codahale.metrics.health.HealthCheck; import com.codahale.metrics.jvm.ThreadDeadlockDetector; import java.util.Set; /** * A health check which returns healthy if no threads are deadlocked. */ public class ThreadDeadlockHealthCheck extends HealthCheck { private final ThreadDeadlockDetector detector; /** * Creates a new health check. */ public ThreadDeadlockHealthCheck() { this(new ThreadDeadlockDetector()); } /** * Creates a new health check with the given detector. * * @param detector a thread deadlock detector */ public ThreadDeadlockHealthCheck(ThreadDeadlockDetector detector) { this.detector = detector; } @Override protected Result check() throws Exception { final Set threads = detector.getDeadlockedThreads(); if (threads.isEmpty()) { return Result.healthy(); } return Result.unhealthy(threads.toString()); } } metrics-3.2.5/metrics-healthchecks/src/test/000077500000000000000000000000001315671014200210275ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/000077500000000000000000000000001315671014200217505ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/000077500000000000000000000000001315671014200225265ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/000077500000000000000000000000001315671014200242665ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200257345ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/health/000077500000000000000000000000001315671014200272015ustar00rootroot00000000000000AsyncHealthCheckDecoratorTest.java000066400000000000000000000210321315671014200356270ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.codahale.metrics.health.annotation.Async; /** * Unit tests for {@link AsyncHealthCheckDecorator}. */ public class AsyncHealthCheckDecoratorTest { private final HealthCheck mockHealthCheck = mock(HealthCheck.class); private final ScheduledExecutorService mockExecutorService = mock(ScheduledExecutorService.class); private final ScheduledFuture mockFuture = mock(ScheduledFuture.class); @Test(expected = IllegalArgumentException.class) public void nullHealthCheckTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(null, mockExecutorService); } @Test(expected = IllegalArgumentException.class) public void nullExecutorServiceTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(mockHealthCheck, null); } @Test(expected = IllegalArgumentException.class) public void nonAsyncHealthCheckTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(mockHealthCheck, mockExecutorService); } @Test(expected = IllegalArgumentException.class) public void negativePeriodTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(new NegativePeriodAsyncHealthCheck(), mockExecutorService); } @Test(expected = IllegalArgumentException.class) public void zeroPeriodTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(new ZeroPeriodAsyncHealthCheck(), mockExecutorService); } @Test(expected = IllegalArgumentException.class) public void negativeInitialValueTriggersInstantiationFailure() { new AsyncHealthCheckDecorator(new NegativeInitialDelayAsyncHealthCheck(), mockExecutorService); } @Test public void defaultAsyncHealthCheckTriggersSuccessfulInstantiationWithFixedRateAndHealthyState() throws Exception { HealthCheck asyncHealthCheck = new DefaultAsyncHealthCheck(); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(asyncHealthCheck, mockExecutorService); verify(mockExecutorService, times(1)).scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS)); assertThat(asyncDecorator.getHealthCheck()).isEqualTo(asyncHealthCheck); assertThat(asyncDecorator.check().isHealthy()).isTrue(); } @Test public void fixedDelayAsyncHealthCheckTriggersSuccessfulInstantiationWithFixedDelay() throws Exception { HealthCheck asyncHealthCheck = new FixedDelayAsyncHealthCheck(); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(asyncHealthCheck, mockExecutorService); verify(mockExecutorService, times(1)).scheduleWithFixedDelay(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS)); assertThat(asyncDecorator.getHealthCheck()).isEqualTo(asyncHealthCheck); } @Test public void unhealthyAsyncHealthCheckTriggersSuccessfulInstantiationWithUnhealthyState() throws Exception { HealthCheck asyncHealthCheck = new UnhealthyAsyncHealthCheck(); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(asyncHealthCheck, mockExecutorService); assertThat(asyncDecorator.check().isHealthy()).isFalse(); } @Test public void tearDownTriggersCancellation() throws Exception { when(mockExecutorService.scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS))). thenReturn(mockFuture); when(mockFuture.cancel(true)).thenReturn(true); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(new DefaultAsyncHealthCheck(), mockExecutorService); asyncDecorator.tearDown(); verify(mockExecutorService, times(1)).scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS)); verify(mockFuture, times(1)).cancel(eq(true)); } @Test public void afterFirstExecutionDecoratedHealthCheckResultIsProvided() throws Exception { HealthCheck.Result expectedResult = HealthCheck.Result.healthy("AsyncHealthCheckTest"); when(mockExecutorService.scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS))) .thenReturn(mockFuture); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(new ConfigurableAsyncHealthCheck(expectedResult), mockExecutorService); HealthCheck.Result initialResult = asyncDecorator.check(); ArgumentCaptor runnableCaptor = forClass(Runnable.class); verify(mockExecutorService, times(1)).scheduleAtFixedRate(runnableCaptor.capture(), eq(0L), eq(1L), eq(TimeUnit.SECONDS)); Runnable capturedRunnable = runnableCaptor.getValue(); capturedRunnable.run(); HealthCheck.Result actualResult = asyncDecorator.check(); assertThat(actualResult).isEqualTo(expectedResult); assertThat(actualResult).isNotEqualTo(initialResult); } @Test public void exceptionInDecoratedHealthCheckWontAffectAsyncDecorator() throws Exception { Exception exception = new Exception("TestException"); when(mockExecutorService.scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(1L), eq(TimeUnit.SECONDS))) .thenReturn(mockFuture); AsyncHealthCheckDecorator asyncDecorator = new AsyncHealthCheckDecorator(new ConfigurableAsyncHealthCheck(exception), mockExecutorService); ArgumentCaptor runnableCaptor = forClass(Runnable.class); verify(mockExecutorService, times(1)).scheduleAtFixedRate(runnableCaptor.capture(), eq(0L), eq(1L), eq(TimeUnit.SECONDS)); Runnable capturedRunnable = runnableCaptor.getValue(); capturedRunnable.run(); HealthCheck.Result result = asyncDecorator.check(); assertThat(result.isHealthy()).isFalse(); assertThat(result.getError()).isEqualTo(exception); } @Async(period = -1) private static class NegativePeriodAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 0) private static class ZeroPeriodAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 1, initialDelay = -1) private static class NegativeInitialDelayAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 1) private static class DefaultAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 1, scheduleType = Async.ScheduleType.FIXED_DELAY) private static class FixedDelayAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 1, initialState = Async.InitialState.UNHEALTHY) private static class UnhealthyAsyncHealthCheck extends HealthCheck { @Override protected Result check() throws Exception { return null; } } @Async(period = 1, initialState = Async.InitialState.UNHEALTHY) private static class ConfigurableAsyncHealthCheck extends HealthCheck { private final Result result; private final Exception exception; ConfigurableAsyncHealthCheck(Result result) { this(result, null); } ConfigurableAsyncHealthCheck(Exception exception) { this(null, exception); } private ConfigurableAsyncHealthCheck(Result result, Exception exception) { this.result = result; this.exception = exception; } @Override protected Result check() throws Exception { if (exception != null) { throw exception; } return result; } } } HealthCheckRegistryTest.java000066400000000000000000000147731315671014200345350ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.codahale.metrics.health.annotation.Async; public class HealthCheckRegistryTest { private final ScheduledExecutorService executorService = mock(ScheduledExecutorService.class); private final HealthCheckRegistry registry = new HealthCheckRegistry(executorService); private final HealthCheckRegistryListener listener = mock(HealthCheckRegistryListener.class); private final HealthCheck hc1 = mock(HealthCheck.class); private final HealthCheck hc2 = mock(HealthCheck.class); private final HealthCheck.Result r1 = mock(HealthCheck.Result.class); private final HealthCheck.Result r2 = mock(HealthCheck.Result.class); private final HealthCheck.Result ar = mock(HealthCheck.Result.class); private final HealthCheck ahc = new TestAsyncHealthCheck(ar); private final ScheduledFuture af = mock(ScheduledFuture.class); @Before public void setUp() throws Exception { registry.addListener(listener); when(hc1.execute()).thenReturn(r1); when(hc2.execute()).thenReturn(r2); when(executorService.scheduleAtFixedRate(any(AsyncHealthCheckDecorator.class),eq(0L), eq(10L), eq(TimeUnit .SECONDS))).thenReturn(af); registry.register("hc1", hc1); registry.register("hc2", hc2); registry.register("ahc", ahc); } @Test public void asyncHealthCheckIsScheduledOnExecutor() { ArgumentCaptor decoratorCaptor = forClass(AsyncHealthCheckDecorator.class); verify(executorService).scheduleAtFixedRate(decoratorCaptor.capture(), eq(0L), eq(10L), eq(TimeUnit.SECONDS)); assertThat(decoratorCaptor.getValue().getHealthCheck()).isEqualTo(ahc); } @Test public void asyncHealthCheckIsCanceledOnRemove() { registry.unregister("ahc"); verify(af).cancel(true); } @Test public void registeringHealthCheckTriggersNotification() { verify(listener).onHealthCheckAdded("hc1", hc1); verify(listener).onHealthCheckAdded("hc2", hc2); verify(listener).onHealthCheckAdded(eq("ahc"), any(AsyncHealthCheckDecorator.class)); } @Test public void removingHealthCheckTriggersNotification() { registry.unregister("hc1"); registry.unregister("hc2"); registry.unregister("ahc"); verify(listener).onHealthCheckRemoved("hc1", hc1); verify(listener).onHealthCheckRemoved("hc2", hc2); verify(listener).onHealthCheckRemoved(eq("ahc"), any(AsyncHealthCheckDecorator.class)); } @Test public void addingListenerCatchesExistingHealthChecks() { HealthCheckRegistryListener listener = mock(HealthCheckRegistryListener.class); HealthCheckRegistry registry = new HealthCheckRegistry(); registry.register("hc1", hc1); registry.register("hc2", hc2); registry.register("ahc", ahc); registry.addListener(listener); verify(listener).onHealthCheckAdded("hc1", hc1); verify(listener).onHealthCheckAdded("hc2", hc2); verify(listener).onHealthCheckAdded(eq("ahc"), any(AsyncHealthCheckDecorator.class)); } @Test public void removedListenerDoesNotReceiveUpdates() { HealthCheckRegistryListener listener = mock(HealthCheckRegistryListener.class); HealthCheckRegistry registry = new HealthCheckRegistry(); registry.addListener(listener); registry.register("hc1", hc1); registry.removeListener(listener); registry.register("hc2", hc2); verify(listener).onHealthCheckAdded("hc1", hc1); } @Test public void runsRegisteredHealthChecks() throws Exception { final Map results = registry.runHealthChecks(); assertThat(results).contains(entry("hc1", r1)); assertThat(results).contains(entry("hc2", r2)); assertThat(results).containsKey("ahc"); } @Test public void runsRegisteredHealthChecksInParallel() throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(10); final Map results = registry.runHealthChecks(executor); executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); assertThat(results).contains(entry("hc1", r1)); assertThat(results).contains(entry("hc2", r2)); assertThat(results).containsKey("ahc"); } @Test public void removesRegisteredHealthChecks() throws Exception { registry.unregister("hc1"); final Map results = registry.runHealthChecks(); assertThat(results).doesNotContainKey("hc1"); assertThat(results).containsKey("hc2"); assertThat(results).containsKey("ahc"); } @Test public void hasASetOfHealthCheckNames() throws Exception { assertThat(registry.getNames()).containsOnly("hc1", "hc2", "ahc"); } @Test public void runsHealthChecksByName() throws Exception { assertThat(registry.runHealthCheck("hc1")).isEqualTo(r1); } @Test public void doesNotRunNonexistentHealthChecks() throws Exception { try { registry.runHealthCheck("what"); failBecauseExceptionWasNotThrown(NoSuchElementException.class); } catch (NoSuchElementException e) { assertThat(e.getMessage()) .isEqualTo("No health check named what exists"); } } @Async(period = 10) private static class TestAsyncHealthCheck extends HealthCheck { private final Result result; TestAsyncHealthCheck(Result result) { this.result = result; } @Override protected Result check() throws Exception { return result; } } } metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/health/HealthCheckTest.java000066400000000000000000000141301315671014200330460ustar00rootroot00000000000000package com.codahale.metrics.health; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.junit.Test; public class HealthCheckTest { private static class ExampleHealthCheck extends HealthCheck { private final HealthCheck underlying; private ExampleHealthCheck(HealthCheck underlying) { this.underlying = underlying; } @Override protected Result check() throws Exception { return underlying.execute(); } } private final HealthCheck underlying = mock(HealthCheck.class); private final HealthCheck healthCheck = new ExampleHealthCheck(underlying); @Test public void canHaveHealthyResults() throws Exception { final HealthCheck.Result result = HealthCheck.Result.healthy(); assertThat(result.isHealthy()) .isTrue(); assertThat(result.getMessage()) .isNull(); assertThat(result.getError()) .isNull(); } @Test public void canHaveHealthyResultsWithMessages() throws Exception { final HealthCheck.Result result = HealthCheck.Result.healthy("woo"); assertThat(result.isHealthy()) .isTrue(); assertThat(result.getMessage()) .isEqualTo("woo"); assertThat(result.getError()) .isNull(); } @Test public void canHaveHealthyResultsWithFormattedMessages() throws Exception { final HealthCheck.Result result = HealthCheck.Result.healthy("foo %s", "bar"); assertThat(result.isHealthy()) .isTrue(); assertThat(result.getMessage()) .isEqualTo("foo bar"); assertThat(result.getError()) .isNull(); } @Test public void canHaveUnhealthyResults() throws Exception { final HealthCheck.Result result = HealthCheck.Result.unhealthy("bad"); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isEqualTo("bad"); assertThat(result.getError()) .isNull(); } @Test public void canHaveUnhealthyResultsWithFormattedMessages() throws Exception { final HealthCheck.Result result = HealthCheck.Result.unhealthy("foo %s %d", "bar", 123); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isEqualTo("foo bar 123"); assertThat(result.getError()) .isNull(); } @Test public void canHaveUnhealthyResultsWithExceptions() throws Exception { final RuntimeException e = mock(RuntimeException.class); when(e.getMessage()).thenReturn("oh noes"); final HealthCheck.Result result = HealthCheck.Result.unhealthy(e); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isEqualTo("oh noes"); assertThat(result.getError()) .isEqualTo(e); } @Test public void canHaveHealthyBuilderWithDetail() throws Exception { final HealthCheck.Result result = HealthCheck.Result.builder() .healthy() .withDetail("detail", "value") .build(); assertThat(result.isHealthy()) .isTrue(); assertThat(result.getMessage()) .isNull(); assertThat(result.getError()) .isNull(); assertThat(result.getDetails()) .containsEntry("detail", "value"); } @Test public void canHaveUnHealthyBuilderWithDetail() throws Exception { final HealthCheck.Result result = HealthCheck.Result.builder() .unhealthy() .withDetail("detail", "value") .build(); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isNull(); assertThat(result.getError()) .isNull(); assertThat(result.getDetails()) .containsEntry("detail", "value"); } @Test public void canHaveUnHealthyBuilderWithDetailAndError() throws Exception { final RuntimeException e = mock(RuntimeException.class); when(e.getMessage()).thenReturn("oh noes"); final HealthCheck.Result result = HealthCheck.Result .builder() .unhealthy(e) .withDetail("detail", "value") .build(); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isEqualTo("oh noes"); assertThat(result.getError()) .isEqualTo(e); assertThat(result.getDetails()) .containsEntry("detail", "value"); } @Test public void returnsResultsWhenExecuted() throws Exception { final HealthCheck.Result result = mock(HealthCheck.Result.class); when(underlying.execute()).thenReturn(result); assertThat(healthCheck.execute()) .isEqualTo(result); } @Test public void wrapsExceptionsWhenExecuted() throws Exception { final RuntimeException e = mock(RuntimeException.class); when(e.getMessage()).thenReturn("oh noes"); when(underlying.execute()).thenThrow(e); HealthCheck.Result actual = healthCheck.execute(); assertThat(actual.isHealthy()) .isFalse(); assertThat(actual.getMessage()) .isEqualTo("oh noes"); assertThat(actual.getError()) .isEqualTo(e); assertThat(actual.getDetails()) .isNull(); } @Test public void toStringWorksEvenForNullAttributes() throws Exception { final HealthCheck.Result resultWithNullDetailValue = HealthCheck.Result.builder() .unhealthy() .withDetail("aNullDetail", null) .build(); assertThat(resultWithNullDetailValue.toString()) .contains( "Result{isHealthy=false, timestamp=", // Skip the timestamp part of the String. ", aNullDetail=null}"); } } SharedHealthCheckRegistriesTest.java000066400000000000000000000062231315671014200361630ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/healthpackage com.codahale.metrics.health; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThat; public class SharedHealthCheckRegistriesTest { @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setUp() { SharedHealthCheckRegistries.setDefaultRegistryName(new AtomicReference()); SharedHealthCheckRegistries.clear(); } @Test public void savesCreatedRegistry() { final HealthCheckRegistry one = SharedHealthCheckRegistries.getOrCreate("db"); final HealthCheckRegistry two = SharedHealthCheckRegistries.getOrCreate("db"); assertThat(one).isSameAs(two); } @Test public void returnsSetOfCreatedRegistries() { SharedHealthCheckRegistries.getOrCreate("db"); assertThat(SharedHealthCheckRegistries.names()).containsOnly("db"); } @Test public void registryCanBeRemoved() { final HealthCheckRegistry first = SharedHealthCheckRegistries.getOrCreate("db"); SharedHealthCheckRegistries.remove("db"); assertThat(SharedHealthCheckRegistries.names()).isEmpty(); assertThat(SharedHealthCheckRegistries.getOrCreate("db")).isNotEqualTo(first); } @Test public void registryCanBeCleared() { SharedHealthCheckRegistries.getOrCreate("db"); SharedHealthCheckRegistries.getOrCreate("web"); SharedHealthCheckRegistries.clear(); assertThat(SharedHealthCheckRegistries.names()).isEmpty(); } @Test public void defaultRegistryIsNotSetByDefault() { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Default registry name has not been set."); SharedHealthCheckRegistries.getDefault(); } @Test public void defaultRegistryCanBeSet() { HealthCheckRegistry registry = SharedHealthCheckRegistries.setDefault("default"); assertThat(SharedHealthCheckRegistries.getDefault()).isEqualTo(registry); } @Test public void specificRegistryCanBeSetAsDefault() { HealthCheckRegistry registry = new HealthCheckRegistry(); SharedHealthCheckRegistries.setDefault("default", registry); assertThat(SharedHealthCheckRegistries.getDefault()).isEqualTo(registry); } @Test public void unableToSetDefaultRegistryTwice() { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Default health check registry is already set."); SharedHealthCheckRegistries.setDefault("default"); SharedHealthCheckRegistries.setDefault("default"); } @Test public void unableToSetCustomDefaultRegistryTwice() { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Default health check registry is already set."); SharedHealthCheckRegistries.setDefault("default", new HealthCheckRegistry()); SharedHealthCheckRegistries.setDefault("default", new HealthCheckRegistry()); } } metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/health/jvm/000077500000000000000000000000001315671014200277755ustar00rootroot00000000000000ThreadDeadlockHealthCheckTest.java000066400000000000000000000034141315671014200363450ustar00rootroot00000000000000metrics-3.2.5/metrics-healthchecks/src/test/java/com/codahale/metrics/health/jvmpackage com.codahale.metrics.health.jvm; import com.codahale.metrics.health.HealthCheck; import com.codahale.metrics.jvm.ThreadDeadlockDetector; import org.junit.Test; import java.util.Collections; import java.util.Set; import java.util.TreeSet; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ThreadDeadlockHealthCheckTest { @Test public void isHealthyIfNoThreadsAreDeadlocked() throws Exception { final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(detector); when(detector.getDeadlockedThreads()).thenReturn(Collections.emptySet()); assertThat(healthCheck.execute().isHealthy()) .isTrue(); } @Test public void isUnhealthyIfThreadsAreDeadlocked() throws Exception { final Set threads = new TreeSet(); threads.add("one"); threads.add("two"); final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(detector); when(detector.getDeadlockedThreads()).thenReturn(threads); final HealthCheck.Result result = healthCheck.execute(); assertThat(result.isHealthy()) .isFalse(); assertThat(result.getMessage()) .isEqualTo("[one, two]"); } @Test public void automaticallyUsesThePlatformThreadBeans() throws Exception { final ThreadDeadlockHealthCheck healthCheck = new ThreadDeadlockHealthCheck(); assertThat(healthCheck.execute().isHealthy()) .isTrue(); } } metrics-3.2.5/metrics-httpasyncclient/000077500000000000000000000000001315671014200200475ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/pom.xml000066400000000000000000000031521315671014200213650ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-httpasyncclient Metrics Integration for Apache HttpAsyncClient bundle An Apache HttpAsyncClient wrapper providing Metrics instrumentation of connection pools, request durations and rates, and other useful information. io.dropwizard.metrics metrics-httpclient ${project.version} org.apache.httpcomponents httpasyncclient 4.1.2 org.apache.httpcomponents httpclient org.apache.httpcomponents httpcore metrics-3.2.5/metrics-httpasyncclient/src/000077500000000000000000000000001315671014200206365ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/000077500000000000000000000000001315671014200215625ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/000077500000000000000000000000001315671014200225035ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/000077500000000000000000000000001315671014200232615ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/codahale/000077500000000000000000000000001315671014200250215ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200264675ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/codahale/metrics/httpasyncclient/000077500000000000000000000000001315671014200317035ustar00rootroot00000000000000InstrumentedNClientConnManager.java000066400000000000000000000057041315671014200405440ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/codahale/metrics/httpasyncclientpackage com.codahale.metrics.httpasyncclient; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import static com.codahale.metrics.MetricRegistry.name; import java.util.concurrent.TimeUnit; import org.apache.http.config.Registry; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.SchemePortResolver; import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; import org.apache.http.nio.conn.ManagedNHttpClientConnection; import org.apache.http.nio.conn.NHttpClientConnectionManager; import org.apache.http.nio.conn.NHttpConnectionFactory; import org.apache.http.nio.conn.SchemeIOSessionStrategy; import org.apache.http.nio.reactor.ConnectingIOReactor; public class InstrumentedNClientConnManager extends PoolingNHttpClientConnectionManager { public InstrumentedNClientConnManager(final ConnectingIOReactor ioreactor, final NHttpConnectionFactory connFactory, final SchemePortResolver schemePortResolver, final MetricRegistry metricRegistry, final Registry iosessionFactoryRegistry, final long timeToLive, final TimeUnit tunit, final DnsResolver dnsResolver, final String name) { super(ioreactor, connFactory, iosessionFactoryRegistry, schemePortResolver, dnsResolver, timeToLive, tunit); metricRegistry.register(name(NHttpClientConnectionManager.class, name, "available-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getAvailable(); } }); metricRegistry.register(name(NHttpClientConnectionManager.class, name, "leased-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getLeased(); } }); metricRegistry.register(name(NHttpClientConnectionManager.class, name, "max-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getMax(); } }); metricRegistry.register(name(NHttpClientConnectionManager.class, name, "pending-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getPending(); } }); } } InstrumentedNHttpClientBuilder.java000066400000000000000000000062351315671014200406020ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/main/java/com/codahale/metrics/httpasyncclientpackage com.codahale.metrics.httpasyncclient; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.httpclient.HttpClientMetricNameStrategies; import com.codahale.metrics.httpclient.HttpClientMetricNameStrategy; import java.io.IOException; import java.util.concurrent.Future; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.nio.protocol.HttpAsyncRequestProducer; import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; import org.apache.http.protocol.HttpContext; public class InstrumentedNHttpClientBuilder extends HttpAsyncClientBuilder { private final MetricRegistry metricRegistry; private final String name; private final HttpClientMetricNameStrategy metricNameStrategy; public InstrumentedNHttpClientBuilder(MetricRegistry metricRegistry, HttpClientMetricNameStrategy metricNameStrategy, String name) { super(); this.metricRegistry = metricRegistry; this.metricNameStrategy = metricNameStrategy; this.name = name; } public InstrumentedNHttpClientBuilder(MetricRegistry metricRegistry) { this(metricRegistry, HttpClientMetricNameStrategies.METHOD_ONLY, null); } public InstrumentedNHttpClientBuilder(MetricRegistry metricRegistry, HttpClientMetricNameStrategy metricNameStrategy) { this(metricRegistry, metricNameStrategy, null); } public InstrumentedNHttpClientBuilder(MetricRegistry metricRegistry, String name) { this(metricRegistry, HttpClientMetricNameStrategies.METHOD_ONLY, name); } private Timer timer(HttpRequest request) { return metricRegistry.timer(metricNameStrategy.getNameFor(name, request)); } @Override public CloseableHttpAsyncClient build() { final CloseableHttpAsyncClient ac = super.build(); return new CloseableHttpAsyncClient() { @Override public boolean isRunning() { return ac.isRunning(); } @Override public void start() { ac.start(); } @Override public Future execute(HttpAsyncRequestProducer requestProducer, HttpAsyncResponseConsumer responseConsumer, HttpContext context, FutureCallback callback) { final Timer.Context timerContext; try { timerContext = timer(requestProducer.generateRequest()).time(); } catch (IOException ex) { throw new AssertionError(ex); } catch (HttpException ex) { throw new AssertionError(ex); } try { return ac.execute(requestProducer, responseConsumer, context, callback); } finally { timerContext.stop(); } } @Override public void close() throws IOException { ac.close(); } }; } } metrics-3.2.5/metrics-httpasyncclient/src/test/000077500000000000000000000000001315671014200216155ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/000077500000000000000000000000001315671014200225365ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/com/000077500000000000000000000000001315671014200233145ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/com/codahale/000077500000000000000000000000001315671014200250545ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200265225ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/com/codahale/metrics/httpasyncclient/000077500000000000000000000000001315671014200317365ustar00rootroot00000000000000InstrumentedHttpClientsTest.java000066400000000000000000000035571315671014200402370ustar00rootroot00000000000000metrics-3.2.5/metrics-httpasyncclient/src/test/java/com/codahale/metrics/httpasyncclientpackage com.codahale.metrics.httpasyncclient; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistryListener; import com.codahale.metrics.Timer; import com.codahale.metrics.httpclient.HttpClientMetricNameStrategy; import com.codahale.metrics.httpclient.InstrumentedHttpClients; import org.apache.http.HttpRequest; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.nio.client.HttpAsyncClient; import org.junit.Before; import org.junit.Test; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; public class InstrumentedHttpClientsTest { private final HttpClientMetricNameStrategy metricNameStrategy = mock(HttpClientMetricNameStrategy.class); private final MetricRegistryListener registryListener = mock(MetricRegistryListener.class); private final MetricRegistry metricRegistry = new MetricRegistry(); private HttpAsyncClient hac; @Before public void setUp() throws Exception { CloseableHttpAsyncClient chac = new InstrumentedNHttpClientBuilder(metricRegistry, metricNameStrategy).build(); chac.start(); hac = chac; metricRegistry.addListener(registryListener); } @Test public void registersExpectedMetricsGivenNameStrategy() throws Exception { final HttpGet get = new HttpGet("http://example.com?q=anything"); final String metricName = "some.made.up.metric.name"; when(metricNameStrategy.getNameFor(anyString(), any(HttpRequest.class))) .thenReturn(metricName); hac.execute(get,null).get(); verify(registryListener).onTimerAdded(eq(metricName), any(Timer.class)); } } metrics-3.2.5/metrics-httpclient/000077500000000000000000000000001315671014200170115ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/pom.xml000066400000000000000000000022731315671014200203320ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-httpclient Metrics Integration for Apache HttpClient bundle An Apache HttpClient wrapper providing Metrics instrumentation of connection pools, request durations and rates, and other useful information. io.dropwizard.metrics metrics-core ${project.version} org.apache.httpcomponents httpclient 4.5.2 metrics-3.2.5/metrics-httpclient/src/000077500000000000000000000000001315671014200176005ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/000077500000000000000000000000001315671014200205245ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/000077500000000000000000000000001315671014200214455ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/000077500000000000000000000000001315671014200222235ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/000077500000000000000000000000001315671014200237635ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200254315ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclient/000077500000000000000000000000001315671014200276075ustar00rootroot00000000000000HttpClientMetricNameStrategies.java000066400000000000000000000050161315671014200364530ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import org.apache.http.HttpRequest; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpRequestWrapper; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URIBuilder; import java.net.URI; import java.net.URISyntaxException; import static com.codahale.metrics.MetricRegistry.name; public class HttpClientMetricNameStrategies { public static final HttpClientMetricNameStrategy METHOD_ONLY = new HttpClientMetricNameStrategy() { @Override public String getNameFor(String name, HttpRequest request) { return name(HttpClient.class, name, methodNameString(request)); } }; public static final HttpClientMetricNameStrategy HOST_AND_METHOD = new HttpClientMetricNameStrategy() { @Override public String getNameFor(String name, HttpRequest request) { return name(HttpClient.class, name, requestURI(request).getHost(), methodNameString(request)); } }; public static final HttpClientMetricNameStrategy QUERYLESS_URL_AND_METHOD = new HttpClientMetricNameStrategy() { @Override public String getNameFor(String name, HttpRequest request) { try { final URIBuilder url = new URIBuilder(requestURI(request)); return name(HttpClient.class, name, url.removeQuery().build().toString(), methodNameString(request)); } catch (URISyntaxException e) { throw new IllegalArgumentException(e); } } }; private static String methodNameString(HttpRequest request) { return request.getRequestLine().getMethod().toLowerCase() + "-requests"; } private static URI requestURI(HttpRequest request) { if (request instanceof HttpRequestWrapper) return requestURI(((HttpRequestWrapper) request).getOriginal()); return (request instanceof HttpUriRequest) ? ((HttpUriRequest) request).getURI() : URI.create(request.getRequestLine().getUri()); } } HttpClientMetricNameStrategy.java000066400000000000000000000002721315671014200361420ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import org.apache.http.HttpRequest; public interface HttpClientMetricNameStrategy { String getNameFor(String name, HttpRequest request); } InstrumentedHttpClientConnectionManager.java000066400000000000000000000131501315671014200403660ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.*; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SystemDefaultDnsResolver; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; /** * A {@link HttpClientConnectionManager} which monitors the number of open connections. */ public class InstrumentedHttpClientConnectionManager extends PoolingHttpClientConnectionManager { protected static Registry getDefaultRegistry() { return RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); } private final MetricRegistry metricsRegistry; private final String name; public InstrumentedHttpClientConnectionManager(MetricRegistry metricRegistry) { this(metricRegistry, getDefaultRegistry()); } public InstrumentedHttpClientConnectionManager(MetricRegistry metricsRegistry, Registry socketFactoryRegistry) { this(metricsRegistry, socketFactoryRegistry, -1, TimeUnit.MILLISECONDS); } public InstrumentedHttpClientConnectionManager(MetricRegistry metricsRegistry, Registry socketFactoryRegistry, long connTTL, TimeUnit connTTLTimeUnit) { this(metricsRegistry, socketFactoryRegistry, null, null, SystemDefaultDnsResolver.INSTANCE, connTTL, connTTLTimeUnit, null); } public InstrumentedHttpClientConnectionManager(MetricRegistry metricsRegistry, Registry socketFactoryRegistry, HttpConnectionFactory connFactory, SchemePortResolver schemePortResolver, DnsResolver dnsResolver, long connTTL, TimeUnit connTTLTimeUnit, String name) { super(socketFactoryRegistry, connFactory, schemePortResolver, dnsResolver, connTTL, connTTLTimeUnit); this.metricsRegistry = metricsRegistry; this.name = name; metricsRegistry.register(name(HttpClientConnectionManager.class, name, "available-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getAvailable(); } }); metricsRegistry.register(name(HttpClientConnectionManager.class, name, "leased-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getLeased(); } }); metricsRegistry.register(name(HttpClientConnectionManager.class, name, "max-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getMax(); } }); metricsRegistry.register(name(HttpClientConnectionManager.class, name, "pending-connections"), new Gauge() { @Override public Integer getValue() { // this acquires a lock on the connection pool; remove if contention sucks return getTotalStats().getPending(); } }); } @Override public void shutdown() { super.shutdown(); metricsRegistry.remove(name(HttpClientConnectionManager.class, name, "available-connections")); metricsRegistry.remove(name(HttpClientConnectionManager.class, name, "leased-connections")); metricsRegistry.remove(name(HttpClientConnectionManager.class, name, "max-connections")); metricsRegistry.remove(name(HttpClientConnectionManager.class, name, "pending-connections")); } } InstrumentedHttpClients.java000066400000000000000000000025261315671014200352430ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import com.codahale.metrics.MetricRegistry; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import static com.codahale.metrics.httpclient.HttpClientMetricNameStrategies.METHOD_ONLY; public class InstrumentedHttpClients { private InstrumentedHttpClients() { super(); } public static CloseableHttpClient createDefault(MetricRegistry metricRegistry) { return createDefault(metricRegistry, METHOD_ONLY); } public static CloseableHttpClient createDefault(MetricRegistry metricRegistry, HttpClientMetricNameStrategy metricNameStrategy) { return custom(metricRegistry, metricNameStrategy).build(); } public static HttpClientBuilder custom(MetricRegistry metricRegistry) { return custom(metricRegistry, METHOD_ONLY); } public static HttpClientBuilder custom(MetricRegistry metricRegistry, HttpClientMetricNameStrategy metricNameStrategy) { return HttpClientBuilder.create() .setRequestExecutor(new InstrumentedHttpRequestExecutor(metricRegistry, metricNameStrategy)) .setConnectionManager(new InstrumentedHttpClientConnectionManager(metricRegistry)); } } InstrumentedHttpRequestExecutor.java000066400000000000000000000040511315671014200370040ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/main/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import org.apache.http.HttpClientConnection; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestExecutor; import java.io.IOException; public class InstrumentedHttpRequestExecutor extends HttpRequestExecutor { private final MetricRegistry registry; private final HttpClientMetricNameStrategy metricNameStrategy; private final String name; public InstrumentedHttpRequestExecutor(MetricRegistry registry, HttpClientMetricNameStrategy metricNameStrategy) { this(registry, metricNameStrategy, null); } public InstrumentedHttpRequestExecutor(MetricRegistry registry, HttpClientMetricNameStrategy metricNameStrategy, String name) { this(registry, metricNameStrategy, name, HttpRequestExecutor.DEFAULT_WAIT_FOR_CONTINUE); } public InstrumentedHttpRequestExecutor(MetricRegistry registry, HttpClientMetricNameStrategy metricNameStrategy, String name, int waitForContinue) { super(waitForContinue); this.registry = registry; this.name = name; this.metricNameStrategy = metricNameStrategy; } @Override public HttpResponse execute(HttpRequest request, HttpClientConnection conn, HttpContext context) throws HttpException, IOException { final Timer.Context timerContext = timer(request).time(); try { return super.execute(request, conn, context); } finally { timerContext.stop(); } } private Timer timer(HttpRequest request) { return registry.timer(metricNameStrategy.getNameFor(name, request)); } } metrics-3.2.5/metrics-httpclient/src/test/000077500000000000000000000000001315671014200205575ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/000077500000000000000000000000001315671014200215005ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/000077500000000000000000000000001315671014200222565ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/000077500000000000000000000000001315671014200240165ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200254645ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/metrics/httpclient/000077500000000000000000000000001315671014200276425ustar00rootroot00000000000000HttpClientMetricNameStrategiesTest.java000066400000000000000000000064751315671014200373600ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import org.apache.http.HttpRequest; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestWrapper; import org.apache.http.client.utils.URIUtils; import org.junit.Test; import java.net.URI; import java.net.URISyntaxException; import static com.codahale.metrics.httpclient.HttpClientMetricNameStrategies.*; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class HttpClientMetricNameStrategiesTest { @Test public void methodOnlyWithName() { assertThat(METHOD_ONLY.getNameFor("some-service", new HttpGet("/whatever")), is("org.apache.http.client.HttpClient.some-service.get-requests")); } @Test public void methodOnlyWithoutName() { assertThat(METHOD_ONLY.getNameFor(null, new HttpGet("/whatever")), is("org.apache.http.client.HttpClient.get-requests")); } @Test public void hostAndMethodWithName() { assertThat(HOST_AND_METHOD.getNameFor("some-service", new HttpPost("http://my.host.com/whatever")), is("org.apache.http.client.HttpClient.some-service.my.host.com.post-requests")); } @Test public void hostAndMethodWithoutName() { assertThat(HOST_AND_METHOD.getNameFor(null, new HttpPost("http://my.host.com/whatever")), is("org.apache.http.client.HttpClient.my.host.com.post-requests")); } @Test public void hostAndMethodWithNameInWrappedRequest() throws URISyntaxException { HttpRequest request = rewriteRequestURI(new HttpPost("http://my.host.com/whatever")); assertThat(HOST_AND_METHOD.getNameFor("some-service", request), is("org.apache.http.client.HttpClient.some-service.my.host.com.post-requests")); } @Test public void hostAndMethodWithoutNameInWrappedRequest() throws URISyntaxException { HttpRequest request = rewriteRequestURI(new HttpPost("http://my.host.com/whatever")); assertThat(HOST_AND_METHOD.getNameFor(null, request), is("org.apache.http.client.HttpClient.my.host.com.post-requests")); } @Test public void querylessUrlAndMethodWithName() { assertThat(QUERYLESS_URL_AND_METHOD.getNameFor( "some-service", new HttpPut("https://thing.com:8090/my/path?ignore=this&and=this")), is("org.apache.http.client.HttpClient.some-service.https://thing.com:8090/my/path.put-requests")); } @Test public void querylessUrlAndMethodWithNameInWrappedRequest() throws URISyntaxException { HttpRequest request = rewriteRequestURI(new HttpPut("https://thing.com:8090/my/path?ignore=this&and=this")); assertThat(QUERYLESS_URL_AND_METHOD.getNameFor( "some-service", request), is("org.apache.http.client.HttpClient.some-service.https://thing.com:8090/my/path.put-requests")); } private static HttpRequest rewriteRequestURI(HttpRequest request) throws URISyntaxException { HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request); URI uri = URIUtils.rewriteURI(wrapper.getURI(), null, true); wrapper.setURI(uri); return wrapper; } } InstrumentedHttpClientConnectionManagerTest.java000066400000000000000000000015221315671014200412610ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import org.junit.Assert; import org.junit.Test; public class InstrumentedHttpClientConnectionManagerTest { private final MetricRegistry metricRegistry = new MetricRegistry(); @Test public void shouldRemoveGauges() { final InstrumentedHttpClientConnectionManager instrumentedHttpClientConnectionManager = new InstrumentedHttpClientConnectionManager(metricRegistry); Assert.assertEquals(4, metricRegistry.getGauges().size()); instrumentedHttpClientConnectionManager.close(); Assert.assertEquals(0, metricRegistry.getGauges().size()); // should be able to create another one with the same name ("") new InstrumentedHttpClientConnectionManager(metricRegistry); } } InstrumentedHttpClientsTest.java000066400000000000000000000030351315671014200361320ustar00rootroot00000000000000metrics-3.2.5/metrics-httpclient/src/test/java/com/codahale/metrics/httpclientpackage com.codahale.metrics.httpclient; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistryListener; import com.codahale.metrics.Timer; import org.apache.http.HttpRequest; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.junit.Before; import org.junit.Test; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; public class InstrumentedHttpClientsTest { private final HttpClientMetricNameStrategy metricNameStrategy = mock(HttpClientMetricNameStrategy.class); private final MetricRegistryListener registryListener = mock(MetricRegistryListener.class); private final MetricRegistry metricRegistry = new MetricRegistry(); private final HttpClient client = InstrumentedHttpClients.createDefault(metricRegistry, metricNameStrategy); @Before public void setUp() throws Exception { metricRegistry.addListener(registryListener); } @Test public void registersExpectedMetricsGivenNameStrategy() throws Exception { final HttpGet get = new HttpGet("http://example.com?q=anything"); final String metricName = "some.made.up.metric.name"; when(metricNameStrategy.getNameFor(anyString(), any(HttpRequest.class))) .thenReturn(metricName); client.execute(get); verify(registryListener).onTimerAdded(eq(metricName), any(Timer.class)); } } metrics-3.2.5/metrics-jcache/000077500000000000000000000000001315671014200160505ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/pom.xml000066400000000000000000000025541315671014200173730ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jcache Metrics Integration for JCache bundle Metrics Integration for JCache, JSR 107 standard for caching. Uses the CacheStatisticsMXBean provided statistics. io.dropwizard.metrics metrics-core ${project.version} javax.cache cache-api 1.0.0 provided org.ehcache ehcache 3.1.3 test metrics-3.2.5/metrics-jcache/src/000077500000000000000000000000001315671014200166375ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/000077500000000000000000000000001315671014200175635ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/000077500000000000000000000000001315671014200205045ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/com/000077500000000000000000000000001315671014200212625ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/com/codahale/000077500000000000000000000000001315671014200230225ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200244705ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/com/codahale/metrics/jcache/000077500000000000000000000000001315671014200257055ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/main/java/com/codahale/metrics/jcache/JCacheGaugeSet.java000066400000000000000000000057441315671014200313240ustar00rootroot00000000000000package com.codahale.metrics.jcache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codahale.metrics.JmxAttributeGauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.cache.management.CacheStatisticsMXBean; import javax.management.MalformedObjectNameException; import javax.management.ObjectInstance; import javax.management.ObjectName; import static com.codahale.metrics.MetricRegistry.name; /** * Gauge set retrieving JCache JMX attributes * * @author Henri Tremblay * @author Anthony Dahanne */ public class JCacheGaugeSet implements MetricSet { private static final String M_BEAN_COORDINATES = "javax.cache:type=CacheStatistics,CacheManager=*,Cache=*"; private static final Logger LOGGER = LoggerFactory.getLogger(JCacheGaugeSet.class); @Override public Map getMetrics() { Set cacheBeans = getCacheBeans(); List availableStatsNames = retrieveStatsNames(); Map gauges = new HashMap(cacheBeans.size() * availableStatsNames.size()); for (ObjectInstance cacheBean : cacheBeans) { ObjectName objectName = cacheBean.getObjectName(); String cacheName = objectName.getKeyProperty("Cache"); for (String statsName : availableStatsNames) { JmxAttributeGauge jmxAttributeGauge = new JmxAttributeGauge(objectName, statsName); gauges.put(name(cacheName, toSpinalCase(statsName)), jmxAttributeGauge); } } return Collections.unmodifiableMap(gauges); } private Set getCacheBeans() { try { return ManagementFactory.getPlatformMBeanServer().queryMBeans(ObjectName.getInstance(M_BEAN_COORDINATES), null); } catch(MalformedObjectNameException e) { LOGGER.error("Unable to retrieve {}. Are JCache statistics enabled?", M_BEAN_COORDINATES); throw new RuntimeException(e); } } private List retrieveStatsNames() { Method[] methods = CacheStatisticsMXBean.class.getDeclaredMethods(); List availableStatsNames = new ArrayList(methods.length); for (Method method : methods) { String methodName = method.getName(); if(methodName.startsWith("get")) { availableStatsNames.add(methodName.substring(3)); } } return availableStatsNames; } private static String toSpinalCase(String camelCase) { return camelCase.replaceAll("(.)(\\p{Upper})", "$1-$2").toLowerCase(Locale.US); } } metrics-3.2.5/metrics-jcache/src/test/000077500000000000000000000000001315671014200176165ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/test/java/000077500000000000000000000000001315671014200205375ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/test/java/JCacheGaugeSetTest.java000066400000000000000000000063461315671014200250150ustar00rootroot00000000000000import org.junit.After; import org.junit.Before; import org.junit.Test; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.jcache.JCacheGaugeSet; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.spi.CachingProvider; import static org.assertj.core.api.Assertions.assertThat; public class JCacheGaugeSetTest { private MetricRegistry registry; private Cache myCache; private Cache myOtherCache; private CacheManager cacheManager; @Before public void setUp() throws Exception { CachingProvider provider = Caching.getCachingProvider(); cacheManager = provider.getCacheManager( getClass().getResource("ehcache.xml").toURI(), getClass().getClassLoader()); myCache = cacheManager.getCache("myCache"); myOtherCache = cacheManager.getCache("myOtherCache"); registry = new MetricRegistry(); registry.register("jcache.statistics", new JCacheGaugeSet()); } @Test public void measuresGauges() throws Exception { myOtherCache.get("woo"); assertThat(registry.getGauges().get("jcache.statistics.myOtherCache.cache-misses").getValue()) .isEqualTo(1L); myCache.get("woo"); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-misses").getValue()) .isEqualTo(1L); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-hits").getValue()) .isEqualTo(0L); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-gets").getValue()) .isEqualTo(1L); myCache.put("woo", "whee"); myCache.get("woo"); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-puts").getValue()) .isEqualTo(1L); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-hits").getValue()) .isEqualTo(1L); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-hit-percentage").getValue()) .isEqualTo(50.0f); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-miss-percentage").getValue()) .isEqualTo(50.0f); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-gets").getValue()) .isEqualTo(2L); // cache size being 1, eviction occurs after this line myCache.put("woo2", "whoza"); assertThat(registry.getGauges().get("jcache.statistics.myCache.cache-evictions").getValue()) .isEqualTo(1L); myCache.remove("woo2"); assertThat((Float)registry.getGauges().get("jcache.statistics.myCache.average-get-time").getValue()) .isGreaterThan(0.0f); assertThat((Float)registry.getGauges().get("jcache.statistics.myCache.average-put-time").getValue()) .isGreaterThan(0.0f); assertThat((Float)registry.getGauges().get("jcache.statistics.myCache.average-remove-time").getValue()) .isGreaterThan(0.0f); } @After public void tearDown() throws Exception { cacheManager.destroyCache("myCache"); cacheManager.destroyCache("myOtherCache"); cacheManager.close(); } } metrics-3.2.5/metrics-jcache/src/test/resources/000077500000000000000000000000001315671014200216305ustar00rootroot00000000000000metrics-3.2.5/metrics-jcache/src/test/resources/ehcache.xml000066400000000000000000000007401315671014200237330ustar00rootroot00000000000000 3600 1 metrics-3.2.5/metrics-jcstress/000077500000000000000000000000001315671014200164735ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/README.md000066400000000000000000000006511315671014200177540ustar00rootroot00000000000000Concurrency test are based on [OpenJDK Java Concurrency Stress tests](https://wiki.openjdk.java.net/display/CodeTools/jcstress). ### Command line launching Build tests jar with maven and run tests: ````bash mvn clean install java -jar target/jcstress.jar ```` Look at results report `results/index.html` ### Command line options The whole list of command line options is available by: java -jar target/jcstress.jar metrics-3.2.5/metrics-jcstress/findbugs-exclude.xml000066400000000000000000000003031315671014200224410ustar00rootroot00000000000000 metrics-3.2.5/metrics-jcstress/pom.xml000066400000000000000000000102611315671014200200100ustar00rootroot00000000000000 4.0.0 metrics-parent io.dropwizard.metrics 3.2.5 metrics-jcstress 3.2.5 jar Metrics JCStress tests 3.0 io.dropwizard.metrics metrics-core ${project.version} org.openjdk.jcstress jcstress-core ${jcstress.version} UTF-8 0.1.1 1.8 jcstress org.apache.maven.plugins maven-deploy-plugin 2.7 true org.apache.maven.plugins maven-compiler-plugin 3.1 ${javac.target} ${javac.target} ${javac.target} org.apache.maven.plugins maven-shade-plugin 2.2 main package shade ${uberjar.name} org.openjdk.jcstress.Main META-INF/TestList org.codehaus.mojo findbugs-maven-plugin findbugs-exclude.xml metrics-3.2.5/metrics-jcstress/src/000077500000000000000000000000001315671014200172625ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/000077500000000000000000000000001315671014200202065ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/000077500000000000000000000000001315671014200211275ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/000077500000000000000000000000001315671014200217055ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/codahale/000077500000000000000000000000001315671014200234455ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200251135ustar00rootroot00000000000000SlidingTimeWindowArrayReservoirTrimReadTest.java000066400000000000000000000040601315671014200364670ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/codahale/metricspackage com.codahale.metrics; import org.openjdk.jcstress.annotations.Actor; import org.openjdk.jcstress.annotations.Expect; import org.openjdk.jcstress.annotations.JCStressTest; import org.openjdk.jcstress.annotations.Outcome; import org.openjdk.jcstress.annotations.State; import org.openjdk.jcstress.infra.results.StringResult1; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; @JCStressTest @Outcome( id = "\\[240, 241, 242, 243, 244, 245, 246, 247, 248, 249\\]", expect = Expect.ACCEPTABLE, desc = "Actor1 made read before Actor2 even started" ) @Outcome( id = "\\[243, 244, 245, 246, 247, 248, 249\\]", expect = Expect.ACCEPTABLE, desc = "Actor2 made trim before Actor1 even started" ) @Outcome( id = "\\[244, 245, 246, 247, 248, 249\\]", expect = Expect.ACCEPTABLE, desc = "Actor1 made trim, then Actor2 started trim and made startIndex change, " + "before Actor1 concurrent read." ) @Outcome( id = "\\[243, 244, 245, 246, 247, 248\\]", expect = Expect.ACCEPTABLE, desc = "Actor1 made trim, then Actor2 started trim, but not finished startIndex change, before Actor1 concurrent read." ) @State public class SlidingTimeWindowArrayReservoirTrimReadTest { private final AtomicLong ticks = new AtomicLong(0); private final SlidingTimeWindowArrayReservoir reservoir; public SlidingTimeWindowArrayReservoirTrimReadTest() { reservoir = new SlidingTimeWindowArrayReservoir(10, TimeUnit.NANOSECONDS, new Clock() { @Override public long getTick() { return ticks.get(); } }); for (int i = 0; i < 250; i++) { ticks.set(i); reservoir.update(i); } } @Actor public void actor1(StringResult1 r) { Snapshot snapshot = reservoir.getSnapshot(); String stringValues = Arrays.toString(snapshot.getValues()); r.r1 = stringValues; } @Actor public void actor2() { ticks.set(253); reservoir.trim(); } }SlidingTimeWindowArrayReservoirWriteReadAllocate.java000066400000000000000000000025121315671014200374530ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/codahale/metricspackage com.codahale.metrics; import org.openjdk.jcstress.annotations.Actor; import org.openjdk.jcstress.annotations.Arbiter; import org.openjdk.jcstress.annotations.Expect; import org.openjdk.jcstress.annotations.JCStressTest; import org.openjdk.jcstress.annotations.Outcome; import org.openjdk.jcstress.annotations.State; import org.openjdk.jcstress.infra.results.StringResult1; import java.util.Arrays; import java.util.concurrent.TimeUnit; @JCStressTest @Outcome(id = "\\[1023, 1029, 1034\\]", expect = Expect.ACCEPTABLE) @State public class SlidingTimeWindowArrayReservoirWriteReadAllocate { private final SlidingTimeWindowArrayReservoir reservoir; public SlidingTimeWindowArrayReservoirWriteReadAllocate() { reservoir = new SlidingTimeWindowArrayReservoir(500, TimeUnit.SECONDS); for (int i = 0; i < 1024; i++) { reservoir.update(i); } } @Actor public void actor1() { reservoir.update(1029L); } @Actor public void actor2() { reservoir.update(1034L); } @Arbiter public void arbiter(StringResult1 r) { Snapshot snapshot = reservoir.getSnapshot(); long[] values = snapshot.getValues(); String stringValues = Arrays.toString(Arrays.copyOfRange(values, values.length - 3, values.length)); r.r1 = stringValues; } }SlidingTimeWindowArrayReservoirWriteReadTest.java000066400000000000000000000024571315671014200366560ustar00rootroot00000000000000metrics-3.2.5/metrics-jcstress/src/main/java/com/codahale/metricspackage com.codahale.metrics; import org.openjdk.jcstress.annotations.Actor; import org.openjdk.jcstress.annotations.Expect; import org.openjdk.jcstress.annotations.JCStressTest; import org.openjdk.jcstress.annotations.Outcome; import org.openjdk.jcstress.annotations.State; import org.openjdk.jcstress.infra.results.StringResult1; import java.util.Arrays; import java.util.concurrent.TimeUnit; @JCStressTest @Outcome(id = "\\[\\]", expect = Expect.ACCEPTABLE) @Outcome(id = "\\[31\\]", expect = Expect.ACCEPTABLE) @Outcome(id = "\\[15\\]", expect = Expect.ACCEPTABLE) @Outcome(id = "\\[31, 15\\]", expect = Expect.ACCEPTABLE) @Outcome(id = "\\[15, 31\\]", expect = Expect.ACCEPTABLE) @State public class SlidingTimeWindowArrayReservoirWriteReadTest { private final SlidingTimeWindowArrayReservoir reservoir; public SlidingTimeWindowArrayReservoirWriteReadTest() { reservoir = new SlidingTimeWindowArrayReservoir(1, TimeUnit.SECONDS); } @Actor public void actor1() { reservoir.update(31L); } @Actor public void actor2() { reservoir.update(15L); } @Actor public void actor3(StringResult1 r) { Snapshot snapshot = reservoir.getSnapshot(); String stringValues = Arrays.toString(snapshot.getValues()); r.r1 = stringValues; } }metrics-3.2.5/metrics-jdbi/000077500000000000000000000000001315671014200155435ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/pom.xml000066400000000000000000000021101315671014200170520ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jdbi Metrics Integration for JDBI bundle A JDBI wrapper providing Metrics instrumentation of query durations and rates. io.dropwizard.metrics metrics-core ${project.version} org.jdbi jdbi 2.55 metrics-3.2.5/metrics-jdbi/src/000077500000000000000000000000001315671014200163325ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/000077500000000000000000000000001315671014200172565ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/000077500000000000000000000000001315671014200201775ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/000077500000000000000000000000001315671014200207555ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/000077500000000000000000000000001315671014200225155ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200241635ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/000077500000000000000000000000001315671014200250735ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/InstrumentedTimingCollector.java000066400000000000000000000025651315671014200334460ustar00rootroot00000000000000package com.codahale.metrics.jdbi; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jdbi.strategies.SmartNameStrategy; import com.codahale.metrics.jdbi.strategies.StatementNameStrategy; import org.skife.jdbi.v2.StatementContext; import org.skife.jdbi.v2.TimingCollector; import java.util.concurrent.TimeUnit; /** * A {@link TimingCollector} implementation for JDBI which uses the SQL objects' class names and * method names for millisecond-precision timers. */ public class InstrumentedTimingCollector implements TimingCollector { private final MetricRegistry registry; private final StatementNameStrategy statementNameStrategy; public InstrumentedTimingCollector(MetricRegistry registry) { this(registry, new SmartNameStrategy()); } public InstrumentedTimingCollector(MetricRegistry registry, StatementNameStrategy statementNameStrategy) { this.registry = registry; this.statementNameStrategy = statementNameStrategy; } @Override public void collect(long elapsedTime, StatementContext ctx) { final Timer timer = getTimer(ctx); timer.update(elapsedTime, TimeUnit.NANOSECONDS); } private Timer getTimer(StatementContext ctx) { return registry.timer(statementNameStrategy.getStatementName(ctx)); } } metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/000077500000000000000000000000001315671014200272455ustar00rootroot00000000000000BasicSqlNameStrategy.java000066400000000000000000000003731315671014200340610ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategiespackage com.codahale.metrics.jdbi.strategies; public class BasicSqlNameStrategy extends DelegatingStatementNameStrategy { public BasicSqlNameStrategy() { super(NameStrategies.CHECK_EMPTY, NameStrategies.SQL_OBJECT); } } ContextNameStrategy.java000066400000000000000000000007631315671014200340070ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategiespackage com.codahale.metrics.jdbi.strategies; /** * Adds statistics for JDBI queries that set the {@link NameStrategies#STATEMENT_GROUP} and {@link * NameStrategies#STATEMENT_NAME} for group based display. */ public class ContextNameStrategy extends DelegatingStatementNameStrategy { public ContextNameStrategy() { super(NameStrategies.CHECK_EMPTY, NameStrategies.CHECK_RAW, NameStrategies.CONTEXT_NAME, NameStrategies.NAIVE_NAME); } } DelegatingStatementNameStrategy.java000066400000000000000000000017731315671014200363150ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategiespackage com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public abstract class DelegatingStatementNameStrategy implements StatementNameStrategy { private final List strategies = new ArrayList(); protected DelegatingStatementNameStrategy(StatementNameStrategy... strategies) { registerStrategies(strategies); } protected void registerStrategies(StatementNameStrategy... strategies) { this.strategies.addAll(Arrays.asList(strategies)); } @Override public String getStatementName(StatementContext statementContext) { for (StatementNameStrategy strategy : strategies) { final String statementName = strategy.getStatementName(statementContext); if (statementName != null) { return statementName; } } return NameStrategies.UNKNOWN_SQL; } } metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NaiveNameStrategy.java000066400000000000000000000005721315671014200335020ustar00rootroot00000000000000package com.codahale.metrics.jdbi.strategies; /** * Very simple strategy, can be used with any JDBI loader to build basic statistics. */ public class NaiveNameStrategy extends DelegatingStatementNameStrategy { public NaiveNameStrategy() { super(NameStrategies.CHECK_EMPTY, NameStrategies.CHECK_RAW, NameStrategies.NAIVE_NAME); } } metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/NameStrategies.java000066400000000000000000000145461315671014200330350ustar00rootroot00000000000000package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.ClasspathStatementLocator; import org.skife.jdbi.v2.StatementContext; import java.lang.reflect.Method; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.codahale.metrics.MetricRegistry.name; public final class NameStrategies { public static final StatementNameStrategy CHECK_EMPTY = new CheckEmptyStrategy(); public static final StatementNameStrategy CHECK_RAW = new CheckRawStrategy(); public static final StatementNameStrategy SQL_OBJECT = new SqlObjectStrategy(); public static final StatementNameStrategy NAIVE_NAME = new NaiveNameStrategy(); public static final StatementNameStrategy CONTEXT_CLASS = new ContextClassStrategy(); public static final StatementNameStrategy CONTEXT_NAME = new ContextNameStrategy(); /** * An empty SQL statement. */ private static final String EMPTY_SQL = "sql.empty"; /** * Unknown SQL. */ static final String UNKNOWN_SQL = "sql.unknown"; /** * Context attribute name for the metric class. */ public static final String STATEMENT_CLASS = "_metric_class"; /** * Context attribute name for the metric group. */ public static final String STATEMENT_GROUP = "_metric_group"; /** * Context attribute name for the metric type. */ public static final String STATEMENT_TYPE = "_metric_type"; /** * Context attribute name for the metric name. */ public static final String STATEMENT_NAME = "_metric_name"; private static String forRawSql(String rawSql) { return name("sql", "raw", rawSql); } static final class CheckEmptyStrategy implements StatementNameStrategy { private CheckEmptyStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); if (rawSql == null || rawSql.length() == 0) { return EMPTY_SQL; } return null; } } static final class CheckRawStrategy implements StatementNameStrategy { private CheckRawStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); if (ClasspathStatementLocator.looksLikeSql(rawSql)) { return forRawSql(rawSql); } return null; } } static final class NaiveNameStrategy implements StatementNameStrategy { private NaiveNameStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final String rawSql = statementContext.getRawSql(); // Is it using the template loader? final int colon = rawSql.indexOf(':'); if (colon == -1) { // No package? Just return the name, JDBI figured out somehow on how to find the raw sql for this statement. return forRawSql(rawSql); } final String group = rawSql.substring(0, colon); final String name = rawSql.substring(colon + 1); return name(group, name); } } static final class SqlObjectStrategy implements StatementNameStrategy { private SqlObjectStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final Class clazz = statementContext.getSqlObjectType(); final Method method = statementContext.getSqlObjectMethod(); if (clazz != null) { final String rawSql = statementContext.getRawSql(); final String group = clazz.getPackage().getName(); final String name = clazz.getSimpleName(); final String type = method == null ? rawSql : method.getName(); return name(group, name, type); } return null; } } static final class ContextClassStrategy implements StatementNameStrategy { private ContextClassStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final Object classObj = statementContext.getAttribute(STATEMENT_CLASS); final Object nameObj = statementContext.getAttribute(STATEMENT_NAME); if (classObj == null || nameObj == null) { return null; } final String className = (String) classObj; final String statementName = (String) nameObj; final int dotPos = className.lastIndexOf('.'); if (dotPos == -1) { return null; } return name(className.substring(0, dotPos), className.substring(dotPos + 1), statementName); } } static final class ContextNameStrategy implements StatementNameStrategy { /** * File pattern to shorten the group name. */ private static final Pattern SHORT_PATTERN = Pattern.compile("^(.*?)/(.*?)(-sql)?\\.st(g)?$"); private ContextNameStrategy() { } @Override public String getStatementName(StatementContext statementContext) { final Object groupObj = statementContext.getAttribute(STATEMENT_GROUP); final Object typeObj = statementContext.getAttribute(STATEMENT_TYPE); final Object nameObj = statementContext.getAttribute(STATEMENT_NAME); if (groupObj == null || nameObj == null) { return null; } final String group = (String) groupObj; final String statementName = (String) nameObj; if (typeObj == null) { final Matcher matcher = SHORT_PATTERN.matcher(group); if (matcher.matches()) { final String groupName = matcher.group(1); final String typeName = matcher.group(2); return name(groupName, typeName, statementName); } return name(group, statementName, ""); } else { final String type = (String) typeObj; return name(group, type, statementName); } } } private NameStrategies() { } } metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/ShortNameStrategy.java000066400000000000000000000070141315671014200335350ustar00rootroot00000000000000package com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.codahale.metrics.MetricRegistry.name; /** * Assembles all JDBI stats under a common prefix (passed in at constructor time). Stats are grouped * by class name and method; a shortening strategy is applied to make the JMX output nicer. */ public final class ShortNameStrategy extends DelegatingStatementNameStrategy { private final ConcurrentMap shortClassNames = new ConcurrentHashMap(); private final String baseJmxName; public ShortNameStrategy(String baseJmxName) { this.baseJmxName = baseJmxName; // Java does not allow super (..., new ShortContextClassStrategy(), new ShortSqlObjectStrategy(), ...); // ==> No enclosing instance of type is available due to some intermediate constructor invocation. Lame. registerStrategies(NameStrategies.CHECK_EMPTY, new ShortContextClassStrategy(), new ShortSqlObjectStrategy(), NameStrategies.CHECK_RAW, NameStrategies.NAIVE_NAME); } private final class ShortContextClassStrategy implements StatementNameStrategy { @Override public String getStatementName(StatementContext statementContext) { final Object classObj = statementContext.getAttribute(NameStrategies.STATEMENT_CLASS); final Object nameObj = statementContext.getAttribute(NameStrategies.STATEMENT_NAME); if (classObj == null || nameObj == null) { return null; } final String className = (String) classObj; final String statementName = (String) nameObj; final int dotPos = className.lastIndexOf('.'); if (dotPos == -1) { return null; } final String shortName = className.substring(dotPos + 1); final String oldClassName = shortClassNames.putIfAbsent(shortName, className); if (oldClassName == null || oldClassName.equals(className)) { return name(baseJmxName, shortName, statementName); } else { return name(baseJmxName, className, statementName); } } } private final class ShortSqlObjectStrategy implements StatementNameStrategy { @Override public String getStatementName(StatementContext statementContext) { final Class clazz = statementContext.getSqlObjectType(); final Method method = statementContext.getSqlObjectMethod(); if (clazz != null && method != null) { final String className = clazz.getName(); final String statementName = method.getName(); final int dotPos = className.lastIndexOf('.'); if (dotPos == -1) { return null; } final String shortName = className.substring(dotPos + 1); final String oldClassName = shortClassNames.putIfAbsent(shortName, className); if (oldClassName == null || oldClassName.equals(className)) { return name(baseJmxName, shortName, statementName); } else { return name(baseJmxName, className, statementName); } } return null; } } } metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategies/SmartNameStrategy.java000066400000000000000000000013611315671014200335230ustar00rootroot00000000000000package com.codahale.metrics.jdbi.strategies; /** * Adds statistics for JDBI queries that set the {@link NameStrategies#STATEMENT_CLASS} and {@link * NameStrategies#STATEMENT_NAME} for class based display or {@link NameStrategies#STATEMENT_GROUP} * and {@link NameStrategies#STATEMENT_NAME} for group based display. *

    * Also knows how to deal with SQL Object statements. */ public class SmartNameStrategy extends DelegatingStatementNameStrategy { public SmartNameStrategy() { super(NameStrategies.CHECK_EMPTY, NameStrategies.CONTEXT_CLASS, NameStrategies.CONTEXT_NAME, NameStrategies.SQL_OBJECT, NameStrategies.CHECK_RAW, NameStrategies.NAIVE_NAME); } } StatementNameStrategy.java000066400000000000000000000004211315671014200343160ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/main/java/com/codahale/metrics/jdbi/strategiespackage com.codahale.metrics.jdbi.strategies; import org.skife.jdbi.v2.StatementContext; /** * Interface for strategies to statement contexts to metric names. */ public interface StatementNameStrategy { String getStatementName(StatementContext statementContext); } metrics-3.2.5/metrics-jdbi/src/test/000077500000000000000000000000001315671014200173115ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/000077500000000000000000000000001315671014200202325ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/com/000077500000000000000000000000001315671014200210105ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/com/codahale/000077500000000000000000000000001315671014200225505ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200242165ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/com/codahale/metrics/jdbi/000077500000000000000000000000001315671014200251265ustar00rootroot00000000000000InstrumentedTimingCollectorTest.java000066400000000000000000000267061315671014200342650ustar00rootroot00000000000000metrics-3.2.5/metrics-jdbi/src/test/java/com/codahale/metrics/jdbipackage com.codahale.metrics.jdbi; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jdbi.strategies.NameStrategies; import com.codahale.metrics.jdbi.strategies.ShortNameStrategy; import com.codahale.metrics.jdbi.strategies.SmartNameStrategy; import com.codahale.metrics.jdbi.strategies.StatementNameStrategy; import org.junit.Test; import org.skife.jdbi.v2.StatementContext; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; public class InstrumentedTimingCollectorTest { private final MetricRegistry registry = new MetricRegistry(); @Test public void updatesTimerForSqlObjects() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); doReturn(getClass().getMethod("updatesTimerForSqlObjects")).when(ctx).getSqlObjectMethod(); collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name(getClass(), "updatesTimerForSqlObjects")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @Test public void updatesTimerForSqlObjectsWithoutMethod() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name(getClass(), "SELECT 1")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @Test public void updatesTimerForRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); collector.collect(TimeUnit.SECONDS.toNanos(2), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("sql", "raw", "SELECT 1")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(2000000000); } @Test public void updatesTimerForNoRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); collector.collect(TimeUnit.SECONDS.toNanos(2), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("sql", "empty")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(2000000000); } @Test public void updatesTimerForNonSqlishRawSql() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("don't know what it is but it's not SQL").when(ctx).getRawSql(); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("sql", "raw", "don't know what it is but it's not SQL")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } @Test public void updatesTimerForContextClass() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass().getName()).when(ctx).getAttribute(NameStrategies.STATEMENT_CLASS); doReturn("updatesTimerForContextClass").when(ctx) .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name(getClass(), "updatesTimerForContextClass")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } @Test public void updatesTimerForTemplateFile() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("foo/bar.stg").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); doReturn("updatesTimerForTemplateFile").when(ctx) .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(4), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("foo", "bar", "updatesTimerForTemplateFile")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(4000000000L); } @Test public void updatesTimerForContextGroupAndName() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("my-group").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); doReturn("updatesTimerForContextGroupAndName").when(ctx) .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(4), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("my-group", "updatesTimerForContextGroupAndName", "")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(4000000000L); } @Test public void updatesTimerForContextGroupTypeAndName() throws Exception { final StatementNameStrategy strategy = new SmartNameStrategy(); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn("my-group").when(ctx).getAttribute(NameStrategies.STATEMENT_GROUP); doReturn("my-type").when(ctx).getAttribute(NameStrategies.STATEMENT_TYPE); doReturn("updatesTimerForContextGroupTypeAndName").when(ctx) .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(5), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("my-group", "my-type", "updatesTimerForContextGroupTypeAndName")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(5000000000L); } @Test public void updatesTimerForShortSqlObjectStrategy() throws Exception { final StatementNameStrategy strategy = new ShortNameStrategy("jdbi"); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass()).when(ctx).getSqlObjectType(); doReturn(getClass().getMethod("updatesTimerForShortSqlObjectStrategy")).when(ctx) .getSqlObjectMethod(); collector.collect(TimeUnit.SECONDS.toNanos(1), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("jdbi", getClass().getSimpleName(), "updatesTimerForShortSqlObjectStrategy")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(1000000000); } @Test public void updatesTimerForShortContextClassStrategy() throws Exception { final StatementNameStrategy strategy = new ShortNameStrategy("jdbi"); final InstrumentedTimingCollector collector = new InstrumentedTimingCollector(registry, strategy); final StatementContext ctx = mock(StatementContext.class); doReturn("SELECT 1").when(ctx).getRawSql(); doReturn(getClass().getName()).when(ctx).getAttribute(NameStrategies.STATEMENT_CLASS); doReturn("updatesTimerForShortContextClassStrategy").when(ctx) .getAttribute(NameStrategies.STATEMENT_NAME); collector.collect(TimeUnit.SECONDS.toNanos(3), ctx); final String name = strategy.getStatementName(ctx); final Timer timer = registry.timer(name); assertThat(name) .isEqualTo(name("jdbi", getClass().getSimpleName(), "updatesTimerForShortContextClassStrategy")); assertThat(timer.getSnapshot().getMax()) .isEqualTo(3000000000L); } } metrics-3.2.5/metrics-jersey/000077500000000000000000000000001315671014200161345ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/pom.xml000066400000000000000000000036271315671014200174610ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jersey Metrics Integration for Jersey 1.x bundle A set of class providing Metrics integration for Jersey, the reference JAX-RS implementation. This module is for the old Jersey 1.x 1.18.1 io.dropwizard.metrics metrics-core ${project.version} io.dropwizard.metrics metrics-annotation ${project.version} com.sun.jersey jersey-server ${jersey.version} com.sun.jersey.jersey-test-framework jersey-test-framework-inmemory ${jersey.version} test junit junit metrics-3.2.5/metrics-jersey/src/000077500000000000000000000000001315671014200167235ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/000077500000000000000000000000001315671014200176475ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/000077500000000000000000000000001315671014200205705ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/000077500000000000000000000000001315671014200213465ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/codahale/000077500000000000000000000000001315671014200231065ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200245545ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/codahale/metrics/jersey/000077500000000000000000000000001315671014200260555ustar00rootroot00000000000000InstrumentedResourceMethodDispatchAdapter.java000066400000000000000000000030361315671014200371560ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/codahale/metrics/jerseypackage com.codahale.metrics.jersey; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import com.sun.jersey.spi.container.ResourceMethodDispatchAdapter; import com.sun.jersey.spi.container.ResourceMethodDispatchProvider; import javax.ws.rs.ext.Provider; /** * A provider that wraps a {@link ResourceMethodDispatchProvider} in an * {@link InstrumentedResourceMethodDispatchProvider} */ @Provider public class InstrumentedResourceMethodDispatchAdapter implements ResourceMethodDispatchAdapter { private final MetricRegistry registry; /** * Construct a resource method dispatch adapter using the given metrics registry name. * * @param registryName the name of a shared metric registry */ public InstrumentedResourceMethodDispatchAdapter(String registryName) { this(SharedMetricRegistries.getOrCreate(registryName)); } /** * Construct a resource method dispatch adapter using the given metrics registry. *

    * When using this constructor, the {@link InstrumentedResourceMethodDispatchAdapter} * should be added to a Jersey {@code ResourceConfig} as a singleton. * * @param registry a {@link MetricRegistry} */ public InstrumentedResourceMethodDispatchAdapter(MetricRegistry registry) { this.registry = registry; } @Override public ResourceMethodDispatchProvider adapt(ResourceMethodDispatchProvider provider) { return new InstrumentedResourceMethodDispatchProvider(provider, registry); } } InstrumentedResourceMethodDispatchProvider.java000066400000000000000000000135751315671014200374010ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/main/java/com/codahale/metrics/jerseypackage com.codahale.metrics.jersey; import com.sun.jersey.api.core.HttpContext; import com.sun.jersey.api.model.AbstractResourceMethod; import com.sun.jersey.spi.container.ResourceMethodDispatchProvider; import com.sun.jersey.spi.dispatch.RequestDispatcher; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.annotation.ExceptionMetered; import com.codahale.metrics.annotation.Metered; import com.codahale.metrics.annotation.Timed; import static com.codahale.metrics.MetricRegistry.name; class InstrumentedResourceMethodDispatchProvider implements ResourceMethodDispatchProvider { private static class TimedRequestDispatcher implements RequestDispatcher { private final RequestDispatcher underlying; private final Timer timer; private TimedRequestDispatcher(RequestDispatcher underlying, Timer timer) { this.underlying = underlying; this.timer = timer; } @Override public void dispatch(Object resource, HttpContext httpContext) { final Timer.Context context = timer.time(); try { underlying.dispatch(resource, httpContext); } finally { context.stop(); } } } private static class MeteredRequestDispatcher implements RequestDispatcher { private final RequestDispatcher underlying; private final Meter meter; private MeteredRequestDispatcher(RequestDispatcher underlying, Meter meter) { this.underlying = underlying; this.meter = meter; } @Override public void dispatch(Object resource, HttpContext httpContext) { meter.mark(); underlying.dispatch(resource, httpContext); } } private static class ExceptionMeteredRequestDispatcher implements RequestDispatcher { private final RequestDispatcher underlying; private final Meter meter; private final Class exceptionClass; private ExceptionMeteredRequestDispatcher(RequestDispatcher underlying, Meter meter, Class exceptionClass) { this.underlying = underlying; this.meter = meter; this.exceptionClass = exceptionClass; } @Override public void dispatch(Object resource, HttpContext httpContext) { try { underlying.dispatch(resource, httpContext); } catch (Exception e) { if (exceptionClass.isAssignableFrom(e.getClass()) || (e.getCause() != null && exceptionClass.isAssignableFrom(e.getCause().getClass()))) { meter.mark(); } InstrumentedResourceMethodDispatchProvider.throwUnchecked(e); } } } /* * A dirty hack to allow us to throw exceptions of any type without bringing down the unsafe * thunder. */ @SuppressWarnings("unchecked") private static void throwUnchecked(Throwable e) throws T { throw (T) e; } private final ResourceMethodDispatchProvider provider; private final MetricRegistry registry; public InstrumentedResourceMethodDispatchProvider(ResourceMethodDispatchProvider provider, MetricRegistry registry) { this.provider = provider; this.registry = registry; } @Override public RequestDispatcher create(AbstractResourceMethod method) { RequestDispatcher dispatcher = provider.create(method); if (dispatcher == null) { return null; } if (method.getMethod().isAnnotationPresent(Timed.class)) { final Timed annotation = method.getMethod().getAnnotation(Timed.class); final String name = chooseName(annotation.name(), annotation.absolute(), method); final Timer timer = registry.timer(name); dispatcher = new TimedRequestDispatcher(dispatcher, timer); } if (method.getMethod().isAnnotationPresent(Metered.class)) { final Metered annotation = method.getMethod().getAnnotation(Metered.class); final String name = chooseName(annotation.name(), annotation.absolute(), method); final Meter meter = registry.meter(name); dispatcher = new MeteredRequestDispatcher(dispatcher, meter); } if (method.getMethod().isAnnotationPresent(ExceptionMetered.class)) { final ExceptionMetered annotation = method.getMethod() .getAnnotation(ExceptionMetered.class); final String name = chooseName(annotation.name(), annotation.absolute(), method, ExceptionMetered.DEFAULT_NAME_SUFFIX); final Meter meter = registry.meter(name); dispatcher = new ExceptionMeteredRequestDispatcher(dispatcher, meter, annotation.cause()); } return dispatcher; } private String chooseName(String explicitName, boolean absolute, AbstractResourceMethod method, String... suffixes) { if (explicitName != null && !explicitName.isEmpty()) { if (absolute) { return explicitName; } return name(method.getDeclaringResource().getResourceClass(), explicitName); } return name(name(method.getDeclaringResource().getResourceClass(), method.getMethod().getName()), suffixes); } } metrics-3.2.5/metrics-jersey/src/test/000077500000000000000000000000001315671014200177025ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/000077500000000000000000000000001315671014200206235ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/000077500000000000000000000000001315671014200214015ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/000077500000000000000000000000001315671014200231415ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200246075ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/metrics/jersey/000077500000000000000000000000001315671014200261105ustar00rootroot00000000000000SingletonMetricsJerseyTest.java000066400000000000000000000057761315671014200342260ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/metrics/jerseypackage com.codahale.metrics.jersey; import com.sun.jersey.api.container.MappableContainerException; import com.sun.jersey.api.core.DefaultResourceConfig; import com.sun.jersey.test.framework.AppDescriptor; import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.LowLevelAppDescriptor; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jersey.resources.InstrumentedResource; import org.junit.Test; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; /** * Tests importing {@link InstrumentedResourceMethodDispatchAdapter} as a singleton * in a Jersey {@link com.sun.jersey.api.core.ResourceConfig} */ public class SingletonMetricsJerseyTest extends JerseyTest { static { Logger.getLogger("com.sun.jersey").setLevel(Level.OFF); } private MetricRegistry registry; @Override protected AppDescriptor configure() { this.registry = new MetricRegistry(); final DefaultResourceConfig config = new DefaultResourceConfig(); config.getSingletons().add(new InstrumentedResourceMethodDispatchAdapter(registry)); config.getClasses().add(InstrumentedResource.class); return new LowLevelAppDescriptor.Builder(config).build(); } @Test public void timedMethodsAreTimed() { assertThat(resource().path("timed").get(String.class)) .isEqualTo("yay"); final Timer timer = registry.timer(name(InstrumentedResource.class, "timed")); assertThat(timer.getCount()) .isEqualTo(1); } @Test public void meteredMethodsAreMetered() { assertThat(resource().path("metered").get(String.class)) .isEqualTo("woo"); final Meter meter = registry.meter(name(InstrumentedResource.class, "metered")); assertThat(meter.getCount()) .isEqualTo(1); } @Test public void exceptionMeteredMethodsAreExceptionMetered() { final Meter meter = registry.meter(name(InstrumentedResource.class, "exceptionMetered", "exceptions")); assertThat(resource().path("exception-metered").get(String.class)) .isEqualTo("fuh"); assertThat(meter.getCount()) .isZero(); try { resource().path("exception-metered").queryParam("splode", "true").get(String.class); failBecauseExceptionWasNotThrown(MappableContainerException.class); } catch (MappableContainerException e) { assertThat(e.getCause()) .isInstanceOf(IOException.class); } assertThat(meter.getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/metrics/jersey/resources/000077500000000000000000000000001315671014200301225ustar00rootroot00000000000000InstrumentedResource.java000066400000000000000000000015551315671014200351050ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey/src/test/java/com/codahale/metrics/jersey/resourcespackage com.codahale.metrics.jersey.resources; import com.codahale.metrics.annotation.ExceptionMetered; import com.codahale.metrics.annotation.Metered; import com.codahale.metrics.annotation.Timed; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.io.IOException; @Path("/") @Produces(MediaType.TEXT_PLAIN) public class InstrumentedResource { @GET @Timed @Path("/timed") public String timed() { return "yay"; } @GET @Metered @Path("/metered") public String metered() { return "woo"; } @GET @ExceptionMetered(cause = IOException.class) @Path("/exception-metered") public String exceptionMetered(@QueryParam("splode") @DefaultValue("false") boolean splode) throws IOException { if (splode) { throw new IOException("AUGH"); } return "fuh"; } } metrics-3.2.5/metrics-jersey2/000077500000000000000000000000001315671014200162165ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/pom.xml000066400000000000000000000034361315671014200175410ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jersey2 Metrics Integration for Jersey 2.x bundle A set of class providing Metrics integration for Jersey, the reference JAX-RS implementation. 2.11 io.dropwizard.metrics metrics-core ${project.version} io.dropwizard.metrics metrics-annotation ${project.version} org.glassfish.jersey.core jersey-server ${jersey.version} org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-inmemory ${jersey.version} test org.apache.maven.plugins maven-compiler-plugin 3.1 1.7 1.7 metrics-3.2.5/metrics-jersey2/src/000077500000000000000000000000001315671014200170055ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/000077500000000000000000000000001315671014200177315ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/000077500000000000000000000000001315671014200206525ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/com/000077500000000000000000000000001315671014200214305ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/com/codahale/000077500000000000000000000000001315671014200231705ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200246365ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/com/codahale/metrics/jersey2/000077500000000000000000000000001315671014200262215ustar00rootroot00000000000000InstrumentedResourceMethodApplicationListener.java000066400000000000000000000307061315671014200402370ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/main/java/com/codahale/metrics/jersey2package com.codahale.metrics.jersey2; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.annotation.ExceptionMetered; import com.codahale.metrics.annotation.Metered; import com.codahale.metrics.annotation.Timed; import org.glassfish.jersey.server.model.ModelProcessor; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.monitoring.ApplicationEvent; import org.glassfish.jersey.server.monitoring.ApplicationEventListener; import org.glassfish.jersey.server.monitoring.RequestEvent; import org.glassfish.jersey.server.monitoring.RequestEventListener; import javax.ws.rs.core.Configuration; import javax.ws.rs.ext.Provider; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.codahale.metrics.MetricRegistry.name; /** * An application event listener that listens for Jersey application initialization to * be finished, then creates a map of resource method that have metrics annotations. *

    * Finally, it listens for method start events, and returns a {@link RequestEventListener} * that updates the relevant metric for suitably annotated methods when it gets the * request events indicating that the method is about to be invoked, or just got done * being invoked. */ @Provider public class InstrumentedResourceMethodApplicationListener implements ApplicationEventListener, ModelProcessor { private final MetricRegistry metrics; private ConcurrentMap timers = new ConcurrentHashMap<>(); private ConcurrentMap meters = new ConcurrentHashMap<>(); private ConcurrentMap exceptionMeters = new ConcurrentHashMap<>(); /** * Construct an application event listener using the given metrics registry. *

    *

    * When using this constructor, the {@link InstrumentedResourceMethodApplicationListener} * should be added to a Jersey {@code ResourceConfig} as a singleton. * * @param metrics a {@link MetricRegistry} */ public InstrumentedResourceMethodApplicationListener(final MetricRegistry metrics) { this.metrics = metrics; } /** * A private class to maintain the metric for a method annotated with the * {@link ExceptionMetered} annotation, which needs to maintain both a meter * and a cause for which the meter should be updated. */ private static class ExceptionMeterMetric { public final Meter meter; public final Class cause; public ExceptionMeterMetric(final MetricRegistry registry, final ResourceMethod method, final ExceptionMetered exceptionMetered) { final String name = chooseName(exceptionMetered.name(), exceptionMetered.absolute(), method, ExceptionMetered.DEFAULT_NAME_SUFFIX); this.meter = registry.meter(name); this.cause = exceptionMetered.cause(); } } private static class TimerRequestEventListener implements RequestEventListener { private final ConcurrentMap timers; private Timer.Context context = null; public TimerRequestEventListener(final ConcurrentMap timers) { this.timers = timers; } @Override public void onEvent(RequestEvent event) { if (event.getType() == RequestEvent.Type.RESOURCE_METHOD_START) { final Timer timer = this.timers.get(event.getUriInfo() .getMatchedResourceMethod().getInvocable().getDefinitionMethod()); if (timer != null) { this.context = timer.time(); } } else if (event.getType() == RequestEvent.Type.RESOURCE_METHOD_FINISHED) { if (this.context != null) { this.context.close(); } } } } private static class MeterRequestEventListener implements RequestEventListener { private final ConcurrentMap meters; public MeterRequestEventListener(final ConcurrentMap meters) { this.meters = meters; } @Override public void onEvent(RequestEvent event) { if (event.getType() == RequestEvent.Type.RESOURCE_METHOD_START) { final Meter meter = this.meters.get(event.getUriInfo() .getMatchedResourceMethod().getInvocable().getDefinitionMethod()); if (meter != null) { meter.mark(); } } } } private static class ExceptionMeterRequestEventListener implements RequestEventListener { private final ConcurrentMap exceptionMeters; public ExceptionMeterRequestEventListener(final ConcurrentMap exceptionMeters) { this.exceptionMeters = exceptionMeters; } @Override public void onEvent(RequestEvent event) { if (event.getType() == RequestEvent.Type.ON_EXCEPTION) { final ResourceMethod method = event.getUriInfo().getMatchedResourceMethod(); final ExceptionMeterMetric metric = (method != null) ? this.exceptionMeters.get(method.getInvocable().getDefinitionMethod()) : null; if (metric != null) { if (metric.cause.isAssignableFrom(event.getException().getClass()) || (event.getException().getCause() != null && metric.cause.isAssignableFrom(event.getException().getCause().getClass()))) { metric.meter.mark(); } } } } } private static class ChainedRequestEventListener implements RequestEventListener { private final RequestEventListener[] listeners; private ChainedRequestEventListener(final RequestEventListener... listeners) { this.listeners = listeners; } @Override public void onEvent(final RequestEvent event) { for (RequestEventListener listener : listeners) { listener.onEvent(event); } } } @Override public void onEvent(ApplicationEvent event) { if (event.getType() == ApplicationEvent.Type.INITIALIZATION_APP_FINISHED) { registerMetricsForModel(event.getResourceModel()); } } @Override public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) { return resourceModel; } @Override public ResourceModel processSubResource(ResourceModel subResourceModel, Configuration configuration) { registerMetricsForModel(subResourceModel); return subResourceModel; } private void registerMetricsForModel(ResourceModel resourceModel) { for (final Resource resource : resourceModel.getResources()) { final Timed classLevelTimed = getClassLevelAnnotation(resource, Timed.class); final Metered classLevelMetered = getClassLevelAnnotation(resource, Metered.class); final ExceptionMetered classLevelExceptionMetered = getClassLevelAnnotation(resource, ExceptionMetered.class); for (final ResourceMethod method : resource.getAllMethods()) { registerTimedAnnotations(method, classLevelTimed); registerMeteredAnnotations(method, classLevelMetered); registerExceptionMeteredAnnotations(method, classLevelExceptionMetered); } for (final Resource childResource : resource.getChildResources()) { final Timed classLevelTimedChild = getClassLevelAnnotation(childResource, Timed.class); final Metered classLevelMeteredChild = getClassLevelAnnotation(childResource, Metered.class); final ExceptionMetered classLevelExceptionMeteredChild = getClassLevelAnnotation(childResource, ExceptionMetered.class); for (final ResourceMethod method : childResource.getAllMethods()) { registerTimedAnnotations(method, classLevelTimedChild); registerMeteredAnnotations(method, classLevelMeteredChild); registerExceptionMeteredAnnotations(method, classLevelExceptionMeteredChild); } } } } @Override public RequestEventListener onRequest(final RequestEvent event) { final RequestEventListener listener = new ChainedRequestEventListener( new TimerRequestEventListener(timers), new MeterRequestEventListener(meters), new ExceptionMeterRequestEventListener(exceptionMeters)); return listener; } private T getClassLevelAnnotation(final Resource resource, final Class annotationClazz) { T annotation = null; for (final Class clazz : resource.getHandlerClasses()) { annotation = clazz.getAnnotation(annotationClazz); if (annotation != null) { break; } } return annotation; } private void registerTimedAnnotations(final ResourceMethod method, final Timed classLevelTimed) { final Method definitionMethod = method.getInvocable().getDefinitionMethod(); if (classLevelTimed != null) { timers.putIfAbsent(definitionMethod, timerMetric(this.metrics, method, classLevelTimed)); return; } final Timed annotation = definitionMethod.getAnnotation(Timed.class); if (annotation != null) { timers.putIfAbsent(definitionMethod, timerMetric(this.metrics, method, annotation)); } } private void registerMeteredAnnotations(final ResourceMethod method, final Metered classLevelMetered) { final Method definitionMethod = method.getInvocable().getDefinitionMethod(); if (classLevelMetered != null) { meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, classLevelMetered)); return; } final Metered annotation = definitionMethod.getAnnotation(Metered.class); if (annotation != null) { meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, annotation)); } } private void registerExceptionMeteredAnnotations(final ResourceMethod method, final ExceptionMetered classLevelExceptionMetered) { final Method definitionMethod = method.getInvocable().getDefinitionMethod(); if (classLevelExceptionMetered != null) { exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, classLevelExceptionMetered)); return; } final ExceptionMetered annotation = definitionMethod.getAnnotation(ExceptionMetered.class); if (annotation != null) { exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, annotation)); } } private static Timer timerMetric(final MetricRegistry registry, final ResourceMethod method, final Timed timed) { final String name = chooseName(timed.name(), timed.absolute(), method); return registry.timer(name); } private static Meter meterMetric(final MetricRegistry registry, final ResourceMethod method, final Metered metered) { final String name = chooseName(metered.name(), metered.absolute(), method); return registry.meter(name); } protected static String chooseName(final String explicitName, final boolean absolute, final ResourceMethod method, final String... suffixes) { if (explicitName != null && !explicitName.isEmpty()) { if (absolute) { return explicitName; } return name(method.getInvocable().getDefinitionMethod().getDeclaringClass(), explicitName); } return name(name(method.getInvocable().getDefinitionMethod().getDeclaringClass(), method.getInvocable().getDefinitionMethod().getName()), suffixes); } } metrics-3.2.5/metrics-jersey2/src/main/java/com/codahale/metrics/jersey2/MetricsFeature.java000066400000000000000000000035071315671014200320130ustar00rootroot00000000000000package com.codahale.metrics.jersey2; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import javax.ws.rs.core.Feature; import javax.ws.rs.core.FeatureContext; /** * A {@link Feature} which registers a {@link InstrumentedResourceMethodApplicationListener} * for recording request events. */ public class MetricsFeature implements Feature { private final MetricRegistry registry; public MetricsFeature(MetricRegistry registry) { this.registry = registry; } public MetricsFeature(String registryName) { this(SharedMetricRegistries.getOrCreate(registryName)); } /** * A call-back method called when the feature is to be enabled in a given * runtime configuration scope. *

    * The responsibility of the feature is to properly update the supplied runtime configuration context * and return {@code true} if the feature was successfully enabled or {@code false} otherwise. *

    * Note that under some circumstances the feature may decide not to enable itself, which * is indicated by returning {@code false}. In such case the configuration context does * not add the feature to the collection of enabled features and a subsequent call to * {@link javax.ws.rs.core.Configuration#isEnabled(javax.ws.rs.core.Feature)} or * {@link javax.ws.rs.core.Configuration#isEnabled(Class)} method * would return {@code false}. *

    * * @param context configurable context in which the feature should be enabled. * @return {@code true} if the feature was successfully enabled, {@code false} * otherwise. */ @Override public boolean configure(FeatureContext context) { context.register(new InstrumentedResourceMethodApplicationListener(registry)); return true; } } metrics-3.2.5/metrics-jersey2/src/test/000077500000000000000000000000001315671014200177645ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/000077500000000000000000000000001315671014200207055ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/000077500000000000000000000000001315671014200214635ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/000077500000000000000000000000001315671014200232235ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200246715ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/000077500000000000000000000000001315671014200262545ustar00rootroot00000000000000SingletonMetricsExceptionMeteredPerClassJerseyTest.java000066400000000000000000000062441315671014200412030ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2package com.codahale.metrics.jersey2; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.jersey2.resources.InstrumentedResourceExceptionMeteredPerClass; import com.codahale.metrics.jersey2.resources.InstrumentedSubResourceExceptionMeteredPerClass; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Application; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; /** * Tests registering {@link InstrumentedResourceMethodApplicationListener} as a singleton * in a Jersey {@link ResourceConfig} */ public class SingletonMetricsExceptionMeteredPerClassJerseyTest extends JerseyTest { static { Logger.getLogger("org.glassfish.jersey").setLevel(Level.OFF); } private MetricRegistry registry; @Override protected Application configure() { this.registry = new MetricRegistry(); ResourceConfig config = new ResourceConfig(); config = config.register(new MetricsFeature(this.registry)); config = config.register(InstrumentedResourceExceptionMeteredPerClass.class); return config; } @Test public void exceptionMeteredMethodsAreExceptionMetered() { final Meter meter = registry.meter(name(InstrumentedResourceExceptionMeteredPerClass.class, "exceptionMetered", "exceptions")); assertThat(target("exception-metered") .request() .get(String.class)) .isEqualTo("fuh"); assertThat(meter.getCount()).isZero(); try { target("exception-metered") .queryParam("splode", true) .request() .get(String.class); failBecauseExceptionWasNotThrown(ProcessingException.class); } catch (ProcessingException e) { assertThat(e.getCause()).isInstanceOf(IOException.class); } assertThat(meter.getCount()).isEqualTo(1); } @Test public void subresourcesFromLocatorsRegisterMetrics() { final Meter meter = registry.meter(name(InstrumentedSubResourceExceptionMeteredPerClass.class, "exceptionMetered", "exceptions")); assertThat(target("subresource/exception-metered") .request() .get(String.class)) .isEqualTo("fuh"); assertThat(meter.getCount()).isZero(); try { target("subresource/exception-metered") .queryParam("splode", true) .request() .get(String.class); failBecauseExceptionWasNotThrown(ProcessingException.class); } catch (ProcessingException e) { assertThat(e.getCause()).isInstanceOf(IOException.class); } assertThat(meter.getCount()).isEqualTo(1); } } SingletonMetricsJerseyTest.java000066400000000000000000000075231315671014200343620ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2package com.codahale.metrics.jersey2; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jersey2.resources.InstrumentedResource; import com.codahale.metrics.jersey2.resources.InstrumentedSubResource; import org.glassfish.jersey.client.ClientResponse; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import javax.ws.rs.NotFoundException; import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Application; import javax.ws.rs.core.Response; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; /** * Tests registering {@link InstrumentedResourceMethodApplicationListener} as a singleton * in a Jersey {@link org.glassfish.jersey.server.ResourceConfig} */ public class SingletonMetricsJerseyTest extends JerseyTest { static { Logger.getLogger("org.glassfish.jersey").setLevel(Level.OFF); } private MetricRegistry registry; @Override protected Application configure() { this.registry = new MetricRegistry(); ResourceConfig config = new ResourceConfig(); config = config.register(new MetricsFeature(this.registry)); config = config.register(InstrumentedResource.class); return config; } @Test public void timedMethodsAreTimed() { assertThat(target("timed") .request() .get(String.class)) .isEqualTo("yay"); final Timer timer = registry.timer(name(InstrumentedResource.class, "timed")); assertThat(timer.getCount()).isEqualTo(1); } @Test public void meteredMethodsAreMetered() { assertThat(target("metered") .request() .get(String.class)) .isEqualTo("woo"); final Meter meter = registry.meter(name(InstrumentedResource.class, "metered")); assertThat(meter.getCount()).isEqualTo(1); } @Test public void exceptionMeteredMethodsAreExceptionMetered() { final Meter meter = registry.meter(name(InstrumentedResource.class, "exceptionMetered", "exceptions")); assertThat(target("exception-metered") .request() .get(String.class)) .isEqualTo("fuh"); assertThat(meter.getCount()).isZero(); try { target("exception-metered") .queryParam("splode", true) .request() .get(String.class); failBecauseExceptionWasNotThrown(ProcessingException.class); } catch (ProcessingException e) { assertThat(e.getCause()).isInstanceOf(IOException.class); } assertThat(meter.getCount()).isEqualTo(1); } @Test public void testResourceNotFound() { final Response response = target().path("not-found").request().get(); assertThat(response.getStatus()).isEqualTo(404); try { target().path("not-found").request().get(ClientResponse.class); failBecauseExceptionWasNotThrown(NotFoundException.class); } catch (NotFoundException e) { assertThat(e.getMessage()).isEqualTo("HTTP 404 Not Found"); } } @Test public void subresourcesFromLocatorsRegisterMetrics() { assertThat(target("subresource/timed") .request() .get(String.class)) .isEqualTo("yay"); final Timer timer = registry.timer(name(InstrumentedSubResource.class, "timed")); assertThat(timer.getCount()).isEqualTo(1); } } SingletonMetricsMeteredPerClassJerseyTest.java000066400000000000000000000040371315671014200373220ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2package com.codahale.metrics.jersey2; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.jersey2.resources.InstrumentedResourceMeteredPerClass; import com.codahale.metrics.jersey2.resources.InstrumentedSubResourceMeteredPerClass; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import javax.ws.rs.core.Application; import java.util.logging.Level; import java.util.logging.Logger; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; /** * Tests registering {@link InstrumentedResourceMethodApplicationListener} as a singleton * in a Jersey {@link ResourceConfig} */ public class SingletonMetricsMeteredPerClassJerseyTest extends JerseyTest { static { Logger.getLogger("org.glassfish.jersey").setLevel(Level.OFF); } private MetricRegistry registry; @Override protected Application configure() { this.registry = new MetricRegistry(); ResourceConfig config = new ResourceConfig(); config = config.register(new MetricsFeature(this.registry)); config = config.register(InstrumentedResourceMeteredPerClass.class); return config; } @Test public void meteredPerClassMethodsAreMetered() { assertThat(target("meteredPerClass") .request() .get(String.class)) .isEqualTo("yay"); final Meter meter = registry.meter(name(InstrumentedResourceMeteredPerClass.class, "meteredPerClass")); assertThat(meter.getCount()).isEqualTo(1); } @Test public void subresourcesFromLocatorsRegisterMetrics() { assertThat(target("subresource/meteredPerClass") .request() .get(String.class)) .isEqualTo("yay"); final Meter meter = registry.meter(name(InstrumentedSubResourceMeteredPerClass.class, "meteredPerClass")); assertThat(meter.getCount()).isEqualTo(1); } } SingletonMetricsTimedPerClassJerseyTest.java000066400000000000000000000040071315671014200367740ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2package com.codahale.metrics.jersey2; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.codahale.metrics.jersey2.resources.InstrumentedResourceTimedPerClass; import com.codahale.metrics.jersey2.resources.InstrumentedSubResourceTimedPerClass; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import javax.ws.rs.core.Application; import java.util.logging.Level; import java.util.logging.Logger; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; /** * Tests registering {@link InstrumentedResourceMethodApplicationListener} as a singleton * in a Jersey {@link ResourceConfig} */ public class SingletonMetricsTimedPerClassJerseyTest extends JerseyTest { static { Logger.getLogger("org.glassfish.jersey").setLevel(Level.OFF); } private MetricRegistry registry; @Override protected Application configure() { this.registry = new MetricRegistry(); ResourceConfig config = new ResourceConfig(); config = config.register(new MetricsFeature(this.registry)); config = config.register(InstrumentedResourceTimedPerClass.class); return config; } @Test public void timedPerClassMethodsAreTimed() { assertThat(target("timedPerClass") .request() .get(String.class)) .isEqualTo("yay"); final Timer timer = registry.timer(name(InstrumentedResourceTimedPerClass.class, "timedPerClass")); assertThat(timer.getCount()).isEqualTo(1); } @Test public void subresourcesFromLocatorsRegisterMetrics() { assertThat(target("subresource/timedPerClass") .request() .get(String.class)) .isEqualTo("yay"); final Timer timer = registry.timer(name(InstrumentedSubResourceTimedPerClass.class, "timedPerClass")); assertThat(timer.getCount()).isEqualTo(1); } } metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resources/000077500000000000000000000000001315671014200302665ustar00rootroot00000000000000InstrumentedResource.java000066400000000000000000000017661315671014200352550ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.ExceptionMetered; import com.codahale.metrics.annotation.Metered; import com.codahale.metrics.annotation.Timed; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.io.IOException; @Path("/") @Produces(MediaType.TEXT_PLAIN) public class InstrumentedResource { @GET @Timed @Path("/timed") public String timed() { return "yay"; } @GET @Metered @Path("/metered") public String metered() { return "woo"; } @GET @ExceptionMetered(cause = IOException.class) @Path("/exception-metered") public String exceptionMetered(@QueryParam("splode") @DefaultValue("false") boolean splode) throws IOException { if (splode) { throw new IOException("AUGH"); } return "fuh"; } @Path("/subresource") public InstrumentedSubResource locateSubResource() { return new InstrumentedSubResource(); } } InstrumentedResourceExceptionMeteredPerClass.java000066400000000000000000000014261315671014200420700ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.ExceptionMetered; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.io.IOException; @ExceptionMetered(cause = IOException.class) @Path("/") @Produces(MediaType.TEXT_PLAIN) public class InstrumentedResourceExceptionMeteredPerClass { @GET @Path("/exception-metered") public String exceptionMetered(@QueryParam("splode") @DefaultValue("false") boolean splode) throws IOException { if (splode) { throw new IOException("AUGH"); } return "fuh"; } @Path("/subresource") public InstrumentedSubResourceExceptionMeteredPerClass locateSubResource() { return new InstrumentedSubResourceExceptionMeteredPerClass(); } } InstrumentedResourceMeteredPerClass.java000066400000000000000000000011161315671014200402050ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.Metered; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Metered @Path("/") @Produces(MediaType.TEXT_PLAIN) public class InstrumentedResourceMeteredPerClass { @GET @Path("/meteredPerClass") public String meteredPerClass() { return "yay"; } @Path("/subresource") public InstrumentedSubResourceMeteredPerClass locateSubResource() { return new InstrumentedSubResourceMeteredPerClass(); } } InstrumentedResourceTimedPerClass.java000066400000000000000000000011001315671014200376530ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.Timed; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Timed @Path("/") @Produces(MediaType.TEXT_PLAIN) public class InstrumentedResourceTimedPerClass { @GET @Path("/timedPerClass") public String timedPerClass() { return "yay"; } @Path("/subresource") public InstrumentedSubResourceTimedPerClass locateSubResource() { return new InstrumentedSubResourceTimedPerClass(); } } InstrumentedSubResource.java000066400000000000000000000005401315671014200357140ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.Timed; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.io.IOException; @Produces(MediaType.TEXT_PLAIN) public class InstrumentedSubResource { @GET @Timed @Path("/timed") public String timed() { return "yay"; } } InstrumentedSubResourceExceptionMeteredPerClass.java000066400000000000000000000011241315671014200425350ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.ExceptionMetered; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.io.IOException; @ExceptionMetered(cause = IOException.class) @Produces(MediaType.TEXT_PLAIN) public class InstrumentedSubResourceExceptionMeteredPerClass { @GET @Path("/exception-metered") public String exceptionMetered(@QueryParam("splode") @DefaultValue("false") boolean splode) throws IOException { if (splode) { throw new IOException("AUGH"); } return "fuh"; } } InstrumentedSubResourceMeteredPerClass.java000066400000000000000000000006361315671014200406650ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.Metered; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Metered @Produces(MediaType.TEXT_PLAIN) public class InstrumentedSubResourceMeteredPerClass { @GET @Path("/meteredPerClass") public String meteredPerClass() { return "yay"; } } InstrumentedSubResourceTimedPerClass.java000066400000000000000000000006241315671014200403370ustar00rootroot00000000000000metrics-3.2.5/metrics-jersey2/src/test/java/com/codahale/metrics/jersey2/resourcespackage com.codahale.metrics.jersey2.resources; import com.codahale.metrics.annotation.Timed; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Timed @Produces(MediaType.TEXT_PLAIN) public class InstrumentedSubResourceTimedPerClass { @GET @Path("/timedPerClass") public String timedPerClass() { return "yay"; } } metrics-3.2.5/metrics-jetty8/000077500000000000000000000000001315671014200160625ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/pom.xml000066400000000000000000000022571315671014200174050ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jetty8 Metrics Integration for Jetty 8 bundle A set of extensions for Jetty 8 which provide instrumentation of thread pools, connector metrics, and application latency and utilization. io.dropwizard.metrics metrics-core ${project.version} org.eclipse.jetty jetty-server ${jetty8.version} metrics-3.2.5/metrics-jetty8/src/000077500000000000000000000000001315671014200166515ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/000077500000000000000000000000001315671014200175755ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/000077500000000000000000000000001315671014200205165ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/000077500000000000000000000000001315671014200212745ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/000077500000000000000000000000001315671014200230345ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200245025ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/000077500000000000000000000000001315671014200257315ustar00rootroot00000000000000InstrumentedBlockingChannelConnector.java000066400000000000000000000047341315671014200360230ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.BlockingChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedBlockingChannelConnector extends BlockingChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; private final Clock clock; public InstrumentedBlockingChannelConnector(MetricRegistry registry, int port, Clock clock) { super(); this.clock = clock; setPort(port); this.duration = registry.timer(name(BlockingChannelConnector.class, Integer.toString(port), "connection-duration")); this.accepts = registry.meter(name(BlockingChannelConnector.class, Integer.toString(port), "accepts")); this.connects = registry.meter(name(BlockingChannelConnector.class, Integer.toString(port), "connects")); this.disconnects = registry.meter(name(BlockingChannelConnector.class, Integer.toString(port), "disconnects")); this.connections = registry.counter(name(BlockingChannelConnector.class, Integer.toString(port), "active-connections")); } @Override public void accept(int acceptorID) throws IOException, InterruptedException { super.accept(acceptorID); accepts.mark(); } @Override protected void connectionOpened(Connection connection) { connections.inc(); super.connectionOpened(connection); connects.mark(); } @Override protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } } metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8/InstrumentedHandler.java000066400000000000000000000231131315671014200325530ustar00rootroot00000000000000package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.server.AsyncContinuation; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; import static org.eclipse.jetty.http.HttpMethods.*; /** * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler} * instance. */ public class InstrumentedHandler extends HandlerWrapper { private static final String PATCH = "PATCH"; private final Timer dispatches; private final Meter requests; private final Meter resumes; private final Meter suspends; private final Meter expires; private final Counter activeRequests; private final Counter activeSuspendedRequests; private final Counter activeDispatches; private final Meter[] responses; private final Timer getRequests, postRequests, headRequests, putRequests, deleteRequests, optionsRequests, traceRequests, connectRequests, patchRequests, otherRequests; private final ContinuationListener listener; /** * Create a new instrumented handler using a given metrics registry. The name of the metric will * be derived from the class of the Handler. * * @param registry the registry for the metrics * @param underlying the handler about which metrics will be collected */ public InstrumentedHandler(MetricRegistry registry, Handler underlying) { this(registry, underlying, name(underlying.getClass())); } /** * Create a new instrumented handler using a given metrics registry and a custom prefix. * * @param registry the registry for the metrics * @param underlying the handler about which metrics will be collected * @param prefix the prefix to use for the metrics names */ public InstrumentedHandler(MetricRegistry registry, Handler underlying, String prefix) { super(); this.dispatches = registry.timer(name(prefix, "dispatches")); this.requests = registry.meter(name(prefix, "requests")); this.resumes = registry.meter(name(prefix, "resumes")); this.suspends = registry.meter(name(prefix, "suspends")); this.expires = registry.meter(name(prefix, "expires")); this.activeRequests = registry.counter(name(prefix, "active-requests")); this.activeSuspendedRequests = registry.counter(name(prefix, "active-suspended-requests")); this.activeDispatches = registry.counter(name(prefix, "active-dispatches")); this.responses = new Meter[]{ registry.meter(name(prefix, "1xx-responses")), // 1xx registry.meter(name(prefix, "2xx-responses")), // 2xx registry.meter(name(prefix, "3xx-responses")), // 3xx registry.meter(name(prefix, "4xx-responses")), // 4xx registry.meter(name(prefix, "5xx-responses")) // 5xx }; registry.register(name(prefix, "percent-4xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getOneMinuteRate(), requests.getOneMinuteRate()); } }); registry.register(name(prefix, "percent-4xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); registry.register(name(prefix, "percent-4xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); registry.register(name(prefix, "percent-5xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getOneMinuteRate(), requests.getOneMinuteRate()); } }); registry.register(name(prefix, "percent-5xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); registry.register(name(prefix, "percent-5xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); this.listener = new ContinuationListener() { @Override public void onComplete(Continuation continuation) { final Request request = ((AsyncContinuation) continuation).getBaseRequest(); updateResponses(request); if (!continuation.isResumed()) { activeSuspendedRequests.dec(); } expires.mark(); } @Override public void onTimeout(Continuation continuation) { final Request request = ((AsyncContinuation) continuation).getBaseRequest(); updateResponses(request); if (!continuation.isResumed()) { activeSuspendedRequests.dec(); } } }; this.getRequests = registry.timer(name(prefix, "get-requests")); this.postRequests = registry.timer(name(prefix, "post-requests")); this.headRequests = registry.timer(name(prefix, "head-requests")); this.putRequests = registry.timer(name(prefix, "put-requests")); this.deleteRequests = registry.timer(name(prefix, "delete-requests")); this.optionsRequests = registry.timer(name(prefix, "options-requests")); this.traceRequests = registry.timer(name(prefix, "trace-requests")); this.connectRequests = registry.timer(name(prefix, "connect-requests")); this.patchRequests = registry.timer(name(prefix, "patch-requests")); this.otherRequests = registry.timer(name(prefix, "other-requests")); setHandler(underlying); } @Override public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { activeDispatches.inc(); final AsyncContinuation continuation = request.getAsyncContinuation(); final long start; final boolean isMilliseconds; if (continuation.isInitial()) { activeRequests.inc(); start = request.getTimeStamp(); isMilliseconds = true; } else { activeSuspendedRequests.dec(); if (continuation.isResumed()) { resumes.mark(); } isMilliseconds = false; start = System.nanoTime(); } try { super.handle(target, request, httpRequest, httpResponse); } finally { if (isMilliseconds) { final long duration = System.currentTimeMillis() - start; dispatches.update(duration, TimeUnit.MILLISECONDS); requestTimer(request.getMethod()).update(duration, TimeUnit.MILLISECONDS); } else { final long duration = System.nanoTime() - start; dispatches.update(duration, TimeUnit.NANOSECONDS); requestTimer(request.getMethod()).update(duration, TimeUnit.NANOSECONDS); } activeDispatches.dec(); if (continuation.isSuspended()) { if (continuation.isInitial()) { continuation.addContinuationListener(listener); } suspends.mark(); activeSuspendedRequests.inc(); } else if (continuation.isInitial()) { updateResponses(request); } } } private Timer requestTimer(String method) { if (GET.equalsIgnoreCase(method)) { return getRequests; } else if (POST.equalsIgnoreCase(method)) { return postRequests; } else if (PUT.equalsIgnoreCase(method)) { return putRequests; } else if (HEAD.equalsIgnoreCase(method)) { return headRequests; } else if (DELETE.equalsIgnoreCase(method)) { return deleteRequests; } else if (OPTIONS.equalsIgnoreCase(method)) { return optionsRequests; } else if (TRACE.equalsIgnoreCase(method)) { return traceRequests; } else if (CONNECT.equalsIgnoreCase(method)) { return connectRequests; } else if (PATCH.equalsIgnoreCase(method)) { return patchRequests; } return otherRequests; } private void updateResponses(Request request) { final int response = request.getResponse().getStatus() / 100; if (response >= 1 && response <= 5) { responses[response - 1].mark(); } activeRequests.dec(); requests.mark(); } } InstrumentedQueuedThreadPool.java000066400000000000000000000034651315671014200343410ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.RatioGauge; import org.eclipse.jetty.util.thread.QueuedThreadPool; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedQueuedThreadPool extends QueuedThreadPool { public InstrumentedQueuedThreadPool(MetricRegistry registry) { super(); registry.register(name(QueuedThreadPool.class, "percent-idle"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getIdleThreads(), getThreads()); } }); registry.register(name(QueuedThreadPool.class, "active-threads"), new Gauge() { @Override public Integer getValue() { return getThreads(); } }); registry.register(name(QueuedThreadPool.class, "idle-threads"), new Gauge() { @Override public Integer getValue() { return getIdleThreads(); } }); registry.register(name(QueuedThreadPool.class, "jobs"), new Gauge() { @Override public Integer getValue() { // This assumes the QueuedThreadPool is using a BlockingArrayQueue or // ArrayBlockingQueue for its queue, and is therefore a constant-time operation. return getQueue() != null ? getQueue().size() : 0; } }); registry.register(name(QueuedThreadPool.class, "utilization-max"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getThreads() - getIdleThreads(), getMaxThreads()); } }); } } InstrumentedSelectChannelConnector.java000066400000000000000000000046611315671014200355110ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.nio.SelectChannelConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSelectChannelConnector extends SelectChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; private final Clock clock; public InstrumentedSelectChannelConnector(MetricRegistry registry, int port, Clock clock) { super(); this.clock = clock; setPort(port); this.duration = registry.timer(name(SelectChannelConnector.class, Integer.toString(port), "connection-duration")); this.accepts = registry.meter(name(SelectChannelConnector.class, Integer.toString(port), "accepts")); this.connects = registry.meter(name(SelectChannelConnector.class, Integer.toString(port), "connects")); this.disconnects = registry.meter(name(SelectChannelConnector.class, Integer.toString(port), "disconnects")); this.connections = registry.counter(name(SelectChannelConnector.class, Integer.toString(port), "active-connections")); } @Override public void accept(int acceptorID) throws IOException { super.accept(acceptorID); accepts.mark(); } @Override protected void connectionOpened(Connection connection) { connections.inc(); super.connectionOpened(connection); connects.mark(); } @Override protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } } InstrumentedSocketConnector.java000066400000000000000000000045711315671014200342310ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.bio.SocketConnector; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSocketConnector extends SocketConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; private final Clock clock; public InstrumentedSocketConnector(MetricRegistry registry, int port, Clock clock) { super(); this.clock = clock; setPort(port); this.duration = registry.timer(name(SocketConnector.class, Integer.toString(port), "connection-duration")); this.accepts = registry.meter(name(SocketConnector.class, Integer.toString(port), "accepts")); this.connects = registry.meter(name(SocketConnector.class, Integer.toString(port), "connects")); this.disconnects = registry.meter(name(SocketConnector.class, Integer.toString(port), "disconnects")); this.connections = registry.counter(name(SocketConnector.class, Integer.toString(port), "active-connections")); } @Override public void accept(int acceptorID) throws IOException, InterruptedException { super.accept(acceptorID); accepts.mark(); } @Override protected void connectionOpened(Connection connection) { connections.inc(); super.connectionOpened(connection); connects.mark(); } @Override protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } } InstrumentedSslSelectChannelConnector.java000066400000000000000000000051321315671014200361650ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSslSelectChannelConnector extends SslSelectChannelConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; private final Clock clock; public InstrumentedSslSelectChannelConnector(MetricRegistry registry, int port, SslContextFactory factory, Clock clock) { super(factory); this.clock = clock; setPort(port); this.duration = registry.timer(name(SslSelectChannelConnector.class, Integer.toString(port), "connection-duration")); this.accepts = registry.meter(name(SslSelectChannelConnector.class, Integer.toString(port), "accepts")); this.connects = registry.meter(name(SslSelectChannelConnector.class, Integer.toString(port), "connects")); this.disconnects = registry.meter(name(SslSelectChannelConnector.class, Integer.toString(port), "disconnects")); this.connections = registry.counter(name(SslSelectChannelConnector.class, Integer.toString(port), "active-connections")); } @Override public void accept(int acceptorID) throws IOException { super.accept(acceptorID); accepts.mark(); } @Override protected void connectionOpened(Connection connection) { connections.inc(); super.connectionOpened(connection); connects.mark(); } @Override protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } } InstrumentedSslSocketConnector.java000066400000000000000000000050331315671014200347050ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty8/src/main/java/com/codahale/metrics/jetty8package com.codahale.metrics.jetty8; import com.codahale.metrics.*; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.ssl.SslSocketConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedSslSocketConnector extends SslSocketConnector { private final Timer duration; private final Meter accepts, connects, disconnects; private final Counter connections; private final Clock clock; public InstrumentedSslSocketConnector(MetricRegistry registry, int port, SslContextFactory factory, Clock clock) { super(factory); this.clock = clock; setPort(port); this.duration = registry.timer(name(SslSocketConnector.class, Integer.toString(port), "connection-duration")); this.accepts = registry.meter(name(SslSocketConnector.class, Integer.toString(port), "accepts")); this.connects = registry.meter(name(SslSocketConnector.class, Integer.toString(port), "connects")); this.disconnects = registry.meter(name(SslSocketConnector.class, Integer.toString(port), "disconnects")); this.connections = registry.counter(name(SslSocketConnector.class, Integer.toString(port), "active-connections")); } @Override public void accept(int acceptorID) throws IOException, InterruptedException { super.accept(acceptorID); accepts.mark(); } @Override protected void connectionOpened(Connection connection) { connections.inc(); super.connectionOpened(connection); connects.mark(); } @Override protected void connectionClosed(Connection connection) { super.connectionClosed(connection); disconnects.mark(); final long duration = clock.getTime() - connection.getTimeStamp(); this.duration.update(duration, TimeUnit.MILLISECONDS); connections.dec(); } } metrics-3.2.5/metrics-jetty9-legacy/000077500000000000000000000000001315671014200173255ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/pom.xml000066400000000000000000000035121315671014200206430ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jetty9-legacy Metrics Integration for Jetty 9.0 bundle A set of extensions for Jetty 9.0 which provide instrumentation of thread pools, connector metrics, and application latency and utilization. io.dropwizard.metrics metrics-core ${project.version} org.eclipse.jetty jetty-server ${jetty9.legacy.version} org.eclipse.jetty jetty-client ${jetty9.legacy.version} test org.apache.maven.plugins maven-compiler-plugin 3.1 1.7 1.7 metrics-3.2.5/metrics-jetty9-legacy/src/000077500000000000000000000000001315671014200201145ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/000077500000000000000000000000001315671014200210405ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/000077500000000000000000000000001315671014200217615ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/000077500000000000000000000000001315671014200225375ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/000077500000000000000000000000001315671014200242775ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200257455ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/metrics/jetty9/000077500000000000000000000000001315671014200271755ustar00rootroot00000000000000InstrumentedConnectionFactory.java000066400000000000000000000026051315671014200360150ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.Timer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.util.component.ContainerLifeCycle; public class InstrumentedConnectionFactory extends ContainerLifeCycle implements ConnectionFactory { private final ConnectionFactory connectionFactory; private final Timer timer; public InstrumentedConnectionFactory(ConnectionFactory connectionFactory, Timer timer) { this.connectionFactory = connectionFactory; this.timer = timer; addBean(connectionFactory); } @Override public String getProtocol() { return connectionFactory.getProtocol(); } @Override public Connection newConnection(Connector connector, EndPoint endPoint) { final Connection connection = connectionFactory.newConnection(connector, endPoint); connection.addListener(new Connection.Listener() { private Timer.Context context; @Override public void onOpened(Connection connection) { this.context = timer.time(); } @Override public void onClosed(Connection connection) { context.stop(); } }); return connection; } } InstrumentedHandler.java000066400000000000000000000250121315671014200337400ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.Counter; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.RatioGauge; import com.codahale.metrics.Timer; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.AsyncContextState; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpChannelState; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; /** * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler} * instance. */ public class InstrumentedHandler extends HandlerWrapper { private final MetricRegistry metricRegistry; private String name; private final String prefix; // the requests handled by this handler, excluding active private Timer requests; // the number of dispatches seen by this handler, excluding active private Timer dispatches; // the number of active requests private Counter activeRequests; // the number of active dispatches private Counter activeDispatches; // the number of requests currently suspended. private Counter activeSuspended; // the number of requests that have been asynchronously dispatched private Meter asyncDispatches; // the number of requests that expired while suspended private Meter asyncTimeouts; private Meter[] responses; private Timer getRequests; private Timer postRequests; private Timer headRequests; private Timer putRequests; private Timer deleteRequests; private Timer optionsRequests; private Timer traceRequests; private Timer connectRequests; private Timer moveRequests; private Timer otherRequests; private AsyncListener listener; /** * Create a new instrumented handler using a given metrics registry. * * @param registry the registry for the metrics * */ public InstrumentedHandler(MetricRegistry registry) { this(registry, null); } /** * Create a new instrumented handler using a given metrics registry. * * @param registry the registry for the metrics * @param prefix the prefix to use for the metrics names * */ public InstrumentedHandler(MetricRegistry registry, String prefix) { this.metricRegistry = registry; this.prefix = prefix; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected void doStart() throws Exception { super.doStart(); final String prefix = this.prefix == null ? name(getHandler().getClass(), name) : name(this.prefix, name); this.requests = metricRegistry.timer(name(prefix, "requests")); this.dispatches = metricRegistry.timer(name(prefix, "dispatches")); this.activeRequests = metricRegistry.counter(name(prefix, "active-requests")); this.activeDispatches = metricRegistry.counter(name(prefix, "active-dispatches")); this.activeSuspended = metricRegistry.counter(name(prefix, "active-suspended")); this.asyncDispatches = metricRegistry.meter(name(prefix, "async-dispatches")); this.asyncTimeouts = metricRegistry.meter(name(prefix, "async-timeouts")); this.responses = new Meter[]{ metricRegistry.meter(name(prefix, "1xx-responses")), // 1xx metricRegistry.meter(name(prefix, "2xx-responses")), // 2xx metricRegistry.meter(name(prefix, "3xx-responses")), // 3xx metricRegistry.meter(name(prefix, "4xx-responses")), // 4xx metricRegistry.meter(name(prefix, "5xx-responses")) // 5xx }; this.getRequests = metricRegistry.timer(name(prefix, "get-requests")); this.postRequests = metricRegistry.timer(name(prefix, "post-requests")); this.headRequests = metricRegistry.timer(name(prefix, "head-requests")); this.putRequests = metricRegistry.timer(name(prefix, "put-requests")); this.deleteRequests = metricRegistry.timer(name(prefix, "delete-requests")); this.optionsRequests = metricRegistry.timer(name(prefix, "options-requests")); this.traceRequests = metricRegistry.timer(name(prefix, "trace-requests")); this.connectRequests = metricRegistry.timer(name(prefix, "connect-requests")); this.moveRequests = metricRegistry.timer(name(prefix, "move-requests")); this.otherRequests = metricRegistry.timer(name(prefix, "other-requests")); metricRegistry.register(name(prefix, "percent-4xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getOneMinuteRate(), requests.getOneMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-4xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-4xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getOneMinuteRate(), requests.getOneMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); this.listener = new AsyncListener() { private long startTime; @Override public void onTimeout(AsyncEvent event) throws IOException { asyncTimeouts.mark(); } @Override public void onStartAsync(AsyncEvent event) throws IOException { startTime = System.currentTimeMillis(); event.getAsyncContext().addListener(this); } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onComplete(AsyncEvent event) throws IOException { final AsyncContextState state = (AsyncContextState) event.getAsyncContext(); final HttpServletRequest request = (HttpServletRequest) state.getRequest(); final HttpServletResponse response = (HttpServletResponse) state.getResponse(); updateResponses(request, response, startTime); if (!state.getHttpChannelState().isDispatched()) { activeSuspended.dec(); } } }; } @Override public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { activeDispatches.inc(); final long start; final HttpChannelState state = request.getHttpChannelState(); if (state.isInitial()) { // new request activeRequests.inc(); start = request.getTimeStamp(); } else { // resumed request start = System.currentTimeMillis(); activeSuspended.dec(); if (state.isDispatched()) { asyncDispatches.mark(); } } try { super.handle(path, request, httpRequest, httpResponse); } finally { final long now = System.currentTimeMillis(); final long dispatched = now - start; activeDispatches.dec(); dispatches.update(dispatched, TimeUnit.MILLISECONDS); if (state.isSuspended()) { if (state.isInitial()) { state.addListener(listener); } activeSuspended.inc(); } else if (state.isInitial()) { updateResponses(httpRequest, httpResponse, start); } // else onCompletion will handle it. } } private Timer requestTimer(String method) { final HttpMethod m = HttpMethod.fromString(method); if (m == null) { return otherRequests; } else { switch (m) { case GET: return getRequests; case POST: return postRequests; case PUT: return putRequests; case HEAD: return headRequests; case DELETE: return deleteRequests; case OPTIONS: return optionsRequests; case TRACE: return traceRequests; case CONNECT: return connectRequests; case MOVE: return moveRequests; default: return otherRequests; } } } private void updateResponses(HttpServletRequest request, HttpServletResponse response, long start) { final int responseStatus = response.getStatus() / 100; if (responseStatus >= 1 && responseStatus <= 5) { responses[responseStatus - 1].mark(); } activeRequests.dec(); final long elapsedTime = System.currentTimeMillis() - start; requests.update(elapsedTime, TimeUnit.MILLISECONDS); requestTimer(request.getMethod()).update(elapsedTime, TimeUnit.MILLISECONDS); } } InstrumentedQueuedThreadPool.java000066400000000000000000000063471315671014200356070ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/main/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.RatioGauge; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.thread.QueuedThreadPool; import java.util.concurrent.BlockingQueue; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedQueuedThreadPool extends QueuedThreadPool { private final MetricRegistry metricRegistry; public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry) { this(registry, 200); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads) { this(registry, maxThreads, 8); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads) { this(registry, maxThreads, minThreads, 60000); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout) { this(registry, maxThreads, minThreads, idleTimeout, null); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue queue) { super(maxThreads, minThreads, idleTimeout, queue); this.metricRegistry = registry; } @Override protected void doStart() throws Exception { super.doStart(); metricRegistry.register(name(QueuedThreadPool.class, getName(), "utilization"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getThreads() - getIdleThreads(), getThreads()); } }); metricRegistry.register(name(QueuedThreadPool.class, getName(), "utilization-max"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getThreads() - getIdleThreads(), getMaxThreads()); } }); metricRegistry.register(name(QueuedThreadPool.class, getName(), "size"), new Gauge() { @Override public Integer getValue() { return getThreads(); } }); metricRegistry.register(name(QueuedThreadPool.class, getName(), "jobs"), new Gauge() { @Override public Integer getValue() { // This assumes the QueuedThreadPool is using a BlockingArrayQueue or // ArrayBlockingQueue for its queue, and is therefore a constant-time operation. return getQueue().size(); } }); } } metrics-3.2.5/metrics-jetty9-legacy/src/test/000077500000000000000000000000001315671014200210735ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/000077500000000000000000000000001315671014200220145ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/000077500000000000000000000000001315671014200225725ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/codahale/000077500000000000000000000000001315671014200243325ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200260005ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/codahale/metrics/jetty9/000077500000000000000000000000001315671014200272305ustar00rootroot00000000000000InstrumentedConnectionFactoryTest.java000066400000000000000000000050221315671014200367040ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedConnectionFactoryTest { private final MetricRegistry registry = new MetricRegistry(); private final Server server = new Server(); private final ServerConnector connector = new ServerConnector(server, new InstrumentedConnectionFactory(new HttpConnectionFactory(), registry.timer("http.connections"))); private final HttpClient client = new HttpClient(); @Before public void setUp() throws Exception { server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { writer.println("OK"); } } }); server.addConnector(connector); server.start(); client.start(); } @After public void tearDown() throws Exception { server.stop(); client.stop(); } @Test public void instrumentsConnectionTimes() throws Exception { final ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/hello"); assertThat(response.getStatus()) .isEqualTo(200); client.stop(); // close the connection Thread.sleep(100); // make sure the connection is closed final Timer timer = registry.timer(name("http.connections")); assertThat(timer.getCount()) .isEqualTo(1); } } InstrumentedHandlerTest.java000066400000000000000000000105331315671014200346350ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9-legacy/src/test/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.MetricRegistry; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.DefaultHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedHandlerTest { private final HttpClient client = new HttpClient(); private final MetricRegistry registry = new MetricRegistry(); private final Server server = new Server(); private final ServerConnector connector = new ServerConnector(server); private final InstrumentedHandler handler = new InstrumentedHandler(registry); @Before public void setUp() throws Exception { handler.setName("handler"); handler.setHandler(new DefaultHandler()); server.addConnector(connector); server.setHandler(handler); server.start(); client.start(); } @After public void tearDown() throws Exception { server.stop(); client.stop(); } @Test public void hasAName() throws Exception { assertThat(handler.getName()) .isEqualTo("handler"); } @Test public void createsMetricsForTheHandler() throws Exception { final ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/hello"); assertThat(response.getStatus()) .isEqualTo(404); assertThat(registry.getNames()) .containsOnly( "org.eclipse.jetty.server.handler.DefaultHandler.handler.1xx-responses", "org.eclipse.jetty.server.handler.DefaultHandler.handler.2xx-responses", "org.eclipse.jetty.server.handler.DefaultHandler.handler.3xx-responses", "org.eclipse.jetty.server.handler.DefaultHandler.handler.4xx-responses", "org.eclipse.jetty.server.handler.DefaultHandler.handler.5xx-responses", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-4xx-1m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-4xx-5m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-4xx-15m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-5xx-1m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-5xx-5m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.percent-5xx-15m", "org.eclipse.jetty.server.handler.DefaultHandler.handler.requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.active-suspended", "org.eclipse.jetty.server.handler.DefaultHandler.handler.async-dispatches", "org.eclipse.jetty.server.handler.DefaultHandler.handler.async-timeouts", "org.eclipse.jetty.server.handler.DefaultHandler.handler.get-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.put-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.active-dispatches", "org.eclipse.jetty.server.handler.DefaultHandler.handler.trace-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.other-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.connect-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.dispatches", "org.eclipse.jetty.server.handler.DefaultHandler.handler.head-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.post-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.options-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.active-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.delete-requests", "org.eclipse.jetty.server.handler.DefaultHandler.handler.move-requests" ); } } metrics-3.2.5/metrics-jetty9/000077500000000000000000000000001315671014200160635ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/pom.xml000066400000000000000000000035131315671014200174020ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jetty9 Metrics Integration for Jetty 9.1 and higher bundle A set of extensions for Jetty 9.1 and higher which provide instrumentation of thread pools, connector metrics, and application latency and utilization. io.dropwizard.metrics metrics-core ${project.version} org.eclipse.jetty jetty-server ${jetty9.version} org.eclipse.jetty jetty-client ${jetty9.version} test org.apache.maven.plugins maven-compiler-plugin 3.1 1.7 1.7 metrics-3.2.5/metrics-jetty9/src/000077500000000000000000000000001315671014200166525ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/000077500000000000000000000000001315671014200175765ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/000077500000000000000000000000001315671014200205175ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/000077500000000000000000000000001315671014200212755ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/000077500000000000000000000000001315671014200230355ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200245035ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/000077500000000000000000000000001315671014200257335ustar00rootroot00000000000000InstrumentedConnectionFactory.java000066400000000000000000000042621315671014200345540ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.Timer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.util.component.ContainerLifeCycle; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; public class InstrumentedConnectionFactory extends ContainerLifeCycle implements ConnectionFactory { private final ConnectionFactory connectionFactory; private final Timer timer; private Method getProtocols; public InstrumentedConnectionFactory(ConnectionFactory connectionFactory, Timer timer) { this.connectionFactory = connectionFactory; this.timer = timer; addBean(connectionFactory); try { getProtocols = connectionFactory.getClass().getMethod("getProtocols"); } catch (NoSuchMethodException ignore) { getProtocols = null; } } @Override public String getProtocol() { return connectionFactory.getProtocol(); } @SuppressWarnings("unchecked") public List getProtocols() { try { return getProtocols != null ? (List) getProtocols.invoke(connectionFactory) : Collections.emptyList(); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Unable to invoke `connectionFactory#getProtocols`", e); } } @Override public Connection newConnection(Connector connector, EndPoint endPoint) { final Connection connection = connectionFactory.newConnection(connector, endPoint); connection.addListener(new Connection.Listener() { private Timer.Context context; @Override public void onOpened(Connection connection) { this.context = timer.time(); } @Override public void onClosed(Connection connection) { context.stop(); } }); return connection; } } metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9/InstrumentedHandler.java000066400000000000000000000250101315671014200325530ustar00rootroot00000000000000package com.codahale.metrics.jetty9; import com.codahale.metrics.Counter; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.RatioGauge; import com.codahale.metrics.Timer; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.AsyncContextState; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpChannelState; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.codahale.metrics.MetricRegistry.name; /** * A Jetty {@link Handler} which records various metrics about an underlying {@link Handler} * instance. */ public class InstrumentedHandler extends HandlerWrapper { private final MetricRegistry metricRegistry; private String name; private final String prefix; // the requests handled by this handler, excluding active private Timer requests; // the number of dispatches seen by this handler, excluding active private Timer dispatches; // the number of active requests private Counter activeRequests; // the number of active dispatches private Counter activeDispatches; // the number of requests currently suspended. private Counter activeSuspended; // the number of requests that have been asynchronously dispatched private Meter asyncDispatches; // the number of requests that expired while suspended private Meter asyncTimeouts; private Meter[] responses; private Timer getRequests; private Timer postRequests; private Timer headRequests; private Timer putRequests; private Timer deleteRequests; private Timer optionsRequests; private Timer traceRequests; private Timer connectRequests; private Timer moveRequests; private Timer otherRequests; private AsyncListener listener; /** * Create a new instrumented handler using a given metrics registry. * * @param registry the registry for the metrics * */ public InstrumentedHandler(MetricRegistry registry) { this(registry, null); } /** * Create a new instrumented handler using a given metrics registry. * * @param registry the registry for the metrics * @param prefix the prefix to use for the metrics names * */ public InstrumentedHandler(MetricRegistry registry, String prefix) { this.metricRegistry = registry; this.prefix = prefix; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected void doStart() throws Exception { super.doStart(); final String prefix = this.prefix == null ? name(getHandler().getClass(), name) : name(this.prefix, name); this.requests = metricRegistry.timer(name(prefix, "requests")); this.dispatches = metricRegistry.timer(name(prefix, "dispatches")); this.activeRequests = metricRegistry.counter(name(prefix, "active-requests")); this.activeDispatches = metricRegistry.counter(name(prefix, "active-dispatches")); this.activeSuspended = metricRegistry.counter(name(prefix, "active-suspended")); this.asyncDispatches = metricRegistry.meter(name(prefix, "async-dispatches")); this.asyncTimeouts = metricRegistry.meter(name(prefix, "async-timeouts")); this.responses = new Meter[]{ metricRegistry.meter(name(prefix, "1xx-responses")), // 1xx metricRegistry.meter(name(prefix, "2xx-responses")), // 2xx metricRegistry.meter(name(prefix, "3xx-responses")), // 3xx metricRegistry.meter(name(prefix, "4xx-responses")), // 4xx metricRegistry.meter(name(prefix, "5xx-responses")) // 5xx }; this.getRequests = metricRegistry.timer(name(prefix, "get-requests")); this.postRequests = metricRegistry.timer(name(prefix, "post-requests")); this.headRequests = metricRegistry.timer(name(prefix, "head-requests")); this.putRequests = metricRegistry.timer(name(prefix, "put-requests")); this.deleteRequests = metricRegistry.timer(name(prefix, "delete-requests")); this.optionsRequests = metricRegistry.timer(name(prefix, "options-requests")); this.traceRequests = metricRegistry.timer(name(prefix, "trace-requests")); this.connectRequests = metricRegistry.timer(name(prefix, "connect-requests")); this.moveRequests = metricRegistry.timer(name(prefix, "move-requests")); this.otherRequests = metricRegistry.timer(name(prefix, "other-requests")); metricRegistry.register(name(prefix, "percent-4xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getOneMinuteRate(), requests.getOneMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-4xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-4xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[3].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-1m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getOneMinuteRate(), requests.getOneMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-5m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFiveMinuteRate(), requests.getFiveMinuteRate()); } }); metricRegistry.register(name(prefix, "percent-5xx-15m"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(responses[4].getFifteenMinuteRate(), requests.getFifteenMinuteRate()); } }); this.listener = new AsyncListener() { private long startTime; @Override public void onTimeout(AsyncEvent event) throws IOException { asyncTimeouts.mark(); } @Override public void onStartAsync(AsyncEvent event) throws IOException { startTime = System.currentTimeMillis(); event.getAsyncContext().addListener(this); } @Override public void onError(AsyncEvent event) throws IOException { } @Override public void onComplete(AsyncEvent event) throws IOException { final AsyncContextState state = (AsyncContextState) event.getAsyncContext(); final HttpServletRequest request = (HttpServletRequest) state.getRequest(); final HttpServletResponse response = (HttpServletResponse) state.getResponse(); updateResponses(request, response, startTime); if (state.getHttpChannelState().getState() != HttpChannelState.State.DISPATCHED) { activeSuspended.dec(); } } }; } @Override public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { activeDispatches.inc(); final long start; final HttpChannelState state = request.getHttpChannelState(); if (state.isInitial()) { // new request activeRequests.inc(); start = request.getTimeStamp(); state.addListener(listener); } else { // resumed request start = System.currentTimeMillis(); activeSuspended.dec(); if (state.getState() == HttpChannelState.State.DISPATCHED) { asyncDispatches.mark(); } } try { super.handle(path, request, httpRequest, httpResponse); } finally { final long now = System.currentTimeMillis(); final long dispatched = now - start; activeDispatches.dec(); dispatches.update(dispatched, TimeUnit.MILLISECONDS); if (state.isSuspended()) { activeSuspended.inc(); } else if (state.isInitial()) { updateResponses(httpRequest, httpResponse, start); } // else onCompletion will handle it. } } private Timer requestTimer(String method) { final HttpMethod m = HttpMethod.fromString(method); if (m == null) { return otherRequests; } else { switch (m) { case GET: return getRequests; case POST: return postRequests; case PUT: return putRequests; case HEAD: return headRequests; case DELETE: return deleteRequests; case OPTIONS: return optionsRequests; case TRACE: return traceRequests; case CONNECT: return connectRequests; case MOVE: return moveRequests; default: return otherRequests; } } } private void updateResponses(HttpServletRequest request, HttpServletResponse response, long start) { final int responseStatus = response.getStatus() / 100; if (responseStatus >= 1 && responseStatus <= 5) { responses[responseStatus - 1].mark(); } activeRequests.dec(); final long elapsedTime = System.currentTimeMillis() - start; requests.update(elapsedTime, TimeUnit.MILLISECONDS); requestTimer(request.getMethod()).update(elapsedTime, TimeUnit.MILLISECONDS); } } InstrumentedQueuedThreadPool.java000066400000000000000000000077511315671014200343450ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/main/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.RatioGauge; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.thread.QueuedThreadPool; import java.util.concurrent.BlockingQueue; import static com.codahale.metrics.MetricRegistry.name; public class InstrumentedQueuedThreadPool extends QueuedThreadPool { private final MetricRegistry metricRegistry; private String prefix; public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry) { this(registry, 200); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads) { this(registry, maxThreads, 8); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads) { this(registry, maxThreads, minThreads, 60000); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout) { this(registry, maxThreads, minThreads, idleTimeout, null); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue queue) { this(registry, maxThreads, minThreads, idleTimeout, queue, null); } public InstrumentedQueuedThreadPool(@Name("registry") MetricRegistry registry, @Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("queue") BlockingQueue queue, @Name("prefix") String prefix) { super(maxThreads, minThreads, idleTimeout, queue); this.metricRegistry = registry; this.prefix = prefix; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } @Override protected void doStart() throws Exception { super.doStart(); final String prefix = this.prefix == null ? name(QueuedThreadPool.class, getName()) : name(this.prefix, getName()); metricRegistry.register(name(prefix, "utilization"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getThreads() - getIdleThreads(), getThreads()); } }); metricRegistry.register(name(prefix, "utilization-max"), new RatioGauge() { @Override protected Ratio getRatio() { return Ratio.of(getThreads() - getIdleThreads(), getMaxThreads()); } }); metricRegistry.register(name(prefix, "size"), new Gauge() { @Override public Integer getValue() { return getThreads(); } }); metricRegistry.register(name(prefix, "jobs"), new Gauge() { @Override public Integer getValue() { // This assumes the QueuedThreadPool is using a BlockingArrayQueue or // ArrayBlockingQueue for its queue, and is therefore a constant-time operation. return getQueue().size(); } }); } } metrics-3.2.5/metrics-jetty9/src/test/000077500000000000000000000000001315671014200176315ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/000077500000000000000000000000001315671014200205525ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/000077500000000000000000000000001315671014200213305ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/000077500000000000000000000000001315671014200230705ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200245365ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/000077500000000000000000000000001315671014200257665ustar00rootroot00000000000000InstrumentedConnectionFactoryTest.java000066400000000000000000000050221315671014200354420ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import static com.codahale.metrics.MetricRegistry.name; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedConnectionFactoryTest { private final MetricRegistry registry = new MetricRegistry(); private final Server server = new Server(); private final ServerConnector connector = new ServerConnector(server, new InstrumentedConnectionFactory(new HttpConnectionFactory(), registry.timer("http.connections"))); private final HttpClient client = new HttpClient(); @Before public void setUp() throws Exception { server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try (PrintWriter writer = response.getWriter()) { writer.println("OK"); } } }); server.addConnector(connector); server.start(); client.start(); } @After public void tearDown() throws Exception { server.stop(); client.stop(); } @Test public void instrumentsConnectionTimes() throws Exception { final ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/hello"); assertThat(response.getStatus()) .isEqualTo(200); client.stop(); // close the connection Thread.sleep(100); // make sure the connection is closed final Timer timer = registry.timer(name("http.connections")); assertThat(timer.getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9/InstrumentedHandlerTest.java000066400000000000000000000207101315671014200334500ustar00rootroot00000000000000package com.codahale.metrics.jetty9; import com.codahale.metrics.MetricRegistry; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedHandlerTest { private final HttpClient client = new HttpClient(); private final MetricRegistry registry = new MetricRegistry(); private final Server server = new Server(); private final ServerConnector connector = new ServerConnector(server); private final InstrumentedHandler handler = new InstrumentedHandler(registry); @Before public void setUp() throws Exception { handler.setName("handler"); handler.setHandler(new TestHandler()); server.addConnector(connector); server.setHandler(handler); server.start(); client.start(); } @After public void tearDown() throws Exception { server.stop(); client.stop(); } @Test public void hasAName() throws Exception { assertThat(handler.getName()) .isEqualTo("handler"); } @Test public void createsMetricsForTheHandler() throws Exception { final ContentResponse response = client.GET(uri("/hello")); assertThat(response.getStatus()) .isEqualTo(404); assertThat(registry.getNames()) .containsOnly( MetricRegistry.name(TestHandler.class,"handler.1xx-responses"), MetricRegistry.name(TestHandler.class,"handler.2xx-responses"), MetricRegistry.name(TestHandler.class,"handler.3xx-responses"), MetricRegistry.name(TestHandler.class,"handler.4xx-responses"), MetricRegistry.name(TestHandler.class,"handler.5xx-responses"), MetricRegistry.name(TestHandler.class,"handler.percent-4xx-1m"), MetricRegistry.name(TestHandler.class,"handler.percent-4xx-5m"), MetricRegistry.name(TestHandler.class,"handler.percent-4xx-15m"), MetricRegistry.name(TestHandler.class,"handler.percent-5xx-1m"), MetricRegistry.name(TestHandler.class,"handler.percent-5xx-5m"), MetricRegistry.name(TestHandler.class,"handler.percent-5xx-15m"), MetricRegistry.name(TestHandler.class,"handler.requests"), MetricRegistry.name(TestHandler.class,"handler.active-suspended"), MetricRegistry.name(TestHandler.class,"handler.async-dispatches"), MetricRegistry.name(TestHandler.class,"handler.async-timeouts"), MetricRegistry.name(TestHandler.class,"handler.get-requests"), MetricRegistry.name(TestHandler.class,"handler.put-requests"), MetricRegistry.name(TestHandler.class,"handler.active-dispatches"), MetricRegistry.name(TestHandler.class,"handler.trace-requests"), MetricRegistry.name(TestHandler.class,"handler.other-requests"), MetricRegistry.name(TestHandler.class,"handler.connect-requests"), MetricRegistry.name(TestHandler.class,"handler.dispatches"), MetricRegistry.name(TestHandler.class,"handler.head-requests"), MetricRegistry.name(TestHandler.class,"handler.post-requests"), MetricRegistry.name(TestHandler.class,"handler.options-requests"), MetricRegistry.name(TestHandler.class,"handler.active-requests"), MetricRegistry.name(TestHandler.class,"handler.delete-requests"), MetricRegistry.name(TestHandler.class,"handler.move-requests") ); } @Test public void responseTimesAreRecordedForBlockingResponses() throws Exception { final ContentResponse response = client.GET(uri("/blocking")); assertThat(response.getStatus()) .isEqualTo(200); assertResponseTimesValid(); } @Test public void responseTimesAreRecordedForAsyncResponses() throws Exception { final ContentResponse response = client.GET(uri("/async")); assertThat(response.getStatus()) .isEqualTo(200); assertResponseTimesValid(); } private void assertResponseTimesValid() { assertThat(registry.getMeters().get(metricName() + ".2xx-responses") .getCount()).isGreaterThan(0L); assertThat(registry.getTimers().get(metricName() + ".get-requests") .getSnapshot().getMedian()).isGreaterThan(0.0).isLessThan(TimeUnit.SECONDS.toNanos(1)); assertThat(registry.getTimers().get(metricName() + ".requests") .getSnapshot().getMedian()).isGreaterThan(0.0).isLessThan(TimeUnit.SECONDS.toNanos(1)); } private String uri(String path) { return "http://localhost:" + connector.getLocalPort() + path; } private String metricName() { return MetricRegistry.name(TestHandler.class.getName(), "handler"); } /** * test handler. * * Supports * * /blocking - uses the standard servlet api * /async - uses the 3.1 async api to complete the request * * all other requests will return 404 */ private static class TestHandler extends AbstractHandler { @Override public void handle( String path, Request request, final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse ) throws IOException, ServletException { request.setHandled(true); if (path.equals("/blocking")) { LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1)); httpServletResponse.setStatus(200); httpServletResponse.setContentType("text/plain"); httpServletResponse.getWriter().write("some content from the blocking request\n"); } else if (path.equals("/async")) { final AsyncContext context = request.startAsync(); Thread t = new Thread() { public void run() { LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1)); httpServletResponse.setStatus(200); httpServletResponse.setContentType("text/plain"); final ServletOutputStream servletOutputStream; try { servletOutputStream = httpServletResponse.getOutputStream(); servletOutputStream.setWriteListener( new WriteListener() { @Override public void onWritePossible() throws IOException { servletOutputStream.write("some content from the async\n".getBytes()); context.complete(); } @Override public void onError(Throwable throwable) { context.complete(); } } ); } catch (IOException e) { context.complete(); } } }; t.start(); } else { httpServletResponse.setStatus(404); httpServletResponse.setContentType("text/plain"); httpServletResponse.getWriter().write("Not Found\n"); } } } } InstrumentedQueuedThreadPoolTest.java000066400000000000000000000033321315671014200352270ustar00rootroot00000000000000metrics-3.2.5/metrics-jetty9/src/test/java/com/codahale/metrics/jetty9package com.codahale.metrics.jetty9; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricRegistry; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Test; import org.mockito.ArgumentCaptor; public class InstrumentedQueuedThreadPoolTest { private static final String PREFIX = "prefix"; private final MetricRegistry metricRegistry = mock(MetricRegistry.class); private final InstrumentedQueuedThreadPool iqtp = new InstrumentedQueuedThreadPool(metricRegistry); private final ArgumentCaptor metricNameCaptor = ArgumentCaptor.forClass(String.class); @After public void tearDown() throws Exception { iqtp.stop(); } @Test public void customMetricsPrefix() throws Exception{ iqtp.setPrefix(PREFIX); iqtp.doStart(); verify(metricRegistry, atLeastOnce()).register(metricNameCaptor.capture(), any(Metric.class)); String metricName = metricNameCaptor.getValue(); assertThat("Custom metric's prefix doesn't match", metricName, startsWith(PREFIX)); } @Test public void metricsPrefixBackwardCompatible() throws Exception{ iqtp.doStart(); verify(metricRegistry, atLeastOnce()).register(metricNameCaptor.capture(), any(Metric.class)); String metricName = metricNameCaptor.getValue(); assertThat("The default metrics prefix was changed", metricName, startsWith(QueuedThreadPool.class.getName())); } } metrics-3.2.5/metrics-json/000077500000000000000000000000001315671014200156045ustar00rootroot00000000000000metrics-3.2.5/metrics-json/pom.xml000066400000000000000000000025471315671014200171310ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-json Jackson Integration for Metrics bundle A set of Jackson modules which provide serializers for most Metrics classes. io.dropwizard.metrics metrics-core ${project.version} io.dropwizard.metrics metrics-healthchecks ${project.version} true com.fasterxml.jackson.core jackson-databind ${jackson.version} metrics-3.2.5/metrics-json/src/000077500000000000000000000000001315671014200163735ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/000077500000000000000000000000001315671014200173175ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/000077500000000000000000000000001315671014200202405ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/com/000077500000000000000000000000001315671014200210165ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/com/codahale/000077500000000000000000000000001315671014200225565ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200242245ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/com/codahale/metrics/json/000077500000000000000000000000001315671014200251755ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/main/java/com/codahale/metrics/json/HealthCheckModule.java000066400000000000000000000055571315671014200313650ustar00rootroot00000000000000package com.codahale.metrics.json; import com.codahale.metrics.health.HealthCheck; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleSerializers; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.Map; public class HealthCheckModule extends Module { private static class HealthCheckResultSerializer extends StdSerializer { private HealthCheckResultSerializer() { super(HealthCheck.Result.class); } @Override public void serialize(HealthCheck.Result result, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); json.writeBooleanField("healthy", result.isHealthy()); final String message = result.getMessage(); if (message != null) { json.writeStringField("message", message); } serializeThrowable(json, result.getError(), "error"); Map details = result.getDetails(); if (details != null && !details.isEmpty()) { Iterator> it = details.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = it.next(); json.writeObjectField(e.getKey(), e.getValue()); } } json.writeEndObject(); } private void serializeThrowable(JsonGenerator json, Throwable error, String name) throws IOException { if (error != null) { json.writeObjectFieldStart(name); json.writeStringField("message", error.getMessage()); json.writeArrayFieldStart("stack"); for (StackTraceElement element : error.getStackTrace()) { json.writeString(element.toString()); } json.writeEndArray(); if (error.getCause() != null) { serializeThrowable(json, error.getCause(), "cause"); } json.writeEndObject(); } } } @Override public String getModuleName() { return "healthchecks"; } @Override public Version version() { return MetricsModule.VERSION; } @Override public void setupModule(SetupContext context) { context.addSerializers(new SimpleSerializers(Arrays.>asList( new HealthCheckResultSerializer() ))); } } metrics-3.2.5/metrics-json/src/main/java/com/codahale/metrics/json/MetricsModule.java000066400000000000000000000232241315671014200306170ustar00rootroot00000000000000package com.codahale.metrics.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleSerializers; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.codahale.metrics.*; import java.io.IOException; import java.util.Arrays; import java.util.Locale; import java.util.concurrent.TimeUnit; public class MetricsModule extends Module { static final Version VERSION = new Version(3, 1, 3, "", "com.codahale.metrics", "metrics-json"); private static class GaugeSerializer extends StdSerializer { private GaugeSerializer() { super(Gauge.class); } @Override public void serialize(Gauge gauge, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); final Object value; try { value = gauge.getValue(); json.writeObjectField("value", value); } catch (RuntimeException e) { json.writeObjectField("error", e.toString()); } json.writeEndObject(); } } private static class CounterSerializer extends StdSerializer { private CounterSerializer() { super(Counter.class); } @Override public void serialize(Counter counter, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); json.writeNumberField("count", counter.getCount()); json.writeEndObject(); } } private static class HistogramSerializer extends StdSerializer { private final boolean showSamples; private HistogramSerializer(boolean showSamples) { super(Histogram.class); this.showSamples = showSamples; } @Override public void serialize(Histogram histogram, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); final Snapshot snapshot = histogram.getSnapshot(); json.writeNumberField("count", histogram.getCount()); json.writeNumberField("max", snapshot.getMax()); json.writeNumberField("mean", snapshot.getMean()); json.writeNumberField("min", snapshot.getMin()); json.writeNumberField("p50", snapshot.getMedian()); json.writeNumberField("p75", snapshot.get75thPercentile()); json.writeNumberField("p95", snapshot.get95thPercentile()); json.writeNumberField("p98", snapshot.get98thPercentile()); json.writeNumberField("p99", snapshot.get99thPercentile()); json.writeNumberField("p999", snapshot.get999thPercentile()); if (showSamples) { json.writeObjectField("values", snapshot.getValues()); } json.writeNumberField("stddev", snapshot.getStdDev()); json.writeEndObject(); } } private static class MeterSerializer extends StdSerializer { private final String rateUnit; private final double rateFactor; public MeterSerializer(TimeUnit rateUnit) { super(Meter.class); this.rateFactor = rateUnit.toSeconds(1); this.rateUnit = calculateRateUnit(rateUnit, "events"); } @Override public void serialize(Meter meter, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); json.writeNumberField("count", meter.getCount()); json.writeNumberField("m15_rate", meter.getFifteenMinuteRate() * rateFactor); json.writeNumberField("m1_rate", meter.getOneMinuteRate() * rateFactor); json.writeNumberField("m5_rate", meter.getFiveMinuteRate() * rateFactor); json.writeNumberField("mean_rate", meter.getMeanRate() * rateFactor); json.writeStringField("units", rateUnit); json.writeEndObject(); } } private static class TimerSerializer extends StdSerializer { private final String rateUnit; private final double rateFactor; private final String durationUnit; private final double durationFactor; private final boolean showSamples; private TimerSerializer(TimeUnit rateUnit, TimeUnit durationUnit, boolean showSamples) { super(Timer.class); this.rateUnit = calculateRateUnit(rateUnit, "calls"); this.rateFactor = rateUnit.toSeconds(1); this.durationUnit = durationUnit.toString().toLowerCase(Locale.US); this.durationFactor = 1.0 / durationUnit.toNanos(1); this.showSamples = showSamples; } @Override public void serialize(Timer timer, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); final Snapshot snapshot = timer.getSnapshot(); json.writeNumberField("count", timer.getCount()); json.writeNumberField("max", snapshot.getMax() * durationFactor); json.writeNumberField("mean", snapshot.getMean() * durationFactor); json.writeNumberField("min", snapshot.getMin() * durationFactor); json.writeNumberField("p50", snapshot.getMedian() * durationFactor); json.writeNumberField("p75", snapshot.get75thPercentile() * durationFactor); json.writeNumberField("p95", snapshot.get95thPercentile() * durationFactor); json.writeNumberField("p98", snapshot.get98thPercentile() * durationFactor); json.writeNumberField("p99", snapshot.get99thPercentile() * durationFactor); json.writeNumberField("p999", snapshot.get999thPercentile() * durationFactor); if (showSamples) { final long[] values = snapshot.getValues(); final double[] scaledValues = new double[values.length]; for (int i = 0; i < values.length; i++) { scaledValues[i] = values[i] * durationFactor; } json.writeObjectField("values", scaledValues); } json.writeNumberField("stddev", snapshot.getStdDev() * durationFactor); json.writeNumberField("m15_rate", timer.getFifteenMinuteRate() * rateFactor); json.writeNumberField("m1_rate", timer.getOneMinuteRate() * rateFactor); json.writeNumberField("m5_rate", timer.getFiveMinuteRate() * rateFactor); json.writeNumberField("mean_rate", timer.getMeanRate() * rateFactor); json.writeStringField("duration_units", durationUnit); json.writeStringField("rate_units", rateUnit); json.writeEndObject(); } } private static class MetricRegistrySerializer extends StdSerializer { private final MetricFilter filter; private MetricRegistrySerializer(MetricFilter filter) { super(MetricRegistry.class); this.filter = filter; } @Override public void serialize(MetricRegistry registry, JsonGenerator json, SerializerProvider provider) throws IOException { json.writeStartObject(); json.writeStringField("version", VERSION.toString()); json.writeObjectField("gauges", registry.getGauges(filter)); json.writeObjectField("counters", registry.getCounters(filter)); json.writeObjectField("histograms", registry.getHistograms(filter)); json.writeObjectField("meters", registry.getMeters(filter)); json.writeObjectField("timers", registry.getTimers(filter)); json.writeEndObject(); } } private final TimeUnit rateUnit; private final TimeUnit durationUnit; private final boolean showSamples; private final MetricFilter filter; public MetricsModule(TimeUnit rateUnit, TimeUnit durationUnit, boolean showSamples) { this(rateUnit, durationUnit, showSamples, MetricFilter.ALL); } public MetricsModule(TimeUnit rateUnit, TimeUnit durationUnit, boolean showSamples, MetricFilter filter) { this.rateUnit = rateUnit; this.durationUnit = durationUnit; this.showSamples = showSamples; this.filter = filter; } @Override public String getModuleName() { return "metrics"; } @Override public Version version() { return VERSION; } @Override public void setupModule(SetupContext context) { context.addSerializers(new SimpleSerializers(Arrays.>asList( new GaugeSerializer(), new CounterSerializer(), new HistogramSerializer(showSamples), new MeterSerializer(rateUnit), new TimerSerializer(rateUnit, durationUnit, showSamples), new MetricRegistrySerializer(filter) ))); } private static String calculateRateUnit(TimeUnit unit, String name) { final String s = unit.toString().toLowerCase(Locale.US); return name + '/' + s.substring(0, s.length() - 1); } } metrics-3.2.5/metrics-json/src/test/000077500000000000000000000000001315671014200173525ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/000077500000000000000000000000001315671014200202735ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/com/000077500000000000000000000000001315671014200210515ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/com/codahale/000077500000000000000000000000001315671014200226115ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200242575ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/com/codahale/metrics/json/000077500000000000000000000000001315671014200252305ustar00rootroot00000000000000metrics-3.2.5/metrics-json/src/test/java/com/codahale/metrics/json/HealthCheckModuleTest.java000066400000000000000000000115741315671014200322540ustar00rootroot00000000000000package com.codahale.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; import com.codahale.metrics.health.HealthCheck; import org.junit.Test; import java.math.BigDecimal; import java.math.BigInteger; import java.util.LinkedHashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class HealthCheckModuleTest { private final ObjectMapper mapper = new ObjectMapper().registerModule(new HealthCheckModule()); @Test public void serializesAHealthyResult() throws Exception { assertThat(mapper.writeValueAsString(HealthCheck.Result.healthy())) .isEqualTo("{\"healthy\":true}"); } @Test public void serializesAHealthyResultWithAMessage() throws Exception { assertThat(mapper.writeValueAsString(HealthCheck.Result.healthy("yay for %s", "me"))) .isEqualTo("{" + "\"healthy\":true," + "\"message\":\"yay for me\"}"); } @Test public void serializesAnUnhealthyResult() throws Exception { assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy("boo"))) .isEqualTo("{" + "\"healthy\":false," + "\"message\":\"boo\"}"); } @Test public void serializesAnUnhealthyResultWithAnException() throws Exception { final Throwable e = mock(Throwable.class); when(e.getMessage()).thenReturn("oh no"); when(e.getStackTrace()).thenReturn(new StackTraceElement[]{ new StackTraceElement("Blah", "bloo", "Blah.java", 100) }); assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy(e))) .isEqualTo("{" + "\"healthy\":false," + "\"message\":\"oh no\"," + "\"error\":{" + "\"message\":\"oh no\"," + "\"stack\":[\"Blah.bloo(Blah.java:100)\"]" + "}" + "}"); } @Test public void serializesAnUnhealthyResultWithNestedExceptions() throws Exception { final Throwable a = mock(Throwable.class); when(a.getMessage()).thenReturn("oh no"); when(a.getStackTrace()).thenReturn(new StackTraceElement[]{ new StackTraceElement("Blah", "bloo", "Blah.java", 100) }); final Throwable b = mock(Throwable.class); when(b.getMessage()).thenReturn("oh well"); when(b.getStackTrace()).thenReturn(new StackTraceElement[]{ new StackTraceElement("Blah", "blee", "Blah.java", 150) }); when(b.getCause()).thenReturn(a); assertThat(mapper.writeValueAsString(HealthCheck.Result.unhealthy(b))) .isEqualTo("{" + "\"healthy\":false," + "\"message\":\"oh well\"," + "\"error\":{" + "\"message\":\"oh well\"," + "\"stack\":[\"Blah.blee(Blah.java:150)\"]," + "\"cause\":{" + "\"message\":\"oh no\"," + "\"stack\":[\"Blah.bloo(Blah.java:100)\"]" + "}" + "}" + "}"); } @Test public void serializeResultWithDetail() throws Exception { Map complex = new LinkedHashMap(); complex.put("field", "value"); HealthCheck.Result result = HealthCheck.Result.builder() .healthy() .withDetail("boolean", true) .withDetail("integer", 1) .withDetail("long", 2L) .withDetail("float", 3.546F) .withDetail("double", 4.567D) .withDetail("BigInteger", new BigInteger("12345")) .withDetail("BigDecimal", new BigDecimal("12345.56789")) .withDetail("String", "string") .withDetail("complex", complex) .build(); assertThat(mapper.writeValueAsString(result)) .isEqualTo("{" + "\"healthy\":true," + "\"boolean\":true," + "\"integer\":1," + "\"long\":2," + "\"float\":3.546," + "\"double\":4.567," + "\"BigInteger\":12345," + "\"BigDecimal\":12345.56789," + "\"String\":\"string\"," + "\"complex\":{" + "\"field\":\"value\"" + "}" + "}"); } } metrics-3.2.5/metrics-json/src/test/java/com/codahale/metrics/json/MetricsModuleTest.java000066400000000000000000000224521315671014200315140ustar00rootroot00000000000000package com.codahale.metrics.json; import com.fasterxml.jackson.databind.ObjectMapper; import com.codahale.metrics.*; import org.junit.Test; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MetricsModuleTest { private final ObjectMapper mapper = new ObjectMapper().registerModule( new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, false, MetricFilter.ALL)); @Test public void serializesGauges() throws Exception { final Gauge gauge = new Gauge() { @Override public Integer getValue() { return 100; } }; assertThat(mapper.writeValueAsString(gauge)) .isEqualTo("{\"value\":100}"); } @Test public void serializesGaugesThatThrowExceptions() throws Exception { final Gauge gauge = new Gauge() { @Override public Integer getValue() { throw new IllegalArgumentException("poops"); } }; assertThat(mapper.writeValueAsString(gauge)) .isEqualTo("{\"error\":\"java.lang.IllegalArgumentException: poops\"}"); } @Test public void serializesCounters() throws Exception { final Counter counter = mock(Counter.class); when(counter.getCount()).thenReturn(100L); assertThat(mapper.writeValueAsString(counter)) .isEqualTo("{\"count\":100}"); } @Test public void serializesHistograms() throws Exception { final Histogram histogram = mock(Histogram.class); when(histogram.getCount()).thenReturn(1L); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(2L); when(snapshot.getMean()).thenReturn(3.0); when(snapshot.getMin()).thenReturn(4L); when(snapshot.getStdDev()).thenReturn(5.0); when(snapshot.getMedian()).thenReturn(6.0); when(snapshot.get75thPercentile()).thenReturn(7.0); when(snapshot.get95thPercentile()).thenReturn(8.0); when(snapshot.get98thPercentile()).thenReturn(9.0); when(snapshot.get99thPercentile()).thenReturn(10.0); when(snapshot.get999thPercentile()).thenReturn(11.0); when(snapshot.getValues()).thenReturn(new long[]{ 1, 2, 3 }); when(histogram.getSnapshot()).thenReturn(snapshot); assertThat(mapper.writeValueAsString(histogram)) .isEqualTo("{" + "\"count\":1," + "\"max\":2," + "\"mean\":3.0," + "\"min\":4," + "\"p50\":6.0," + "\"p75\":7.0," + "\"p95\":8.0," + "\"p98\":9.0," + "\"p99\":10.0," + "\"p999\":11.0," + "\"stddev\":5.0}"); final ObjectMapper fullMapper = new ObjectMapper().registerModule( new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true, MetricFilter.ALL)); assertThat(fullMapper.writeValueAsString(histogram)) .isEqualTo("{" + "\"count\":1," + "\"max\":2," + "\"mean\":3.0," + "\"min\":4," + "\"p50\":6.0," + "\"p75\":7.0," + "\"p95\":8.0," + "\"p98\":9.0," + "\"p99\":10.0," + "\"p999\":11.0," + "\"values\":[1,2,3]," + "\"stddev\":5.0}"); } @Test public void serializesMeters() throws Exception { final Meter meter = mock(Meter.class); when(meter.getCount()).thenReturn(1L); when(meter.getMeanRate()).thenReturn(2.0); when(meter.getOneMinuteRate()).thenReturn(5.0); when(meter.getFiveMinuteRate()).thenReturn(4.0); when(meter.getFifteenMinuteRate()).thenReturn(3.0); assertThat(mapper.writeValueAsString(meter)) .isEqualTo("{" + "\"count\":1," + "\"m15_rate\":3.0," + "\"m1_rate\":5.0," + "\"m5_rate\":4.0," + "\"mean_rate\":2.0," + "\"units\":\"events/second\"}"); } @Test public void serializesTimers() throws Exception { final Timer timer = mock(Timer.class); when(timer.getCount()).thenReturn(1L); when(timer.getMeanRate()).thenReturn(2.0); when(timer.getOneMinuteRate()).thenReturn(3.0); when(timer.getFiveMinuteRate()).thenReturn(4.0); when(timer.getFifteenMinuteRate()).thenReturn(5.0); final Snapshot snapshot = mock(Snapshot.class); when(snapshot.getMax()).thenReturn(TimeUnit.MILLISECONDS.toNanos(100)); when(snapshot.getMean()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(200)); when(snapshot.getMin()).thenReturn(TimeUnit.MILLISECONDS.toNanos(300)); when(snapshot.getStdDev()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(400)); when(snapshot.getMedian()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(500)); when(snapshot.get75thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(600)); when(snapshot.get95thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(700)); when(snapshot.get98thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(800)); when(snapshot.get99thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(900)); when(snapshot.get999thPercentile()).thenReturn((double) TimeUnit.MILLISECONDS.toNanos(1000)); when(snapshot.getValues()).thenReturn(new long[]{ TimeUnit.MILLISECONDS.toNanos(1), TimeUnit.MILLISECONDS.toNanos(2), TimeUnit.MILLISECONDS.toNanos(3) }); when(timer.getSnapshot()).thenReturn(snapshot); assertThat(mapper.writeValueAsString(timer)) .isEqualTo("{" + "\"count\":1," + "\"max\":100.0," + "\"mean\":200.0," + "\"min\":300.0," + "\"p50\":500.0," + "\"p75\":600.0," + "\"p95\":700.0," + "\"p98\":800.0," + "\"p99\":900.0," + "\"p999\":1000.0," + "\"stddev\":400.0," + "\"m15_rate\":5.0," + "\"m1_rate\":3.0," + "\"m5_rate\":4.0," + "\"mean_rate\":2.0," + "\"duration_units\":\"milliseconds\"," + "\"rate_units\":\"calls/second\"}"); final ObjectMapper fullMapper = new ObjectMapper().registerModule( new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true, MetricFilter.ALL)); assertThat(fullMapper.writeValueAsString(timer)) .isEqualTo("{" + "\"count\":1," + "\"max\":100.0," + "\"mean\":200.0," + "\"min\":300.0," + "\"p50\":500.0," + "\"p75\":600.0," + "\"p95\":700.0," + "\"p98\":800.0," + "\"p99\":900.0," + "\"p999\":1000.0," + "\"values\":[1.0,2.0,3.0]," + "\"stddev\":400.0," + "\"m15_rate\":5.0," + "\"m1_rate\":3.0," + "\"m5_rate\":4.0," + "\"mean_rate\":2.0," + "\"duration_units\":\"milliseconds\"," + "\"rate_units\":\"calls/second\"}"); } @Test public void serializesMetricRegistries() throws Exception { final MetricRegistry registry = new MetricRegistry(); assertThat(mapper.writeValueAsString(registry)) .isEqualTo("{" + "\"version\":\"3.1.3\"," + "\"gauges\":{}," + "\"counters\":{}," + "\"histograms\":{}," + "\"meters\":{}," + "\"timers\":{}}"); } } metrics-3.2.5/metrics-jvm/000077500000000000000000000000001315671014200154275ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/pom.xml000066400000000000000000000017071315671014200167510ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-jvm JVM Integration for Metrics bundle A set of classes which allow you to monitor critical aspects of your Java Virtual Machine using Metrics. io.dropwizard.metrics metrics-core ${project.version} metrics-3.2.5/metrics-jvm/src/000077500000000000000000000000001315671014200162165ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/000077500000000000000000000000001315671014200171425ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/000077500000000000000000000000001315671014200200635ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/com/000077500000000000000000000000001315671014200206415ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/000077500000000000000000000000001315671014200224015ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200240475ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/000077500000000000000000000000001315671014200246435ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/BufferPoolMetricSet.java000066400000000000000000000040011315671014200313640ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.JmxAttributeGauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the count, usage, and capacity of the JVM's direct and mapped buffer pools. *

    * These JMX objects are only available on Java 7 and above. */ public class BufferPoolMetricSet implements MetricSet { private static final Logger LOGGER = LoggerFactory.getLogger(BufferPoolMetricSet.class); private static final String[] ATTRIBUTES = { "Count", "MemoryUsed", "TotalCapacity" }; private static final String[] NAMES = { "count", "used", "capacity" }; private static final String[] POOLS = { "direct", "mapped" }; private final MBeanServer mBeanServer; public BufferPoolMetricSet(MBeanServer mBeanServer) { this.mBeanServer = mBeanServer; } @Override public Map getMetrics() { final Map gauges = new HashMap(); for (String pool : POOLS) { for (int i = 0; i < ATTRIBUTES.length; i++) { final String attribute = ATTRIBUTES[i]; final String name = NAMES[i]; try { final ObjectName on = new ObjectName("java.nio:type=BufferPool,name=" + pool); mBeanServer.getMBeanInfo(on); gauges.put(name(pool, name), new JmxAttributeGauge(mBeanServer, on, attribute)); } catch (JMException ignored) { LOGGER.debug("Unable to load buffer pool MBeans, possibly running on Java 6"); } } } return Collections.unmodifiableMap(gauges); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/CachedThreadStatesGaugeSet.java000066400000000000000000000034551315671014200326250ustar00rootroot00000000000000package com.codahale.metrics.jvm; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.concurrent.TimeUnit; import com.codahale.metrics.CachedGauge; /** * A variation of ThreadStatesGaugeSet that caches the ThreadInfo[] objects for * a given interval. */ public class CachedThreadStatesGaugeSet extends ThreadStatesGaugeSet { private final CachedGauge threadInfo; /** * Creates a new set of gauges using the given MXBean and detector. * Caches the information for the given interval and time unit. * * @param threadMXBean a thread MXBean * @param deadlockDetector a deadlock detector * @param interval cache interval * @param unit cache interval time unit */ public CachedThreadStatesGaugeSet(final ThreadMXBean threadMXBean, ThreadDeadlockDetector deadlockDetector, long interval, TimeUnit unit) { super(threadMXBean, deadlockDetector); threadInfo = new CachedGauge(interval, unit) { @Override protected ThreadInfo[] loadValue() { return CachedThreadStatesGaugeSet.super.getThreadInfo(); } }; } /** * Creates a new set of gauges using the default MXBeans. * Caches the information for the given interval and time unit. * @param interval cache interval * @param unit cache interval time unit */ public CachedThreadStatesGaugeSet(long interval, TimeUnit unit) { this(ManagementFactory.getThreadMXBean(), new ThreadDeadlockDetector(), interval, unit); } @Override ThreadInfo[] getThreadInfo() { return threadInfo.getValue(); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ClassLoadingGaugeSet.java000066400000000000000000000022551315671014200315020ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import java.lang.management.ClassLoadingMXBean; import java.lang.management.ManagementFactory; import java.util.HashMap; import java.util.Map; /** * A set of gauges for JVM classloader usage. */ public class ClassLoadingGaugeSet implements MetricSet { private final ClassLoadingMXBean mxBean; public ClassLoadingGaugeSet() { this(ManagementFactory.getClassLoadingMXBean()); } public ClassLoadingGaugeSet(ClassLoadingMXBean mxBean) { this.mxBean = mxBean; } @Override public Map getMetrics() { final Map gauges = new HashMap(); gauges.put("loaded", new Gauge() { @Override public Long getValue() { return mxBean.getTotalLoadedClassCount(); } }); gauges.put("unloaded", new Gauge() { @Override public Long getValue() { return mxBean.getUnloadedClassCount(); } }); return gauges; } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/FileDescriptorRatioGauge.java000066400000000000000000000031121315671014200323710ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.RatioGauge; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * A gauge for the ratio of used to total file descriptors. */ public class FileDescriptorRatioGauge extends RatioGauge { private final OperatingSystemMXBean os; /** * Creates a new gauge using the platform OS bean. */ public FileDescriptorRatioGauge() { this(ManagementFactory.getOperatingSystemMXBean()); } /** * Creates a new gauge using the given OS bean. * * @param os an {@link OperatingSystemMXBean} */ public FileDescriptorRatioGauge(OperatingSystemMXBean os) { this.os = os; } @Override protected Ratio getRatio() { try { return Ratio.of(invoke("getOpenFileDescriptorCount"), invoke("getMaxFileDescriptorCount")); } catch (NoSuchMethodException e) { return Ratio.of(Double.NaN, Double.NaN); } catch (IllegalAccessException e) { return Ratio.of(Double.NaN, Double.NaN); } catch (InvocationTargetException e) { return Ratio.of(Double.NaN, Double.NaN); } } private long invoke(String name) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { final Method method = os.getClass().getDeclaredMethod(name); method.setAccessible(true); return (Long) method.invoke(os); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/GarbageCollectorMetricSet.java000066400000000000000000000037021315671014200325270ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.*; import java.util.regex.Pattern; import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the counts and elapsed times of garbage collections. */ public class GarbageCollectorMetricSet implements MetricSet { private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); private final List garbageCollectors; /** * Creates a new set of gauges for all discoverable garbage collectors. */ public GarbageCollectorMetricSet() { this(ManagementFactory.getGarbageCollectorMXBeans()); } /** * Creates a new set of gauges for the given collection of garbage collectors. * * @param garbageCollectors the garbage collectors */ public GarbageCollectorMetricSet(Collection garbageCollectors) { this.garbageCollectors = new ArrayList(garbageCollectors); } @Override public Map getMetrics() { final Map gauges = new HashMap(); for (final GarbageCollectorMXBean gc : garbageCollectors) { final String name = WHITESPACE.matcher(gc.getName()).replaceAll("-"); gauges.put(name(name, "count"), new Gauge() { @Override public Long getValue() { return gc.getCollectionCount(); } }); gauges.put(name(name, "time"), new Gauge() { @Override public Long getValue() { return gc.getCollectionTime(); } }); } return Collections.unmodifiableMap(gauges); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/MemoryUsageGaugeSet.java000066400000000000000000000147771315671014200314100ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import com.codahale.metrics.RatioGauge; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.*; import java.util.regex.Pattern; import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for JVM memory usage, including stats on heap vs. non-heap memory, plus * GC-specific memory pools. */ public class MemoryUsageGaugeSet implements MetricSet { private static final Pattern WHITESPACE = Pattern.compile("[\\s]+"); private final MemoryMXBean mxBean; private final List memoryPools; public MemoryUsageGaugeSet() { this(ManagementFactory.getMemoryMXBean(), ManagementFactory.getMemoryPoolMXBeans()); } public MemoryUsageGaugeSet(MemoryMXBean mxBean, Collection memoryPools) { this.mxBean = mxBean; this.memoryPools = new ArrayList(memoryPools); } @Override public Map getMetrics() { final Map gauges = new HashMap(); gauges.put("total.init", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getInit() + mxBean.getNonHeapMemoryUsage().getInit(); } }); gauges.put("total.used", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getUsed() + mxBean.getNonHeapMemoryUsage().getUsed(); } }); gauges.put("total.max", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getMax() + mxBean.getNonHeapMemoryUsage().getMax(); } }); gauges.put("total.committed", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getCommitted() + mxBean.getNonHeapMemoryUsage().getCommitted(); } }); gauges.put("heap.init", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getInit(); } }); gauges.put("heap.used", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getUsed(); } }); gauges.put("heap.max", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getMax(); } }); gauges.put("heap.committed", new Gauge() { @Override public Long getValue() { return mxBean.getHeapMemoryUsage().getCommitted(); } }); gauges.put("heap.usage", new RatioGauge() { @Override protected Ratio getRatio() { final MemoryUsage usage = mxBean.getHeapMemoryUsage(); return Ratio.of(usage.getUsed(), usage.getMax()); } }); gauges.put("non-heap.init", new Gauge() { @Override public Long getValue() { return mxBean.getNonHeapMemoryUsage().getInit(); } }); gauges.put("non-heap.used", new Gauge() { @Override public Long getValue() { return mxBean.getNonHeapMemoryUsage().getUsed(); } }); gauges.put("non-heap.max", new Gauge() { @Override public Long getValue() { return mxBean.getNonHeapMemoryUsage().getMax(); } }); gauges.put("non-heap.committed", new Gauge() { @Override public Long getValue() { return mxBean.getNonHeapMemoryUsage().getCommitted(); } }); gauges.put("non-heap.usage", new RatioGauge() { @Override protected Ratio getRatio() { final MemoryUsage usage = mxBean.getNonHeapMemoryUsage(); return Ratio.of(usage.getUsed(), usage.getMax()); } }); for (final MemoryPoolMXBean pool : memoryPools) { final String poolName = name("pools", WHITESPACE.matcher(pool.getName()).replaceAll("-")); gauges.put(name(poolName, "usage"), new RatioGauge() { @Override protected Ratio getRatio() { MemoryUsage usage = pool.getUsage(); return Ratio.of(usage.getUsed(), usage.getMax() == -1 ? usage.getCommitted() : usage.getMax()); } }); gauges.put(name(poolName, "max"),new Gauge() { @Override public Long getValue() { return pool.getUsage().getMax(); } }); gauges.put(name(poolName, "used"),new Gauge() { @Override public Long getValue() { return pool.getUsage().getUsed(); } }); gauges.put(name(poolName, "committed"),new Gauge() { @Override public Long getValue() { return pool.getUsage().getCommitted(); } }); // Only register GC usage metrics if the memory pool supports usage statistics. if (pool.getCollectionUsage() != null) { gauges.put(name(poolName, "used-after-gc"),new Gauge() { @Override public Long getValue() { return pool.getCollectionUsage().getUsed(); } }); } gauges.put(name(poolName, "init"),new Gauge() { @Override public Long getValue() { return pool.getUsage().getInit(); } }); } return Collections.unmodifiableMap(gauges); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDeadlockDetector.java000066400000000000000000000042111315671014200320340ustar00rootroot00000000000000package com.codahale.metrics.jvm; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * A utility class for detecting deadlocked threads. */ public class ThreadDeadlockDetector { private static final int MAX_STACK_TRACE_DEPTH = 100; private final ThreadMXBean threads; /** * Creates a new detector. */ public ThreadDeadlockDetector() { this(ManagementFactory.getThreadMXBean()); } /** * Creates a new detector using the given {@link ThreadMXBean}. * * @param threads a {@link ThreadMXBean} */ public ThreadDeadlockDetector(ThreadMXBean threads) { this.threads = threads; } /** * Returns a set of diagnostic stack traces for any deadlocked threads. If no threads are * deadlocked, returns an empty set. * * @return stack traces for deadlocked threads or an empty set */ public Set getDeadlockedThreads() { final long[] ids = threads.findDeadlockedThreads(); if (ids != null) { final Set deadlocks = new HashSet(); for (ThreadInfo info : threads.getThreadInfo(ids, MAX_STACK_TRACE_DEPTH)) { final StringBuilder stackTrace = new StringBuilder(); for (StackTraceElement element : info.getStackTrace()) { stackTrace.append("\t at ") .append(element.toString()) .append(String.format("%n")); } deadlocks.add( String.format("%s locked on %s (owned by %s):%n%s", info.getThreadName(), info.getLockName(), info.getLockOwnerName(), stackTrace.toString() ) ); } return Collections.unmodifiableSet(deadlocks); } return Collections.emptySet(); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadDump.java000077500000000000000000000066321315671014200275550ustar00rootroot00000000000000package com.codahale.metrics.jvm; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.management.LockInfo; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.nio.charset.Charset; /** * A convenience class for getting a thread dump. */ public class ThreadDump { private static final Charset UTF_8 = Charset.forName("UTF-8"); private final ThreadMXBean threadMXBean; public ThreadDump(ThreadMXBean threadMXBean) { this.threadMXBean = threadMXBean; } /** * Dumps all of the threads' current information to an output stream. * * @param out an output stream */ public void dump(OutputStream out) { final ThreadInfo[] threads = this.threadMXBean.dumpAllThreads(true, true); final PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, UTF_8)); for (int ti = threads.length - 1; ti >= 0; ti--) { final ThreadInfo t = threads[ti]; writer.printf("\"%s\" id=%d state=%s", t.getThreadName(), t.getThreadId(), t.getThreadState()); final LockInfo lock = t.getLockInfo(); if (lock != null && t.getThreadState() != Thread.State.BLOCKED) { writer.printf("%n - waiting on <0x%08x> (a %s)", lock.getIdentityHashCode(), lock.getClassName()); writer.printf("%n - locked <0x%08x> (a %s)", lock.getIdentityHashCode(), lock.getClassName()); } else if (lock != null && t.getThreadState() == Thread.State.BLOCKED) { writer.printf("%n - waiting to lock <0x%08x> (a %s)", lock.getIdentityHashCode(), lock.getClassName()); } if (t.isSuspended()) { writer.print(" (suspended)"); } if (t.isInNative()) { writer.print(" (running in native)"); } writer.println(); if (t.getLockOwnerName() != null) { writer.printf(" owned by %s id=%d%n", t.getLockOwnerName(), t.getLockOwnerId()); } final StackTraceElement[] elements = t.getStackTrace(); final MonitorInfo[] monitors = t.getLockedMonitors(); for (int i = 0; i < elements.length; i++) { final StackTraceElement element = elements[i]; writer.printf(" at %s%n", element); for (int j = 1; j < monitors.length; j++) { final MonitorInfo monitor = monitors[j]; if (monitor.getLockedStackDepth() == i) { writer.printf(" - locked %s%n", monitor); } } } writer.println(); final LockInfo[] locks = t.getLockedSynchronizers(); if (locks.length > 0) { writer.printf(" Locked synchronizers: count = %d%n", locks.length); for (LockInfo l : locks) { writer.printf(" - %s%n", l); } writer.println(); } } writer.println(); writer.flush(); } } metrics-3.2.5/metrics-jvm/src/main/java/com/codahale/metrics/jvm/ThreadStatesGaugeSet.java000066400000000000000000000063001315671014200315250ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import com.codahale.metrics.Metric; import com.codahale.metrics.MetricSet; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import static com.codahale.metrics.MetricRegistry.name; /** * A set of gauges for the number of threads in their various states and deadlock detection. */ public class ThreadStatesGaugeSet implements MetricSet { // do not compute stack traces. private final static int STACK_TRACE_DEPTH = 0; private final ThreadMXBean threads; private final ThreadDeadlockDetector deadlockDetector; /** * Creates a new set of gauges using the default MXBeans. */ public ThreadStatesGaugeSet() { this(ManagementFactory.getThreadMXBean(), new ThreadDeadlockDetector()); } /** * Creates a new set of gauges using the given MXBean and detector. * * @param threads a thread MXBean * @param deadlockDetector a deadlock detector */ public ThreadStatesGaugeSet(ThreadMXBean threads, ThreadDeadlockDetector deadlockDetector) { this.threads = threads; this.deadlockDetector = deadlockDetector; } @Override public Map getMetrics() { final Map gauges = new HashMap(); for (final Thread.State state : Thread.State.values()) { gauges.put(name(state.toString().toLowerCase(), "count"), new Gauge() { @Override public Object getValue() { return getThreadCount(state); } }); } gauges.put("count", new Gauge() { @Override public Integer getValue() { return threads.getThreadCount(); } }); gauges.put("daemon.count", new Gauge() { @Override public Integer getValue() { return threads.getDaemonThreadCount(); } }); gauges.put("deadlock.count", new Gauge() { @Override public Integer getValue() { return deadlockDetector.getDeadlockedThreads().size(); } }); gauges.put("deadlocks", new Gauge>() { @Override public Set getValue() { return deadlockDetector.getDeadlockedThreads(); } }); return Collections.unmodifiableMap(gauges); } private int getThreadCount(Thread.State state) { final ThreadInfo[] allThreads = getThreadInfo(); int count = 0; for (ThreadInfo info : allThreads) { if (info != null && info.getThreadState() == state) { count++; } } return count; } ThreadInfo[] getThreadInfo() { return threads.getThreadInfo(threads.getAllThreadIds(), STACK_TRACE_DEPTH); } } metrics-3.2.5/metrics-jvm/src/test/000077500000000000000000000000001315671014200171755ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/000077500000000000000000000000001315671014200201165ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/com/000077500000000000000000000000001315671014200206745ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/000077500000000000000000000000001315671014200224345ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200241025ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/000077500000000000000000000000001315671014200246765ustar00rootroot00000000000000metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/BufferPoolMetricSetTest.java000066400000000000000000000067351315671014200322770ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.ObjectName; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class BufferPoolMetricSetTest { private final MBeanServer mBeanServer = mock(MBeanServer.class); private final BufferPoolMetricSet buffers = new BufferPoolMetricSet(mBeanServer); private ObjectName mapped; private ObjectName direct; @Before public void setUp() throws Exception { this.mapped = new ObjectName("java.nio:type=BufferPool,name=mapped"); this.direct = new ObjectName("java.nio:type=BufferPool,name=direct"); } @Test public void includesGaugesForDirectAndMappedPools() throws Exception { assertThat(buffers.getMetrics().keySet()) .containsOnly("direct.count", "mapped.used", "mapped.capacity", "direct.capacity", "mapped.count", "direct.used"); } @Test public void ignoresGaugesForObjectsWhichCannotBeFound() throws Exception { when(mBeanServer.getMBeanInfo(mapped)).thenThrow(new InstanceNotFoundException()); assertThat(buffers.getMetrics().keySet()) .containsOnly("direct.count", "direct.capacity", "direct.used"); } @Test public void includesAGaugeForDirectCount() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.count"); when(mBeanServer.getAttribute(direct, "Count")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } @Test public void includesAGaugeForDirectMemoryUsed() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.used"); when(mBeanServer.getAttribute(direct, "MemoryUsed")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } @Test public void includesAGaugeForDirectCapacity() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("direct.capacity"); when(mBeanServer.getAttribute(direct, "TotalCapacity")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } @Test public void includesAGaugeForMappedCount() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.count"); when(mBeanServer.getAttribute(mapped, "Count")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } @Test public void includesAGaugeForMappedMemoryUsed() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.used"); when(mBeanServer.getAttribute(mapped, "MemoryUsed")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } @Test public void includesAGaugeForMappedCapacity() throws Exception { final Gauge gauge = (Gauge) buffers.getMetrics().get("mapped.capacity"); when(mBeanServer.getAttribute(mapped, "TotalCapacity")).thenReturn(100); assertThat(gauge.getValue()) .isEqualTo(100); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ClassLoadingGaugeSetTest.java000066400000000000000000000020501315671014200323660ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; import java.lang.management.ClassLoadingMXBean; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ClassLoadingGaugeSetTest { private final ClassLoadingMXBean cl = mock(ClassLoadingMXBean.class); private final ClassLoadingGaugeSet gauges = new ClassLoadingGaugeSet(cl); @Before public void setUp() throws Exception { when(cl.getTotalLoadedClassCount()).thenReturn(2L); when(cl.getUnloadedClassCount()).thenReturn(1L); } @Test public void loadedGauge() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("loaded"); assertThat(gauge.getValue()).isEqualTo(2L); } @Test public void unLoadedGauge() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("unloaded"); assertThat(gauge.getValue()).isEqualTo(1L); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/FileDescriptorRatioGaugeTest.java000066400000000000000000000043011315671014200332650ustar00rootroot00000000000000package com.codahale.metrics.jvm; import org.junit.Test; import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.mock; @SuppressWarnings("UnusedDeclaration") public class FileDescriptorRatioGaugeTest { private final OperatingSystemMXBean os = new OperatingSystemMXBean() { @Override public String getName() { return null; } @Override public String getArch() { return null; } @Override public String getVersion() { return null; } @Override public int getAvailableProcessors() { return 0; } @Override public double getSystemLoadAverage() { return 0; } // these duplicate methods from UnixOperatingSystem private long getOpenFileDescriptorCount() { return 10; } private long getMaxFileDescriptorCount() { return 100; } // overridden on Java 1.7; random crap on Java 1.6 public ObjectName getObjectName() { return null; } }; private final FileDescriptorRatioGauge gauge = new FileDescriptorRatioGauge(os); @Test public void calculatesTheRatioOfUsedToTotalFileDescriptors() throws Exception { assertThat(gauge.getValue()) .isEqualTo(0.1); } @Test public void validateFileDescriptorRatioPresenceOnNixPlatforms() throws Exception { OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); assumeTrue(osBean instanceof com.sun.management.UnixOperatingSystemMXBean); assertThat(new FileDescriptorRatioGauge().getValue()) .isGreaterThanOrEqualTo(0.0) .isLessThanOrEqualTo(1.0); } @Test public void returnsNaNWhenTheInformationIsUnavailable() throws Exception { assertThat(new FileDescriptorRatioGauge(mock(OperatingSystemMXBean.class)).getValue()) .isNaN(); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/GarbageCollectorMetricSetTest.java000066400000000000000000000031261315671014200334220ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; import java.lang.management.GarbageCollectorMXBean; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class GarbageCollectorMetricSetTest { private final GarbageCollectorMXBean gc = mock(GarbageCollectorMXBean.class); private final GarbageCollectorMetricSet metrics = new GarbageCollectorMetricSet(Arrays.asList(gc)); @Before public void setUp() throws Exception { when(gc.getName()).thenReturn("PS OldGen"); when(gc.getCollectionCount()).thenReturn(1L); when(gc.getCollectionTime()).thenReturn(2L); } @Test public void hasGaugesForGcCountsAndElapsedTimes() throws Exception { assertThat(metrics.getMetrics().keySet()) .containsOnly("PS-OldGen.time", "PS-OldGen.count"); } @Test public void hasAGaugeForGcCounts() throws Exception { final Gauge gauge = (Gauge) metrics.getMetrics().get("PS-OldGen.count"); assertThat(gauge.getValue()) .isEqualTo(1L); } @Test public void hasAGaugeForGcTimes() throws Exception { final Gauge gauge = (Gauge) metrics.getMetrics().get("PS-OldGen.time"); assertThat(gauge.getValue()) .isEqualTo(2L); } @Test public void autoDiscoversGCs() throws Exception { assertThat(new GarbageCollectorMetricSet().getMetrics().keySet()) .isNotEmpty(); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/MemoryUsageGaugeSetTest.java000066400000000000000000000220401315671014200322610ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MemoryUsageGaugeSetTest { private final MemoryUsage heap = mock(MemoryUsage.class); private final MemoryUsage nonHeap = mock(MemoryUsage.class); private final MemoryUsage pool = mock(MemoryUsage.class); private final MemoryUsage weirdPool = mock(MemoryUsage.class); private final MemoryUsage weirdCollection = mock(MemoryUsage.class); private final MemoryMXBean mxBean = mock(MemoryMXBean.class); private final MemoryPoolMXBean memoryPool = mock(MemoryPoolMXBean.class); private final MemoryPoolMXBean weirdMemoryPool = mock(MemoryPoolMXBean.class); private final MemoryUsageGaugeSet gauges = new MemoryUsageGaugeSet(mxBean, Arrays.asList(memoryPool, weirdMemoryPool)); @Before public void setUp() throws Exception { when(heap.getCommitted()).thenReturn(10L); when(heap.getInit()).thenReturn(20L); when(heap.getUsed()).thenReturn(30L); when(heap.getMax()).thenReturn(40L); when(nonHeap.getCommitted()).thenReturn(1L); when(nonHeap.getInit()).thenReturn(2L); when(nonHeap.getUsed()).thenReturn(3L); when(nonHeap.getMax()).thenReturn(4L); when(pool.getCommitted()).thenReturn(100L); when(pool.getInit()).thenReturn(200L); when(pool.getUsed()).thenReturn(300L); when(pool.getMax()).thenReturn(400L); when(weirdPool.getCommitted()).thenReturn(100L); when(weirdPool.getInit()).thenReturn(200L); when(weirdPool.getUsed()).thenReturn(300L); when(weirdPool.getMax()).thenReturn(-1L); when(weirdCollection.getUsed()).thenReturn(290L); when(mxBean.getHeapMemoryUsage()).thenReturn(heap); when(mxBean.getNonHeapMemoryUsage()).thenReturn(nonHeap); when(memoryPool.getUsage()).thenReturn(pool); // Mock that "Big Pool" is a non-collected pool therefore doesn't // have collection usage statistics. when(memoryPool.getCollectionUsage()).thenReturn(null); when(memoryPool.getName()).thenReturn("Big Pool"); when(weirdMemoryPool.getUsage()).thenReturn(weirdPool); when(weirdMemoryPool.getCollectionUsage()).thenReturn(weirdCollection); when(weirdMemoryPool.getName()).thenReturn("Weird Pool"); } @Test public void hasASetOfGauges() throws Exception { assertThat(gauges.getMetrics().keySet()) .containsOnly( "heap.init", "heap.committed", "heap.used", "heap.usage", "heap.max", "non-heap.init", "non-heap.committed", "non-heap.used", "non-heap.usage", "non-heap.max", "total.init", "total.committed", "total.used", "total.max", "pools.Big-Pool.init", "pools.Big-Pool.committed", "pools.Big-Pool.used", "pools.Big-Pool.usage", "pools.Big-Pool.max", // skip in non-collected pools - "pools.Big-Pool.used-after-gc", "pools.Weird-Pool.init", "pools.Weird-Pool.committed", "pools.Weird-Pool.used", "pools.Weird-Pool.used-after-gc", "pools.Weird-Pool.usage", "pools.Weird-Pool.max"); } @Test public void hasAGaugeForTotalCommitted() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("total.committed"); assertThat(gauge.getValue()) .isEqualTo(11L); } @Test public void hasAGaugeForTotalInit() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("total.init"); assertThat(gauge.getValue()) .isEqualTo(22L); } @Test public void hasAGaugeForTotalUsed() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("total.used"); assertThat(gauge.getValue()) .isEqualTo(33L); } @Test public void hasAGaugeForTotalMax() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("total.max"); assertThat(gauge.getValue()) .isEqualTo(44L); } @Test public void hasAGaugeForHeapCommitted() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.committed"); assertThat(gauge.getValue()) .isEqualTo(10L); } @Test public void hasAGaugeForHeapInit() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.init"); assertThat(gauge.getValue()) .isEqualTo(20L); } @Test public void hasAGaugeForHeapUsed() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.used"); assertThat(gauge.getValue()) .isEqualTo(30L); } @Test public void hasAGaugeForHeapMax() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.max"); assertThat(gauge.getValue()) .isEqualTo(40L); } @Test public void hasAGaugeForHeapUsage() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("heap.usage"); assertThat(gauge.getValue()) .isEqualTo(0.75); } @Test public void hasAGaugeForNonHeapCommitted() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.committed"); assertThat(gauge.getValue()) .isEqualTo(1L); } @Test public void hasAGaugeForNonHeapInit() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.init"); assertThat(gauge.getValue()) .isEqualTo(2L); } @Test public void hasAGaugeForNonHeapUsed() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.used"); assertThat(gauge.getValue()) .isEqualTo(3L); } @Test public void hasAGaugeForNonHeapMax() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.max"); assertThat(gauge.getValue()) .isEqualTo(4L); } @Test public void hasAGaugeForNonHeapUsage() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("non-heap.usage"); assertThat(gauge.getValue()) .isEqualTo(0.75); } @Test public void hasAGaugeForMemoryPoolUsage() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Big-Pool.usage"); assertThat(gauge.getValue()) .isEqualTo(0.75); } @Test public void hasAGaugeForWeirdMemoryPoolInit() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.init"); assertThat(gauge.getValue()) .isEqualTo(200L); } @Test public void hasAGaugeForWeirdMemoryPoolCommitted() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.committed"); assertThat(gauge.getValue()) .isEqualTo(100L); } @Test public void hasAGaugeForWeirdMemoryPoolUsed() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.used"); assertThat(gauge.getValue()) .isEqualTo(300L); } @Test public void hasAGaugeForWeirdMemoryPoolUsage() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.usage"); assertThat(gauge.getValue()) .isEqualTo(3.0); } @Test public void hasAGaugeForWeirdMemoryPoolMax() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.max"); assertThat(gauge.getValue()) .isEqualTo(-1L); } @Test public void hasAGaugeForWeirdCollectionPoolUsed() throws Exception { final Gauge gauge = (Gauge) gauges.getMetrics().get("pools.Weird-Pool.used-after-gc"); assertThat(gauge.getValue()) .isEqualTo(290L); } @Test public void autoDetectsMemoryUsageBeanAndMemoryPools() throws Exception { assertThat(new MemoryUsageGaugeSet().getMetrics().keySet()) .isNotEmpty(); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDeadlockDetectorTest.java000066400000000000000000000056471315671014200327450ustar00rootroot00000000000000package com.codahale.metrics.jvm; import org.junit.Test; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.Locale; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ThreadDeadlockDetectorTest { private final ThreadMXBean threads = mock(ThreadMXBean.class); private final ThreadDeadlockDetector detector = new ThreadDeadlockDetector(threads); @Test public void returnsAnEmptySetIfNoThreadsAreDeadlocked() throws Exception { when(threads.findDeadlockedThreads()).thenReturn(null); assertThat(detector.getDeadlockedThreads()) .isEmpty(); } @Test public void returnsASetOfThreadsIfAnyAreDeadlocked() throws Exception { final ThreadInfo thread1 = mock(ThreadInfo.class); when(thread1.getThreadName()).thenReturn("thread1"); when(thread1.getLockName()).thenReturn("lock2"); when(thread1.getLockOwnerName()).thenReturn("thread2"); when(thread1.getStackTrace()).thenReturn(new StackTraceElement[]{ new StackTraceElement("Blah", "bloo", "Blah.java", 150), new StackTraceElement("Blah", "blee", "Blah.java", 100) }); final ThreadInfo thread2 = mock(ThreadInfo.class); when(thread2.getThreadName()).thenReturn("thread2"); when(thread2.getLockName()).thenReturn("lock1"); when(thread2.getLockOwnerName()).thenReturn("thread1"); when(thread2.getStackTrace()).thenReturn(new StackTraceElement[]{ new StackTraceElement("Blah", "blee", "Blah.java", 100), new StackTraceElement("Blah", "bloo", "Blah.java", 150) }); final long[] ids = { 1, 2 }; when(threads.findDeadlockedThreads()).thenReturn(ids); when(threads.getThreadInfo(eq(ids), anyInt())) .thenReturn(new ThreadInfo[]{ thread1, thread2 }); assertThat(detector.getDeadlockedThreads()) .containsOnly(String.format(Locale.US, "thread1 locked on lock2 (owned by thread2):%n" + "\t at Blah.bloo(Blah.java:150)%n" + "\t at Blah.blee(Blah.java:100)%n"), String.format(Locale.US, "thread2 locked on lock1 (owned by thread1):%n" + "\t at Blah.blee(Blah.java:100)%n" + "\t at Blah.bloo(Blah.java:150)%n")); } @Test public void autoDiscoversTheThreadMXBean() throws Exception { assertThat(new ThreadDeadlockDetector().getDeadlockedThreads()) .isNotNull(); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadDumpTest.java000077500000000000000000000036301315671014200304430ustar00rootroot00000000000000package com.codahale.metrics.jvm; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.lang.management.LockInfo; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; // TODO: 3/12/13 -- improve test coverage for ThreadDump public class ThreadDumpTest { private final ThreadMXBean threadMXBean = mock(ThreadMXBean.class); private final ThreadDump threadDump = new ThreadDump(threadMXBean); private final ThreadInfo runnable = mock(ThreadInfo.class); @Before public void setUp() throws Exception { final StackTraceElement rLine1 = new StackTraceElement("Blah", "blee", "Blah.java", 100); when(runnable.getThreadName()).thenReturn("runnable"); when(runnable.getThreadId()).thenReturn(100L); when(runnable.getThreadState()).thenReturn(Thread.State.RUNNABLE); when(runnable.getStackTrace()).thenReturn(new StackTraceElement[]{ rLine1 }); when(runnable.getLockedMonitors()).thenReturn(new MonitorInfo[]{ }); when(runnable.getLockedSynchronizers()).thenReturn(new LockInfo[]{ }); when(threadMXBean.dumpAllThreads(true, true)).thenReturn(new ThreadInfo[]{ runnable }); } @Test public void dumpsAllThreads() throws Exception { final ByteArrayOutputStream output = new ByteArrayOutputStream(); threadDump.dump(output); assertThat(output.toString()) .isEqualTo(String.format("\"runnable\" id=100 state=RUNNABLE%n" + " at Blah.blee(Blah.java:100)%n" + "%n" + "%n")); } } metrics-3.2.5/metrics-jvm/src/test/java/com/codahale/metrics/jvm/ThreadStatesGaugeSetTest.java000066400000000000000000000112151315671014200324210ustar00rootroot00000000000000package com.codahale.metrics.jvm; import com.codahale.metrics.Gauge; import org.junit.Before; import org.junit.Test; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.HashSet; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ThreadStatesGaugeSetTest { private final ThreadMXBean threads = mock(ThreadMXBean.class); private final ThreadDeadlockDetector detector = mock(ThreadDeadlockDetector.class); private final ThreadStatesGaugeSet gauges = new ThreadStatesGaugeSet(threads, detector); private final long[] ids = new long[]{ 1, 2, 3 }; private final ThreadInfo newThread = mock(ThreadInfo.class); private final ThreadInfo runnableThread = mock(ThreadInfo.class); private final ThreadInfo blockedThread = mock(ThreadInfo.class); private final ThreadInfo waitingThread = mock(ThreadInfo.class); private final ThreadInfo timedWaitingThread = mock(ThreadInfo.class); private final ThreadInfo terminatedThread = mock(ThreadInfo.class); private final Set deadlocks = new HashSet(); @Before public void setUp() throws Exception { deadlocks.add("yay"); when(newThread.getThreadState()).thenReturn(Thread.State.NEW); when(runnableThread.getThreadState()).thenReturn(Thread.State.RUNNABLE); when(blockedThread.getThreadState()).thenReturn(Thread.State.BLOCKED); when(waitingThread.getThreadState()).thenReturn(Thread.State.WAITING); when(timedWaitingThread.getThreadState()).thenReturn(Thread.State.TIMED_WAITING); when(terminatedThread.getThreadState()).thenReturn(Thread.State.TERMINATED); when(threads.getAllThreadIds()).thenReturn(ids); when(threads.getThreadInfo(ids, 0)).thenReturn(new ThreadInfo[]{ newThread, runnableThread, blockedThread, waitingThread, timedWaitingThread, terminatedThread }); when(threads.getThreadCount()).thenReturn(12); when(threads.getDaemonThreadCount()).thenReturn(13); when(detector.getDeadlockedThreads()).thenReturn(deadlocks); } @Test public void hasASetOfGauges() throws Exception { assertThat(gauges.getMetrics().keySet()) .containsOnly("terminated.count", "new.count", "count", "timed_waiting.count", "deadlocks", "blocked.count", "waiting.count", "daemon.count", "runnable.count", "deadlock.count"); } @Test public void hasAGaugeForEachThreadState() throws Exception { assertThat(((Gauge) gauges.getMetrics().get("new.count")).getValue()) .isEqualTo(1); assertThat(((Gauge) gauges.getMetrics().get("runnable.count")).getValue()) .isEqualTo(1); assertThat(((Gauge) gauges.getMetrics().get("blocked.count")).getValue()) .isEqualTo(1); assertThat(((Gauge) gauges.getMetrics().get("waiting.count")).getValue()) .isEqualTo(1); assertThat(((Gauge) gauges.getMetrics().get("timed_waiting.count")).getValue()) .isEqualTo(1); assertThat(((Gauge) gauges.getMetrics().get("terminated.count")).getValue()) .isEqualTo(1); } @Test public void hasAGaugeForTheNumberOfThreads() throws Exception { assertThat(((Gauge) gauges.getMetrics().get("count")).getValue()) .isEqualTo(12); } @Test public void hasAGaugeForTheNumberOfDaemonThreads() throws Exception { assertThat(((Gauge) gauges.getMetrics().get("daemon.count")).getValue()) .isEqualTo(13); } @Test public void hasAGaugeForAnyDeadlocks() throws Exception { assertThat(((Gauge) gauges.getMetrics().get("deadlocks")).getValue()) .isEqualTo(deadlocks); } @Test public void hasAGaugeForAnyDeadlockCount() throws Exception { assertThat(((Gauge) gauges.getMetrics().get("deadlock.count")).getValue()) .isEqualTo(1); } @Test public void autoDiscoversTheMXBeans() throws Exception { final ThreadStatesGaugeSet set = new ThreadStatesGaugeSet(); assertThat(((Gauge) set.getMetrics().get("count")).getValue()) .isNotNull(); assertThat(((Gauge) set.getMetrics().get("deadlocks")).getValue()) .isNotNull(); } } metrics-3.2.5/metrics-log4j/000077500000000000000000000000001315671014200156525ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/pom.xml000066400000000000000000000020471315671014200171720ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-log4j Metrics Integration for Log4j 1.x bundle An instrumented appender for Log4j 1.x. io.dropwizard.metrics metrics-core ${project.version} log4j log4j 1.2.17 metrics-3.2.5/metrics-log4j/src/000077500000000000000000000000001315671014200164415ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/000077500000000000000000000000001315671014200173655ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/000077500000000000000000000000001315671014200203065ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/com/000077500000000000000000000000001315671014200210645ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/com/codahale/000077500000000000000000000000001315671014200226245ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200242725ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/com/codahale/metrics/log4j/000077500000000000000000000000001315671014200253115ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/main/java/com/codahale/metrics/log4j/InstrumentedAppender.java000066400000000000000000000053761315671014200323270ustar00rootroot00000000000000package com.codahale.metrics.log4j; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import static com.codahale.metrics.MetricRegistry.name; /** * A Log4J {@link Appender} which has seven meters, one for each logging level and one for the total * number of statements being logged. The meter names are the logging level names appended to the * name of the appender. */ public class InstrumentedAppender extends AppenderSkeleton { private final MetricRegistry registry; private Meter all; private Meter trace; private Meter debug; private Meter info; private Meter warn; private Meter error; private Meter fatal; /** * Create a new instrumented appender using the given registry name. * * @param registryName the name of the registry in {@link SharedMetricRegistries} */ public InstrumentedAppender(String registryName) { this(SharedMetricRegistries.getOrCreate(registryName)); } /** * Create a new instrumented appender using the given registry. * * @param registry the metric registry */ public InstrumentedAppender(MetricRegistry registry) { this.registry = registry; setName(name(Appender.class)); } @Override public void activateOptions() { this.all = registry.meter(name(getName(), "all")); this.trace = registry.meter(name(getName(), "trace")); this.debug = registry.meter(name(getName(), "debug")); this.info = registry.meter(name(getName(), "info")); this.warn = registry.meter(name(getName(), "warn")); this.error = registry.meter(name(getName(), "error")); this.fatal = registry.meter(name(getName(), "fatal")); } @Override protected void append(LoggingEvent event) { all.mark(); switch (event.getLevel().toInt()) { case Level.TRACE_INT: trace.mark(); break; case Level.DEBUG_INT: debug.mark(); break; case Level.INFO_INT: info.mark(); break; case Level.WARN_INT: warn.mark(); break; case Level.ERROR_INT: error.mark(); break; case Level.FATAL_INT: fatal.mark(); break; default: break; } } @Override public void close() { // nothing doing } @Override public boolean requiresLayout() { return false; } } metrics-3.2.5/metrics-log4j/src/test/000077500000000000000000000000001315671014200174205ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/000077500000000000000000000000001315671014200203415ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/com/000077500000000000000000000000001315671014200211175ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/com/codahale/000077500000000000000000000000001315671014200226575ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200243255ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/com/codahale/metrics/log4j/000077500000000000000000000000001315671014200253445ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j/src/test/java/com/codahale/metrics/log4j/InstrumentedAppenderTest.java000066400000000000000000000072141315671014200332130ustar00rootroot00000000000000package com.codahale.metrics.log4j; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class InstrumentedAppenderTest { public static final String METRIC_NAME_PREFIX = "org.apache.log4j.Appender"; private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedAppender appender = new InstrumentedAppender(registry); private final LoggingEvent event = mock(LoggingEvent.class); @Before public void setUp() throws Exception { appender.activateOptions(); } @After public void tearDown() throws Exception { SharedMetricRegistries.clear(); } @Test public void metersTraceEvents() throws Exception { when(event.getLevel()).thenReturn(Level.TRACE); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".trace").getCount()) .isEqualTo(1); } @Test public void metersDebugEvents() throws Exception { when(event.getLevel()).thenReturn(Level.DEBUG); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".debug").getCount()) .isEqualTo(1); } @Test public void metersInfoEvents() throws Exception { when(event.getLevel()).thenReturn(Level.INFO); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } @Test public void metersWarnEvents() throws Exception { when(event.getLevel()).thenReturn(Level.WARN); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".warn").getCount()) .isEqualTo(1); } @Test public void metersErrorEvents() throws Exception { when(event.getLevel()).thenReturn(Level.ERROR); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".error").getCount()) .isEqualTo(1); } @Test public void metersFatalEvents() throws Exception { when(event.getLevel()).thenReturn(Level.FATAL); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".fatal").getCount()) .isEqualTo(1); } @Test public void usesSharedRegistries() throws Exception { String registryName = "registry"; SharedMetricRegistries.add(registryName, registry); final InstrumentedAppender shared = new InstrumentedAppender(registryName); shared.activateOptions(); when(event.getLevel()).thenReturn(Level.INFO); shared.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-log4j2/000077500000000000000000000000001315671014200157345ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/pom.xml000066400000000000000000000033301315671014200172500ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-log4j2 Metrics Integration for Log4j 2.x bundle An instrumented appender for Log4j 2.x. 2.3 org.apache.maven.plugins maven-surefire-plugin suites 1 io.dropwizard.metrics metrics-core ${project.version} org.apache.logging.log4j log4j-api ${log4j2.version} org.apache.logging.log4j log4j-core ${log4j2.version} metrics-3.2.5/metrics-log4j2/src/000077500000000000000000000000001315671014200165235ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/000077500000000000000000000000001315671014200174475ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/000077500000000000000000000000001315671014200203705ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/com/000077500000000000000000000000001315671014200211465ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/com/codahale/000077500000000000000000000000001315671014200227065ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200243545ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/com/codahale/metrics/log4j2/000077500000000000000000000000001315671014200254555ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/main/java/com/codahale/metrics/log4j2/InstrumentedAppender.java000066400000000000000000000121211315671014200324550ustar00rootroot00000000000000package com.codahale.metrics.log4j2; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import java.io.Serializable; import static com.codahale.metrics.MetricRegistry.name; /** * A Log4J 2.x {@link Appender} which has seven meters, one for each logging level and one for the total * number of statements being logged. The meter names are the logging level names appended to the * name of the appender. */ @Plugin(name = "MetricsAppender", category = "Core", elementType = "appender") public class InstrumentedAppender extends AbstractAppender { private static final long serialVersionUID = 1L; private transient final MetricRegistry registry; private transient Meter all; private transient Meter trace; private transient Meter debug; private transient Meter info; private transient Meter warn; private transient Meter error; private transient Meter fatal; /** * Create a new instrumented appender using the given registry name. * * @param registryName the name of the registry in {@link SharedMetricRegistries} * @param filter The Filter to associate with the Appender. * @param layout The layout to use to format the event. * @param ignoreExceptions If true, exceptions will be logged and suppressed. If false errors will be * logged and then passed to the application. * */ public InstrumentedAppender(String registryName, Filter filter, Layout layout, boolean ignoreExceptions) { this(SharedMetricRegistries.getOrCreate(registryName), filter, layout, ignoreExceptions); } /** * Create a new instrumented appender using the given registry name. * * @param registryName the name of the registry in {@link SharedMetricRegistries} */ public InstrumentedAppender(String registryName) { this(SharedMetricRegistries.getOrCreate(registryName)); } /** * Create a new instrumented appender using the given registry. * * @param registry the metric registry */ public InstrumentedAppender(MetricRegistry registry) { this(registry, null, null, true); } /** * Create a new instrumented appender using the given registry. * * @param registry the metric registry * @param filter The Filter to associate with the Appender. * @param layout The layout to use to format the event. * @param ignoreExceptions If true, exceptions will be logged and suppressed. If false errors will be * logged and then passed to the application. */ public InstrumentedAppender(MetricRegistry registry, Filter filter, Layout layout, boolean ignoreExceptions) { super(name(Appender.class), filter, layout, ignoreExceptions); this.registry = registry; } /** * Create a new instrumented appender using the given appender name and registry. * @param appenderName The name of the appender. * @param registry the metric registry */ public InstrumentedAppender(String appenderName, MetricRegistry registry) { super(appenderName, null, null, true); this.registry = registry; } @PluginFactory public static InstrumentedAppender createAppender( @PluginAttribute("name") String name, @PluginAttribute( value = "registryName", defaultString = "log4j2Metrics") String registry) { return new InstrumentedAppender(name, SharedMetricRegistries.getOrCreate(registry)); } @Override public void start() { this.all = registry.meter(name(getName(), "all")); this.trace = registry.meter(name(getName(), "trace")); this.debug = registry.meter(name(getName(), "debug")); this.info = registry.meter(name(getName(), "info")); this.warn = registry.meter(name(getName(), "warn")); this.error = registry.meter(name(getName(), "error")); this.fatal = registry.meter(name(getName(), "fatal")); super.start(); } @Override public void append(LogEvent event) { all.mark(); switch (event.getLevel().getStandardLevel()) { case TRACE: trace.mark(); break; case DEBUG: debug.mark(); break; case INFO: info.mark(); break; case WARN: warn.mark(); break; case ERROR: error.mark(); break; case FATAL: fatal.mark(); break; default: break; } } } metrics-3.2.5/metrics-log4j2/src/test/000077500000000000000000000000001315671014200175025ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/000077500000000000000000000000001315671014200204235ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/com/000077500000000000000000000000001315671014200212015ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/com/codahale/000077500000000000000000000000001315671014200227415ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200244075ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/com/codahale/metrics/log4j2/000077500000000000000000000000001315671014200255105ustar00rootroot00000000000000InstrumentedAppenderConfigTest.java000066400000000000000000000044201315671014200344220ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/java/com/codahale/metrics/log4j2package com.codahale.metrics.log4j2; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Configurator; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class InstrumentedAppenderConfigTest { public static final String METRIC_NAME_PREFIX = "metrics"; public static final String REGISTRY_NAME = "shared-metrics-registry"; private final MetricRegistry registry = SharedMetricRegistries.getOrCreate(REGISTRY_NAME); private ConfigurationSource source; private LoggerContext context; @Before public void setUp() throws Exception { source = new ConfigurationSource(this.getClass().getClassLoader().getResourceAsStream("log4j2-testconfig.xml")); context = Configurator.initialize(null, source); } @After public void tearDown() throws Exception { context.stop(); } // The biggest test is that we can initialize the log4j2 config at all. @Test public void canRecordAll() throws Exception { Logger logger = context.getLogger(this.getClass().getName()); long initialAllCount = registry.meter(METRIC_NAME_PREFIX + ".all").getCount(); logger.error("an error message"); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(initialAllCount + 1); } @Test public void canRecordError() throws Exception { Logger logger = context.getLogger(this.getClass().getName()); long initialErrorCount = registry.meter(METRIC_NAME_PREFIX + ".error").getCount(); logger.error("an error message"); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(initialErrorCount + 1); } @Test public void noInvalidRecording() throws Exception { Logger logger = context.getLogger(this.getClass().getName()); long initialInfoCount = registry.meter(METRIC_NAME_PREFIX + ".info").getCount(); logger.error("an error message"); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(initialInfoCount); } } metrics-3.2.5/metrics-log4j2/src/test/java/com/codahale/metrics/log4j2/InstrumentedAppenderTest.java000066400000000000000000000072701315671014200333610ustar00rootroot00000000000000package com.codahale.metrics.log4j2; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import com.codahale.metrics.log4j2.InstrumentedAppender; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class InstrumentedAppenderTest { public static final String METRIC_NAME_PREFIX = "org.apache.logging.log4j.core.Appender"; private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedAppender appender = new InstrumentedAppender(registry); private final LogEvent event = mock(LogEvent.class); @Before public void setUp() throws Exception { appender.start(); } @After public void tearDown() throws Exception { SharedMetricRegistries.clear(); } @Test public void metersTraceEvents() throws Exception { when(event.getLevel()).thenReturn(Level.TRACE); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".trace").getCount()) .isEqualTo(1); } @Test public void metersDebugEvents() throws Exception { when(event.getLevel()).thenReturn(Level.DEBUG); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".debug").getCount()) .isEqualTo(1); } @Test public void metersInfoEvents() throws Exception { when(event.getLevel()).thenReturn(Level.INFO); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } @Test public void metersWarnEvents() throws Exception { when(event.getLevel()).thenReturn(Level.WARN); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".warn").getCount()) .isEqualTo(1); } @Test public void metersErrorEvents() throws Exception { when(event.getLevel()).thenReturn(Level.ERROR); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".error").getCount()) .isEqualTo(1); } @Test public void metersFatalEvents() throws Exception { when(event.getLevel()).thenReturn(Level.FATAL); appender.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".fatal").getCount()) .isEqualTo(1); } @Test public void usesSharedRegistries() throws Exception { String registryName = "registry"; SharedMetricRegistries.add(registryName, registry); final InstrumentedAppender shared = new InstrumentedAppender(registryName); shared.start(); when(event.getLevel()).thenReturn(Level.INFO); shared.append(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-log4j2/src/test/resources/000077500000000000000000000000001315671014200215145ustar00rootroot00000000000000metrics-3.2.5/metrics-log4j2/src/test/resources/log4j2-testconfig.xml000066400000000000000000000005671315671014200255120ustar00rootroot00000000000000 metrics-3.2.5/metrics-logback/000077500000000000000000000000001315671014200162355ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/pom.xml000066400000000000000000000022321315671014200175510ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-logback Metrics Integration for Logback bundle An instrumented appender for Logback. 1.1.10 io.dropwizard.metrics metrics-core ${project.version} ch.qos.logback logback-classic ${logback.version} metrics-3.2.5/metrics-logback/src/000077500000000000000000000000001315671014200170245ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/000077500000000000000000000000001315671014200177505ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/000077500000000000000000000000001315671014200206715ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/com/000077500000000000000000000000001315671014200214475ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/com/codahale/000077500000000000000000000000001315671014200232075ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200246555ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/com/codahale/metrics/logback/000077500000000000000000000000001315671014200262575ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/main/java/com/codahale/metrics/logback/InstrumentedAppender.java000066400000000000000000000055561315671014200332750ustar00rootroot00000000000000package com.codahale.metrics.logback; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.UnsynchronizedAppenderBase; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import static com.codahale.metrics.MetricRegistry.name; /** * A Logback {@link Appender} which has six meters, one for each logging level and one for the total * number of statements being logged. The meter names are the logging level names appended to the * name of the appender. */ public class InstrumentedAppender extends UnsynchronizedAppenderBase { private final MetricRegistry registry; public static final String DEFAULT_REGISTRY = "logback-metrics"; public static final String REGISTRY_PROPERTY_NAME = "metrics.logback.registry"; private Meter all; private Meter trace; private Meter debug; private Meter info; private Meter warn; private Meter error; /** * Create a new instrumented appender using the given registry name. * */ public InstrumentedAppender() { this(System.getProperty(REGISTRY_PROPERTY_NAME, DEFAULT_REGISTRY)); } /** * Create a new instrumented appender using the given registry name. * * @param registryName the name of the registry in {@link SharedMetricRegistries} */ public InstrumentedAppender(String registryName) { this(SharedMetricRegistries.getOrCreate(registryName)); } /** * Create a new instrumented appender using the given registry. * * @param registry the metric registry */ public InstrumentedAppender(MetricRegistry registry) { this.registry = registry; setName(Appender.class.getName()); } @Override public void start() { this.all = registry.meter(name(getName(), "all")); this.trace = registry.meter(name(getName(), "trace")); this.debug = registry.meter(name(getName(), "debug")); this.info = registry.meter(name(getName(), "info")); this.warn = registry.meter(name(getName(), "warn")); this.error = registry.meter(name(getName(), "error")); super.start(); } @Override protected void append(ILoggingEvent event) { all.mark(); switch (event.getLevel().toInt()) { case Level.TRACE_INT: trace.mark(); break; case Level.DEBUG_INT: debug.mark(); break; case Level.INFO_INT: info.mark(); break; case Level.WARN_INT: warn.mark(); break; case Level.ERROR_INT: error.mark(); break; default: break; } } } metrics-3.2.5/metrics-logback/src/test/000077500000000000000000000000001315671014200200035ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/000077500000000000000000000000001315671014200207245ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/com/000077500000000000000000000000001315671014200215025ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/com/codahale/000077500000000000000000000000001315671014200232425ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200247105ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/com/codahale/metrics/logback/000077500000000000000000000000001315671014200263125ustar00rootroot00000000000000InstrumentedAppenderTest.java000066400000000000000000000105631315671014200341030ustar00rootroot00000000000000metrics-3.2.5/metrics-logback/src/test/java/com/codahale/metrics/logbackpackage com.codahale.metrics.logback; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.SharedMetricRegistries; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class InstrumentedAppenderTest { public static final String METRIC_NAME_PREFIX = "ch.qos.logback.core.Appender"; private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedAppender appender = new InstrumentedAppender(registry); private final ILoggingEvent event = mock(ILoggingEvent.class); @Before public void setUp() throws Exception { appender.start(); } @After public void tearDown() throws Exception { SharedMetricRegistries.clear(); } @Test public void metersTraceEvents() throws Exception { when(event.getLevel()).thenReturn(Level.TRACE); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".trace").getCount()) .isEqualTo(1); } @Test public void metersDebugEvents() throws Exception { when(event.getLevel()).thenReturn(Level.DEBUG); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".debug").getCount()) .isEqualTo(1); } @Test public void metersInfoEvents() throws Exception { when(event.getLevel()).thenReturn(Level.INFO); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } @Test public void metersWarnEvents() throws Exception { when(event.getLevel()).thenReturn(Level.WARN); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".warn").getCount()) .isEqualTo(1); } @Test public void metersErrorEvents() throws Exception { when(event.getLevel()).thenReturn(Level.ERROR); appender.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".all").getCount()) .isEqualTo(1); assertThat(registry.meter(METRIC_NAME_PREFIX + ".error").getCount()) .isEqualTo(1); } @Test public void usesSharedRegistries() throws Exception { String registryName = "registry"; SharedMetricRegistries.add(registryName, registry); final InstrumentedAppender shared = new InstrumentedAppender(registryName); shared.start(); when(event.getLevel()).thenReturn(Level.INFO); shared.doAppend(event); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } @Test public void usesDefaultRegistry() throws Exception { SharedMetricRegistries.add(InstrumentedAppender.DEFAULT_REGISTRY, registry); final InstrumentedAppender shared = new InstrumentedAppender(); shared.start(); when(event.getLevel()).thenReturn(Level.INFO); shared.doAppend(event); assertThat(SharedMetricRegistries.names().contains(InstrumentedAppender.DEFAULT_REGISTRY)); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } @Test public void usesRegistryFromProperty() throws Exception { SharedMetricRegistries.add("something_else", registry); System.setProperty(InstrumentedAppender.REGISTRY_PROPERTY_NAME, "something_else"); final InstrumentedAppender shared = new InstrumentedAppender(); shared.start(); when(event.getLevel()).thenReturn(Level.INFO); shared.doAppend(event); assertThat(SharedMetricRegistries.names().contains("something_else")); assertThat(registry.meter(METRIC_NAME_PREFIX + ".info").getCount()) .isEqualTo(1); } } metrics-3.2.5/metrics-servlet/000077500000000000000000000000001315671014200163175ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/pom.xml000066400000000000000000000021651315671014200176400ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-servlet Metrics Integration for Servlets bundle An instrumented filter for servlet environments. io.dropwizard.metrics metrics-core ${project.version} javax.servlet javax.servlet-api ${servlet.version} provided metrics-3.2.5/metrics-servlet/src/000077500000000000000000000000001315671014200171065ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/000077500000000000000000000000001315671014200200325ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/000077500000000000000000000000001315671014200207535ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/000077500000000000000000000000001315671014200215315ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/000077500000000000000000000000001315671014200232715ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200247375ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/metrics/servlet/000077500000000000000000000000001315671014200264235ustar00rootroot00000000000000AbstractInstrumentedFilter.java000066400000000000000000000167171315671014200345360ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/metrics/servletpackage com.codahale.metrics.servlet; import com.codahale.metrics.Counter; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import javax.servlet.*; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.codahale.metrics.MetricRegistry.name; /** * {@link Filter} implementation which captures request information and a breakdown of the response * codes being returned. */ public abstract class AbstractInstrumentedFilter implements Filter { static final String METRIC_PREFIX = "name-prefix"; private final String otherMetricName; private final Map meterNamesByStatusCode; private final String registryAttribute; // initialized after call of init method private ConcurrentMap metersByStatusCode; private Meter otherMeter; private Meter timeoutsMeter; private Meter errorsMeter; private Counter activeRequests; private Timer requestTimer; /** * Creates a new instance of the filter. * * @param registryAttribute the attribute used to look up the metrics registry in the * servlet context * @param meterNamesByStatusCode A map, keyed by status code, of meter names that we are * interested in. * @param otherMetricName The name used for the catch-all meter. */ protected AbstractInstrumentedFilter(String registryAttribute, Map meterNamesByStatusCode, String otherMetricName) { this.registryAttribute = registryAttribute; this.otherMetricName = otherMetricName; this.meterNamesByStatusCode = meterNamesByStatusCode; } @Override public void init(FilterConfig filterConfig) throws ServletException { final MetricRegistry metricsRegistry = getMetricsFactory(filterConfig); String metricName = filterConfig.getInitParameter(METRIC_PREFIX); if(metricName == null || metricName.isEmpty()) { metricName = getClass().getName(); } this.metersByStatusCode = new ConcurrentHashMap(meterNamesByStatusCode .size()); for (Entry entry : meterNamesByStatusCode.entrySet()) { metersByStatusCode.put(entry.getKey(), metricsRegistry.meter(name(metricName, entry.getValue()))); } this.otherMeter = metricsRegistry.meter(name(metricName, otherMetricName)); this.timeoutsMeter = metricsRegistry.meter(name(metricName, "timeouts")); this.errorsMeter = metricsRegistry.meter(name(metricName, "errors")); this.activeRequests = metricsRegistry.counter(name(metricName, "activeRequests")); this.requestTimer = metricsRegistry.timer(name(metricName, "requests")); } private MetricRegistry getMetricsFactory(FilterConfig filterConfig) { final MetricRegistry metricsRegistry; final Object o = filterConfig.getServletContext().getAttribute(this.registryAttribute); if (o instanceof MetricRegistry) { metricsRegistry = (MetricRegistry) o; } else { metricsRegistry = new MetricRegistry(); } return metricsRegistry; } @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final StatusExposingServletResponse wrappedResponse = new StatusExposingServletResponse((HttpServletResponse) response); activeRequests.inc(); final Timer.Context context = requestTimer.time(); boolean error = false; try { chain.doFilter(request, wrappedResponse); } catch (IOException e) { error = true; throw e; } catch (ServletException e) { error = true; throw e; } catch (RuntimeException e) { error = true; throw e; } finally { if (!error && request.isAsyncStarted()) { request.getAsyncContext().addListener(new AsyncResultListener(context)); } else { context.stop(); activeRequests.dec(); if (error) { errorsMeter.mark(); } else { markMeterForStatusCode(wrappedResponse.getStatus()); } } } } private void markMeterForStatusCode(int status) { final Meter metric = metersByStatusCode.get(status); if (metric != null) { metric.mark(); } else { otherMeter.mark(); } } private static class StatusExposingServletResponse extends HttpServletResponseWrapper { // The Servlet spec says: calling setStatus is optional, if no status is set, the default is 200. private int httpStatus = 200; public StatusExposingServletResponse(HttpServletResponse response) { super(response); } @Override public void sendError(int sc) throws IOException { httpStatus = sc; super.sendError(sc); } @Override public void sendError(int sc, String msg) throws IOException { httpStatus = sc; super.sendError(sc, msg); } @Override public void setStatus(int sc) { httpStatus = sc; super.setStatus(sc); } @Override public void setStatus(int sc, String sm) { httpStatus = sc; super.setStatus(sc, sm); } public int getStatus() { return httpStatus; } } private class AsyncResultListener implements AsyncListener { private Timer.Context context; private boolean done = false; public AsyncResultListener(Timer.Context context) { this.context = context; } @Override public void onComplete(AsyncEvent event) throws IOException { if (!done) { HttpServletResponse suppliedResponse = (HttpServletResponse) event.getSuppliedResponse(); context.stop(); activeRequests.dec(); markMeterForStatusCode(suppliedResponse.getStatus()); } } @Override public void onTimeout(AsyncEvent event) throws IOException { context.stop(); activeRequests.dec(); timeoutsMeter.mark(); done = true; } @Override public void onError(AsyncEvent event) throws IOException { context.stop(); activeRequests.dec(); errorsMeter.mark(); done = true; } @Override public void onStartAsync(AsyncEvent event) throws IOException { } } } metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/metrics/servlet/InstrumentedFilter.java000066400000000000000000000036351315671014200331240ustar00rootroot00000000000000package com.codahale.metrics.servlet; import java.util.HashMap; import java.util.Map; /** * Implementation of the {@link AbstractInstrumentedFilter} which provides a default set of response codes * to capture information about.

    Use it in your servlet.xml like this:

    *
    {@code
     * 
     *     instrumentedFilter
     *     com.codahale.metrics.servlet.InstrumentedFilter
     * 
     * 
     *     instrumentedFilter
     *     /*
     * 
     * }
    */ public class InstrumentedFilter extends AbstractInstrumentedFilter { public static final String REGISTRY_ATTRIBUTE = InstrumentedFilter.class.getName() + ".registry"; private static final String NAME_PREFIX = "responseCodes."; private static final int OK = 200; private static final int CREATED = 201; private static final int NO_CONTENT = 204; private static final int BAD_REQUEST = 400; private static final int NOT_FOUND = 404; private static final int SERVER_ERROR = 500; /** * Creates a new instance of the filter. */ public InstrumentedFilter() { super(REGISTRY_ATTRIBUTE, createMeterNamesByStatusCode(), NAME_PREFIX + "other"); } private static Map createMeterNamesByStatusCode() { final Map meterNamesByStatusCode = new HashMap(6); meterNamesByStatusCode.put(OK, NAME_PREFIX + "ok"); meterNamesByStatusCode.put(CREATED, NAME_PREFIX + "created"); meterNamesByStatusCode.put(NO_CONTENT, NAME_PREFIX + "noContent"); meterNamesByStatusCode.put(BAD_REQUEST, NAME_PREFIX + "badRequest"); meterNamesByStatusCode.put(NOT_FOUND, NAME_PREFIX + "notFound"); meterNamesByStatusCode.put(SERVER_ERROR, NAME_PREFIX + "serverError"); return meterNamesByStatusCode; } } InstrumentedFilterContextListener.java000066400000000000000000000016651315671014200361210ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/main/java/com/codahale/metrics/servletpackage com.codahale.metrics.servlet; import com.codahale.metrics.MetricRegistry; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * A listener implementation which injects a {@link MetricRegistry} instance into the servlet * context. Implement {@link #getMetricRegistry()} to return the {@link MetricRegistry} for your * application. */ public abstract class InstrumentedFilterContextListener implements ServletContextListener { /** * @return the {@link MetricRegistry} to inject into the servlet context. */ protected abstract MetricRegistry getMetricRegistry(); @Override public void contextInitialized(ServletContextEvent sce) { sce.getServletContext().setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE, getMetricRegistry()); } @Override public void contextDestroyed(ServletContextEvent sce) { } } metrics-3.2.5/metrics-servlet/src/test/000077500000000000000000000000001315671014200200655ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/000077500000000000000000000000001315671014200210065ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/com/000077500000000000000000000000001315671014200215645ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/com/codahale/000077500000000000000000000000001315671014200233245ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200247725ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/com/codahale/metrics/servlet/000077500000000000000000000000001315671014200264565ustar00rootroot00000000000000InstrumentedFilterContextListenerTest.java000066400000000000000000000021371315671014200370070ustar00rootroot00000000000000metrics-3.2.5/metrics-servlet/src/test/java/com/codahale/metrics/servletpackage com.codahale.metrics.servlet; import com.codahale.metrics.MetricRegistry; import org.junit.Test; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class InstrumentedFilterContextListenerTest { private final MetricRegistry registry = mock(MetricRegistry.class); private final InstrumentedFilterContextListener listener = new InstrumentedFilterContextListener() { @Override protected MetricRegistry getMetricRegistry() { return registry; } }; @Test public void injectsTheMetricRegistryIntoTheServletContext() throws Exception { final ServletContext context = mock(ServletContext.class); final ServletContextEvent event = mock(ServletContextEvent.class); when(event.getServletContext()).thenReturn(context); listener.contextInitialized(event); verify(context).setAttribute("com.codahale.metrics.servlet.InstrumentedFilter.registry", registry); } } metrics-3.2.5/metrics-servlets/000077500000000000000000000000001315671014200165025ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/pom.xml000066400000000000000000000051151315671014200200210ustar00rootroot00000000000000 4.0.0 io.dropwizard.metrics metrics-parent 3.2.5 metrics-servlets Metrics Utility Servlets bundle A set of utility servlets for Metrics, allowing you to expose valuable information about your production environment. io.dropwizard.metrics metrics-core ${project.version} io.dropwizard.metrics metrics-healthchecks ${project.version} io.dropwizard.metrics metrics-json ${project.version} io.dropwizard.metrics metrics-jvm ${project.version} com.papertrail profiler 1.0.2 javax.servlet javax.servlet-api ${servlet.version} provided com.fasterxml.jackson.core jackson-databind ${jackson.version} org.eclipse.jetty jetty-servlet ${jetty9.version} test io.dropwizard.metrics metrics-jetty9 ${project.version} test metrics-3.2.5/metrics-servlets/src/000077500000000000000000000000001315671014200172715ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/000077500000000000000000000000001315671014200202155ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/000077500000000000000000000000001315671014200211365ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/000077500000000000000000000000001315671014200217145ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/000077500000000000000000000000001315671014200234545ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/000077500000000000000000000000001315671014200251225ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/000077500000000000000000000000001315671014200267715ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/AdminServlet.java000077500000000000000000000133771315671014200322470ustar00rootroot00000000000000package com.codahale.metrics.servlets; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.text.MessageFormat; public class AdminServlet extends HttpServlet { public static final String DEFAULT_HEALTHCHECK_URI = "/healthcheck"; public static final String DEFAULT_METRICS_URI = "/metrics"; public static final String DEFAULT_PING_URI = "/ping"; public static final String DEFAULT_THREADS_URI = "/threads"; public static final String DEFAULT_CPU_PROFILE_URI = "/pprof"; public static final String METRICS_URI_PARAM_KEY = "metrics-uri"; public static final String PING_URI_PARAM_KEY = "ping-uri"; public static final String THREADS_URI_PARAM_KEY = "threads-uri"; public static final String HEALTHCHECK_URI_PARAM_KEY = "healthcheck-uri"; public static final String SERVICE_NAME_PARAM_KEY= "service-name"; public static final String CPU_PROFILE_URI_PARAM_KEY = "cpu-profile-uri"; private static final String TEMPLATE = String.format( "%n" + "%n" + "%n" + " Metrics{10}%n" + "%n" + "%n" + "

    Operational Menu{10}

    %n" + " %n" + "%n" + "" ); private static final String CONTENT_TYPE = "text/html"; private static final long serialVersionUID = -2850794040708785318L; private transient HealthCheckServlet healthCheckServlet; private transient MetricsServlet metricsServlet; private transient PingServlet pingServlet; private transient ThreadDumpServlet threadDumpServlet; private transient CpuProfileServlet cpuProfileServlet; private transient String metricsUri; private transient String pingUri; private transient String threadsUri; private transient String healthcheckUri; private transient String cpuprofileUri; private transient String serviceName; @Override public void init(ServletConfig config) throws ServletException { super.init(config); this.healthCheckServlet = new HealthCheckServlet(); healthCheckServlet.init(config); this.metricsServlet = new MetricsServlet(); metricsServlet.init(config); this.pingServlet = new PingServlet(); pingServlet.init(config); this.threadDumpServlet = new ThreadDumpServlet(); threadDumpServlet.init(config); this.cpuProfileServlet = new CpuProfileServlet(); cpuProfileServlet.init(config); this.metricsUri = getParam(config.getInitParameter(METRICS_URI_PARAM_KEY), DEFAULT_METRICS_URI); this.pingUri = getParam(config.getInitParameter(PING_URI_PARAM_KEY), DEFAULT_PING_URI); this.threadsUri = getParam(config.getInitParameter(THREADS_URI_PARAM_KEY), DEFAULT_THREADS_URI); this.healthcheckUri = getParam(config.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI); this.cpuprofileUri = getParam(config.getInitParameter(CPU_PROFILE_URI_PARAM_KEY), DEFAULT_CPU_PROFILE_URI); this.serviceName = getParam(config.getInitParameter(SERVICE_NAME_PARAM_KEY), null); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final String path = req.getContextPath() + req.getServletPath(); resp.setStatus(HttpServletResponse.SC_OK); resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); resp.setContentType(CONTENT_TYPE); final PrintWriter writer = resp.getWriter(); try { writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path, threadsUri, path, healthcheckUri, path, cpuprofileUri, serviceName == null ? "" : " (" + serviceName + ")")); } finally { writer.close(); } } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final String uri = req.getPathInfo(); if (uri == null || uri.equals("/")) { super.service(req, resp); } else if (uri.equals(healthcheckUri)) { healthCheckServlet.service(req, resp); } else if (uri.startsWith(metricsUri)) { metricsServlet.service(req, resp); } else if (uri.equals(pingUri)) { pingServlet.service(req, resp); } else if (uri.equals(threadsUri)) { threadDumpServlet.service(req, resp); } else if (uri.equals(cpuprofileUri)) { cpuProfileServlet.service(req, resp); } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } } private static String getParam(String initParam, String defaultValue) { return initParam == null ? defaultValue : initParam; } } AdminServletContextListener.java000066400000000000000000000036671315671014200352410ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.health.HealthCheckRegistry; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.util.concurrent.ExecutorService; /** * A listener implementation which injects a {@link MetricRegistry} instance, a * {@link HealthCheckRegistry} instance, and an optional {@link ExecutorService} instance into the * servlet context as named attributes. * * @deprecated Use {@link MetricsServlet.ContextListener} and * {@link HealthCheckServlet.ContextListener} instead. */ @Deprecated public abstract class AdminServletContextListener implements ServletContextListener { /** * @return the {@link MetricRegistry} to inject into the servlet context. */ protected abstract MetricRegistry getMetricRegistry(); /** * @return the {@link HealthCheckRegistry} to inject into the servlet context. */ protected abstract HealthCheckRegistry getHealthCheckRegistry(); /** * @return the {@link ExecutorService} to inject into the servlet context, or {@code null} if * the health checks should be run in the servlet worker thread. */ protected ExecutorService getExecutorService() { // don't use a thread pool by default return null; } @Override public void contextInitialized(ServletContextEvent servletContextEvent) { final ServletContext context = servletContextEvent.getServletContext(); context.setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY, getHealthCheckRegistry()); context.setAttribute(HealthCheckServlet.HEALTH_CHECK_EXECUTOR, getExecutorService()); context.setAttribute(MetricsServlet.METRICS_REGISTRY, getMetricRegistry()); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { // no-op... } } metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/CpuProfileServlet.java000066400000000000000000000055231315671014200332560ustar00rootroot00000000000000package com.codahale.metrics.servlets; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.joda.time.Duration; import com.papertrail.profiler.CpuProfile; /** * An HTTP servlets which outputs a pprof parseable response. */ public class CpuProfileServlet extends HttpServlet { private static final long serialVersionUID = -668666696530287501L; private static final String CONTENT_TYPE = "pprof/raw"; private static final String CACHE_CONTROL = "Cache-Control"; private static final String NO_CACHE = "must-revalidate,no-cache,no-store"; private final Lock lock = new ReentrantLock(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int duration = 10; if (req.getParameter("duration") != null) { try { duration = Integer.parseInt(req.getParameter("duration")); } catch (NumberFormatException e) { duration = 10; } } int frequency = 100; if (req.getParameter("frequency") != null) { try { frequency = Integer.parseInt(req.getParameter("frequency")); } catch (NumberFormatException e) { frequency = 100; } } final Thread.State state; if ("blocked".equalsIgnoreCase(req.getParameter("state"))) { state = Thread.State.BLOCKED; } else { state = Thread.State.RUNNABLE; } resp.setStatus(HttpServletResponse.SC_OK); resp.setHeader(CACHE_CONTROL, NO_CACHE); resp.setContentType(CONTENT_TYPE); final OutputStream output = resp.getOutputStream(); try { doProfile(output, duration, frequency, state); } finally { output.close(); } } protected void doProfile(OutputStream out, int duration, int frequency, Thread.State state) throws IOException { if (lock.tryLock()) { try { CpuProfile profile = CpuProfile.record(Duration.standardSeconds(duration), frequency, state); if (profile == null) { throw new RuntimeException("could not create CpuProfile"); } profile.writeGoogleProfile(out); return; } finally { lock.unlock(); } } throw new RuntimeException("Only one profile request may be active at a time"); } } metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/HealthCheckServlet.java000066400000000000000000000116321315671014200333470ustar00rootroot00000000000000package com.codahale.metrics.servlets; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.codahale.metrics.health.HealthCheck; import com.codahale.metrics.health.HealthCheckRegistry; import com.codahale.metrics.json.HealthCheckModule; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.ExecutorService; public class HealthCheckServlet extends HttpServlet { public static abstract class ContextListener implements ServletContextListener { /** * @return the {@link HealthCheckRegistry} to inject into the servlet context. */ protected abstract HealthCheckRegistry getHealthCheckRegistry(); /** * @return the {@link ExecutorService} to inject into the servlet context, or {@code null} * if the health checks should be run in the servlet worker thread. */ protected ExecutorService getExecutorService() { // don't use a thread pool by default return null; } @Override public void contextInitialized(ServletContextEvent event) { final ServletContext context = event.getServletContext(); context.setAttribute(HEALTH_CHECK_REGISTRY, getHealthCheckRegistry()); context.setAttribute(HEALTH_CHECK_EXECUTOR, getExecutorService()); } @Override public void contextDestroyed(ServletContextEvent event) { // no-op } } public static final String HEALTH_CHECK_REGISTRY = HealthCheckServlet.class.getCanonicalName() + ".registry"; public static final String HEALTH_CHECK_EXECUTOR = HealthCheckServlet.class.getCanonicalName() + ".executor"; private static final long serialVersionUID = -8432996484889177321L; private static final String CONTENT_TYPE = "application/json"; private transient HealthCheckRegistry registry; private transient ExecutorService executorService; private transient ObjectMapper mapper; public HealthCheckServlet() { } public HealthCheckServlet(HealthCheckRegistry registry) { this.registry = registry; } @Override public void init(ServletConfig config) throws ServletException { super.init(config); if (null == registry) { final Object registryAttr = config.getServletContext().getAttribute(HEALTH_CHECK_REGISTRY); if (registryAttr instanceof HealthCheckRegistry) { this.registry = (HealthCheckRegistry) registryAttr; } else { throw new ServletException("Couldn't find a HealthCheckRegistry instance."); } } final Object executorAttr = config.getServletContext().getAttribute(HEALTH_CHECK_EXECUTOR); if (executorAttr instanceof ExecutorService) { this.executorService = (ExecutorService) executorAttr; } this.mapper = new ObjectMapper().registerModule(new HealthCheckModule()); } @Override public void destroy() { super.destroy(); registry.shutdown(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final SortedMap results = runHealthChecks(); resp.setContentType(CONTENT_TYPE); resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); if (results.isEmpty()) { resp.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); } else { if (isAllHealthy(results)) { resp.setStatus(HttpServletResponse.SC_OK); } else { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } final OutputStream output = resp.getOutputStream(); try { getWriter(req).writeValue(output, results); } finally { output.close(); } } private ObjectWriter getWriter(HttpServletRequest request) { final boolean prettyPrint = Boolean.parseBoolean(request.getParameter("pretty")); if (prettyPrint) { return mapper.writerWithDefaultPrettyPrinter(); } return mapper.writer(); } private SortedMap runHealthChecks() { if (executorService == null) { return registry.runHealthChecks(); } return registry.runHealthChecks(executorService); } private static boolean isAllHealthy(Map results) { for (HealthCheck.Result result : results.values()) { if (!result.isHealthy()) { return false; } } return true; } } metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/MetricsServlet.java000066400000000000000000000174311315671014200326150ustar00rootroot00000000000000package com.codahale.metrics.servlets; import java.io.IOException; import java.io.OutputStream; import java.util.Locale; import java.util.concurrent.TimeUnit; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.codahale.metrics.MetricFilter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.json.MetricsModule; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.util.JSONPObject; /** * A servlet which returns the metrics in a given registry as an {@code application/json} response. */ public class MetricsServlet extends HttpServlet { /** * An abstract {@link ServletContextListener} which allows you to programmatically inject the * {@link MetricRegistry}, rate and duration units, and allowed origin for * {@link MetricsServlet}. */ public static abstract class ContextListener implements ServletContextListener { /** * @return the {@link MetricRegistry} to inject into the servlet context. */ protected abstract MetricRegistry getMetricRegistry(); /** * @return the {@link TimeUnit} to which rates should be converted, or {@code null} if the * default should be used. */ protected TimeUnit getRateUnit() { // use the default return null; } /** * @return the {@link TimeUnit} to which durations should be converted, or {@code null} if * the default should be used. */ protected TimeUnit getDurationUnit() { // use the default return null; } /** * @return the {@code Access-Control-Allow-Origin} header value, if any. */ protected String getAllowedOrigin() { // use the default return null; } /** * Returns the name of the parameter used to specify the jsonp callback, if any. */ protected String getJsonpCallbackParameter() { return null; } /** * Returns the {@link MetricFilter} that shall be used to filter metrics, or {@link MetricFilter#ALL} if * the default should be used. */ protected MetricFilter getMetricFilter() { // use the default return MetricFilter.ALL; } @Override public void contextInitialized(ServletContextEvent event) { final ServletContext context = event.getServletContext(); context.setAttribute(METRICS_REGISTRY, getMetricRegistry()); context.setAttribute(METRIC_FILTER, getMetricFilter()); if (getDurationUnit() != null) { context.setInitParameter(MetricsServlet.DURATION_UNIT, getDurationUnit().toString()); } if (getRateUnit() != null) { context.setInitParameter(MetricsServlet.RATE_UNIT, getRateUnit().toString()); } if (getAllowedOrigin() != null) { context.setInitParameter(MetricsServlet.ALLOWED_ORIGIN, getAllowedOrigin()); } if (getJsonpCallbackParameter() != null) { context.setAttribute(CALLBACK_PARAM, getJsonpCallbackParameter()); } } @Override public void contextDestroyed(ServletContextEvent event) { // no-op } } public static final String RATE_UNIT = MetricsServlet.class.getCanonicalName() + ".rateUnit"; public static final String DURATION_UNIT = MetricsServlet.class.getCanonicalName() + ".durationUnit"; public static final String SHOW_SAMPLES = MetricsServlet.class.getCanonicalName() + ".showSamples"; public static final String METRICS_REGISTRY = MetricsServlet.class.getCanonicalName() + ".registry"; public static final String ALLOWED_ORIGIN = MetricsServlet.class.getCanonicalName() + ".allowedOrigin"; public static final String METRIC_FILTER = MetricsServlet.class.getCanonicalName() + ".metricFilter"; public static final String CALLBACK_PARAM = MetricsServlet.class.getCanonicalName() + ".jsonpCallback"; private static final long serialVersionUID = 1049773947734939602L; private static final String CONTENT_TYPE = "application/json"; private String allowedOrigin; private String jsonpParamName; private transient MetricRegistry registry; private transient ObjectMapper mapper; public MetricsServlet() { } public MetricsServlet(MetricRegistry registry) { this.registry = registry; } @Override public void init(ServletConfig config) throws ServletException { super.init(config); final ServletContext context = config.getServletContext(); if (null == registry) { final Object registryAttr = context.getAttribute(METRICS_REGISTRY); if (registryAttr instanceof MetricRegistry) { this.registry = (MetricRegistry) registryAttr; } else { throw new ServletException("Couldn't find a MetricRegistry instance."); } } final TimeUnit rateUnit = parseTimeUnit(context.getInitParameter(RATE_UNIT), TimeUnit.SECONDS); final TimeUnit durationUnit = parseTimeUnit(context.getInitParameter(DURATION_UNIT), TimeUnit.SECONDS); final boolean showSamples = Boolean.parseBoolean(context.getInitParameter(SHOW_SAMPLES)); MetricFilter filter = (MetricFilter) context.getAttribute(METRIC_FILTER); if (filter == null) { filter = MetricFilter.ALL; } this.mapper = new ObjectMapper().registerModule(new MetricsModule(rateUnit, durationUnit, showSamples, filter)); this.allowedOrigin = context.getInitParameter(ALLOWED_ORIGIN); this.jsonpParamName = context.getInitParameter(CALLBACK_PARAM); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(CONTENT_TYPE); if (allowedOrigin != null) { resp.setHeader("Access-Control-Allow-Origin", allowedOrigin); } resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); resp.setStatus(HttpServletResponse.SC_OK); final OutputStream output = resp.getOutputStream(); try { if (jsonpParamName != null && req.getParameter(jsonpParamName) != null) { getWriter(req).writeValue(output, new JSONPObject(req.getParameter(jsonpParamName), registry)); } else { getWriter(req).writeValue(output, registry); } } finally { output.close(); } } private ObjectWriter getWriter(HttpServletRequest request) { final boolean prettyPrint = Boolean.parseBoolean(request.getParameter("pretty")); if (prettyPrint) { return mapper.writerWithDefaultPrettyPrinter(); } return mapper.writer(); } private TimeUnit parseTimeUnit(String value, TimeUnit defaultValue) { try { return TimeUnit.valueOf(String.valueOf(value).toUpperCase(Locale.US)); } catch (IllegalArgumentException e) { return defaultValue; } } } metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/PingServlet.java000066400000000000000000000022551315671014200321020ustar00rootroot00000000000000package com.codahale.metrics.servlets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * An HTTP servlets which outputs a {@code text/plain} {@code "pong"} response. */ public class PingServlet extends HttpServlet { private static final long serialVersionUID = 3772654177231086757L; private static final String CONTENT_TYPE = "text/plain"; private static final String CONTENT = "pong"; private static final String CACHE_CONTROL = "Cache-Control"; private static final String NO_CACHE = "must-revalidate,no-cache,no-store"; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setHeader(CACHE_CONTROL, NO_CACHE); resp.setContentType(CONTENT_TYPE); final PrintWriter writer = resp.getWriter(); try { writer.println(CONTENT); } finally { writer.close(); } } } metrics-3.2.5/metrics-servlets/src/main/java/com/codahale/metrics/servlets/ThreadDumpServlet.java000066400000000000000000000033521315671014200332410ustar00rootroot00000000000000package com.codahale.metrics.servlets; import com.codahale.metrics.jvm.ThreadDump; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.lang.management.ManagementFactory; /** * An HTTP servlets which outputs a {@code text/plain} dump of all threads in * the VM. Only responds to {@code GET} requests. */ public class ThreadDumpServlet extends HttpServlet { private static final long serialVersionUID = -2690343532336103046L; private static final String CONTENT_TYPE = "text/plain"; private transient ThreadDump threadDump; @Override public void init() throws ServletException { try { // Some PaaS like Google App Engine blacklist java.lang.managament this.threadDump = new ThreadDump(ManagementFactory.getThreadMXBean()); } catch (NoClassDefFoundError ncdfe) { this.threadDump = null; // we won't be able to provide thread dump } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType(CONTENT_TYPE); resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store"); if (threadDump == null) { resp.getWriter().println("Sorry your runtime environment does not allow to dump threads."); return; } final OutputStream output = resp.getOutputStream(); try { threadDump.dump(output); } finally { output.close(); } } } metrics-3.2.5/metrics-servlets/src/test/000077500000000000000000000000001315671014200202505ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/000077500000000000000000000000001315671014200211715ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/000077500000000000000000000000001315671014200217475ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/000077500000000000000000000000001315671014200235075ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/000077500000000000000000000000001315671014200251555ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/000077500000000000000000000000001315671014200270245ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AbstractServletTest.java000066400000000000000000000014711315671014200336420ustar00rootroot00000000000000package com.codahale.metrics.servlets; import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.servlet.ServletTester; import org.junit.After; import org.junit.Before; public abstract class AbstractServletTest { private final ServletTester tester = new ServletTester(); protected final HttpTester.Request request = HttpTester.newRequest(); protected HttpTester.Response response; @Before public void setUpTester() throws Exception { setUp(tester); tester.start(); } protected abstract void setUp(ServletTester tester); @After public void tearDownTester() throws Exception { tester.stop(); } protected void processRequest() throws Exception { this.response = HttpTester.parseResponse(tester.getResponses(request.generate())); } } AdminServletContextListenerTest.java000066400000000000000000000043111315671014200361170ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.health.HealthCheckRegistry; import org.junit.Before; import org.junit.Test; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import java.util.concurrent.ExecutorService; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @SuppressWarnings("deprecation") public class AdminServletContextListenerTest { private final MetricRegistry metricRegistry = mock(MetricRegistry.class); private final HealthCheckRegistry healthCheckRegistry = mock(HealthCheckRegistry.class); private final ExecutorService executorService = mock(ExecutorService.class); private final AdminServletContextListener listener = new AdminServletContextListener() { @Override protected MetricRegistry getMetricRegistry() { return metricRegistry; } @Override protected HealthCheckRegistry getHealthCheckRegistry() { return healthCheckRegistry; } @Override protected ExecutorService getExecutorService() { return executorService; } }; private final ServletContext context = mock(ServletContext.class); private final ServletContextEvent event = mock(ServletContextEvent.class); @Before public void setUp() throws Exception { when(event.getServletContext()).thenReturn(context); } @Test public void injectsTheMetricRegistry() throws Exception { listener.contextInitialized(event); verify(context).setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", metricRegistry); } @Test public void injectsTheHealthCheckRegistry() throws Exception { listener.contextInitialized(event); verify(context).setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry); } @Test public void injectsTheHealthCheckExecutor() throws Exception { listener.contextInitialized(event); verify(context).setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.executor", executorService); } } metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/AdminServletTest.java000077500000000000000000000053701315671014200331340ustar00rootroot00000000000000package com.codahale.metrics.servlets; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.health.HealthCheckRegistry; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class AdminServletTest extends AbstractServletTest { private final MetricRegistry registry = new MetricRegistry(); private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); @Override protected void setUp(ServletTester tester) { tester.setContextPath("/context"); tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry); tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry); tester.addServlet(AdminServlet.class, "/admin"); } @Before public void setUp() throws Exception { request.setMethod("GET"); request.setURI("/context/admin"); request.setVersion("HTTP/1.0"); } @Test public void returnsA200() throws Exception { processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.getContent()) .isEqualTo(String.format( "%n" + "%n" + "%n" + " Metrics%n" + "%n" + "%n" + "

    Operational Menu

    %n" + " %n" + "%n" + "%n" )); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("text/html; charset=ISO-8859-1"); } } CpuProfileServletTest.java000066400000000000000000000022001315671014200340570ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import static org.assertj.core.api.Assertions.assertThat; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Test; public class CpuProfileServletTest extends AbstractServletTest { @Override protected void setUp(ServletTester tester) { tester.addServlet(CpuProfileServlet.class, "/pprof"); } @Before public void setUp() throws Exception { request.setMethod("GET"); request.setURI("/pprof?duration=1"); request.setVersion("HTTP/1.0"); processRequest(); } @Test public void returns200OK() throws Exception { assertThat(response.getStatus()) .isEqualTo(200); } @Test public void returnsPprofRaw() throws Exception { assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("pprof/raw"); } @Test public void returnsUncacheable() throws Exception { assertThat(response.get(HttpHeader.CACHE_CONTROL)) .isEqualTo("must-revalidate,no-cache,no-store"); } } HealthCheckServletTest.java000066400000000000000000000146651315671014200341740ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import com.codahale.metrics.health.HealthCheck; import com.codahale.metrics.health.HealthCheckRegistry; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import static org.assertj.core.api.Assertions.assertThat; public class HealthCheckServletTest extends AbstractServletTest { private final HealthCheckRegistry registry = new HealthCheckRegistry(); private final ExecutorService threadPool = Executors.newCachedThreadPool(); @Override protected void setUp(ServletTester tester) { tester.addServlet(HealthCheckServlet.class, "/healthchecks"); tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", registry); tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.executor", threadPool); } @Before public void setUp() throws Exception { request.setMethod("GET"); request.setURI("/healthchecks"); request.setVersion("HTTP/1.0"); } @After public void tearDown() throws Exception { threadPool.shutdown(); } @Test public void returns501IfNoHealthChecksAreRegistered() throws Exception { processRequest(); assertThat(response.getStatus()) .isEqualTo(501); assertThat(response.getContent()) .isEqualTo("{}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void returnsA200IfAllHealthChecksAreHealthy() throws Exception { registry.register("fun", new HealthCheck() { @Override protected Result check() throws Exception { return Result.healthy("whee"); } }); processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.getContent()) .isEqualTo("{\"fun\":{\"healthy\":true,\"message\":\"whee\"}}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void returnsA500IfAnyHealthChecksAreUnhealthy() throws Exception { registry.register("fun", new HealthCheck() { @Override protected Result check() throws Exception { return Result.healthy("whee"); } }); registry.register("notFun", new HealthCheck() { @Override protected Result check() throws Exception { return Result.unhealthy("whee"); } }); processRequest(); assertThat(response.getStatus()) .isEqualTo(500); assertThat(response.getContent()) .isEqualTo("{\"fun\":{\"healthy\":true,\"message\":\"whee\"},\"notFun\":{\"healthy\":false,\"message\":\"whee\"}}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void optionallyPrettyPrintsTheJson() throws Exception { registry.register("fun", new HealthCheck() { @Override protected Result check() throws Exception { return Result.healthy("whee"); } }); request.setURI("/healthchecks?pretty=true"); processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.getContent()) .isEqualTo(String.format("{%n" + " \"fun\" : {%n" + " \"healthy\" : true,%n" + " \"message\" : \"whee\"%n" + " }%n" + "}")); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void constructorWithRegistryAsArgumentIsUsedInPreferenceOverServletConfig() throws Exception { final HealthCheckRegistry healthCheckRegistry = mock(HealthCheckRegistry.class); final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); final HealthCheckServlet healthCheckServlet = new HealthCheckServlet(healthCheckRegistry); healthCheckServlet.init(servletConfig); verify(servletConfig, times(1)).getServletContext(); verify(servletContext, never()).getAttribute(eq(HealthCheckServlet.HEALTH_CHECK_REGISTRY)); } @Test public void constructorWithRegistryAsArgumentUsesServletConfigWhenNull() throws Exception { final HealthCheckRegistry healthCheckRegistry = mock(HealthCheckRegistry.class); final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); when(servletContext.getAttribute(eq(HealthCheckServlet.HEALTH_CHECK_REGISTRY))) .thenReturn(healthCheckRegistry); final HealthCheckServlet healthCheckServlet = new HealthCheckServlet(null); healthCheckServlet.init(servletConfig); verify(servletConfig, times(2)).getServletContext(); verify(servletContext, times(1)).getAttribute(eq(HealthCheckServlet.HEALTH_CHECK_REGISTRY)); } @Test(expected = ServletException.class) public void constructorWithRegistryAsArgumentUsesServletConfigWhenNullButWrongTypeInContext() throws Exception { final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); when(servletContext.getAttribute(eq(HealthCheckServlet.HEALTH_CHECK_REGISTRY))) .thenReturn("IRELLEVANT_STRING"); final HealthCheckServlet healthCheckServlet = new HealthCheckServlet(null); healthCheckServlet.init(servletConfig); } } MetricsServletContextListenerTest.java000066400000000000000000000157631315671014200365120ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import com.codahale.metrics.*; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MetricsServletContextListenerTest extends AbstractServletTest { private final Clock clock = mock(Clock.class); private final MetricRegistry registry = new MetricRegistry(); private final String allowedOrigin = "some.other.origin"; @Override protected void setUp(ServletTester tester) { tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry); tester.addServlet(MetricsServlet.class, "/metrics"); tester.getContext().addEventListener(new MetricsServlet.ContextListener(){ @Override protected MetricRegistry getMetricRegistry() { return registry; } @Override protected TimeUnit getDurationUnit() { return TimeUnit.MILLISECONDS; } @Override protected TimeUnit getRateUnit() { return TimeUnit.MINUTES; } @Override protected String getAllowedOrigin() { return allowedOrigin; } }); } @Before public void setUp() throws Exception { when(clock.getTick()).thenReturn(100L, 200L, 300L, 400L); registry.register("g1", new Gauge() { @Override public Long getValue() { return 100L; } }); registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); registry.register("t", new Timer(new ExponentiallyDecayingReservoir(), clock)) .update(1, TimeUnit.SECONDS); request.setMethod("GET"); request.setURI("/metrics"); request.setVersion("HTTP/1.0"); } @Test public void returnsA200() throws Exception { processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo(allowedOrigin); assertThat(response.getContent()) .isEqualTo("{" + "\"version\":\"3.1.3\"," + "\"gauges\":{" + "\"g1\":{\"value\":100}" + "}," + "\"counters\":{" + "\"c\":{\"count\":1}" + "}," + "\"histograms\":{" + "\"h\":{\"count\":1,\"max\":1,\"mean\":1.0,\"min\":1,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0}" + "}," + "\"meters\":{" + "\"m\":{\"count\":1,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":2.0E8,\"units\":\"events/minute\"}},\"timers\":{\"t\":{\"count\":1,\"max\":1000.0,\"mean\":1000.0,\"min\":1000.0,\"p50\":1000.0,\"p75\":1000.0,\"p95\":1000.0,\"p98\":1000.0,\"p99\":1000.0,\"p999\":1000.0,\"stddev\":0.0,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":6.0E8,\"duration_units\":\"milliseconds\",\"rate_units\":\"calls/minute\"}" + "}" + "}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void optionallyPrettyPrintsTheJson() throws Exception { request.setURI("/metrics?pretty=true"); processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo(allowedOrigin); assertThat(response.getContent()) .isEqualTo(String.format("{%n" + " \"version\" : \"3.1.3\",%n" + " \"gauges\" : {%n" + " \"g1\" : {%n" + " \"value\" : 100%n" + " }%n" + " },%n" + " \"counters\" : {%n" + " \"c\" : {%n" + " \"count\" : 1%n" + " }%n" + " },%n" + " \"histograms\" : {%n" + " \"h\" : {%n" + " \"count\" : 1,%n" + " \"max\" : 1,%n" + " \"mean\" : 1.0,%n" + " \"min\" : 1,%n" + " \"p50\" : 1.0,%n" + " \"p75\" : 1.0,%n" + " \"p95\" : 1.0,%n" + " \"p98\" : 1.0,%n" + " \"p99\" : 1.0,%n" + " \"p999\" : 1.0,%n" + " \"stddev\" : 0.0%n" + " }%n" + " },%n" + " \"meters\" : {%n" + " \"m\" : {%n" + " \"count\" : 1,%n" + " \"m15_rate\" : 0.0,%n" + " \"m1_rate\" : 0.0,%n" + " \"m5_rate\" : 0.0,%n" + " \"mean_rate\" : 2.0E8,%n" + " \"units\" : \"events/minute\"%n" + " }%n" + " },%n" + " \"timers\" : {%n" + " \"t\" : {%n" + " \"count\" : 1,%n" + " \"max\" : 1000.0,%n" + " \"mean\" : 1000.0,%n" + " \"min\" : 1000.0,%n" + " \"p50\" : 1000.0,%n" + " \"p75\" : 1000.0,%n" + " \"p95\" : 1000.0,%n" + " \"p98\" : 1000.0,%n" + " \"p99\" : 1000.0,%n" + " \"p999\" : 1000.0,%n" + " \"stddev\" : 0.0,%n" + " \"m15_rate\" : 0.0,%n" + " \"m1_rate\" : 0.0,%n" + " \"m5_rate\" : 0.0,%n" + " \"mean_rate\" : 6.0E8,%n" + " \"duration_units\" : \"milliseconds\",%n" + " \"rate_units\" : \"calls/minute\"%n" + " }%n" + " }%n" + "}")); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } } metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/MetricsServletTest.java000066400000000000000000000342751315671014200335150ustar00rootroot00000000000000package com.codahale.metrics.servlets; import com.codahale.metrics.*; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Test; import java.util.concurrent.TimeUnit; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class MetricsServletTest extends AbstractServletTest { private final Clock clock = mock(Clock.class); private final MetricRegistry registry = new MetricRegistry(); private ServletTester tester; @Override protected void setUp(ServletTester tester) { this.tester = tester; tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry); tester.addServlet(MetricsServlet.class, "/metrics"); tester.getContext().setInitParameter("com.codahale.metrics.servlets.MetricsServlet.allowedOrigin", "*"); } @Before public void setUp() throws Exception { when(clock.getTick()).thenReturn(100L, 200L, 300L, 400L); registry.register("g1", new Gauge() { @Override public Long getValue() { return 100L; } }); registry.counter("c").inc(); registry.histogram("h").update(1); registry.register("m", new Meter(clock)).mark(); registry.register("t", new Timer(new ExponentiallyDecayingReservoir(), clock)) .update(1, TimeUnit.SECONDS); request.setMethod("GET"); request.setURI("/metrics"); request.setVersion("HTTP/1.0"); } @Test public void returnsA200() throws Exception { processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo("*"); assertThat(response.getContent()) .isEqualTo("{" + "\"version\":\"3.1.3\"," + "\"gauges\":{" + "\"g1\":{\"value\":100}" + "}," + "\"counters\":{" + "\"c\":{\"count\":1}" + "}," + "\"histograms\":{" + "\"h\":{\"count\":1,\"max\":1,\"mean\":1.0,\"min\":1,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0}" + "}," + "\"meters\":{" + "\"m\":{\"count\":1,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":3333333.3333333335,\"units\":\"events/second\"}},\"timers\":{\"t\":{\"count\":1,\"max\":1.0,\"mean\":1.0,\"min\":1.0,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":1.0E7,\"duration_units\":\"seconds\",\"rate_units\":\"calls/second\"}" + "}" + "}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void returnsJsonWhenJsonpInitParamNotSet() throws Exception { String callbackParamName = "callbackParam"; String callbackParamVal = "callbackParamVal"; request.setURI("/metrics?" + callbackParamName + "=" + callbackParamVal); processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo("*"); assertThat(response.getContent()) .isEqualTo("{" + "\"version\":\"3.1.3\"," + "\"gauges\":{" + "\"g1\":{\"value\":100}" + "}," + "\"counters\":{" + "\"c\":{\"count\":1}" + "}," + "\"histograms\":{" + "\"h\":{\"count\":1,\"max\":1,\"mean\":1.0,\"min\":1,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0}" + "}," + "\"meters\":{" + "\"m\":{\"count\":1,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":3333333.3333333335,\"units\":\"events/second\"}},\"timers\":{\"t\":{\"count\":1,\"max\":1.0,\"mean\":1.0,\"min\":1.0,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":1.0E7,\"duration_units\":\"seconds\",\"rate_units\":\"calls/second\"}" + "}" + "}"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void returnsJsonpWhenInitParamSet() throws Exception { String callbackParamName = "callbackParam"; String callbackParamVal = "callbackParamVal"; request.setURI("/metrics?" + callbackParamName + "=" + callbackParamVal); tester.getContext().setInitParameter("com.codahale.metrics.servlets.MetricsServlet.jsonpCallback", callbackParamName); processRequest(); assertThat(response.getStatus()).isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo("*"); assertThat(response.getContent()) .isEqualTo(callbackParamVal + "({" + "\"version\":\"3.1.3\"," + "\"gauges\":{" + "\"g1\":{\"value\":100}" + "}," + "\"counters\":{" + "\"c\":{\"count\":1}" + "}," + "\"histograms\":{" + "\"h\":{\"count\":1,\"max\":1,\"mean\":1.0,\"min\":1,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0}" + "}," + "\"meters\":{" + "\"m\":{\"count\":1,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":3333333.3333333335,\"units\":\"events/second\"}},\"timers\":{\"t\":{\"count\":1,\"max\":1.0,\"mean\":1.0,\"min\":1.0,\"p50\":1.0,\"p75\":1.0,\"p95\":1.0,\"p98\":1.0,\"p99\":1.0,\"p999\":1.0,\"stddev\":0.0,\"m15_rate\":0.0,\"m1_rate\":0.0,\"m5_rate\":0.0,\"mean_rate\":1.0E7,\"duration_units\":\"seconds\",\"rate_units\":\"calls/second\"}" + "}" + "})"); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void optionallyPrettyPrintsTheJson() throws Exception { request.setURI("/metrics?pretty=true"); processRequest(); assertThat(response.getStatus()) .isEqualTo(200); assertThat(response.get("Access-Control-Allow-Origin")) .isEqualTo("*"); assertThat(response.getContent()) .isEqualTo(String.format("{%n" + " \"version\" : \"3.1.3\",%n" + " \"gauges\" : {%n" + " \"g1\" : {%n" + " \"value\" : 100%n" + " }%n" + " },%n" + " \"counters\" : {%n" + " \"c\" : {%n" + " \"count\" : 1%n" + " }%n" + " },%n" + " \"histograms\" : {%n" + " \"h\" : {%n" + " \"count\" : 1,%n" + " \"max\" : 1,%n" + " \"mean\" : 1.0,%n" + " \"min\" : 1,%n" + " \"p50\" : 1.0,%n" + " \"p75\" : 1.0,%n" + " \"p95\" : 1.0,%n" + " \"p98\" : 1.0,%n" + " \"p99\" : 1.0,%n" + " \"p999\" : 1.0,%n" + " \"stddev\" : 0.0%n" + " }%n" + " },%n" + " \"meters\" : {%n" + " \"m\" : {%n" + " \"count\" : 1,%n" + " \"m15_rate\" : 0.0,%n" + " \"m1_rate\" : 0.0,%n" + " \"m5_rate\" : 0.0,%n" + " \"mean_rate\" : 3333333.3333333335,%n" + " \"units\" : \"events/second\"%n" + " }%n" + " },%n" + " \"timers\" : {%n" + " \"t\" : {%n" + " \"count\" : 1,%n" + " \"max\" : 1.0,%n" + " \"mean\" : 1.0,%n" + " \"min\" : 1.0,%n" + " \"p50\" : 1.0,%n" + " \"p75\" : 1.0,%n" + " \"p95\" : 1.0,%n" + " \"p98\" : 1.0,%n" + " \"p99\" : 1.0,%n" + " \"p999\" : 1.0,%n" + " \"stddev\" : 0.0,%n" + " \"m15_rate\" : 0.0,%n" + " \"m1_rate\" : 0.0,%n" + " \"m5_rate\" : 0.0,%n" + " \"mean_rate\" : 1.0E7,%n" + " \"duration_units\" : \"seconds\",%n" + " \"rate_units\" : \"calls/second\"%n" + " }%n" + " }%n" + "}")); assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("application/json"); } @Test public void constructorWithRegistryAsArgumentIsUsedInPreferenceOverServletConfig() throws Exception { final MetricRegistry metricRegistry = mock(MetricRegistry.class); final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); final MetricsServlet metricsServlet = new MetricsServlet(metricRegistry); metricsServlet.init(servletConfig); verify(servletConfig, times(1)).getServletContext(); verify(servletContext, never()).getAttribute(eq(MetricsServlet.METRICS_REGISTRY)); } @Test public void constructorWithRegistryAsArgumentUsesServletConfigWhenNull() throws Exception { final MetricRegistry metricRegistry = mock(MetricRegistry.class); final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); when(servletContext.getAttribute(eq(MetricsServlet.METRICS_REGISTRY))) .thenReturn(metricRegistry); final MetricsServlet metricsServlet = new MetricsServlet(null); metricsServlet.init(servletConfig); verify(servletConfig, times(1)).getServletContext(); verify(servletContext, times(1)).getAttribute(eq(MetricsServlet.METRICS_REGISTRY)); } @Test(expected = ServletException.class) public void constructorWithRegistryAsArgumentUsesServletConfigWhenNullButWrongTypeInContext() throws Exception { final ServletContext servletContext = mock(ServletContext.class); final ServletConfig servletConfig = mock(ServletConfig.class); when(servletConfig.getServletContext()).thenReturn(servletContext); when(servletContext.getAttribute(eq(MetricsServlet.METRICS_REGISTRY))) .thenReturn("IRELLEVANT_STRING"); final MetricsServlet metricsServlet = new MetricsServlet(null); metricsServlet.init(servletConfig); } } metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/PingServletTest.java000066400000000000000000000024361315671014200327760ustar00rootroot00000000000000package com.codahale.metrics.servlets; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class PingServletTest extends AbstractServletTest { @Override protected void setUp(ServletTester tester) { tester.addServlet(PingServlet.class, "/ping"); } @Before public void setUp() throws Exception { request.setMethod("GET"); request.setURI("/ping"); request.setVersion("HTTP/1.0"); processRequest(); } @Test public void returns200OK() throws Exception { assertThat(response.getStatus()) .isEqualTo(200); } @Test public void returnsPong() throws Exception { assertThat(response.getContent()) .isEqualTo(String.format("pong%n")); } @Test public void returnsTextPlain() throws Exception { assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("text/plain; charset=ISO-8859-1"); } @Test public void returnsUncacheable() throws Exception { assertThat(response.get(HttpHeader.CACHE_CONTROL)) .isEqualTo("must-revalidate,no-cache,no-store"); } } ThreadDumpServletTest.java000066400000000000000000000024261315671014200340560ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servletspackage com.codahale.metrics.servlets; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.servlet.ServletTester; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class ThreadDumpServletTest extends AbstractServletTest { @Override protected void setUp(ServletTester tester) { tester.addServlet(ThreadDumpServlet.class, "/threads"); } @Before public void setUp() throws Exception { request.setMethod("GET"); request.setURI("/threads"); request.setVersion("HTTP/1.0"); processRequest(); } @Test public void returns200OK() throws Exception { assertThat(response.getStatus()) .isEqualTo(200); } @Test public void returnsAThreadDump() throws Exception { assertThat(response.getContent()) .contains("Finalizer"); } @Test public void returnsTextPlain() throws Exception { assertThat(response.get(HttpHeader.CONTENT_TYPE)) .isEqualTo("text/plain"); } @Test public void returnsUncacheable() throws Exception { assertThat(response.get(HttpHeader.CACHE_CONTROL)) .isEqualTo("must-revalidate,no-cache,no-store"); } } metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/experiments/000077500000000000000000000000001315671014200313675ustar00rootroot00000000000000ExampleServer.java000066400000000000000000000055121315671014200347400ustar00rootroot00000000000000metrics-3.2.5/metrics-servlets/src/test/java/com/codahale/metrics/servlets/experimentspackage com.codahale.metrics.servlets.experiments; import com.codahale.metrics.Counter; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.health.HealthCheckRegistry; import com.codahale.metrics.jetty9.InstrumentedConnectionFactory; import com.codahale.metrics.jetty9.InstrumentedHandler; import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool; import com.codahale.metrics.servlets.AdminServlet; import com.codahale.metrics.servlets.HealthCheckServlet; import com.codahale.metrics.servlets.MetricsServlet; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.thread.ThreadPool; import static com.codahale.metrics.MetricRegistry.name; public class ExampleServer { private static final MetricRegistry REGISTRY = new MetricRegistry(); private static final Counter COUNTER_1 = REGISTRY.counter(name(ExampleServer.class, "wah", "doody")); private static final Counter COUNTER_2 = REGISTRY.counter(name(ExampleServer.class, "woo")); static { REGISTRY.register(name(ExampleServer.class, "boo"), new Gauge() { @Override public Integer getValue() { throw new RuntimeException("asplode!"); } }); } public static void main(String[] args) throws Exception { COUNTER_1.inc(); COUNTER_2.inc(); final ThreadPool threadPool = new InstrumentedQueuedThreadPool(REGISTRY); final Server server = new Server(threadPool); final Connector connector = new ServerConnector(server, new InstrumentedConnectionFactory(new HttpConnectionFactory(), REGISTRY.timer("http.connection"))); server.addConnector(connector); final ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/initial"); context.setAttribute(MetricsServlet.METRICS_REGISTRY, REGISTRY); context.setAttribute(HealthCheckServlet.HEALTH_CHECK_REGISTRY, new HealthCheckRegistry()); final ServletHolder holder = new ServletHolder(new AdminServlet()); context.addServlet(holder, "/dingo/*"); final InstrumentedHandler handler = new InstrumentedHandler(REGISTRY); handler.setHandler(context); server.setHandler(handler); server.start(); server.join(); } } metrics-3.2.5/pom.xml000066400000000000000000000276561315671014200145240ustar00rootroot00000000000000 4.0.0 3.0.1 io.dropwizard.metrics metrics-parent 3.2.5 pom Metrics Parent The Metrics library. http://metrics.dropwizard.io/ docs metrics-annotation metrics-benchmarks metrics-core metrics-healthchecks metrics-ehcache metrics-ganglia metrics-graphite metrics-httpclient metrics-httpasyncclient metrics-jcache metrics-jdbi metrics-jersey metrics-jersey2 metrics-jetty8 metrics-jetty9 metrics-jetty9-legacy metrics-json metrics-jvm metrics-log4j metrics-log4j2 metrics-logback metrics-servlet metrics-servlets metrics-jcstress UTF-8 UTF-8 3.1.0 1.7.22 2.6.6 8.1.11.v20130520 9.0.4.v20130625 9.2.2.v20140723 3.6.6 Coda Hale coda.hale@gmail.com America/Los_Angeles architect Ryan Tenney ryan@10e.us America/New_York committer Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0.html repo scm:git:git://github.com/dropwizard/metrics.git scm:git:git@github.com:dropwizard/metrics.git http://github.com/dropwizard/metrics/ v3.2.5 github http://github.com/dropwizard/metrics/issues#issue/ sonatype-nexus-snapshots Sonatype Nexus Snapshots http://oss.sonatype.org/content/repositories/snapshots sonatype-nexus-staging Nexus Release Repository http://oss.sonatype.org/service/local/staging/deploy/maven2/ org.slf4j slf4j-api ${slf4j.version} org.slf4j slf4j-api junit junit 4.11 test org.assertj assertj-core 1.6.1 test org.mockito mockito-all 1.9.5 test org.slf4j slf4j-simple ${slf4j.version} test doclint-java8-disable [1.8,) org.apache.maven.plugins maven-javadoc-plugin -Xdoclint:all -Xdoclint:-html release-sign-artifacts performRelease true org.apache.maven.plugins maven-gpg-plugin 1.6 --no-tty sign-artifacts verify sign org.apache.maven.plugins maven-compiler-plugin 3.1 1.6 1.6 org.apache.felix maven-bundle-plugin 2.3.7 true org.apache.maven.plugins maven-surefire-plugin 2.14.1 classes org.apache.maven.plugins maven-source-plugin 2.2.1 attach-sources jar org.apache.maven.plugins maven-enforcer-plugin 1.2 enforce enforce org.apache.maven.plugins maven-javadoc-plugin 2.9 attach-javadocs jar org.apache.maven.plugins maven-release-plugin 2.5.3 true forked-path v@{project.version} clean test org.codehaus.mojo findbugs-maven-plugin 3.0.0 Max Default true check org.apache.maven.plugins maven-jar-plugin 2.4 true