pax_global_header00006660000000000000000000000064144417043410014514gustar00rootroot0000000000000052 comment=f3e9bf4b264409b4fb705ce1a49eb83bfe52553d golang-github-uber-jaeger-lib-2.4.1/000077500000000000000000000000001444170434100172015ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/.github/000077500000000000000000000000001444170434100205415ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000020131444170434100232420ustar00rootroot00000000000000 ## Requirement - what kind of business use case are you trying to solve? ## Problem - what in Jaeger blocks you from solving the requirement? ## Proposal - what do you suggest to solve the problem or improve the existing situation? ## Any open questions to address golang-github-uber-jaeger-lib-2.4.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000012471444170434100243460ustar00rootroot00000000000000 ## Which problem is this PR solving? - ## Short description of the changes - golang-github-uber-jaeger-lib-2.4.1/.github/actions/000077500000000000000000000000001444170434100222015ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/.github/actions/setup-gopath/000077500000000000000000000000001444170434100246215ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/.github/actions/setup-gopath/action.yml000066400000000000000000000011721444170434100266220ustar00rootroot00000000000000name: 'Setup GOPATH' description: 'Link repo into GOPATH and export $PROJECT var' runs: using: "composite" steps: - name: Setup GOPATH shell: bash run: | export GOPATH=$(go env GOPATH) export PROJECT=$GOPATH/src/github.com/uber/jaeger-lib mkdir -p $(dirname $PROJECT) ln -s $(pwd) $PROJECT # write to GH files to make vars available to later steps echo "PROJECT=$PROJECT" >>"$GITHUB_ENV" echo "GOPATH=$GOPATH" >>"$GITHUB_ENV" echo "$GOPATH/bin" >>"$GITHUB_PATH" # print summary echo "GOPATH=$GOPATH" echo "PROJECT=$PROJECT" golang-github-uber-jaeger-lib-2.4.1/.github/workflows/000077500000000000000000000000001444170434100225765ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/.github/workflows/unit-tests.yml000066400000000000000000000021051444170434100254360ustar00rootroot00000000000000name: "Unit Tests" on: push: branches: [ master ] pull_request: branches: [ master ] jobs: unit-tests: runs-on: ubuntu-latest strategy: fail-fast: true matrix: job: - dep: true glide: false name: unit-tests with dep - dep: false glide: true name: unit-tests with glide name: ${{ matrix.job.name }} steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: ^1.15 - name: Setup GOPATH uses: ./.github/actions/setup-gopath - name: Install dependencies and tools run: | cd $PROJECT make install-ci USE_DEP=${{ matrix.job.dep }} USE_GLIDE=${{ matrix.job.glide }} - name: Run tests run: | cd $PROJECT make test-ci USE_DEP=${{ matrix.job.dep }} USE_GLIDE=${{ matrix.job.glide }} - name: Upload coverage to codecov uses: codecov/codecov-action@v1 with: file: cover.out fail_ci_if_error: true verbose: true golang-github-uber-jaeger-lib-2.4.1/.gitignore000066400000000000000000000001071444170434100211670ustar00rootroot00000000000000*.out *.test *.xml *.swp .idea/ .tmp/ *.iml *.cov *.html *.log vendor/ golang-github-uber-jaeger-lib-2.4.1/CHANGELOG.md000066400000000000000000000046751444170434100210260ustar00rootroot00000000000000Changes by Version ================== 2.4.1 (2021-03-29) ------------------ - [bug fix] Apply default buckets in Prometheus Factory (#90) -- Konstantinos Moraitis 2.4.0 (2020-10-01) ------------------ - Implement fork.Factory (#84) -- Daniil Rutskiy 2.3.0 (2020-09-23) ------------------ - Change codahale/hdrhistogram dependency to HdrHistogram/hdrhistogram (#82) -- filipe oliveira - Upgrade Go to 1.15 (#83) -- Yuri Shkuro 2.2.0 (2019-09-23) ------------------ - Upgrade Prometheus client to 1.1 and go-kit to 0.9 (#78) -- Yuri Shkuro 2.1.1 (2019-09-06) ------------------ - Remove go modules support (#76) -- Yuri Shkuro 2.1.0 (2019-09-05) ------------------ - Add support for go modules (#75) -- Yuri Shkuro - Fix typo in metrics Init method docs (#72) -- Sergei Zimakov - Remove GO15VENDOREXPERIMENT variable (#71) -- Takuya N - Ensure Gopkg.lock files remain in sync with Gopkg.yaml (#69) -- Prithvi Raj 2.0.0 (2018-12-17) ------------------ Breaking changes: - Add first class support for Histograms and change factory args to structs (#63) - Add metric description to factory API (#61) - Remove metrics/go-kit/prometheus as metrics/prometheus now available (#58) - Add `_total` suffix for counters reported to prometheus (#54) - LocalBackend / Test factory moved to metrics/metricstest/ package (#46) , - Change AssertCounterMetrics/AssertGaugeMetrics to be functions on the test factory (#51) 1.5.0 (2018-05-11) ------------------ - Change default metrics namespace separator from colon to underscore (#43) - Use an interface to be compatible with Prometheus 0.9.x (#42) 1.4.0 (2018-03-05) ------------------ - Reimplement expvar metrics to be tolerant of duplicates (#40) 1.3.1 (2018-01-12) ------------------- - Add Gopkg.toml to allow using the lib with `dep` 1.3.0 (2018-01-08) ------------------ - Move rate limiter from client to jaeger-lib [#35](https://github.com/jaegertracing/jaeger-lib/pull/35) 1.2.1 (2017-11-14) ------------------ - *breaking* Change prometheus.New() to accept options instead of fixed arguments 1.2.0 (2017-11-12) ------------------ - Support Prometheus metrics directly [#29](https://github.com/jaegertracing/jaeger-lib/pull/29). 1.1.0 (2017-09-10) ------------------ - Re-releasing the project under Apache 2.0 license. 1.0.0 (2017-08-22) ------------------ - First semver release. golang-github-uber-jaeger-lib-2.4.1/CONTRIBUTING.md000066400000000000000000000131071444170434100214340ustar00rootroot00000000000000# How to Contribute to `jaeger-lib` We'd love your help! Jaeger is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub pull requests. This document outlines some of the conventions on development workflow, commit message formatting, contact points and other resources to make it easier to get your contribution accepted. We gratefully welcome improvements to documentation as well as to code. # Certificate of Origin By contributing to this project you agree to the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This document was created by the Linux Kernel community and is a simple statement that you, as a contributor, have the legal right to make the contribution. See the [DCO](DCO) file for details. ## Getting Started This library uses [dep](https://golang.github.io/dep/) to manage dependencies. To get started, make sure you clone the Git repository into the correct location `github.com/uber/jaeger-lib` relative to `$GOPATH`: ``` mkdir -p $GOPATH/src/github.com/uber cd $GOPATH/src/github.com/uber git clone git@github.com:jaegertracing/jaeger-lib.git jaeger-lib cd jaeger-lib ``` Then install dependencies and run the tests: ``` make install make test ``` ## Imports grouping This projects follows the following pattern for grouping imports in Go files: * imports from standard library * imports from other projects * imports from this `jaeger-lib` project For example: ```go import ( "fmt" "go.uber.org/zap" "github.com/uber/jaeger-lib/metrics" ) ``` ## Making A Change *Before making any significant changes, please [open an issue](https://github.com/uber/jaeger-lib/issues).* Discussing your proposed changes ahead of time will make the contribution process smooth for everyone. Once we've discussed your changes and you've got your code ready, make sure that tests are passing (`make test` or `make cover`) and open your PR. Your pull request is most likely to be accepted if it: * Includes tests for new functionality. * Follows the guidelines in [Effective Go](https://golang.org/doc/effective_go.html) and the [Go team's common code review comments](https://github.com/golang/go/wiki/CodeReviewComments). * Has a [good commit message](https://chris.beams.io/posts/git-commit/): * Separate subject from body with a blank line * Limit the subject line to 50 characters * Capitalize the subject line * Do not end the subject line with a period * Use the imperative mood in the subject line * Wrap the body at 72 characters * Use the body to explain _what_ and _why_ instead of _how_ * Each commit must be signed by the author ([see below](#sign-your-work)). ## License By contributing your code, you agree to license your contribution under the terms of the [Apache License](LICENSE). If you are adding a new file it should have a header like below. The easiest way to add such header is to run `make fmt`. ``` // Copyright (c) 2017 The Jaeger Authors. // // 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. ``` ## Sign your work The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` then you just add a line to every git commit message: Signed-off-by: Joe Smith using your real name (sorry, no pseudonyms or anonymous contributions.) You can add the sign off when creating the git commit via `git commit -s`. If you want this to be automatic you can set up some aliases: ``` git config --add alias.amend "commit -s --amend" git config --add alias.c "commit -s" ``` golang-github-uber-jaeger-lib-2.4.1/DCO000066400000000000000000000026171444170434100175370ustar00rootroot00000000000000Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. golang-github-uber-jaeger-lib-2.4.1/Gopkg.lock000066400000000000000000000134351444170434100211300ustar00rootroot00000000000000# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] digest = "1:4c4c33075b704791d6a7f09dfb55c66769e8a1dc6adf87026292d274fe8ad113" name = "github.com/HdrHistogram/hdrhistogram-go" packages = ["."] pruneopts = "UT" revision = "3a0bb77429bd3a61596f5e8a3172445844342120" version = "0.9.0" [[projects]] digest = "1:8515c0ca4381246cf332cee05fc84070bbbb07bd679b639161506ba532f47128" name = "github.com/VividCortex/gohistogram" packages = ["."] pruneopts = "UT" revision = "51564d9861991fb0ad0f531c99ef602d0f9866e6" version = "v1.0.0" [[projects]] digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" name = "github.com/beorn7/perks" packages = ["quantile"] pruneopts = "UT" revision = "37c8de3658fcb183f997c4e13e8337516ab753e6" version = "v1.0.1" [[projects]] digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" name = "github.com/davecgh/go-spew" packages = ["spew"] pruneopts = "UT" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" [[projects]] digest = "1:83c5ef75cc53ba2e8a59e421f5f8b86acb780f2b143bbc386611ea3e1b5c4087" name = "github.com/go-kit/kit" packages = [ "log", "log/level", "metrics", "metrics/expvar", "metrics/generic", "metrics/influx", "metrics/internal/lv", ] pruneopts = "UT" revision = "150a65a7ec6156b4b640c1fd55f26fd3d475d656" version = "v0.9.0" [[projects]] digest = "1:4062bc6de62d73e2be342243cf138cf499b34d558876db8d9430e2149388a4d8" name = "github.com/go-logfmt/logfmt" packages = ["."] pruneopts = "UT" revision = "07c9b44f60d7ffdfb7d8efe1ad539965737836dc" version = "v0.4.0" [[projects]] digest = "1:573ca21d3669500ff845bdebee890eb7fc7f0f50c59f2132f2a0c6b03d85086a" name = "github.com/golang/protobuf" packages = ["proto"] pruneopts = "UT" revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" version = "v1.3.2" [[projects]] branch = "master" digest = "1:50708c8fc92aec981df5c446581cf9f90ba9e2a5692118e0ce75d4534aaa14a2" name = "github.com/influxdata/influxdb1-client" packages = [ "models", "pkg/escape", "v2", ] pruneopts = "UT" revision = "fc22c7df067eefd070157f157893fbce961d6359" [[projects]] branch = "master" digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" name = "github.com/kr/logfmt" packages = ["."] pruneopts = "UT" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] pruneopts = "UT" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" name = "github.com/pmezard/go-difflib" packages = ["difflib"] pruneopts = "UT" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] digest = "1:7097829edd12fd7211fca0d29496b44f94ef9e6d72f88fb64f3d7b06315818ad" name = "github.com/prometheus/client_golang" packages = [ "prometheus", "prometheus/internal", ] pruneopts = "UT" revision = "170205fb58decfd011f1550d4cfb737230d7ae4f" version = "v1.1.0" [[projects]] branch = "master" digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] pruneopts = "UT" revision = "14fe0d1b01d4d5fc031dd4bec1823bd3ebbe8016" [[projects]] digest = "1:f119e3205d3a1f0f19dbd7038eb37528e2c6f0933269dc344e305951fb87d632" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", "model", ] pruneopts = "UT" revision = "287d3e634a1e550c9e463dd7e5a75a422c614505" version = "v0.7.0" [[projects]] digest = "1:a210815b437763623ecca8eb91e6a0bf4f2d6773c5a6c9aec0e28f19e5fd6deb" name = "github.com/prometheus/procfs" packages = [ ".", "internal/fs", "internal/util", ] pruneopts = "UT" revision = "499c85531f756d1129edd26485a5f73871eeb308" version = "v0.0.5" [[projects]] digest = "1:99d32780e5238c2621fff621123997c3e3cca96db8be13179013aea77dfab551" name = "github.com/stretchr/testify" packages = [ "assert", "require", ] pruneopts = "UT" revision = "221dbe5ed46703ee255b1da0dec05086f5035f62" version = "v1.4.0" [[projects]] digest = "1:228597ec86f00b7270b9f9e230f38abb285765850d5b7dfbe38b38f6ea1c409a" name = "github.com/uber-go/tally" packages = ["."] pruneopts = "UT" revision = "3332297784e46cd346ab6d9894fd4ea027dc9368" version = "v3.3.12" [[projects]] branch = "master" digest = "1:6f104e30a35d62427b90130710ff507d6b9fc95ca76ceafb0025cd2809d93232" name = "golang.org/x/sys" packages = ["windows"] pruneopts = "UT" revision = "14da1ac737ccc89e3a28bf770cbbd260ce7e190b" [[projects]] digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" name = "gopkg.in/yaml.v2" packages = ["."] pruneopts = "UT" revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" version = "v2.2.2" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/HdrHistogram/hdrhistogram-go", "github.com/go-kit/kit/log", "github.com/go-kit/kit/log/level", "github.com/go-kit/kit/metrics", "github.com/go-kit/kit/metrics/expvar", "github.com/go-kit/kit/metrics/generic", "github.com/go-kit/kit/metrics/influx", "github.com/influxdata/influxdb1-client/v2", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_model/go", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", "github.com/uber-go/tally", ] solver-name = "gps-cdcl" solver-version = 1 golang-github-uber-jaeger-lib-2.4.1/Gopkg.toml000066400000000000000000000006771444170434100211570ustar00rootroot00000000000000[[constraint]] name = "github.com/go-kit/kit" version = "0.9.0" [[constraint]] name = "github.com/HdrHistogram/hdrhistogram-go" version = "0.9.0" [[constraint]] name = "github.com/uber-go/tally" version = ">=2.1.0, <4.0.0" [[constraint]] name = "github.com/prometheus/client_golang" version = "1.1.0" [[constraint]] name = "github.com/stretchr/testify" version = "1.4.0" [prune] go-tests = true unused-packages = true golang-github-uber-jaeger-lib-2.4.1/LICENSE000066400000000000000000000261351444170434100202150ustar00rootroot00000000000000 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 [yyyy] [name of copyright owner] 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. golang-github-uber-jaeger-lib-2.4.1/Makefile000066400000000000000000000043071444170434100206450ustar00rootroot00000000000000PROJECT_ROOT=github.com/uber/jaeger-lib export GO111MODULE=off PACKAGES := $(shell GO111MODULE=off go list ./... | awk -F/ 'NR>1 {print "./"$$4"/..."}' | sort -u) # all .go files that don't exist in hidden directories ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor \ -e ".*/\..*" \ -e ".*/_.*" \ -e ".*/mocks.*") USE_DEP := true RACE=-race GOTEST=go test -v $(RACE) GOLINT=golint GOVET=go vet GOFMT=gofmt FMT_LOG=fmt.log LINT_LOG=lint.log PASS=$(shell printf "\033[32mPASS\033[0m") FAIL=$(shell printf "\033[31mFAIL\033[0m") COLORIZE=sed ''/PASS/s//$(PASS)/'' | sed ''/FAIL/s//$(FAIL)/'' .DEFAULT_GOAL := test-and-lint .PHONY: test-and-lint test-and-lint: test fmt lint .PHONY: dep-check dep-check: ifeq ($(USE_DEP),true) dep check endif .PHONY: test test: dep-check $(GOTEST) $(PACKAGES) | $(COLORIZE) .PHONY: fmt fmt: $(GOFMT) -e -s -l -w $(ALL_SRC) ./scripts/updateLicenses.sh .PHONY: lint lint: $(GOVET) ./... @cat /dev/null > $(LINT_LOG) @$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) >> $(LINT_LOG) || true;) @[ ! -s "$(LINT_LOG)" ] || (echo "Lint Failures" | cat - $(LINT_LOG) && false) @$(GOFMT) -e -s -l $(ALL_SRC) > $(FMT_LOG) @./scripts/updateLicenses.sh >> $(FMT_LOG) @[ ! -s "$(FMT_LOG)" ] || (echo "go fmt or license check failures, run 'make fmt'" | cat - $(FMT_LOG) && false) .PHONY: install install: ifeq ($(USE_DEP),true) dep version || make install-dep dep version dep ensure -vendor-only dep status else ifeq ($(USE_GLIDE),true) glide --version || go get github.com/Masterminds/glide glide --version glide install endif .PHONY: cover cover: $(GOTEST) -cover -coverprofile cover.out ./... .PHONY: cover-html cover-html: cover go tool cover -html=cover.out -o cover.html idl-submodule: git submodule init git submodule update thrift-image: $(THRIFT) -version .PHONY: install-dep install-dep: - curl -L -s https://github.com/golang/dep/releases/download/v0.5.4/dep-linux-amd64 -o $$GOPATH/bin/dep - chmod +x $$GOPATH/bin/dep .PHONY: install-ci install-ci: install go get github.com/wadey/gocovmerge go get github.com/mattn/goveralls go get golang.org/x/tools/cmd/cover go get golang.org/x/lint/golint .PHONY: test-ci test-ci: dep-check cover lint golang-github-uber-jaeger-lib-2.4.1/README.md000066400000000000000000000020641444170434100204620ustar00rootroot00000000000000[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] # jaeger-lib A collection of shared infrastructure libraries used by different components of [Jaeger](https://github.com/jaegertracing/jaeger) backend and [jaeger-client-go](https://github.com/uber/jaeger-client-go). This library is *not intended to be used standalone*, and provides *no guarantees of backwards compatibility*. The library's import path is `github.com/uber/jaeger-lib`. ## How to Contribute Please see [CONTRIBUTING.md](CONTRIBUTING.md). ## License [Apache 2.0 License](./LICENSE). [doc-img]: https://godoc.org/github.com/uber/jaeger-lib?status.svg [doc]: https://godoc.org/github.com/uber/jaeger-lib [ci-img]: https://github.com/jaegertracing/jaeger-lib/workflows/Unit%20Tests/badge.svg?branch=master [ci]: https://github.com/jaegertracing/jaeger-lib/actions?query=branch%3Amaster [cov-img]: https://coveralls.io/repos/jaegertracing/jaeger-lib/badge.svg?branch=master&service=github [cov]: https://coveralls.io/github/jaegertracing/jaeger-lib?branch=master golang-github-uber-jaeger-lib-2.4.1/RELEASE.md000066400000000000000000000004061444170434100206030ustar00rootroot00000000000000# Releasing new version - Create a PR with title like "Preparing release 2.1.0" - Add recent changes to the `CHANGELOG.md`, e.g. from `git log --pretty=format:'- %s -- %an'` - Once the PR is merged, create a release on GitHub, e.g. v2.1.0 - Celebrate golang-github-uber-jaeger-lib-2.4.1/client/000077500000000000000000000000001444170434100204575ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/client/log/000077500000000000000000000000001444170434100212405ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/client/log/go-kit/000077500000000000000000000000001444170434100224325ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/client/log/go-kit/logger.go000066400000000000000000000034231444170434100242420ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package xkit import ( "fmt" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" ) // LoggerOption sets a parameter for the Logger. type LoggerOption func(*Logger) // MessageKey sets the key for the actual log message. By default, it's "msg". func MessageKey(key string) LoggerOption { return func(l *Logger) { l.messageKey = key } } // Logger wraps a go-kit logger instance in a Jaeger client compatible one. type Logger struct { infoLogger log.Logger errorLogger log.Logger messageKey string } // NewLogger creates a new Jaeger client logger from a go-kit one. func NewLogger(kitlogger log.Logger, options ...LoggerOption) *Logger { logger := &Logger{ infoLogger: level.Info(kitlogger), errorLogger: level.Error(kitlogger), messageKey: "msg", } for _, option := range options { option(logger) } return logger } // Error implements the github.com/uber/jaeger-client-go/log.Logger interface. func (l *Logger) Error(msg string) { l.errorLogger.Log(l.messageKey, msg) } // Infof implements the github.com/uber/jaeger-client-go/log.Logger interface. func (l *Logger) Infof(msg string, args ...interface{}) { l.infoLogger.Log(l.messageKey, fmt.Sprintf(msg, args...)) } golang-github-uber-jaeger-lib-2.4.1/client/log/go-kit/logger_test.go000066400000000000000000000015321444170434100253000ustar00rootroot00000000000000package xkit import ( "bytes" "testing" "github.com/go-kit/kit/log" "github.com/stretchr/testify/assert" ) func TestLogger_Infof(t *testing.T) { buf := &bytes.Buffer{} logger := NewLogger(log.NewLogfmtLogger(buf)) logger.Infof("Formatted %s", "string") assert.Equal(t, "level=info msg=\"Formatted string\"\n", buf.String()) } func TestLogger_Error(t *testing.T) { buf := &bytes.Buffer{} logger := NewLogger(log.NewLogfmtLogger(buf)) logger.Error("Something really bad happened") assert.Equal(t, "level=error msg=\"Something really bad happened\"\n", buf.String()) } func TestMessageKey(t *testing.T) { buf := &bytes.Buffer{} logger := NewLogger(log.NewLogfmtLogger(buf), MessageKey("message")) logger.Error("Something really bad happened") assert.Equal(t, "level=error message=\"Something really bad happened\"\n", buf.String()) } golang-github-uber-jaeger-lib-2.4.1/doc.go000066400000000000000000000013011444170434100202700ustar00rootroot00000000000000// Copyright (c) 2019 The Jaeger Authors. // // 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 jaegerlib is a set of utilities shared by Jaeger backend and Jaeger Go Client. package jaegerlib golang-github-uber-jaeger-lib-2.4.1/glide.lock000066400000000000000000000045531444170434100211460ustar00rootroot00000000000000hash: d6b884cd5ad2c1622d157697251fbf2a0b5c5cd9e03af2258caf8f35cb03d8ba updated: 2019-09-22T21:03:42.412209-04:00 imports: - name: github.com/beorn7/perks version: 37c8de3658fcb183f997c4e13e8337516ab753e6 subpackages: - quantile - name: github.com/HdrHistogram/hdrhistogram-go version: 3a0bb77429bd3a61596f5e8a3172445844342120 - name: github.com/davecgh/go-spew version: d8f796af33cc11cb798c1aaeb27a4ebc5099927d subpackages: - spew - name: github.com/go-kit/kit version: 150a65a7ec6156b4b640c1fd55f26fd3d475d656 subpackages: - log - log/level - metrics - metrics/expvar - metrics/generic - metrics/influx - metrics/internal/lv - name: github.com/go-logfmt/logfmt version: 432dd90af23366a89a611c020003fc8ba281ae5d - name: github.com/golang/protobuf version: 1680a479a2cfb3fa22b972af7e36d0a0fde47bf8 subpackages: - proto - name: github.com/influxdata/influxdb1-client version: fc22c7df067eefd070157f157893fbce961d6359 subpackages: - models - pkg/escape - v2 - name: github.com/kr/logfmt version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/matttproud/golang_protobuf_extensions version: c182affec369e30f25d3eb8cd8a478dee585ae7d subpackages: - pbutil - name: github.com/pmezard/go-difflib version: 5d4384ee4fb2527b0a1256a821ebfc92f91efefc subpackages: - difflib - name: github.com/prometheus/client_golang version: 170205fb58decfd011f1550d4cfb737230d7ae4f subpackages: - prometheus - prometheus/internal - name: github.com/prometheus/client_model version: 14fe0d1b01d4d5fc031dd4bec1823bd3ebbe8016 subpackages: - go - name: github.com/prometheus/common version: 287d3e634a1e550c9e463dd7e5a75a422c614505 subpackages: - expfmt - internal/bitbucket.org/ww/goautoneg - model - name: github.com/prometheus/procfs version: de25ac347ef9305868b04dc42425c973b863b18c subpackages: - internal/fs - internal/util - name: github.com/stretchr/testify version: 85f2b59c4459e5bf57488796be8c3667cb8246d6 subpackages: - assert - require - name: github.com/uber-go/tally version: 3332297784e46cd346ab6d9894fd4ea027dc9368 - name: github.com/VividCortex/gohistogram version: 51564d9861991fb0ad0f531c99ef602d0f9866e6 - name: golang.org/x/sys version: 0a153f010e6963173baba2306531d173aa843137 subpackages: - windows - name: gopkg.in/yaml.v2 version: 51d6538a90f86fe93ac480b35f37b2be17fef232 testImports: [] golang-github-uber-jaeger-lib-2.4.1/glide.yaml000066400000000000000000000006011444170434100211460ustar00rootroot00000000000000package: github.com/uber/jaeger-lib import: - package: github.com/HdrHistogram/hdrhistogram-go version: '0.9.0' - package: github.com/go-kit/kit version: '~0.9' subpackages: - metrics/influx - package: github.com/uber-go/tally version: '>= 2.1.0, < 4' - package: github.com/prometheus/client_golang version: '>= 0.8, < 2' testImport: - package: github.com/stretchr/testify golang-github-uber-jaeger-lib-2.4.1/metrics/000077500000000000000000000000001444170434100206475ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/000077500000000000000000000000001444170434100224525ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/cache.go000066400000000000000000000037141444170434100240510ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package adapters import ( "sync" "github.com/uber/jaeger-lib/metrics" ) type cache struct { lock sync.Mutex counters map[string]metrics.Counter gauges map[string]metrics.Gauge timers map[string]metrics.Timer histograms map[string]metrics.Histogram } func newCache() *cache { return &cache{ counters: make(map[string]metrics.Counter), gauges: make(map[string]metrics.Gauge), timers: make(map[string]metrics.Timer), histograms: make(map[string]metrics.Histogram), } } func (r *cache) getOrSetCounter(name string, create func() metrics.Counter) metrics.Counter { r.lock.Lock() defer r.lock.Unlock() c, ok := r.counters[name] if !ok { c = create() r.counters[name] = c } return c } func (r *cache) getOrSetGauge(name string, create func() metrics.Gauge) metrics.Gauge { r.lock.Lock() defer r.lock.Unlock() g, ok := r.gauges[name] if !ok { g = create() r.gauges[name] = g } return g } func (r *cache) getOrSetTimer(name string, create func() metrics.Timer) metrics.Timer { r.lock.Lock() defer r.lock.Unlock() t, ok := r.timers[name] if !ok { t = create() r.timers[name] = t } return t } func (r *cache) getOrSetHistogram(name string, create func() metrics.Histogram) metrics.Histogram { r.lock.Lock() defer r.lock.Unlock() t, ok := r.histograms[name] if !ok { t = create() r.histograms[name] = t } return t } golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/cache_test.go000066400000000000000000000034271444170434100251110ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package adapters import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/metricstest" ) func TestCache(t *testing.T) { f := metricstest.NewFactory(100 * time.Second) c1 := f.Counter(metrics.Options{Name: "x"}) g1 := f.Gauge(metrics.Options{Name: "y"}) t1 := f.Timer(metrics.TimerOptions{Name: "z"}) h1 := f.Histogram(metrics.HistogramOptions{Name: "h"}) c := newCache() c2 := c.getOrSetCounter("x", func() metrics.Counter { return c1 }) assert.Equal(t, c1, c2) g2 := c.getOrSetGauge("y", func() metrics.Gauge { return g1 }) assert.Equal(t, g1, g2) t2 := c.getOrSetTimer("z", func() metrics.Timer { return t1 }) assert.Equal(t, t1, t2) h2 := c.getOrSetHistogram("h", func() metrics.Histogram { return h1 }) assert.Equal(t, h1, h2) c3 := c.getOrSetCounter("x", func() metrics.Counter { panic("c1") }) assert.Equal(t, c1, c3) g3 := c.getOrSetGauge("y", func() metrics.Gauge { panic("g1") }) assert.Equal(t, g1, g3) t3 := c.getOrSetTimer("z", func() metrics.Timer { panic("t1") }) assert.Equal(t, t1, t3) h3 := c.getOrSetHistogram("h", func() metrics.Histogram { panic("h1") }) assert.Equal(t, h1, h3) } golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/factory.go000066400000000000000000000075131444170434100244560ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package adapters import ( "github.com/uber/jaeger-lib/metrics" ) // FactoryWithTags creates metrics with fully qualified name and tags. type FactoryWithTags interface { Counter(options metrics.Options) metrics.Counter Gauge(options metrics.Options) metrics.Gauge Timer(options metrics.TimerOptions) metrics.Timer Histogram(options metrics.HistogramOptions) metrics.Histogram } // Options affect how the adapter factory behaves. type Options struct { ScopeSep string TagsSep string TagKVSep string } func defaultOptions(options Options) Options { o := options if o.ScopeSep == "" { o.ScopeSep = "." } if o.TagsSep == "" { o.TagsSep = "." } if o.TagKVSep == "" { o.TagKVSep = "_" } return o } // WrapFactoryWithTags creates a real metrics.Factory that supports subscopes. func WrapFactoryWithTags(f FactoryWithTags, options Options) metrics.Factory { return &factory{ Options: defaultOptions(options), factory: f, cache: newCache(), } } type factory struct { Options factory FactoryWithTags scope string tags map[string]string cache *cache } func (f *factory) Counter(options metrics.Options) metrics.Counter { fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetCounter(key, func() metrics.Counter { return f.factory.Counter(metrics.Options{ Name: fullName, Tags: fullTags, Help: options.Help, }) }) } func (f *factory) Gauge(options metrics.Options) metrics.Gauge { fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetGauge(key, func() metrics.Gauge { return f.factory.Gauge(metrics.Options{ Name: fullName, Tags: fullTags, Help: options.Help, }) }) } func (f *factory) Timer(options metrics.TimerOptions) metrics.Timer { fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetTimer(key, func() metrics.Timer { return f.factory.Timer(metrics.TimerOptions{ Name: fullName, Tags: fullTags, Help: options.Help, Buckets: options.Buckets, }) }) } func (f *factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetHistogram(key, func() metrics.Histogram { return f.factory.Histogram(metrics.HistogramOptions{ Name: fullName, Tags: fullTags, Help: options.Help, Buckets: options.Buckets, }) }) } func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ cache: f.cache, scope: f.subScope(scope.Name), tags: f.mergeTags(scope.Tags), factory: f.factory, Options: f.Options, } } func (f *factory) getKey(name string, tags map[string]string) (fullName string, fullTags map[string]string, key string) { fullName = f.subScope(name) fullTags = f.mergeTags(tags) key = metrics.GetKey(fullName, fullTags, f.TagsSep, f.TagKVSep) return } func (f *factory) mergeTags(tags map[string]string) map[string]string { ret := make(map[string]string, len(f.tags)+len(tags)) for k, v := range f.tags { ret[k] = v } for k, v := range tags { ret[k] = v } return ret } func (f *factory) subScope(name string) string { if f.scope == "" { return name } if name == "" { return f.scope } return f.scope + f.ScopeSep + name } golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/factory_test.go000066400000000000000000000122121444170434100255050ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package adapters import ( "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/metricstest" ) func TestDefaultOptions(t *testing.T) { o := defaultOptions(Options{}) assert.Equal(t, ".", o.ScopeSep) assert.Equal(t, ".", o.TagsSep) assert.Equal(t, "_", o.TagKVSep) } func TestSubScope(t *testing.T) { f := &factory{ Options: defaultOptions(Options{}), } assert.Equal(t, "", f.subScope("")) assert.Equal(t, "x", f.subScope("x")) f.scope = "x" assert.Equal(t, "x", f.subScope("")) assert.Equal(t, "x.y", f.subScope("y")) } func TestFactory(t *testing.T) { var ( counterPrefix = "counter_" gaugePrefix = "gauge_" timerPrefix = "timer_" histogramPrefix = "histogram_" tagsA = map[string]string{"a": "b"} tagsX = map[string]string{"x": "y"} ) testCases := []struct { name string tags map[string]string buckets []float64 durationBuckets []time.Duration namespace string nsTags map[string]string fullName string expectedCounter string }{ {name: "x", fullName: "%sx"}, {tags: tagsX, fullName: "%s.x_y"}, {name: "x", tags: tagsA, fullName: "%sx.a_b"}, {namespace: "y", fullName: "y.%s"}, {nsTags: tagsA, fullName: "%s.a_b"}, {namespace: "y", nsTags: tagsX, fullName: "y.%s.x_y"}, {name: "x", namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y"}, {name: "x", tags: tagsX, namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y", expectedCounter: "84"}, {name: "x", tags: tagsA, namespace: "y", nsTags: tagsX, fullName: "y.%sx.a_b.x_y"}, {name: "x", tags: tagsX, namespace: "y", nsTags: tagsA, fullName: "y.%sx.a_b.x_y", expectedCounter: "84"}, } local := metricstest.NewFactory(100 * time.Second) for _, testCase := range testCases { t.Run("", func(t *testing.T) { if testCase.expectedCounter == "" { testCase.expectedCounter = "42" } ff := &fakeTagless{factory: local} f := WrapFactoryWithoutTags(ff, Options{}) if testCase.namespace != "" || testCase.nsTags != nil { f = f.Namespace(metrics.NSOptions{ Name: testCase.namespace, Tags: testCase.nsTags, }) } counter := f.Counter(metrics.Options{ Name: counterPrefix + testCase.name, Tags: testCase.tags, }) gauge := f.Gauge(metrics.Options{ Name: gaugePrefix + testCase.name, Tags: testCase.tags, }) timer := f.Timer(metrics.TimerOptions{ Name: timerPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.durationBuckets, }) histogram := f.Histogram(metrics.HistogramOptions{ Name: histogramPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.buckets, }) assert.Equal(t, counter, f.Counter(metrics.Options{ Name: counterPrefix + testCase.name, Tags: testCase.tags, })) assert.Equal(t, gauge, f.Gauge(metrics.Options{ Name: gaugePrefix + testCase.name, Tags: testCase.tags, })) assert.Equal(t, timer, f.Timer(metrics.TimerOptions{ Name: timerPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.durationBuckets, })) assert.Equal(t, histogram, f.Histogram(metrics.HistogramOptions{ Name: histogramPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.buckets, })) assert.Equal(t, fmt.Sprintf(testCase.fullName, counterPrefix), ff.counter) assert.Equal(t, fmt.Sprintf(testCase.fullName, gaugePrefix), ff.gauge) assert.Equal(t, fmt.Sprintf(testCase.fullName, timerPrefix), ff.timer) assert.Equal(t, fmt.Sprintf(testCase.fullName, histogramPrefix), ff.histogram) }) } } type fakeTagless struct { factory metrics.Factory counter string gauge string timer string histogram string } func (f *fakeTagless) Counter(options TaglessOptions) metrics.Counter { f.counter = options.Name return f.factory.Counter(metrics.Options{ Name: options.Name, Help: options.Help, }) } func (f *fakeTagless) Gauge(options TaglessOptions) metrics.Gauge { f.gauge = options.Name return f.factory.Gauge(metrics.Options{ Name: options.Name, Help: options.Help, }) } func (f *fakeTagless) Timer(options TaglessTimerOptions) metrics.Timer { f.timer = options.Name return f.factory.Timer(metrics.TimerOptions{ Name: options.Name, Help: options.Help, Buckets: options.Buckets, }) } func (f *fakeTagless) Histogram(options TaglessHistogramOptions) metrics.Histogram { f.histogram = options.Name return f.factory.Histogram(metrics.HistogramOptions{ Name: options.Name, Help: options.Help, Buckets: options.Buckets, }) } golang-github-uber-jaeger-lib-2.4.1/metrics/adapters/tagless.go000066400000000000000000000056611444170434100244530ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package adapters import ( "time" "github.com/uber/jaeger-lib/metrics" ) // TaglessOptions defines the information associated with a metric type TaglessOptions struct { Name string Help string } // TaglessTimerOptions defines the information associated with a metric type TaglessTimerOptions struct { Name string Help string Buckets []time.Duration } // TaglessHistogramOptions defines the information associated with a metric type TaglessHistogramOptions struct { Name string Help string Buckets []float64 } // FactoryWithoutTags creates metrics based on name only, without tags. // Suitable for integrating with statsd-like backends that don't support tags. type FactoryWithoutTags interface { Counter(options TaglessOptions) metrics.Counter Gauge(options TaglessOptions) metrics.Gauge Timer(options TaglessTimerOptions) metrics.Timer Histogram(options TaglessHistogramOptions) metrics.Histogram } // WrapFactoryWithoutTags creates a real metrics.Factory that supports subscopes. func WrapFactoryWithoutTags(f FactoryWithoutTags, options Options) metrics.Factory { return WrapFactoryWithTags( &tagless{ Options: defaultOptions(options), factory: f, }, options, ) } // tagless implements FactoryWithTags type tagless struct { Options factory FactoryWithoutTags } func (f *tagless) Counter(options metrics.Options) metrics.Counter { fullName := f.getFullName(options.Name, options.Tags) return f.factory.Counter(TaglessOptions{ Name: fullName, Help: options.Help, }) } func (f *tagless) Gauge(options metrics.Options) metrics.Gauge { fullName := f.getFullName(options.Name, options.Tags) return f.factory.Gauge(TaglessOptions{ Name: fullName, Help: options.Help, }) } func (f *tagless) Timer(options metrics.TimerOptions) metrics.Timer { fullName := f.getFullName(options.Name, options.Tags) return f.factory.Timer(TaglessTimerOptions{ Name: fullName, Help: options.Help, Buckets: options.Buckets, }) } func (f *tagless) Histogram(options metrics.HistogramOptions) metrics.Histogram { fullName := f.getFullName(options.Name, options.Tags) return f.factory.Histogram(TaglessHistogramOptions{ Name: fullName, Help: options.Help, Buckets: options.Buckets, }) } func (f *tagless) getFullName(name string, tags map[string]string) string { return metrics.GetKey(name, tags, f.TagsSep, f.TagKVSep) } golang-github-uber-jaeger-lib-2.4.1/metrics/counter.go000066400000000000000000000016111444170434100226540ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics // Counter tracks the number of times an event has occurred type Counter interface { // Inc adds the given value to the counter. Inc(int64) } // NullCounter counter that does nothing var NullCounter Counter = nullCounter{} type nullCounter struct{} func (nullCounter) Inc(int64) {} golang-github-uber-jaeger-lib-2.4.1/metrics/expvar/000077500000000000000000000000001444170434100221545ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/expvar/factory.go000066400000000000000000000034141444170434100241540ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package expvar import ( "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/adapters" xkit "github.com/uber/jaeger-lib/metrics/go-kit" "github.com/uber/jaeger-lib/metrics/go-kit/expvar" ) // NewFactory creates a new metrics factory using go-kit expvar package. // buckets is the number of buckets to be used in histograms. func NewFactory(buckets int) metrics.Factory { return adapters.WrapFactoryWithoutTags( &factory{ factory: expvar.NewFactory(buckets), }, adapters.Options{}, ) } type factory struct { factory xkit.Factory } func (f *factory) Counter(options adapters.TaglessOptions) metrics.Counter { return xkit.NewCounter(f.factory.Counter(options.Name)) } func (f *factory) Gauge(options adapters.TaglessOptions) metrics.Gauge { return xkit.NewGauge(f.factory.Gauge(options.Name)) } func (f *factory) Timer(options adapters.TaglessTimerOptions) metrics.Timer { // TODO: Provide support for buckets return xkit.NewTimer(f.factory.Histogram(options.Name)) } func (f *factory) Histogram(options adapters.TaglessHistogramOptions) metrics.Histogram { // TODO: Provide support for buckets return xkit.NewHistogram(f.factory.Histogram(options.Name)) } golang-github-uber-jaeger-lib-2.4.1/metrics/expvar/factory_test.go000066400000000000000000000106421444170434100252140ustar00rootroot00000000000000// Copyright (c) 2018 Uber Technologies, 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. package expvar import ( "expvar" "fmt" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" ) var ( id = time.Now().UnixNano() prefix = fmt.Sprintf("test_%d", id) counterPrefix = prefix + "_counter_" gaugePrefix = prefix + "_gauge_" timerPrefix = prefix + "_timer_" histogramPrefix = prefix + "_histogram_" tagsA = map[string]string{"a": "b"} tagsX = map[string]string{"x": "y"} ) func TestFactory(t *testing.T) { buckets := []float64{10, 20, 30, 40, 50, 60} testCases := []struct { name string tags map[string]string buckets []float64 durationBuckets []time.Duration namespace string nsTags map[string]string fullName string expectedCounter string }{ {name: "x", fullName: "%sx", buckets: buckets}, {tags: tagsX, fullName: "%s.x_y", buckets: buckets}, {name: "x", tags: tagsA, fullName: "%sx.a_b", buckets: buckets}, {namespace: "y", fullName: "y.%s", buckets: buckets}, {nsTags: tagsA, fullName: "%s.a_b", buckets: buckets}, {namespace: "y", nsTags: tagsX, fullName: "y.%s.x_y", buckets: buckets}, {name: "x", namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y", buckets: buckets}, {name: "x", tags: tagsX, namespace: "y", nsTags: tagsX, fullName: "y.%sx.x_y", expectedCounter: "84", buckets: buckets}, {name: "x", tags: tagsA, namespace: "y", nsTags: tagsX, fullName: "y.%sx.a_b.x_y", buckets: buckets}, {name: "x", tags: tagsX, namespace: "y", nsTags: tagsA, fullName: "y.%sx.a_b.x_y", expectedCounter: "84", buckets: buckets}, } f := NewFactory(2) for _, testCase := range testCases { t.Run("", func(t *testing.T) { if testCase.expectedCounter == "" { testCase.expectedCounter = "42" } ff := f if testCase.namespace != "" || testCase.nsTags != nil { ff = f.Namespace(metrics.NSOptions{ Name: testCase.namespace, Tags: testCase.nsTags, }) } counter := ff.Counter(metrics.Options{ Name: counterPrefix + testCase.name, Tags: testCase.tags, }) gauge := ff.Gauge(metrics.Options{ Name: gaugePrefix + testCase.name, Tags: testCase.tags, }) timer := ff.Timer(metrics.TimerOptions{ Name: timerPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.durationBuckets, }) histogram := ff.Histogram(metrics.HistogramOptions{ Name: histogramPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.buckets, }) // register second time, should not panic ff.Counter(metrics.Options{ Name: counterPrefix + testCase.name, Tags: testCase.tags, }) ff.Gauge(metrics.Options{ Name: gaugePrefix + testCase.name, Tags: testCase.tags, }) ff.Timer(metrics.TimerOptions{ Name: timerPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.durationBuckets, }) ff.Histogram(metrics.HistogramOptions{ Name: histogramPrefix + testCase.name, Tags: testCase.tags, Buckets: testCase.buckets, }) counter.Inc(42) gauge.Update(42) timer.Record(42 * time.Millisecond) histogram.Record(42) assertExpvar(t, fmt.Sprintf(testCase.fullName, counterPrefix), testCase.expectedCounter) assertExpvar(t, fmt.Sprintf(testCase.fullName, gaugePrefix), "42") assertExpvar(t, fmt.Sprintf(testCase.fullName, timerPrefix)+".p99", "0.042") assertExpvar(t, fmt.Sprintf(testCase.fullName, histogramPrefix)+".p99", "42") }) } } func assertExpvar(t *testing.T, fullName string, value string) { var found expvar.KeyValue expvar.Do(func(kv expvar.KeyValue) { if kv.Key == fullName { found = kv } }) if !assert.Equal(t, fullName, found.Key) { expvar.Do(func(kv expvar.KeyValue) { if strings.HasPrefix(kv.Key, prefix) { // t.Log(kv) } }) return } assert.Equal(t, value, found.Value.String(), fullName) } golang-github-uber-jaeger-lib-2.4.1/metrics/factory.go000066400000000000000000000040731444170434100226510ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics import ( "time" ) // NSOptions defines the name and tags map associated with a factory namespace type NSOptions struct { Name string Tags map[string]string } // Options defines the information associated with a metric type Options struct { Name string Tags map[string]string Help string } // TimerOptions defines the information associated with a metric type TimerOptions struct { Name string Tags map[string]string Help string Buckets []time.Duration } // HistogramOptions defines the information associated with a metric type HistogramOptions struct { Name string Tags map[string]string Help string Buckets []float64 } // Factory creates new metrics type Factory interface { Counter(metric Options) Counter Timer(metric TimerOptions) Timer Gauge(metric Options) Gauge Histogram(metric HistogramOptions) Histogram // Namespace returns a nested metrics factory. Namespace(scope NSOptions) Factory } // NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge. var NullFactory Factory = nullFactory{} type nullFactory struct{} func (nullFactory) Counter(options Options) Counter { return NullCounter } func (nullFactory) Timer(options TimerOptions) Timer { return NullTimer } func (nullFactory) Gauge(options Options) Gauge { return NullGauge } func (nullFactory) Histogram(options HistogramOptions) Histogram { return NullHistogram } func (nullFactory) Namespace(scope NSOptions) Factory { return NullFactory } golang-github-uber-jaeger-lib-2.4.1/metrics/fork/000077500000000000000000000000001444170434100216105ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/fork/fork.go000066400000000000000000000041701444170434100231020ustar00rootroot00000000000000// Copyright (c) 2020 The Jaeger Authors. // // 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 fork import "github.com/uber/jaeger-lib/metrics" // Factory represents a metrics factory that delegates metrics with // forkNamespace to forkFactory otherwise - defaultFactory is used. type Factory struct { forkNamespace string forkFactory metrics.Factory defaultFactory metrics.Factory } // New creates new fork.Factory. func New(forkNamespace string, forkFactory, defaultFactory metrics.Factory) metrics.Factory { return &Factory{ forkNamespace: forkNamespace, forkFactory: forkFactory, defaultFactory: defaultFactory, } } // Gauge implements metrics.Factory interface. func (f *Factory) Gauge(options metrics.Options) metrics.Gauge { return f.defaultFactory.Gauge(options) } // Counter implements metrics.Factory interface. func (f *Factory) Counter(metric metrics.Options) metrics.Counter { return f.defaultFactory.Counter(metric) } // Timer implements metrics.Factory interface. func (f *Factory) Timer(metric metrics.TimerOptions) metrics.Timer { return f.defaultFactory.Timer(metric) } // Histogram implements metrics.Factory interface. func (f *Factory) Histogram(metric metrics.HistogramOptions) metrics.Histogram { return f.defaultFactory.Histogram(metric) } // Namespace implements metrics.Factory interface. func (f *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { if scope.Name == f.forkNamespace { return f.forkFactory.Namespace(scope) } return &Factory{ forkNamespace: f.forkNamespace, forkFactory: f.forkFactory.Namespace(scope), defaultFactory: f.defaultFactory.Namespace(scope), } } golang-github-uber-jaeger-lib-2.4.1/metrics/fork/fork_test.go000066400000000000000000000056361444170434100241510ustar00rootroot00000000000000// Copyright (c) 2020 The Jaeger Authors. // // 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 fork import ( "testing" "time" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/metricstest" ) var _ metrics.Factory = (*Factory)(nil) func TestForkFactory(t *testing.T) { forkNamespace := "internal" forkFactory := metricstest.NewFactory(time.Second) defaultFactory := metricstest.NewFactory(time.Second) // Create factory that will delegate namespaced metrics to forkFactory // and add some metrics ff := New(forkNamespace, forkFactory, defaultFactory) ff.Gauge(metrics.Options{ Name: "somegauge", }).Update(42) ff.Counter(metrics.Options{ Name: "somecounter", }).Inc(2) // Check that metrics are presented in defaultFactory backend defaultFactory.AssertCounterMetrics(t, metricstest.ExpectedMetric{ Name: "somecounter", Value: 2, }) defaultFactory.AssertGaugeMetrics(t, metricstest.ExpectedMetric{ Name: "somegauge", Value: 42, }) // Get default namespaced factory defaultNamespacedFactory := ff.Namespace(metrics.NSOptions{ Name: "default", }) // Add some metrics defaultNamespacedFactory.Counter(metrics.Options{ Name: "somenamespacedcounter", }).Inc(111) defaultNamespacedFactory.Gauge(metrics.Options{ Name: "somenamespacedgauge", }).Update(222) defaultNamespacedFactory.Histogram(metrics.HistogramOptions{ Name: "somenamespacedhist", }).Record(1) defaultNamespacedFactory.Timer(metrics.TimerOptions{ Name: "somenamespacedtimer", }).Record(time.Millisecond) // Check values in default namespaced factory backend defaultFactory.AssertCounterMetrics(t, metricstest.ExpectedMetric{ Name: "default.somenamespacedcounter", Value: 111, }) defaultFactory.AssertGaugeMetrics(t, metricstest.ExpectedMetric{ Name: "default.somenamespacedgauge", Value: 222, }) // Get factory with forkNamespace and add some metrics internalFactory := ff.Namespace(metrics.NSOptions{ Name: forkNamespace, }) internalFactory.Gauge(metrics.Options{ Name: "someinternalgauge", }).Update(20) internalFactory.Counter(metrics.Options{ Name: "someinternalcounter", }).Inc(50) // Check that metrics are presented in forkFactory backend forkFactory.AssertGaugeMetrics(t, metricstest.ExpectedMetric{ Name: "internal.someinternalgauge", Value: 20, }) forkFactory.AssertCounterMetrics(t, metricstest.ExpectedMetric{ Name: "internal.someinternalcounter", Value: 50, }) } golang-github-uber-jaeger-lib-2.4.1/metrics/gauge.go000066400000000000000000000016161444170434100222720ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics // Gauge returns instantaneous measurements of something as an int64 value type Gauge interface { // Update the gauge to the value passed in. Update(int64) } // NullGauge gauge that does nothing var NullGauge Gauge = nullGauge{} type nullGauge struct{} func (nullGauge) Update(int64) {} golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/000077500000000000000000000000001444170434100220415ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/expvar/000077500000000000000000000000001444170434100233465ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/expvar/factory.go000066400000000000000000000030701444170434100253440ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package expvar import ( "github.com/go-kit/kit/metrics" "github.com/go-kit/kit/metrics/expvar" "github.com/uber/jaeger-lib/metrics/go-kit" ) // NewFactory creates a new metrics factory using go-kit expvar package. // buckets is the number of buckets to be used in histograms. // // Deprecated: the recommended way is to use metrics/expvar module: // // "github.com/uber/jaeger-lib/metrics/expvar" // // var numBuckets = 10 // var metricsFactory = expvar.NewFactory(numBuckets) // func NewFactory(buckets int) xkit.Factory { return factory{ buckets: buckets, } } type factory struct { buckets int } func (f factory) Counter(name string) metrics.Counter { return expvar.NewCounter(name) } func (f factory) Histogram(name string) metrics.Histogram { return expvar.NewHistogram(name, f.buckets) } func (f factory) Gauge(name string) metrics.Gauge { return expvar.NewGauge(name) } func (f factory) Capabilities() xkit.Capabilities { return xkit.Capabilities{Tagging: false} } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/expvar/factory_test.go000066400000000000000000000015541444170434100264100ustar00rootroot00000000000000package expvar import ( "expvar" "testing" "github.com/stretchr/testify/assert" ) func TestCounter(t *testing.T) { f := NewFactory(10) assert.False(t, f.Capabilities().Tagging) c := f.Counter("gokit_expvar_counter") c.Add(42) kv := findExpvar("gokit_expvar_counter") assert.Equal(t, "42", kv.Value.String()) } func TestGauge(t *testing.T) { f := NewFactory(10) g := f.Gauge("gokit_expvar_gauge") g.Set(42) kv := findExpvar("gokit_expvar_gauge") assert.Equal(t, "42", kv.Value.String()) } func TestHistogram(t *testing.T) { f := NewFactory(10) hist := f.Histogram("gokit_expvar_hist") hist.Observe(10.5) kv := findExpvar("gokit_expvar_hist.p50") assert.Equal(t, "10.5", kv.Value.String()) } func findExpvar(key string) *expvar.KeyValue { var kv *expvar.KeyValue expvar.Do(func(v expvar.KeyValue) { if v.Key == key { kv = &v } }) return kv } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/factory.go000066400000000000000000000111161444170434100240370ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package xkit import ( kit "github.com/go-kit/kit/metrics" "github.com/uber/jaeger-lib/metrics" ) // Factory provides a unified interface for creating named metrics // from various go-kit metrics implementations. type Factory interface { Counter(name string) kit.Counter Gauge(name string) kit.Gauge Histogram(name string) kit.Histogram Capabilities() Capabilities } // Capabilities describes capabilities of a specific metrics factory. type Capabilities struct { // Tagging indicates whether the factory has the capability for tagged metrics Tagging bool } // FactoryOption is a function that adjusts some parameters of the factory. type FactoryOption func(*factory) // Wrap is used to create an adapter from xkit.Factory to metrics.Factory. func Wrap(namespace string, f Factory, options ...FactoryOption) metrics.Factory { factory := &factory{ scope: namespace, factory: f, scopeSep: ".", tagsSep: ".", tagKVSep: "_", } for i := range options { options[i](factory) } return factory } // ScopeSeparator returns an option that overrides default scope separator. func ScopeSeparator(scopeSep string) FactoryOption { return func(f *factory) { f.scopeSep = scopeSep } } // TagsSeparator returns an option that overrides default tags separator. func TagsSeparator(tagsSep string) FactoryOption { return func(f *factory) { f.tagsSep = tagsSep } } type factory struct { scope string tags map[string]string factory Factory scopeSep string tagsSep string tagKVSep string } func (f *factory) subScope(name string) string { if f.scope == "" { return name } if name == "" { return f.scope } return f.scope + f.scopeSep + name } // nameAndTagsList returns a name and tags list for the new metrics. // The name is a concatenation of nom and the current factory scope. // The tags list is a flattened list of passed tags merged with factory tags. // If the underlying factory does not support tags, then the tags are // transformed into a string and appended to the name. func (f *factory) nameAndTagsList(nom string, tags map[string]string) (name string, tagsList []string) { mergedTags := f.mergeTags(tags) name = f.subScope(nom) tagsList = f.tagsList(mergedTags) if len(tagsList) == 0 || f.factory.Capabilities().Tagging { return } name = metrics.GetKey(name, mergedTags, f.tagsSep, f.tagKVSep) tagsList = nil return } func (f *factory) Counter(options metrics.Options) metrics.Counter { name, tagsList := f.nameAndTagsList(options.Name, options.Tags) counter := f.factory.Counter(name) if len(tagsList) > 0 { counter = counter.With(tagsList...) } return NewCounter(counter) } func (f *factory) Timer(options metrics.TimerOptions) metrics.Timer { name, tagsList := f.nameAndTagsList(options.Name, options.Tags) hist := f.factory.Histogram(name) if len(tagsList) > 0 { hist = hist.With(tagsList...) } return NewTimer(hist) } func (f *factory) Gauge(options metrics.Options) metrics.Gauge { name, tagsList := f.nameAndTagsList(options.Name, options.Tags) gauge := f.factory.Gauge(name) if len(tagsList) > 0 { gauge = gauge.With(tagsList...) } return NewGauge(gauge) } func (f *factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { name, tagsList := f.nameAndTagsList(options.Name, options.Tags) hist := f.factory.Histogram(name) if len(tagsList) > 0 { hist = hist.With(tagsList...) } return NewHistogram(hist) } func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ scope: f.subScope(scope.Name), tags: f.mergeTags(scope.Tags), factory: f.factory, scopeSep: f.scopeSep, tagsSep: f.tagsSep, tagKVSep: f.tagKVSep, } } func (f *factory) tagsList(a map[string]string) []string { ret := make([]string, 0, 2*len(a)) for k, v := range a { ret = append(ret, k, v) } return ret } func (f *factory) mergeTags(tags map[string]string) map[string]string { ret := make(map[string]string, len(f.tags)+len(tags)) for k, v := range f.tags { ret[k] = v } for k, v := range tags { ret[k] = v } return ret } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/factory_test.go000066400000000000000000000155661444170434100251130ustar00rootroot00000000000000package xkit import ( "sort" "testing" "time" kit "github.com/go-kit/kit/metrics" "github.com/go-kit/kit/metrics/generic" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" ) type genericFactory struct{} func (f genericFactory) Counter(name string) kit.Counter { return generic.NewCounter(name) } func (f genericFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } func (f genericFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } func (f genericFactory) Capabilities() Capabilities { return Capabilities{Tagging: true} } // noTagsFactory is similar to genericFactory but overrides With() methods to no-op type noTagsFactory struct{} func (f noTagsFactory) Counter(name string) kit.Counter { return noTagsCounter{generic.NewCounter(name)} } func (f noTagsFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } func (f noTagsFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } func (f noTagsFactory) Capabilities() Capabilities { return Capabilities{Tagging: false} } type noTagsCounter struct { counter *generic.Counter } func (c noTagsCounter) Add(delta float64) { c.counter.Add(delta) } func (c noTagsCounter) With(labelValues ...string) kit.Counter { return c } type noTagsGauge struct { gauge *generic.Gauge } func (g noTagsGauge) Set(value float64) { g.gauge.Set(value) } func (g noTagsGauge) Add(delta float64) { g.gauge.Add(delta) } func (g noTagsGauge) With(labelValues ...string) kit.Gauge { return g } type noTagsHistogram struct { hist *generic.Histogram } func (h noTagsHistogram) Observe(value float64) { h.hist.Observe(value) } func (h noTagsHistogram) With(labelValues ...string) kit.Histogram { return h } type Tags map[string]string type metricFunc func(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) type testCase struct { f Factory prefix string name string tags Tags buckets []float64 durationBuckets []time.Duration useNamespace bool namespace string namespaceTags Tags options []FactoryOption expName string expTags []string } func TestFactoryScoping(t *testing.T) { genericFactory := genericFactory{} noTagsFactory := noTagsFactory{} testSuites := []struct { metricType string metricFunc metricFunc }{ {"counter", testCounter}, {"gauge", testGauge}, {"timer", testTimer}, {"histogram", testHistogram}, } for _, ts := range testSuites { testSuite := ts // capture loop var testCases := []testCase{ {f: genericFactory, prefix: "x", name: "", expName: "x"}, {f: genericFactory, prefix: "", name: "y", expName: "y"}, {f: genericFactory, prefix: "x", name: "y", expName: "x.y"}, {f: genericFactory, prefix: "x", name: "z", expName: "x.z", tags: Tags{"a": "b"}, expTags: []string{"a", "b"}}, { f: genericFactory, name: "x", useNamespace: true, namespace: "w", expName: "w.x", }, { f: genericFactory, name: "y", useNamespace: true, namespace: "w", namespaceTags: Tags{"a": "b"}, expName: "w.y", expTags: []string{"a", "b"}, }, { f: genericFactory, name: "z", tags: Tags{"a": "b"}, useNamespace: true, namespace: "w", namespaceTags: Tags{"c": "d"}, expName: "w.z", expTags: []string{"a", "b", "c", "d"}, }, {f: noTagsFactory, prefix: "x", name: "", expName: "x"}, {f: noTagsFactory, prefix: "", name: "y", expName: "y"}, {f: noTagsFactory, prefix: "x", name: "y", expName: "x.y"}, {f: noTagsFactory, prefix: "x", name: "z", expName: "x.z.a_b", tags: Tags{"a": "b"}}, { f: noTagsFactory, name: "x", useNamespace: true, namespace: "w", expName: "w.x", }, { f: noTagsFactory, name: "y", useNamespace: true, namespace: "w", namespaceTags: Tags{"a": "b"}, expName: "w.y.a_b", }, { f: noTagsFactory, options: []FactoryOption{ScopeSeparator(":"), TagsSeparator(":")}, name: "z", tags: Tags{"a": "b"}, useNamespace: true, namespace: "w", namespaceTags: Tags{"c": "d"}, expName: "w:z:a_b:c_d", }, } for _, tc := range testCases { testCase := tc // capture loop var factoryName := "genericFactory" if testCase.f == noTagsFactory { factoryName = "noTagsFactory" } t.Run(factoryName+"_"+testSuite.metricType+"_"+testCase.expName, func(t *testing.T) { f := Wrap(testCase.prefix, testCase.f, testCase.options...) if testCase.useNamespace { f = f.Namespace(metrics.NSOptions{ Name: testCase.namespace, Tags: testCase.namespaceTags, }) } name, labels := testSuite.metricFunc(t, testCase, f) assert.Equal(t, testCase.expName, name()) actualTags := labels() sort.Strings(actualTags) assert.Equal(t, testCase.expTags, actualTags) }) } } } func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { c := f.Counter(metrics.Options{ Name: testCase.name, Tags: testCase.tags, }) c.Inc(123) kc := c.(*Counter).counter var gc *generic.Counter if c, ok := kc.(*generic.Counter); ok { gc = c } else { gc = kc.(noTagsCounter).counter } assert.EqualValues(t, 123.0, gc.Value()) name = func() string { return gc.Name } labels = gc.LabelValues return } func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { g := f.Gauge(metrics.Options{ Name: testCase.name, Tags: testCase.tags, }) g.Update(123) gg := g.(*Gauge).gauge.(*generic.Gauge) assert.EqualValues(t, 123.0, gg.Value()) name = func() string { return gg.Name } labels = gg.LabelValues return } func testTimer(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { tm := f.Timer(metrics.TimerOptions{ Name: testCase.name, Tags: testCase.tags, Buckets: testCase.durationBuckets, }) tm.Record(123 * time.Millisecond) gt := tm.(*Timer).hist.(*generic.Histogram) assert.InDelta(t, 0.123, gt.Quantile(0.9), 0.00001) name = func() string { return gt.Name } labels = gt.LabelValues return } func testHistogram(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { histogram := f.Histogram(metrics.HistogramOptions{ Name: testCase.name, Tags: testCase.tags, Buckets: testCase.buckets, }) histogram.Record(123) gt := histogram.(*Histogram).hist.(*generic.Histogram) assert.InDelta(t, 123, gt.Quantile(0.9), 0.00001) name = func() string { return gt.Name } labels = gt.LabelValues return } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/influx/000077500000000000000000000000001444170434100233465ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/influx/factory.go000066400000000000000000000024721444170434100253510ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package influx import ( "github.com/go-kit/kit/metrics" "github.com/go-kit/kit/metrics/influx" "github.com/uber/jaeger-lib/metrics/go-kit" ) // NewFactory creates a new metrics factory using go-kit influx package. func NewFactory(client *influx.Influx) xkit.Factory { return factory{ client: client, } } type factory struct { client *influx.Influx } func (f factory) Counter(name string) metrics.Counter { return f.client.NewCounter(name) } func (f factory) Histogram(name string) metrics.Histogram { return f.client.NewHistogram(name) } func (f factory) Gauge(name string) metrics.Gauge { return f.client.NewGauge(name) } func (f factory) Capabilities() xkit.Capabilities { return xkit.Capabilities{Tagging: true} } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/influx/factory_test.go000066400000000000000000000060471444170434100264120ustar00rootroot00000000000000package influx import ( "bytes" "fmt" "testing" "time" "github.com/go-kit/kit/log" "github.com/go-kit/kit/metrics/influx" influxdb "github.com/influxdata/influxdb1-client/v2" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/go-kit" ) func TestCounter(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) c := wf.Counter(metrics.Options{ Name: "gokit.infl-counter", Tags: map[string]string{"label": "val1"}, }) c.Inc(7) assert.Contains(t, reportToString(in), "namespace.gokit.infl-counter,label=val1 count=7") } func TestGauge(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) g := wf.Gauge(metrics.Options{ Name: "gokit.infl-gauge", Tags: map[string]string{"x": "y"}, }) g.Update(42) assert.Contains(t, reportToString(in), "namespace.gokit.infl-gauge,x=y value=42") } func TestTimer(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) timer := wf.Timer(metrics.TimerOptions{ Name: "gokit.infl-timer", Tags: map[string]string{"x": "y"}, }) timer.Record(time.Second * 1) timer.Record(time.Second * 1) timer.Record(time.Second * 10) assert.Contains(t, reportToString(in), "namespace.gokit.infl-timer,x=y p50=1,p90=10,p95=10,p99=10") } func TestHistogram(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) histogram := wf.Histogram(metrics.HistogramOptions{ Name: "gokit.infl-histogram", Tags: map[string]string{"x": "y"}, }) histogram.Record(1) histogram.Record(1) histogram.Record(10) assert.Contains(t, reportToString(in), "namespace.gokit.infl-histogram,x=y p50=1,p90=10,p95=10,p99=10") } func TestWrapperNamespaces(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) wf = wf.Namespace(metrics.NSOptions{ Name: "bar", Tags: map[string]string{"bar_tag": "bar_tag"}, }) c := wf.Counter(metrics.Options{ Name: "gokit.prom-wrapped-counter", Tags: map[string]string{"x": "y"}, }) c.Inc(42) assert.Contains(t, reportToString(in), "namespace.bar.gokit.prom-wrapped-counter,bar_tag=bar_tag,x=y count=42") } func TestCapabilities(t *testing.T) { in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) inf := NewFactory(in) assert.True(t, inf.Capabilities().Tagging) } func reportToString(in *influx.Influx) string { client := &bufWriter{} in.WriteTo(client) return client.buf.String() } type bufWriter struct { buf bytes.Buffer } func (w *bufWriter) Write(bp influxdb.BatchPoints) error { for _, p := range bp.Points() { fmt.Fprintf(&w.buf, p.String()+"\n") } return nil } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/metrics.go000066400000000000000000000037501444170434100240430ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package xkit import ( "time" kit "github.com/go-kit/kit/metrics" ) // Counter is an adapter from go-kit Counter to jaeger-lib Counter type Counter struct { counter kit.Counter } // NewCounter creates a new Counter func NewCounter(counter kit.Counter) *Counter { return &Counter{counter: counter} } // Inc adds the given value to the counter. func (c *Counter) Inc(delta int64) { c.counter.Add(float64(delta)) } // Gauge is an adapter from go-kit Gauge to jaeger-lib Gauge type Gauge struct { gauge kit.Gauge } // NewGauge creates a new Gauge func NewGauge(gauge kit.Gauge) *Gauge { return &Gauge{gauge: gauge} } // Update the gauge to the value passed in. func (g *Gauge) Update(value int64) { g.gauge.Set(float64(value)) } // Timer is an adapter from go-kit Histogram to jaeger-lib Timer type Timer struct { hist kit.Histogram } // NewTimer creates a new Timer func NewTimer(hist kit.Histogram) *Timer { return &Timer{hist: hist} } // Record saves the time passed in. func (t *Timer) Record(delta time.Duration) { t.hist.Observe(delta.Seconds()) } // Histogram is an adapter from go-kit Histogram to jaeger-lib Histogram type Histogram struct { hist kit.Histogram } // NewHistogram creates a new Histogram func NewHistogram(hist kit.Histogram) *Histogram { return &Histogram{hist: hist} } // Record saves the value passed in. func (t *Histogram) Record(value float64) { t.hist.Observe(value) } golang-github-uber-jaeger-lib-2.4.1/metrics/go-kit/metrics_test.go000066400000000000000000000017651444170434100251060ustar00rootroot00000000000000package xkit import ( "testing" "time" "github.com/go-kit/kit/metrics/generic" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" ) func TestCounter(t *testing.T) { kitCounter := generic.NewCounter("abc") var counter metrics.Counter = NewCounter(kitCounter) counter.Inc(123) assert.EqualValues(t, 123, kitCounter.Value()) } func TestGauge(t *testing.T) { kitGauge := generic.NewGauge("abc") var gauge metrics.Gauge = NewGauge(kitGauge) gauge.Update(123) assert.EqualValues(t, 123, kitGauge.Value()) } func TestTimer(t *testing.T) { kitHist := generic.NewHistogram("abc", 10) var timer metrics.Timer = NewTimer(kitHist) timer.Record(100*time.Millisecond + 500*time.Microsecond) // 100.5 milliseconds assert.EqualValues(t, 0.1005, kitHist.Quantile(0.9)) } func TestHistogram(t *testing.T) { kitHist := generic.NewHistogram("abc", 10) var histogram metrics.Histogram = NewHistogram(kitHist) histogram.Record(100) assert.EqualValues(t, 100, kitHist.Quantile(0.9)) } golang-github-uber-jaeger-lib-2.4.1/metrics/histogram.go000066400000000000000000000016071444170434100231770ustar00rootroot00000000000000// Copyright (c) 2018 The Jaeger Authors // // 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 metrics // Histogram that keeps track of a distribution of values. type Histogram interface { // Records the value passed in. Record(float64) } // NullHistogram that does nothing var NullHistogram Histogram = nullHistogram{} type nullHistogram struct{} func (nullHistogram) Record(float64) {} golang-github-uber-jaeger-lib-2.4.1/metrics/keys.go000066400000000000000000000021001444170434100221420ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics import ( "sort" ) // GetKey converts name+tags into a single string of the form // "name|tag1=value1|...|tagN=valueN", where tag names are // sorted alphabetically. func GetKey(name string, tags map[string]string, tagsSep string, tagKVSep string) string { keys := make([]string, 0, len(tags)) for k := range tags { keys = append(keys, k) } sort.Strings(keys) key := name for _, k := range keys { key = key + tagsSep + k + tagKVSep + tags[k] } return key } golang-github-uber-jaeger-lib-2.4.1/metrics/metrics.go000066400000000000000000000103161444170434100226450ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics import ( "fmt" "reflect" "strconv" "strings" ) // MustInit initializes the passed in metrics and initializes its fields using the passed in factory. // // It uses reflection to initialize a struct containing metrics fields // by assigning new Counter/Gauge/Timer values with the metric name retrieved // from the `metric` tag and stats tags retrieved from the `tags` tag. // // Note: all fields of the struct must be exported, have a `metric` tag, and be // of type Counter or Gauge or Timer. // // Errors during Init lead to a panic. func MustInit(metrics interface{}, factory Factory, globalTags map[string]string) { if err := Init(metrics, factory, globalTags); err != nil { panic(err.Error()) } } // Init does the same as MustInit, but returns an error instead of // panicking. func Init(m interface{}, factory Factory, globalTags map[string]string) error { // Allow user to opt out of reporting metrics by passing in nil. if factory == nil { factory = NullFactory } counterPtrType := reflect.TypeOf((*Counter)(nil)).Elem() gaugePtrType := reflect.TypeOf((*Gauge)(nil)).Elem() timerPtrType := reflect.TypeOf((*Timer)(nil)).Elem() histogramPtrType := reflect.TypeOf((*Histogram)(nil)).Elem() v := reflect.ValueOf(m).Elem() t := v.Type() for i := 0; i < t.NumField(); i++ { tags := make(map[string]string) for k, v := range globalTags { tags[k] = v } var buckets []float64 field := t.Field(i) metric := field.Tag.Get("metric") if metric == "" { return fmt.Errorf("Field %s is missing a tag 'metric'", field.Name) } if tagString := field.Tag.Get("tags"); tagString != "" { tagPairs := strings.Split(tagString, ",") for _, tagPair := range tagPairs { tag := strings.Split(tagPair, "=") if len(tag) != 2 { return fmt.Errorf( "Field [%s]: Tag [%s] is not of the form key=value in 'tags' string [%s]", field.Name, tagPair, tagString) } tags[tag[0]] = tag[1] } } if bucketString := field.Tag.Get("buckets"); bucketString != "" { if field.Type.AssignableTo(timerPtrType) { // TODO: Parse timer duration buckets return fmt.Errorf( "Field [%s]: Buckets are not currently initialized for timer metrics", field.Name) } else if field.Type.AssignableTo(histogramPtrType) { bucketValues := strings.Split(bucketString, ",") for _, bucket := range bucketValues { b, err := strconv.ParseFloat(bucket, 64) if err != nil { return fmt.Errorf( "Field [%s]: Bucket [%s] could not be converted to float64 in 'buckets' string [%s]", field.Name, bucket, bucketString) } buckets = append(buckets, b) } } else { return fmt.Errorf( "Field [%s]: Buckets should only be defined for Timer and Histogram metric types", field.Name) } } help := field.Tag.Get("help") var obj interface{} if field.Type.AssignableTo(counterPtrType) { obj = factory.Counter(Options{ Name: metric, Tags: tags, Help: help, }) } else if field.Type.AssignableTo(gaugePtrType) { obj = factory.Gauge(Options{ Name: metric, Tags: tags, Help: help, }) } else if field.Type.AssignableTo(timerPtrType) { // TODO: Add buckets once parsed (see TODO above) obj = factory.Timer(TimerOptions{ Name: metric, Tags: tags, Help: help, }) } else if field.Type.AssignableTo(histogramPtrType) { obj = factory.Histogram(HistogramOptions{ Name: metric, Tags: tags, Help: help, Buckets: buckets, }) } else { return fmt.Errorf( "Field %s is not a pointer to timer, gauge, or counter", field.Name) } v.Field(i).Set(reflect.ValueOf(obj)) } return nil } golang-github-uber-jaeger-lib-2.4.1/metrics/metrics_test.go000066400000000000000000000067721444170434100237170ustar00rootroot00000000000000// Must use separate test package to break import cycle. package metrics_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/metricstest" ) func TestInitMetrics(t *testing.T) { testMetrics := struct { Gauge metrics.Gauge `metric:"gauge" tags:"1=one,2=two"` Counter metrics.Counter `metric:"counter"` Timer metrics.Timer `metric:"timer"` Histogram metrics.Histogram `metric:"histogram" buckets:"20,40,60,80"` }{} f := metricstest.NewFactory(0) defer f.Stop() globalTags := map[string]string{"key": "value"} err := metrics.Init(&testMetrics, f, globalTags) assert.NoError(t, err) testMetrics.Gauge.Update(10) testMetrics.Counter.Inc(5) testMetrics.Timer.Record(time.Duration(time.Second * 35)) testMetrics.Histogram.Record(42) // wait for metrics for i := 0; i < 1000; i++ { c, _ := f.Snapshot() if _, ok := c["counter"]; ok { break } time.Sleep(1 * time.Millisecond) } c, g := f.Snapshot() assert.EqualValues(t, 5, c["counter|key=value"]) assert.EqualValues(t, 10, g["gauge|1=one|2=two|key=value"]) assert.EqualValues(t, 36863, g["timer|key=value.P50"]) assert.EqualValues(t, 43, g["histogram|key=value.P50"]) stopwatch := metrics.StartStopwatch(testMetrics.Timer) stopwatch.Stop() assert.True(t, 0 < stopwatch.ElapsedTime()) } var ( noMetricTag = struct { NoMetricTag metrics.Counter }{} badTags = struct { BadTags metrics.Counter `metric:"counter" tags:"1=one,noValue"` }{} invalidMetricType = struct { InvalidMetricType int64 `metric:"counter"` }{} badHistogramBucket = struct { BadHistogramBucket metrics.Histogram `metric:"histogram" buckets:"1,2,a,4"` }{} badTimerBucket = struct { BadTimerBucket metrics.Timer `metric:"timer" buckets:"1"` }{} invalidBuckets = struct { InvalidBuckets metrics.Counter `metric:"counter" buckets:"1"` }{} ) func TestInitMetricsFailures(t *testing.T) { assert.EqualError(t, metrics.Init(&noMetricTag, nil, nil), "Field NoMetricTag is missing a tag 'metric'") assert.EqualError(t, metrics.Init(&badTags, nil, nil), "Field [BadTags]: Tag [noValue] is not of the form key=value in 'tags' string [1=one,noValue]") assert.EqualError(t, metrics.Init(&invalidMetricType, nil, nil), "Field InvalidMetricType is not a pointer to timer, gauge, or counter") assert.EqualError(t, metrics.Init(&badHistogramBucket, nil, nil), "Field [BadHistogramBucket]: Bucket [a] could not be converted to float64 in 'buckets' string [1,2,a,4]") assert.EqualError(t, metrics.Init(&badTimerBucket, nil, nil), "Field [BadTimerBucket]: Buckets are not currently initialized for timer metrics") assert.EqualError(t, metrics.Init(&invalidBuckets, nil, nil), "Field [InvalidBuckets]: Buckets should only be defined for Timer and Histogram metric types") } func TestInitPanic(t *testing.T) { defer func() { if r := recover(); r == nil { t.Error("The code did not panic") } }() metrics.MustInit(&noMetricTag, metrics.NullFactory, nil) } func TestNullMetrics(t *testing.T) { // This test is just for cover metrics.NullFactory.Timer(metrics.TimerOptions{ Name: "name", }).Record(0) metrics.NullFactory.Counter(metrics.Options{ Name: "name", }).Inc(0) metrics.NullFactory.Gauge(metrics.Options{ Name: "name", }).Update(0) metrics.NullFactory.Histogram(metrics.HistogramOptions{ Name: "name", }).Record(0) metrics.NullFactory.Namespace(metrics.NSOptions{ Name: "name", }).Gauge(metrics.Options{ Name: "name2", }).Update(0) } golang-github-uber-jaeger-lib-2.4.1/metrics/metricstest/000077500000000000000000000000001444170434100232155ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/metricstest/local.go000066400000000000000000000224261444170434100246440ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metricstest import ( "sync" "sync/atomic" "time" "github.com/HdrHistogram/hdrhistogram-go" "github.com/uber/jaeger-lib/metrics" ) // This is intentionally very similar to github.com/codahale/metrics, the // main difference being that counters/gauges are scoped to the provider // rather than being global (to facilitate testing). // A Backend is a metrics provider which aggregates data in-vm, and // allows exporting snapshots to shove the data into a remote collector type Backend struct { cm sync.Mutex gm sync.Mutex tm sync.Mutex hm sync.Mutex counters map[string]*int64 gauges map[string]*int64 timers map[string]*localBackendTimer histograms map[string]*localBackendHistogram stop chan struct{} wg sync.WaitGroup TagsSep string TagKVSep string } // NewBackend returns a new Backend. The collectionInterval is the histogram // time window for each timer. func NewBackend(collectionInterval time.Duration) *Backend { b := &Backend{ counters: make(map[string]*int64), gauges: make(map[string]*int64), timers: make(map[string]*localBackendTimer), histograms: make(map[string]*localBackendHistogram), stop: make(chan struct{}), TagsSep: "|", TagKVSep: "=", } if collectionInterval == 0 { // Use one histogram time window for all timers return b } b.wg.Add(1) go b.runLoop(collectionInterval) return b } // Clear discards accumulated stats func (b *Backend) Clear() { b.cm.Lock() defer b.cm.Unlock() b.gm.Lock() defer b.gm.Unlock() b.tm.Lock() defer b.tm.Unlock() b.hm.Lock() defer b.hm.Unlock() b.counters = make(map[string]*int64) b.gauges = make(map[string]*int64) b.timers = make(map[string]*localBackendTimer) b.histograms = make(map[string]*localBackendHistogram) } func (b *Backend) runLoop(collectionInterval time.Duration) { defer b.wg.Done() ticker := time.NewTicker(collectionInterval) for { select { case <-ticker.C: b.tm.Lock() timers := make(map[string]*localBackendTimer, len(b.timers)) for timerName, timer := range b.timers { timers[timerName] = timer } b.tm.Unlock() for _, t := range timers { t.Lock() t.hist.Rotate() t.Unlock() } case <-b.stop: ticker.Stop() return } } } // IncCounter increments a counter value func (b *Backend) IncCounter(name string, tags map[string]string, delta int64) { name = metrics.GetKey(name, tags, b.TagsSep, b.TagKVSep) b.cm.Lock() defer b.cm.Unlock() counter := b.counters[name] if counter == nil { b.counters[name] = new(int64) *b.counters[name] = delta return } atomic.AddInt64(counter, delta) } // UpdateGauge updates the value of a gauge func (b *Backend) UpdateGauge(name string, tags map[string]string, value int64) { name = metrics.GetKey(name, tags, b.TagsSep, b.TagKVSep) b.gm.Lock() defer b.gm.Unlock() gauge := b.gauges[name] if gauge == nil { b.gauges[name] = new(int64) *b.gauges[name] = value return } atomic.StoreInt64(gauge, value) } // RecordHistogram records a timing duration func (b *Backend) RecordHistogram(name string, tags map[string]string, v float64) { name = metrics.GetKey(name, tags, b.TagsSep, b.TagKVSep) histogram := b.findOrCreateHistogram(name) histogram.Lock() histogram.hist.Current.RecordValue(int64(v)) histogram.Unlock() } func (b *Backend) findOrCreateHistogram(name string) *localBackendHistogram { b.hm.Lock() defer b.hm.Unlock() if t, ok := b.histograms[name]; ok { return t } t := &localBackendHistogram{ hist: hdrhistogram.NewWindowed(5, 0, int64((5*time.Minute)/time.Millisecond), 1), } b.histograms[name] = t return t } type localBackendHistogram struct { sync.Mutex hist *hdrhistogram.WindowedHistogram } // RecordTimer records a timing duration func (b *Backend) RecordTimer(name string, tags map[string]string, d time.Duration) { name = metrics.GetKey(name, tags, b.TagsSep, b.TagKVSep) timer := b.findOrCreateTimer(name) timer.Lock() timer.hist.Current.RecordValue(int64(d / time.Millisecond)) timer.Unlock() } func (b *Backend) findOrCreateTimer(name string) *localBackendTimer { b.tm.Lock() defer b.tm.Unlock() if t, ok := b.timers[name]; ok { return t } t := &localBackendTimer{ hist: hdrhistogram.NewWindowed(5, 0, int64((5*time.Minute)/time.Millisecond), 1), } b.timers[name] = t return t } type localBackendTimer struct { sync.Mutex hist *hdrhistogram.WindowedHistogram } var ( percentiles = map[string]float64{ "P50": 50, "P75": 75, "P90": 90, "P95": 95, "P99": 99, "P999": 99.9, } ) // Snapshot captures a snapshot of the current counter and gauge values func (b *Backend) Snapshot() (counters, gauges map[string]int64) { b.cm.Lock() defer b.cm.Unlock() counters = make(map[string]int64, len(b.counters)) for name, value := range b.counters { counters[name] = atomic.LoadInt64(value) } b.gm.Lock() defer b.gm.Unlock() gauges = make(map[string]int64, len(b.gauges)) for name, value := range b.gauges { gauges[name] = atomic.LoadInt64(value) } b.tm.Lock() timers := make(map[string]*localBackendTimer) for timerName, timer := range b.timers { timers[timerName] = timer } b.tm.Unlock() for timerName, timer := range timers { timer.Lock() hist := timer.hist.Merge() timer.Unlock() for name, q := range percentiles { gauges[timerName+"."+name] = hist.ValueAtQuantile(q) } } b.hm.Lock() histograms := make(map[string]*localBackendHistogram) for histogramName, histogram := range b.histograms { histograms[histogramName] = histogram } b.hm.Unlock() for histogramName, histogram := range histograms { histogram.Lock() hist := histogram.hist.Merge() histogram.Unlock() for name, q := range percentiles { gauges[histogramName+"."+name] = hist.ValueAtQuantile(q) } } return } // Stop cleanly closes the background goroutine spawned by NewBackend. func (b *Backend) Stop() { close(b.stop) b.wg.Wait() } type stats struct { name string tags map[string]string buckets []float64 durationBuckets []time.Duration localBackend *Backend } type localTimer struct { stats } func (l *localTimer) Record(d time.Duration) { l.localBackend.RecordTimer(l.name, l.tags, d) } type localHistogram struct { stats } func (l *localHistogram) Record(v float64) { l.localBackend.RecordHistogram(l.name, l.tags, v) } type localCounter struct { stats } func (l *localCounter) Inc(delta int64) { l.localBackend.IncCounter(l.name, l.tags, delta) } type localGauge struct { stats } func (l *localGauge) Update(value int64) { l.localBackend.UpdateGauge(l.name, l.tags, value) } // Factory stats factory that creates metrics that are stored locally type Factory struct { *Backend namespace string tags map[string]string } // NewFactory returns a new LocalMetricsFactory func NewFactory(collectionInterval time.Duration) *Factory { return &Factory{ Backend: NewBackend(collectionInterval), } } // appendTags adds the tags to the namespace tags and returns a combined map. func (l *Factory) appendTags(tags map[string]string) map[string]string { newTags := make(map[string]string) for k, v := range l.tags { newTags[k] = v } for k, v := range tags { newTags[k] = v } return newTags } func (l *Factory) newNamespace(name string) string { if l.namespace == "" { return name } if name == "" { return l.namespace } return l.namespace + "." + name } // Counter returns a local stats counter func (l *Factory) Counter(options metrics.Options) metrics.Counter { return &localCounter{ stats{ name: l.newNamespace(options.Name), tags: l.appendTags(options.Tags), localBackend: l.Backend, }, } } // Timer returns a local stats timer. func (l *Factory) Timer(options metrics.TimerOptions) metrics.Timer { return &localTimer{ stats{ name: l.newNamespace(options.Name), tags: l.appendTags(options.Tags), durationBuckets: options.Buckets, localBackend: l.Backend, }, } } // Gauge returns a local stats gauge. func (l *Factory) Gauge(options metrics.Options) metrics.Gauge { return &localGauge{ stats{ name: l.newNamespace(options.Name), tags: l.appendTags(options.Tags), localBackend: l.Backend, }, } } // Histogram returns a local stats histogram. func (l *Factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { return &localHistogram{ stats{ name: l.newNamespace(options.Name), tags: l.appendTags(options.Tags), buckets: options.Buckets, localBackend: l.Backend, }, } } // Namespace returns a new namespace. func (l *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &Factory{ namespace: l.newNamespace(scope.Name), tags: l.appendTags(scope.Tags), Backend: l.Backend, } } golang-github-uber-jaeger-lib-2.4.1/metrics/metricstest/local_test.go000066400000000000000000000065351444170434100257060ustar00rootroot00000000000000package metricstest import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/jaeger-lib/metrics" ) func TestLocalMetrics(t *testing.T) { tags := map[string]string{ "x": "y", } f := NewFactory(0) defer f.Stop() f.Counter(metrics.Options{ Name: "my-counter", Tags: tags, }).Inc(4) f.Counter(metrics.Options{ Name: "my-counter", Tags: tags, }).Inc(6) f.Counter(metrics.Options{ Name: "my-counter", }).Inc(6) f.Counter(metrics.Options{ Name: "other-counter", }).Inc(8) f.Gauge(metrics.Options{ Name: "my-gauge", }).Update(25) f.Gauge(metrics.Options{ Name: "my-gauge", }).Update(43) f.Gauge(metrics.Options{ Name: "other-gauge", }).Update(74) f.Namespace(metrics.NSOptions{ Name: "namespace", Tags: tags, }).Counter(metrics.Options{ Name: "my-counter", }).Inc(7) f.Namespace(metrics.NSOptions{ Name: "ns.subns", }).Counter(metrics.Options{ Tags: map[string]string{"service": "a-service"}, }).Inc(9) timings := map[string][]time.Duration{ "foo-latency": { time.Second * 35, time.Second * 6, time.Millisecond * 576, time.Second * 12, }, "bar-latency": { time.Minute*4 + time.Second*34, time.Minute*7 + time.Second*12, time.Second * 625, time.Second * 12, }, } for metric, timing := range timings { for _, d := range timing { f.Timer(metrics.TimerOptions{ Name: metric, }).Record(d) } } histogram := f.Histogram(metrics.HistogramOptions{ Name: "my-histo", }) histogram.Record(321) histogram.Record(42) c, g := f.Snapshot() require.NotNil(t, c) require.NotNil(t, g) assert.Equal(t, map[string]int64{ "my-counter|x=y": 10, "my-counter": 6, "other-counter": 8, "namespace.my-counter|x=y": 7, "ns.subns|service=a-service": 9, }, c) assert.Equal(t, map[string]int64{ "bar-latency.P50": 278527, "bar-latency.P75": 278527, "bar-latency.P90": 442367, "bar-latency.P95": 442367, "bar-latency.P99": 442367, "bar-latency.P999": 442367, "foo-latency.P50": 6143, "foo-latency.P75": 12287, "foo-latency.P90": 36863, "foo-latency.P95": 36863, "foo-latency.P99": 36863, "foo-latency.P999": 36863, "my-gauge": 43, "my-histo.P50": 43, "my-histo.P75": 335, "my-histo.P90": 335, "my-histo.P95": 335, "my-histo.P99": 335, "my-histo.P999": 335, "other-gauge": 74, }, g) f.Clear() c, g = f.Snapshot() require.Empty(t, c) require.Empty(t, g) } func TestLocalMetricsInterval(t *testing.T) { refreshInterval := time.Millisecond const relativeCheckFrequency = 5 // check 5 times per refreshInterval const maxChecks = 2 * relativeCheckFrequency checkInterval := (refreshInterval * relativeCheckFrequency) / maxChecks f := NewFactory(refreshInterval) defer f.Stop() f.Timer(metrics.TimerOptions{ Name: "timer", }).Record(1) f.tm.Lock() timer := f.timers["timer"] f.tm.Unlock() assert.NotNil(t, timer) // timer.hist.Current is modified on every Rotate(), which is called by Backend after every refreshInterval getCurr := func() interface{} { timer.Lock() defer timer.Unlock() return timer.hist.Current } curr := getCurr() // wait for twice as long as the refresh interval for i := 0; i < maxChecks; i++ { time.Sleep(checkInterval) if getCurr() != curr { return } } t.Fail() } golang-github-uber-jaeger-lib-2.4.1/metrics/metricstest/metricstest.go000066400000000000000000000032361444170434100261160ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metricstest import ( "testing" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" ) // ExpectedMetric contains metrics under test. type ExpectedMetric struct { Name string Tags map[string]string Value int } // TODO do something similar for Timers // AssertCounterMetrics checks if counter metrics exist. func (f *Factory) AssertCounterMetrics(t *testing.T, expectedMetrics ...ExpectedMetric) { counters, _ := f.Snapshot() assertMetrics(t, counters, expectedMetrics...) } // AssertGaugeMetrics checks if gauge metrics exist. func (f *Factory) AssertGaugeMetrics(t *testing.T, expectedMetrics ...ExpectedMetric) { _, gauges := f.Snapshot() assertMetrics(t, gauges, expectedMetrics...) } func assertMetrics(t *testing.T, actualMetrics map[string]int64, expectedMetrics ...ExpectedMetric) { for _, expected := range expectedMetrics { key := metrics.GetKey(expected.Name, expected.Tags, "|", "=") assert.EqualValues(t, expected.Value, actualMetrics[key], "expected metric name: %s, tags: %+v", expected.Name, expected.Tags, ) } } golang-github-uber-jaeger-lib-2.4.1/metrics/metricstest/metricstest_test.go000066400000000000000000000005721444170434100271550ustar00rootroot00000000000000package metricstest import ( "testing" ) func TestAssertMetrics(t *testing.T) { f := NewFactory(0) tags := map[string]string{"key": "value"} f.IncCounter("counter", tags, 1) f.UpdateGauge("gauge", tags, 11) f.AssertCounterMetrics(t, ExpectedMetric{Name: "counter", Tags: tags, Value: 1}) f.AssertGaugeMetrics(t, ExpectedMetric{Name: "gauge", Tags: tags, Value: 11}) } golang-github-uber-jaeger-lib-2.4.1/metrics/multi/000077500000000000000000000000001444170434100220015ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/multi/multi.go000066400000000000000000000061341444170434100234660ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package multi import ( "time" "github.com/uber/jaeger-lib/metrics" ) // Factory is a metrics factory that dispatches to multiple metrics backends. type Factory struct { factories []metrics.Factory } // New creates a new multi.Factory that will dispatch to multiple metrics backends. func New(factories ...metrics.Factory) *Factory { return &Factory{ factories: factories, } } type counter struct { counters []metrics.Counter } func (c *counter) Inc(delta int64) { for _, counter := range c.counters { counter.Inc(delta) } } // Counter implements metrics.Factory interface func (f *Factory) Counter(options metrics.Options) metrics.Counter { counter := &counter{ counters: make([]metrics.Counter, len(f.factories)), } for i, factory := range f.factories { counter.counters[i] = factory.Counter(options) } return counter } type timer struct { timers []metrics.Timer } func (t *timer) Record(delta time.Duration) { for _, timer := range t.timers { timer.Record(delta) } } // Timer implements metrics.Factory interface func (f *Factory) Timer(options metrics.TimerOptions) metrics.Timer { timer := &timer{ timers: make([]metrics.Timer, len(f.factories)), } for i, factory := range f.factories { timer.timers[i] = factory.Timer(options) } return timer } type histogram struct { histograms []metrics.Histogram } func (h *histogram) Record(value float64) { for _, histogram := range h.histograms { histogram.Record(value) } } // Histogram implements metrics.Factory interface func (f *Factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { histogram := &histogram{ histograms: make([]metrics.Histogram, len(f.factories)), } for i, factory := range f.factories { histogram.histograms[i] = factory.Histogram(options) } return histogram } type gauge struct { gauges []metrics.Gauge } func (t *gauge) Update(value int64) { for _, gauge := range t.gauges { gauge.Update(value) } } // Gauge implements metrics.Factory interface func (f *Factory) Gauge(options metrics.Options) metrics.Gauge { gauge := &gauge{ gauges: make([]metrics.Gauge, len(f.factories)), } for i, factory := range f.factories { gauge.gauges[i] = factory.Gauge(options) } return gauge } // Namespace implements metrics.Factory interface func (f *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { newFactory := &Factory{ factories: make([]metrics.Factory, len(f.factories)), } for i, factory := range f.factories { newFactory.factories[i] = factory.Namespace(scope) } return newFactory } golang-github-uber-jaeger-lib-2.4.1/metrics/multi/multi_test.go000066400000000000000000000022751444170434100245270ustar00rootroot00000000000000package multi import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/metricstest" ) var _ metrics.Factory = &Factory{} // API check func TestMultiFactory(t *testing.T) { f1 := metricstest.NewFactory(time.Second) f2 := metricstest.NewFactory(time.Second) multi1 := New(f1, f2) multi2 := multi1.Namespace(metrics.NSOptions{ Name: "ns2", }) tags := map[string]string{"x": "y"} multi2.Counter(metrics.Options{ Name: "counter", Tags: tags, }).Inc(42) multi2.Gauge(metrics.Options{ Name: "gauge", Tags: tags, }).Update(42) multi2.Timer(metrics.TimerOptions{ Name: "timer", Tags: tags, }).Record(42 * time.Millisecond) multi2.Histogram(metrics.HistogramOptions{ Name: "histogram", Tags: tags, }).Record(42) for _, f := range []*metricstest.Factory{f1, f2} { f.AssertCounterMetrics(t, metricstest.ExpectedMetric{Name: "ns2.counter", Tags: tags, Value: 42}) f.AssertGaugeMetrics(t, metricstest.ExpectedMetric{Name: "ns2.gauge", Tags: tags, Value: 42}) _, g := f.Snapshot() assert.EqualValues(t, 43, g["ns2.timer|x=y.P99"]) assert.EqualValues(t, 43, g["ns2.histogram|x=y.P99"]) } } golang-github-uber-jaeger-lib-2.4.1/metrics/prometheus/000077500000000000000000000000001444170434100230425ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/prometheus/cache.go000066400000000000000000000050431444170434100244360ustar00rootroot00000000000000// Copyright (c) 2017 The Jaeger Authors. // // 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 prometheus import ( "strings" "sync" "github.com/prometheus/client_golang/prometheus" ) // vectorCache is used to avoid creating Prometheus vectors with the same set of labels more than once. type vectorCache struct { registerer prometheus.Registerer lock sync.Mutex cVecs map[string]*prometheus.CounterVec gVecs map[string]*prometheus.GaugeVec hVecs map[string]*prometheus.HistogramVec } func newVectorCache(registerer prometheus.Registerer) *vectorCache { return &vectorCache{ registerer: registerer, cVecs: make(map[string]*prometheus.CounterVec), gVecs: make(map[string]*prometheus.GaugeVec), hVecs: make(map[string]*prometheus.HistogramVec), } } func (c *vectorCache) getOrMakeCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec { c.lock.Lock() defer c.lock.Unlock() cacheKey := c.getCacheKey(opts.Name, labelNames) cv, cvExists := c.cVecs[cacheKey] if !cvExists { cv = prometheus.NewCounterVec(opts, labelNames) c.registerer.MustRegister(cv) c.cVecs[cacheKey] = cv } return cv } func (c *vectorCache) getOrMakeGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { c.lock.Lock() defer c.lock.Unlock() cacheKey := c.getCacheKey(opts.Name, labelNames) gv, gvExists := c.gVecs[cacheKey] if !gvExists { gv = prometheus.NewGaugeVec(opts, labelNames) c.registerer.MustRegister(gv) c.gVecs[cacheKey] = gv } return gv } func (c *vectorCache) getOrMakeHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec { c.lock.Lock() defer c.lock.Unlock() cacheKey := c.getCacheKey(opts.Name, labelNames) hv, hvExists := c.hVecs[cacheKey] if !hvExists { hv = prometheus.NewHistogramVec(opts, labelNames) c.registerer.MustRegister(hv) c.hVecs[cacheKey] = hv } return hv } func (c *vectorCache) getCacheKey(name string, labels []string) string { return strings.Join(append([]string{name}, labels...), "||") } golang-github-uber-jaeger-lib-2.4.1/metrics/prometheus/factory.go000066400000000000000000000173621444170434100250510ustar00rootroot00000000000000// Copyright (c) 2017 The Jaeger Authors. // // 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 prometheus import ( "sort" "strings" "time" "github.com/prometheus/client_golang/prometheus" "github.com/uber/jaeger-lib/metrics" ) // Factory implements metrics.Factory backed by Prometheus registry. type Factory struct { scope string tags map[string]string cache *vectorCache buckets []float64 normalizer *strings.Replacer separator Separator } type options struct { registerer prometheus.Registerer buckets []float64 separator Separator } // Separator represents the namespace separator to use type Separator rune const ( // SeparatorUnderscore uses an underscore as separator SeparatorUnderscore Separator = '_' // SeparatorColon uses a colon as separator SeparatorColon = ':' ) // Option is a function that sets some option for the Factory constructor. type Option func(*options) // WithRegisterer returns an option that sets the registerer. // If not used we fallback to prometheus.DefaultRegisterer. func WithRegisterer(registerer prometheus.Registerer) Option { return func(opts *options) { opts.registerer = registerer } } // WithBuckets returns an option that sets the default buckets for histogram. // If not used, we fallback to default Prometheus buckets. func WithBuckets(buckets []float64) Option { return func(opts *options) { opts.buckets = buckets } } // WithSeparator returns an option that sets the default separator for the namespace // If not used, we fallback to underscore. func WithSeparator(separator Separator) Option { return func(opts *options) { opts.separator = separator } } func applyOptions(opts []Option) *options { options := new(options) for _, o := range opts { o(options) } if options.registerer == nil { options.registerer = prometheus.DefaultRegisterer } if options.separator == '\x00' { options.separator = SeparatorUnderscore } return options } // New creates a Factory backed by Prometheus registry. // Typically the first argument should be prometheus.DefaultRegisterer. // // Parameter buckets defines the buckets into which Timer observations are counted. // Each element in the slice is the upper inclusive bound of a bucket. The // values must be sorted in strictly increasing order. There is no need // to add a highest bucket with +Inf bound, it will be added // implicitly. The default value is prometheus.DefBuckets. func New(opts ...Option) *Factory { options := applyOptions(opts) return newFactory( &Factory{ // dummy struct to be discarded cache: newVectorCache(options.registerer), buckets: options.buckets, normalizer: strings.NewReplacer(".", "_", "-", "_"), separator: options.separator, }, "", // scope nil) // tags } func newFactory(parent *Factory, scope string, tags map[string]string) *Factory { return &Factory{ cache: parent.cache, buckets: parent.buckets, normalizer: parent.normalizer, separator: parent.separator, scope: scope, tags: tags, } } // Counter implements Counter of metrics.Factory. func (f *Factory) Counter(options metrics.Options) metrics.Counter { help := strings.TrimSpace(options.Help) if len(help) == 0 { help = options.Name } name := counterNamingConvention(f.subScope(options.Name)) tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.CounterOpts{ Name: name, Help: help, } cv := f.cache.getOrMakeCounterVec(opts, labelNames) return &counter{ counter: cv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), } } // Gauge implements Gauge of metrics.Factory. func (f *Factory) Gauge(options metrics.Options) metrics.Gauge { help := strings.TrimSpace(options.Help) if len(help) == 0 { help = options.Name } name := f.subScope(options.Name) tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.GaugeOpts{ Name: name, Help: help, } gv := f.cache.getOrMakeGaugeVec(opts, labelNames) return &gauge{ gauge: gv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), } } // Timer implements Timer of metrics.Factory. func (f *Factory) Timer(options metrics.TimerOptions) metrics.Timer { help := strings.TrimSpace(options.Help) if len(help) == 0 { help = options.Name } name := f.subScope(options.Name) tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.HistogramOpts{ Name: name, Help: help, Buckets: asFloatBuckets(options.Buckets), } hv := f.cache.getOrMakeHistogramVec(opts, labelNames) return &timer{ histogram: hv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), } } func asFloatBuckets(buckets []time.Duration) []float64 { data := make([]float64, len(buckets)) for i := range data { data[i] = float64(buckets[i]) / float64(time.Second) } return data } // Histogram implements Histogram of metrics.Factory. func (f *Factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { help := strings.TrimSpace(options.Help) if len(help) == 0 { help = options.Name } name := f.subScope(options.Name) buckets := f.selectBuckets(options.Buckets) tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.HistogramOpts{ Name: name, Help: help, Buckets: buckets, } hv := f.cache.getOrMakeHistogramVec(opts, labelNames) return &histogram{ histogram: hv.WithLabelValues(f.tagsAsLabelValues(labelNames, tags)...), } } // Namespace implements Namespace of metrics.Factory. func (f *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { return newFactory(f, f.subScope(scope.Name), f.mergeTags(scope.Tags)) } type counter struct { counter prometheus.Counter } func (c *counter) Inc(v int64) { c.counter.Add(float64(v)) } type gauge struct { gauge prometheus.Gauge } func (g *gauge) Update(v int64) { g.gauge.Set(float64(v)) } type observer interface { Observe(v float64) } type timer struct { histogram observer } func (t *timer) Record(v time.Duration) { t.histogram.Observe(float64(v.Nanoseconds()) / float64(time.Second/time.Nanosecond)) } type histogram struct { histogram observer } func (h *histogram) Record(v float64) { h.histogram.Observe(v) } func (f *Factory) subScope(name string) string { if f.scope == "" { return f.normalize(name) } if name == "" { return f.normalize(f.scope) } return f.normalize(f.scope + string(f.separator) + name) } func (f *Factory) normalize(v string) string { return f.normalizer.Replace(v) } func (f *Factory) mergeTags(tags map[string]string) map[string]string { ret := make(map[string]string, len(f.tags)+len(tags)) for k, v := range f.tags { ret[k] = v } for k, v := range tags { ret[k] = v } return ret } func (f *Factory) tagNames(tags map[string]string) []string { ret := make([]string, 0, len(tags)) for k := range tags { ret = append(ret, k) } sort.Strings(ret) return ret } func (f *Factory) tagsAsLabelValues(labels []string, tags map[string]string) []string { ret := make([]string, 0, len(tags)) for _, l := range labels { ret = append(ret, tags[l]) } return ret } func (f *Factory) selectBuckets(buckets []float64) []float64 { if len(buckets) > 0 { return buckets } return f.buckets } func counterNamingConvention(name string) string { if !strings.HasSuffix(name, "_total") { name += "_total" } return name } golang-github-uber-jaeger-lib-2.4.1/metrics/prometheus/factory_test.go000066400000000000000000000305011444170434100260760ustar00rootroot00000000000000// Copyright (c) 2017 The Jaeger Authors. // // 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 prometheus_test import ( "testing" "time" "github.com/prometheus/client_golang/prometheus" promModel "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber/jaeger-lib/metrics" . "github.com/uber/jaeger-lib/metrics/prometheus" ) var _ metrics.Factory = new(Factory) func TestOptions(t *testing.T) { f1 := New() assert.NotNil(t, f1) } func TestSeparator(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry), WithSeparator(SeparatorColon)) c1 := f1.Namespace(metrics.NSOptions{ Name: "bender", }).Counter(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"a": "b"}, Help: "Help message", }) c1.Inc(1) snapshot, err := registry.Gather() require.NoError(t, err) m1 := findMetric(t, snapshot, "bender:rodriguez_total", map[string]string{"a": "b"}) assert.EqualValues(t, 1, m1.GetCounter().GetValue(), "%+v", m1) } func TestCounter(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) fDummy := f1.Namespace(metrics.NSOptions{}) f2 := fDummy.Namespace(metrics.NSOptions{ Name: "bender", Tags: map[string]string{"a": "b"}, }) f3 := f2.Namespace(metrics.NSOptions{}) c1 := f2.Counter(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, Help: "Help message", }) c2 := f2.Counter(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) c3 := f3.Counter(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) // same tags as c2, but from f3 c1.Inc(1) c1.Inc(2) c2.Inc(3) c3.Inc(4) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) m1 := findMetric(t, snapshot, "bender_rodriguez_total", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 3, m1.GetCounter().GetValue(), "%+v", m1) m2 := findMetric(t, snapshot, "bender_rodriguez_total", map[string]string{"a": "b", "x": "z"}) assert.EqualValues(t, 7, m2.GetCounter().GetValue(), "%+v", m2) } func TestCounterDefaultHelp(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) c1 := f1.Counter(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, }) c1.Inc(1) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) } func TestGauge(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) f2 := f1.Namespace(metrics.NSOptions{ Name: "bender", Tags: map[string]string{"a": "b"}, }) f3 := f2.Namespace(metrics.NSOptions{ Tags: map[string]string{"a": "b"}, }) // essentially same as f2 g1 := f2.Gauge(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, Help: "Help message", }) g2 := f2.Gauge(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) g3 := f3.Gauge(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) // same as g2, but from f3 g1.Update(1) g1.Update(2) g2.Update(3) g3.Update(4) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 2, m1.GetGauge().GetValue(), "%+v", m1) m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) assert.EqualValues(t, 4, m2.GetGauge().GetValue(), "%+v", m2) } func TestGaugeDefaultHelp(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) g1 := f1.Gauge(metrics.Options{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, }) g1.Update(1) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) } func TestTimer(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) f2 := f1.Namespace(metrics.NSOptions{ Name: "bender", Tags: map[string]string{"a": "b"}, }) f3 := f2.Namespace(metrics.NSOptions{ Tags: map[string]string{"a": "b"}, }) // essentially same as f2 t1 := f2.Timer(metrics.TimerOptions{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, Help: "Help message", }) t2 := f2.Timer(metrics.TimerOptions{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) t3 := f3.Timer(metrics.TimerOptions{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) // same as t2, but from f3 t1.Record(1 * time.Second) t1.Record(2 * time.Second) t2.Record(3 * time.Second) t3.Record(4 * time.Second) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) for _, bucket := range m1.GetHistogram().GetBucket() { if bucket.GetUpperBound() < 1 { assert.EqualValues(t, 0, bucket.GetCumulativeCount()) } else if bucket.GetUpperBound() < 2 { assert.EqualValues(t, 1, bucket.GetCumulativeCount()) } else { assert.EqualValues(t, 2, bucket.GetCumulativeCount()) } } m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) assert.EqualValues(t, 2, m2.GetHistogram().GetSampleCount(), "%+v", m2) assert.EqualValues(t, 7, m2.GetHistogram().GetSampleSum(), "%+v", m2) for _, bucket := range m2.GetHistogram().GetBucket() { if bucket.GetUpperBound() < 3 { assert.EqualValues(t, 0, bucket.GetCumulativeCount()) } else if bucket.GetUpperBound() < 4 { assert.EqualValues(t, 1, bucket.GetCumulativeCount()) } else { assert.EqualValues(t, 2, bucket.GetCumulativeCount()) } } } func TestTimerDefaultHelp(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) t1 := f1.Timer(metrics.TimerOptions{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, }) t1.Record(1 * time.Second) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) } func TestTimerCustomBuckets(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry), WithBuckets([]float64{1.5})) // dot and dash in the metric name will be replaced with underscore t1 := f1.Timer(metrics.TimerOptions{ Name: "bender.bending-rodriguez", Tags: map[string]string{"x": "y"}, Buckets: []time.Duration{time.Nanosecond, 5 * time.Nanosecond}, }) t1.Record(1 * time.Second) t1.Record(2 * time.Second) snapshot, err := registry.Gather() require.NoError(t, err) m1 := findMetric(t, snapshot, "bender_bending_rodriguez", map[string]string{"x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) assert.Len(t, m1.GetHistogram().GetBucket(), 2) } func TestHistogram(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) f2 := f1.Namespace(metrics.NSOptions{ Name: "bender", Tags: map[string]string{"a": "b"}, }) f3 := f2.Namespace(metrics.NSOptions{ Tags: map[string]string{"a": "b"}, }) // essentially same as f2 t1 := f2.Histogram(metrics.HistogramOptions{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, Help: "Help message", }) t2 := f2.Histogram(metrics.HistogramOptions{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) t3 := f3.Histogram(metrics.HistogramOptions{ Name: "rodriguez", Tags: map[string]string{"x": "z"}, Help: "Help message", }) // same as t2, but from f3 t1.Record(1) t1.Record(2) t2.Record(3) t3.Record(4) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) for _, bucket := range m1.GetHistogram().GetBucket() { if bucket.GetUpperBound() < 1 { assert.EqualValues(t, 0, bucket.GetCumulativeCount()) } else if bucket.GetUpperBound() < 2 { assert.EqualValues(t, 1, bucket.GetCumulativeCount()) } else { assert.EqualValues(t, 2, bucket.GetCumulativeCount()) } } m2 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "z"}) assert.EqualValues(t, 2, m2.GetHistogram().GetSampleCount(), "%+v", m2) assert.EqualValues(t, 7, m2.GetHistogram().GetSampleSum(), "%+v", m2) for _, bucket := range m2.GetHistogram().GetBucket() { if bucket.GetUpperBound() < 3 { assert.EqualValues(t, 0, bucket.GetCumulativeCount()) } else if bucket.GetUpperBound() < 4 { assert.EqualValues(t, 1, bucket.GetCumulativeCount()) } else { assert.EqualValues(t, 2, bucket.GetCumulativeCount()) } } } func TestHistogramDefaultHelp(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) t1 := f1.Histogram(metrics.HistogramOptions{ Name: "rodriguez", Tags: map[string]string{"x": "y"}, }) t1.Record(1) snapshot, err := registry.Gather() require.NoError(t, err) assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) } func TestHistogramCustomBuckets(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) // dot and dash in the metric name will be replaced with underscore t1 := f1.Histogram(metrics.HistogramOptions{ Name: "bender.bending-rodriguez", Tags: map[string]string{"x": "y"}, Buckets: []float64{1.5}, }) t1.Record(1) t1.Record(2) snapshot, err := registry.Gather() require.NoError(t, err) m1 := findMetric(t, snapshot, "bender_bending_rodriguez", map[string]string{"x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) assert.Len(t, m1.GetHistogram().GetBucket(), 1) } func TestHistogramDefaultBuckets(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry), WithBuckets([]float64{1.5})) // dot and dash in the metric name will be replaced with underscore t1 := f1.Histogram(metrics.HistogramOptions{ Name: "bender.bending-rodriguez", Tags: map[string]string{"x": "y"}, Buckets: nil, }) t1.Record(1) t1.Record(2) snapshot, err := registry.Gather() require.NoError(t, err) m1 := findMetric(t, snapshot, "bender_bending_rodriguez", map[string]string{"x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) assert.Len(t, m1.GetHistogram().GetBucket(), 1) } func findMetric(t *testing.T, snapshot []*promModel.MetricFamily, name string, tags map[string]string) *promModel.Metric { for _, mf := range snapshot { if mf.GetName() != name { continue } for _, m := range mf.GetMetric() { if len(m.GetLabel()) != len(tags) { t.Fatalf("Mismatching labels for metric %v: want %v, have %v", name, tags, m.GetLabel()) } match := true for _, l := range m.GetLabel() { if v, ok := tags[l.GetName()]; !ok || v != l.GetValue() { match = false } } if match { return m } } } t.Logf("Cannot find metric %v %v", name, tags) for _, nf := range snapshot { t.Logf("Family: %v", nf.GetName()) for _, m := range nf.GetMetric() { t.Logf("==> %v", m) } } t.FailNow() return nil } golang-github-uber-jaeger-lib-2.4.1/metrics/stopwatch.go000066400000000000000000000025721444170434100232200ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics import ( "time" ) // StartStopwatch begins recording the executing time of an event, returning // a Stopwatch that should be used to stop the recording the time for // that event. Multiple events can be occurring simultaneously each // represented by different active Stopwatches func StartStopwatch(timer Timer) Stopwatch { return Stopwatch{t: timer, start: time.Now()} } // A Stopwatch tracks the execution time of a specific event type Stopwatch struct { t Timer start time.Time } // Stop stops executing of the stopwatch and records the amount of elapsed time func (s Stopwatch) Stop() { s.t.Record(s.ElapsedTime()) } // ElapsedTime returns the amount of elapsed time (in time.Duration) func (s Stopwatch) ElapsedTime() time.Duration { return time.Since(s.start) } golang-github-uber-jaeger-lib-2.4.1/metrics/tally/000077500000000000000000000000001444170434100217745ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/metrics/tally/factory.go000066400000000000000000000037611444170434100240010ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package tally import ( "github.com/uber-go/tally" "github.com/uber/jaeger-lib/metrics" ) // Wrap takes a tally Scope and returns jaeger-lib metrics.Factory. func Wrap(scope tally.Scope) metrics.Factory { return &factory{ tally: scope, } } // TODO implement support for tags if tally.Scope does not support them type factory struct { tally tally.Scope } func (f *factory) Counter(options metrics.Options) metrics.Counter { scope := f.tally if len(options.Tags) > 0 { scope = scope.Tagged(options.Tags) } return NewCounter(scope.Counter(options.Name)) } func (f *factory) Gauge(options metrics.Options) metrics.Gauge { scope := f.tally if len(options.Tags) > 0 { scope = scope.Tagged(options.Tags) } return NewGauge(scope.Gauge(options.Name)) } func (f *factory) Timer(options metrics.TimerOptions) metrics.Timer { scope := f.tally if len(options.Tags) > 0 { scope = scope.Tagged(options.Tags) } // TODO: Determine whether buckets can be used return NewTimer(scope.Timer(options.Name)) } func (f *factory) Histogram(options metrics.HistogramOptions) metrics.Histogram { scope := f.tally if len(options.Tags) > 0 { scope = scope.Tagged(options.Tags) } return NewHistogram(scope.Histogram(options.Name, tally.ValueBuckets(options.Buckets))) } func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ tally: f.tally.SubScope(scope.Name).Tagged(scope.Tags), } } golang-github-uber-jaeger-lib-2.4.1/metrics/tally/factory_test.go000066400000000000000000000040621444170434100250330ustar00rootroot00000000000000package tally import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/uber/jaeger-lib/metrics" "github.com/uber-go/tally" ) func TestFactory(t *testing.T) { testScope := tally.NewTestScope("pre", map[string]string{"a": "b"}) factory := Wrap(testScope).Namespace(metrics.NSOptions{ Name: "fix", Tags: map[string]string{"c": "d"}, }) counter := factory.Counter(metrics.Options{ Name: "counter", Tags: map[string]string{"x": "y"}, }) counter.Inc(42) gauge := factory.Gauge(metrics.Options{ Name: "gauge", Tags: map[string]string{"x": "y"}, }) gauge.Update(42) timer := factory.Timer(metrics.TimerOptions{ Name: "timer", Tags: map[string]string{"x": "y"}, }) timer.Record(42 * time.Millisecond) histogram := factory.Histogram(metrics.HistogramOptions{ Name: "histogram", Tags: map[string]string{"x": "y"}, Buckets: []float64{0, 100, 200}, }) histogram.Record(42) snapshot := testScope.Snapshot() // tally v3 includes tags in the name, so look c := snapshot.Counters()["pre.fix.counter"] if c == nil { // tally v3 includes tags in the name. c = snapshot.Counters()["pre.fix.counter+a=b,c=d,x=y"] } g := snapshot.Gauges()["pre.fix.gauge"] if g == nil { g = snapshot.Gauges()["pre.fix.gauge+a=b,c=d,x=y"] } h := snapshot.Timers()["pre.fix.timer"] if h == nil { h = snapshot.Timers()["pre.fix.timer+a=b,c=d,x=y"] } hs := snapshot.Histograms()["pre.fix.histogram"] if hs == nil { hs = snapshot.Histograms()["pre.fix.histogram+a=b,c=d,x=y"] } expectedTags := map[string]string{"a": "b", "c": "d", "x": "y"} assert.EqualValues(t, 42, c.Value()) assert.EqualValues(t, expectedTags, c.Tags()) assert.EqualValues(t, 42, g.Value()) assert.EqualValues(t, expectedTags, g.Tags()) assert.Equal(t, []time.Duration{42 * time.Millisecond}, h.Values()) assert.EqualValues(t, expectedTags, h.Tags()) assert.Len(t, hs.Values(), 4) assert.Equal(t, int64(0), hs.Values()[0]) assert.Equal(t, int64(1), hs.Values()[100]) assert.Equal(t, int64(0), hs.Values()[200]) assert.EqualValues(t, expectedTags, hs.Tags()) } golang-github-uber-jaeger-lib-2.4.1/metrics/tally/metrics.go000066400000000000000000000040011444170434100237640ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package tally import ( "time" "github.com/uber-go/tally" ) // Counter is an adapter from go-tally Counter to jaeger-lib Counter type Counter struct { counter tally.Counter } // NewCounter creates a new Counter func NewCounter(counter tally.Counter) *Counter { return &Counter{counter: counter} } // Inc adds the given value to the counter. func (c *Counter) Inc(delta int64) { c.counter.Inc(delta) } // Gauge is an adapter from go-tally Gauge to jaeger-lib Gauge type Gauge struct { gauge tally.Gauge } // NewGauge creates a new Gauge func NewGauge(gauge tally.Gauge) *Gauge { return &Gauge{gauge: gauge} } // Update the gauge to the value passed in. func (g *Gauge) Update(value int64) { g.gauge.Update(float64(value)) } // Timer is an adapter from go-tally Histogram to jaeger-lib Timer type Timer struct { timer tally.Timer } // NewTimer creates a new Timer func NewTimer(timer tally.Timer) *Timer { return &Timer{timer: timer} } // Record saves the time passed in. func (t *Timer) Record(delta time.Duration) { t.timer.Record(delta) } // Histogram is an adapter from go-tally Histogram to jaeger-lib Histogram type Histogram struct { histogram tally.Histogram } // NewHistogram creates a new Histogram func NewHistogram(histogram tally.Histogram) *Histogram { return &Histogram{histogram: histogram} } // Record saves the value passed in. func (h *Histogram) Record(value float64) { h.histogram.RecordValue(value) } golang-github-uber-jaeger-lib-2.4.1/metrics/timer.go000066400000000000000000000017221444170434100223200ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package metrics import ( "time" ) // Timer accumulates observations about how long some operation took, // and also maintains a historgam of percentiles. type Timer interface { // Records the time passed in. Record(time.Duration) } // NullTimer timer that does nothing var NullTimer Timer = nullTimer{} type nullTimer struct{} func (nullTimer) Record(time.Duration) {} golang-github-uber-jaeger-lib-2.4.1/sample/000077500000000000000000000000001444170434100204625ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/sample/sample.go000066400000000000000000000013231444170434100222710ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package sample import ( "fmt" ) // SayHello is a sample function func SayHello() { fmt.Println("Hello, playground") } golang-github-uber-jaeger-lib-2.4.1/sample/sample_test.go000066400000000000000000000002301444170434100233240ustar00rootroot00000000000000package sample import "testing" func ExampleSayHello() { SayHello() // Output: Hello, playground } func TestSayHello(t *testing.T) { SayHello() } golang-github-uber-jaeger-lib-2.4.1/scripts/000077500000000000000000000000001444170434100206705ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/scripts/updateLicense.py000066400000000000000000000050071444170434100240310ustar00rootroot00000000000000from __future__ import ( absolute_import, print_function, division, unicode_literals ) import logging import re import sys from datetime import datetime logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) CURRENT_YEAR = datetime.today().year LICENSE_BLOB = """Copyright (c) %d The Jaeger Authors. 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.""" % CURRENT_YEAR LICENSE_BLOB_LINES_GO = [ ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n') ] COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I) def update_go_license(name, force=False): with open(name) as f: orig_lines = list(f) lines = list(orig_lines) found = False changed = False for i, line in enumerate(lines[:5]): m = COPYRIGHT_RE.search(line) if not m: continue found = True year = int(m.group(1)) if year == CURRENT_YEAR: break # Avoid updating the copyright year. # # new_line = COPYRIGHT_RE.sub('Copyright (c) %d' % CURRENT_YEAR, line) # assert line != new_line, ('Could not change year in: %s' % line) # lines[i] = new_line # changed = True break if not found: if 'Code generated by' in lines[0]: lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO else: lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n'] changed = True if changed: with open(name, 'w') as f: for line in lines: f.write(line) print(name) def main(): if len(sys.argv) == 1: print('USAGE: %s FILE ...' % sys.argv[0]) sys.exit(1) for name in sys.argv[1:]: if name.endswith('.go'): try: update_go_license(name) except Exception as error: logger.error('Failed to process file %s', name) logger.exception(error) raise error else: raise NotImplementedError('Unsupported file type: %s' % name) if __name__ == "__main__": main() golang-github-uber-jaeger-lib-2.4.1/scripts/updateLicenses.sh000077500000000000000000000001641444170434100242000ustar00rootroot00000000000000#!/bin/bash set -e python scripts/updateLicense.py $(go list -json ./... | jq -r '.Dir + "/" + (.GoFiles | .[])') golang-github-uber-jaeger-lib-2.4.1/utils/000077500000000000000000000000001444170434100203415ustar00rootroot00000000000000golang-github-uber-jaeger-lib-2.4.1/utils/rate_limiter.go000066400000000000000000000055511444170434100233560ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package utils import ( "sync" "time" ) // RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. type RateLimiter interface { CheckCredit(itemCost float64) bool } type rateLimiter struct { sync.Mutex creditsPerSecond float64 balance float64 maxBalance float64 lastTick time.Time timeNow func() time.Time } // NewRateLimiter creates a new rate limiter based on leaky bucket algorithm, formulated in terms of a // credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional // to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost // of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" // and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. // // This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the // max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message // to determine if the message is within the rate limit. // // It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput // as bytes/second, and calling CheckCredit() with the actual message size. func NewRateLimiter(creditsPerSecond, maxBalance float64) RateLimiter { return &rateLimiter{ creditsPerSecond: creditsPerSecond, balance: maxBalance, maxBalance: maxBalance, lastTick: time.Now(), timeNow: time.Now} } func (b *rateLimiter) CheckCredit(itemCost float64) bool { b.Lock() defer b.Unlock() // calculate how much time passed since the last tick, and update current tick currentTime := b.timeNow() elapsedTime := currentTime.Sub(b.lastTick) b.lastTick = currentTime // calculate how much credit have we accumulated since the last tick b.balance += elapsedTime.Seconds() * b.creditsPerSecond if b.balance > b.maxBalance { b.balance = b.maxBalance } // if we have enough credits to pay for current item, then reduce balance and allow if b.balance >= itemCost { b.balance -= itemCost return true } return false } golang-github-uber-jaeger-lib-2.4.1/utils/rate_limiter_test.go000066400000000000000000000047451444170434100244210ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, 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. package utils import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestRateLimiter(t *testing.T) { limiter := NewRateLimiter(2.0, 2.0) // stop time ts := time.Now() limiter.(*rateLimiter).lastTick = ts limiter.(*rateLimiter).timeNow = func() time.Time { return ts } assert.True(t, limiter.CheckCredit(1.0)) assert.True(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) // move time 250ms forward, not enough credits to pay for 1.0 item limiter.(*rateLimiter).timeNow = func() time.Time { return ts.Add(time.Second / 4) } assert.False(t, limiter.CheckCredit(1.0)) // move time 500ms forward, now enough credits to pay for 1.0 item limiter.(*rateLimiter).timeNow = func() time.Time { return ts.Add(time.Second/4 + time.Second/2) } assert.True(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 limiter.(*rateLimiter).lastTick = ts limiter.(*rateLimiter).timeNow = func() time.Time { return ts.Add(5 * time.Second) } assert.True(t, limiter.CheckCredit(1.0)) assert.True(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) } func TestMaxBalance(t *testing.T) { limiter := NewRateLimiter(0.1, 1.0) // stop time ts := time.Now() limiter.(*rateLimiter).lastTick = ts limiter.(*rateLimiter).timeNow = func() time.Time { return ts } // on initialization, should have enough credits for 1 message assert.True(t, limiter.CheckCredit(1.0)) // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 limiter.(*rateLimiter).timeNow = func() time.Time { return ts.Add(time.Second * 20) } assert.True(t, limiter.CheckCredit(1.0)) assert.False(t, limiter.CheckCredit(1.0)) }