replace dep with go modules (#16017)

- guide shamelessly stolen from prometheus/prometheus
- updates local interface of oauth exchange
- updates local impl of hclogger
- bump jaeger client version

closes #16088
This commit is contained in:
Carl Bergquist
2019-04-16 12:00:55 +02:00
committed by GitHub
parent d6b48ee099
commit 68f5ddf18c
944 changed files with 181567 additions and 137219 deletions
+15
View File
@@ -0,0 +1,15 @@
*.out
*.test
*.xml
*.swp
.idea/
.tmp/
*.iml
*.cov
*.html
*.log
gen/thrift/js
gen/thrift/py
vendor/
crossdock-main
crossdock/jaeger-docker-compose.yml
+3
View File
@@ -0,0 +1,3 @@
[submodule "idl"]
path = idl
url = https://github.com/uber/jaeger-idl.git
+210
View File
@@ -0,0 +1,210 @@
Changes by Version
==================
2.16.0 (2019-03-24)
-------------------
- Add baggage to B3 codec (#319) <Pavol Loffay>
- Add support for 128bit trace ids to zipkin thrift spans. (#378) <Douglas Reid>
- Update zipkin propagation logic to support 128bit traceIDs (#373) <Douglas Reid>
- Accept "true" for the x-b3-sampled header (#356) <Adrian Bogatu>
- Allow setting of PoolSpans from Config object (#322) <Matthew Pound>
- Make propagators public to allow wrapping (#379) <Ivan Babrou>
- Change default metric namespace to use relevant separator for the metric backend (#364) <Gary Brown>
- Change metrics prefix to jaeger_tracer and add descriptions (#346) <Gary Brown>
- Bump OpenTracing to ^1.1.x (#383) <Yuri Shkuro>
- Upgrade jaeger-lib to v2.0.0 (#359) <Gary Brown>
- Avoid defer when generating random number (#358) <Gary Brown>
- Use a pool of rand.Source to reduce lock contention when creating span ids (#357) <Gary Brown>
- Make JAEGER_ENDPOINT take priority over JAEGER_AGENT_XXX (#342) <Eundoo Song>
2.15.0 (2018-10-10)
-------------------
- Fix FollowsFrom spans ignoring baggage/debug header from dummy parent context (#313) <Zvi Cahana>
- Make maximum annotation length configurable in tracer options (#318) <Eric Chang>
- Support more environment variables in configuration (#323) <Daneyon Hansen>
- Print error on Sampler Query failure (#328) <Goutham Veeramachaneni>
- Add an HTTPOption to support custom http.RoundTripper (#333) <Michael Puncel>
- Return an error when an HTTP error code is seen in zipkin HTTP transport (#331) <Michael Puncel>
2.14.0 (2018-04-30)
-------------------
- Support throttling for debug traces (#274) <Isaac Hier>
- Remove dependency on Apache Thrift (#303) <Yuri Shkuro>
- Remove dependency on tchannel (#295) (#294) <Yuri Shkuro>
- Test with Go 1.9 (#298) <Yuri Shkuro>
2.13.0 (2018-04-15)
-------------------
- Use value receiver for config.NewTracer() (#283) <Yuri Shkuro>
- Lock span during jaeger thrift conversion (#273) <Won Jun Jang>
- Fix the RemotelyControlledSampler so that it terminates go-routine on Close() (#260) <Scott Kidder> <Yuri Shkuro>
- Added support for client configuration via env vars (#275) <Juraci Paixão Kröhling>
- Allow overriding sampler in the Config (#270) <Mike Kabischev>
2.12.0 (2018-03-14)
-------------------
- Use lock when retrieving span.Context() (#268)
- Add Configuration support for custom Injector and Extractor (#263) <Martin Liu>
2.11.2 (2018-01-12)
-------------------
- Add Gopkg.toml to allow using the lib with `dep`
2.11.1 (2018-01-03)
-------------------
- Do not enqueue spans after Reporter is closed (#235, #245)
- Change default flush interval to 1sec (#243)
2.11.0 (2017-11-27)
-------------------
- Normalize metric names and tags to be compatible with Prometheus (#222)
2.10.0 (2017-11-14)
-------------------
- Support custom tracing headers (#176)
- Add BaggageRestrictionManager (#178) and RemoteBaggageRestrictionManager (#182)
- Do not coerce baggage keys to lower case (#196)
- Log span name when span cannot be reported (#198)
- Add option to enable gen128Bit for tracer (#193) and allow custom generator for high bits of trace ID (#219)
2.9.0 (2017-07-29)
------------------
- Pin thrift <= 0.10 (#179)
- Introduce a parallel interface ContribObserver (#159)
2.8.0 (2017-07-05)
------------------
- Drop `jaeger.` prefix from `jaeger.hostname` process-level tag
- Add options to set tracer tags
2.7.0 (2017-06-21)
------------------
- Fix rate limiter balance [#135](https://github.com/uber/jaeger-client-go/pull/135) [#140](https://github.com/uber/jaeger-client-go/pull/140)
- Default client to send Jaeger.thrift [#147](https://github.com/uber/jaeger-client-go/pull/147)
- Save baggage in span [#153](https://github.com/uber/jaeger-client-go/pull/153)
- Move reporter.queueLength to the top of the struct to guarantee 64bit alignment [#158](https://github.com/uber/jaeger-client-go/pull/158)
- Support HTTP transport with jaeger.thrift [#161](https://github.com/uber/jaeger-client-go/pull/161)
2.6.0 (2017-03-28)
------------------
- Add config option to initialize RPC Metrics feature
2.5.0 (2017-03-23)
------------------
- Split request latency metric by success/failure [#123](https://github.com/uber/jaeger-client-go/pull/123)
- Add mutex to adaptive sampler and fix race condition [#124](https://github.com/uber/jaeger-client-go/pull/124)
- Fix rate limiter panic [#125](https://github.com/uber/jaeger-client-go/pull/125)
2.4.0 (2017-03-21)
------------------
- Remove `_ms` suffix from request latency metric name [#121](https://github.com/uber/jaeger-client-go/pull/121)
- Rename all metrics to "request" and "http_request" and use tags for other dimensions [#121](https://github.com/uber/jaeger-client-go/pull/121)
2.3.0 (2017-03-20)
------------------
- Make Span type public to allow access to non-std methods for testing [#117](https://github.com/uber/jaeger-client-go/pull/117)
- Add a structured way to extract traces for logging with zap [#118](https://github.com/uber/jaeger-client-go/pull/118)
2.2.1 (2017-03-14)
------------------
- Fix panic caused by updating the remote sampler from adaptive sampler to any other sampler type (https://github.com/uber/jaeger-client-go/pull/111)
2.2.0 (2017-03-10)
------------------
- Introduce Observer and SpanObserver (https://github.com/uber/jaeger-client-go/pull/94)
- Add RPC metrics emitter as Observer/SpanObserver (https://github.com/uber/jaeger-client-go/pull/103)
2.1.2 (2017-02-27)
-------------------
- Fix leaky bucket bug (https://github.com/uber/jaeger-client-go/pull/99)
- Fix zap logger Infof (https://github.com/uber/jaeger-client-go/pull/100)
- Add tracer initialization godoc examples
2.1.1 (2017-02-21)
-------------------
- Fix inefficient usage of zap.Logger
2.1.0 (2017-02-17)
-------------------
- Add adapter for zap.Logger (https://github.com/uber-go/zap)
- Move logging API to ./log/ package
2.0.0 (2017-02-08)
-------------------
- Support Adaptive Sampling
- Support 128bit Trace IDs
- Change trace/span IDs from uint64 to strong types TraceID and SpanID
- Add Zipkin HTTP B3 Propagation format support #72
- Rip out existing metrics and use github.com/uber/jaeger-lib/metrics
- Change API for tracer, reporter, sampler initialization
1.6.0 (2016-10-14)
-------------------
- Add Zipkin HTTP transport
- Support external baggage via jaeger-baggage header
- Unpin Thrift version, keep to master
1.5.1 (2016-09-27)
-------------------
- Relax dependency on opentracing to ^1
1.5.0 (2016-09-27)
-------------------
- Upgrade to opentracing-go 1.0
- Support KV logging for Spans
1.4.0 (2016-09-14)
-------------------
- Support debug traces via HTTP header "jaeger-debug-id"
+170
View File
@@ -0,0 +1,170 @@
# How to Contribute to Jaeger
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 [glide](https://github.com/Masterminds/glide) to manage dependencies.
To get started, make sure you clone the Git repository into the correct location
`github.com/uber/jaeger-client-go` relative to `$GOPATH`:
```
mkdir -p $GOPATH/src/github.com/uber
cd $GOPATH/src/github.com/uber
git clone git@github.com:jaegertracing/jaeger-client-go.git jaeger-client-go
cd jaeger-client-go
```
Then install dependencies and run the tests:
```
git submodule update --init --recursive
glide 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 `jaeger-client-go` project
For example:
```go
import (
"fmt"
"github.com/uber/jaeger-lib/metrics"
"go.uber.org/zap"
"github.com/uber/jaeger-client-go/config"
)
```
## Making A Change
*Before making any significant changes, please [open an
issue](https://github.com/jaegertracing/jaeger-client-go/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 <joe@gmail.com>
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"
```
+37
View File
@@ -0,0 +1,37 @@
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.
+223
View File
@@ -0,0 +1,223 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "UT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
branch = "master"
digest = "1:4c4c33075b704791d6a7f09dfb55c66769e8a1dc6adf87026292d274fe8ad113"
name = "github.com/codahale/hdrhistogram"
packages = ["."]
pruneopts = "UT"
revision = "3a0bb77429bd3a61596f5e8a3172445844342120"
[[projects]]
branch = "master"
digest = "1:a382acd6150713655ded76ab5fbcbc7924a7808dab4312dda5d1f23dd8ce5277"
name = "github.com/crossdock/crossdock-go"
packages = [
".",
"assert",
"require",
]
pruneopts = "UT"
revision = "049aabb0122b03bc9bd30cab8f3f91fb60166361"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad"
name = "github.com/golang/protobuf"
packages = ["proto"]
pruneopts = "UT"
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
version = "v1.3.1"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "UT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
digest = "1:727b8f567a30d0739d6c26b9472b3422b351c93cf62095164c845a54b16fc18e"
name = "github.com/opentracing/opentracing-go"
packages = [
".",
"ext",
"harness",
"log",
]
pruneopts = "UT"
revision = "659c90643e714681897ec2521c60567dd21da733"
version = "v1.1.0"
[[projects]]
digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:b6221ec0f8903b556e127c449e7106b63e6867170c2d10a7c058623d086f2081"
name = "github.com/prometheus/client_golang"
packages = ["prometheus"]
pruneopts = "UT"
revision = "c5b7fccd204277076155f10851dad72b76a49317"
version = "v0.8.0"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "UT"
revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8"
[[projects]]
digest = "1:35cf6bdf68db765988baa9c4f10cc5d7dda1126a54bd62e252dbcd0b1fc8da90"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model",
]
pruneopts = "UT"
revision = "cfeb6f9992ffa54aaa4f2170ade4067ee478b250"
version = "v0.2.0"
[[projects]]
branch = "master"
digest = "1:c31163bd62461e0c5f7ddc7363e39ef8d9e929693e77b5c11c709b05f9cb9219"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"iostats",
"nfs",
"xfs",
]
pruneopts = "UT"
revision = "55ae3d9d557340b5bc24cd8aa5f6fa2c2ab31352"
[[projects]]
digest = "1:8ff03ccc603abb0d7cce94d34b613f5f6251a9e1931eba1a3f9888a9029b055c"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require",
"suite",
]
pruneopts = "UT"
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
[[projects]]
digest = "1:3c1a69cdae3501bf75e76d0d86dc6f2b0a7421bc205c0cb7b96b19eed464a34d"
name = "github.com/uber-go/atomic"
packages = ["."]
pruneopts = "UT"
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
version = "v1.3.2"
[[projects]]
digest = "1:f5c5ad1e08141e18aee1b9c37729d93d06805840421ccfc9d407787ffe969ce6"
name = "github.com/uber/jaeger-lib"
packages = [
"metrics",
"metrics/metricstest",
"metrics/prometheus",
]
pruneopts = "UT"
revision = "0e30338a695636fe5bcf7301e8030ce8dd2a8530"
version = "v2.0.0"
[[projects]]
digest = "1:3c1a69cdae3501bf75e76d0d86dc6f2b0a7421bc205c0cb7b96b19eed464a34d"
name = "go.uber.org/atomic"
packages = ["."]
pruneopts = "UT"
revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289"
version = "v1.3.2"
[[projects]]
digest = "1:60bf2a5e347af463c42ed31a493d817f8a72f102543060ed992754e689805d1a"
name = "go.uber.org/multierr"
packages = ["."]
pruneopts = "UT"
revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a"
version = "v1.1.0"
[[projects]]
digest = "1:c52caf7bd44f92e54627a31b85baf06a68333a196b3d8d241480a774733dcf8b"
name = "go.uber.org/zap"
packages = [
".",
"buffer",
"internal/bufferpool",
"internal/color",
"internal/exit",
"zapcore",
]
pruneopts = "UT"
revision = "ff33455a0e382e8a81d14dd7c922020b6b5e7982"
version = "v1.9.1"
[[projects]]
branch = "master"
digest = "1:f8b491a7c25030a895a0e579742d07136e6958e77ef2d46e769db8eec4e58fcd"
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp",
]
pruneopts = "UT"
revision = "addf6b3196f61cd44ce5a76657913698c73479d0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/crossdock/crossdock-go",
"github.com/opentracing/opentracing-go",
"github.com/opentracing/opentracing-go/ext",
"github.com/opentracing/opentracing-go/harness",
"github.com/opentracing/opentracing-go/log",
"github.com/pkg/errors",
"github.com/prometheus/client_golang/prometheus",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"github.com/stretchr/testify/suite",
"github.com/uber-go/atomic",
"github.com/uber/jaeger-lib/metrics",
"github.com/uber/jaeger-lib/metrics/metricstest",
"github.com/uber/jaeger-lib/metrics/prometheus",
"go.uber.org/zap",
"go.uber.org/zap/zapcore",
]
solver-name = "gps-cdcl"
solver-version = 1
+31
View File
@@ -0,0 +1,31 @@
[[constraint]]
name = "github.com/crossdock/crossdock-go"
branch = "master"
[[constraint]]
name = "github.com/opentracing/opentracing-go"
version = "^1.1"
[[constraint]]
name = "github.com/prometheus/client_golang"
version = "0.8.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "^1.1.3"
[[constraint]]
name = "github.com/uber-go/atomic"
version = "^1"
[[constraint]]
name = "github.com/uber/jaeger-lib"
version = "^2.0"
[[constraint]]
name = "go.uber.org/zap"
version = "^1"
[prune]
go-tests = true
unused-packages = true
+123
View File
@@ -0,0 +1,123 @@
PROJECT_ROOT=github.com/uber/jaeger-client-go
PACKAGES := $(shell glide novendor | grep -v -e ./thrift-gen/... -e ./thrift/...)
# all .go files that don't exist in hidden directories
ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor -e thrift-gen -e ./thrift/ \
-e ".*/\..*" \
-e ".*/_.*" \
-e ".*/mocks.*")
-include crossdock/rules.mk
export GO15VENDOREXPERIMENT=1
RACE=-race
GOTEST=go test -v $(RACE)
GOLINT=golint
GOVET=go vet
GOFMT=gofmt
FMT_LOG=fmt.log
LINT_LOG=lint.log
THRIFT_VER=0.9.3
THRIFT_IMG=thrift:$(THRIFT_VER)
THRIFT=docker run -v "${PWD}:/data" $(THRIFT_IMG) thrift
THRIFT_GO_ARGS=thrift_import="github.com/apache/thrift/lib/go/thrift"
THRIFT_GEN_DIR=thrift-gen
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: test
test:
ifeq ($(USE_DEP),true)
dep check
endif
bash -c "set -e; set -o pipefail; $(GOTEST) $(PACKAGES) | $(COLORIZE)"
.PHONY: fmt
fmt:
$(GOFMT) -e -s -l -w $(ALL_SRC)
./scripts/updateLicenses.sh
.PHONY: lint
lint:
$(GOVET) $(PACKAGES)
@cat /dev/null > $(LINT_LOG)
@$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) | grep -v crossdock/thrift >> $(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:
glide --version || go get github.com/Masterminds/glide
ifeq ($(USE_DEP),true)
dep ensure
else
glide install
endif
.PHONY: cover
cover:
./scripts/cover.sh $(shell go list $(PACKAGES))
go tool cover -html=cover.out -o cover.html
# This is not part of the regular test target because we don't want to slow it
# down.
.PHONY: test-examples
test-examples:
make -C examples
# TODO at the moment we're not generating tchan_*.go files
thrift: idl-submodule thrift-image
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/agent.thrift
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/sampling.thrift
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/jaeger.thrift
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/zipkincore.thrift
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/baggage.thrift
$(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/crossdock/thrift/ /data/idl/thrift/crossdock/tracetest.thrift
sed -i '' 's|"zipkincore"|"$(PROJECT_ROOT)/thrift-gen/zipkincore"|g' $(THRIFT_GEN_DIR)/agent/*.go
sed -i '' 's|"jaeger"|"$(PROJECT_ROOT)/thrift-gen/jaeger"|g' $(THRIFT_GEN_DIR)/agent/*.go
sed -i '' 's|"github.com/apache/thrift/lib/go/thrift"|"github.com/uber/jaeger-client-go/thrift"|g' \
$(THRIFT_GEN_DIR)/*/*.go crossdock/thrift/tracetest/*.go
rm -rf thrift-gen/*/*-remote
rm -rf crossdock/thrift/*/*-remote
rm -rf thrift-gen/jaeger/collector.go
idl-submodule:
git submodule init
git submodule update
thrift-image:
$(THRIFT) -version
.PHONY: install-dep-ci
install-dep-ci:
- curl -L -s https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 -o $$GOPATH/bin/dep
- chmod +x $$GOPATH/bin/dep
.PHONY: install-ci
install-ci: install-dep-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:
@./scripts/cover.sh $(shell go list $(PACKAGES))
ifeq ($(CI_SKIP_LINT),true)
echo 'skipping lint'
else
make lint
endif
+270
View File
@@ -0,0 +1,270 @@
[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![OpenTracing 1.0 Enabled][ot-img]][ot-url]
# Jaeger Bindings for Go OpenTracing API
Instrumentation library that implements an
[OpenTracing](http://opentracing.io) Tracer for Jaeger (https://jaegertracing.io).
**IMPORTANT**: The library's import path is based on its original location under `github.com/uber`. Do not try to import it as `github.com/jaegertracing`, it will not compile. We might revisit this in the next major release.
* :white_check_mark: `import "github.com/uber/jaeger-client-go"`
* :x: `import "github.com/jaegertracing/jaeger-client-go"`
## How to Contribute
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
## Installation
We recommended using a dependency manager like [glide](https://github.com/Masterminds/glide)
and [semantic versioning](http://semver.org/) when including this library into an application.
For example, Jaeger backend imports this library like this:
```yaml
- package: github.com/uber/jaeger-client-go
version: ^2.7.0
```
If you instead want to use the latest version in `master`, you can pull it via `go get`.
Note that during `go get` you may see build errors due to incompatible dependencies, which is why
we recommend using semantic versions for dependencies. The error may be fixed by running
`make install` (it will install `glide` if you don't have it):
```shell
go get -u github.com/uber/jaeger-client-go/
cd $GOPATH/src/github.com/uber/jaeger-client-go/
git submodule update --init --recursive
make install
```
## Initialization
See tracer initialization examples in [godoc](https://godoc.org/github.com/uber/jaeger-client-go/config#pkg-examples)
and [config/example_test.go](./config/example_test.go).
### Environment variables
The tracer can be initialized with values coming from environment variables. None of the env vars are required
and all of them can be overriden via direct setting of the property on the configuration object.
Property| Description
--- | ---
JAEGER_SERVICE_NAME | The service name
JAEGER_AGENT_HOST | The hostname for communicating with agent via UDP
JAEGER_AGENT_PORT | The port for communicating with agent via UDP
JAEGER_ENDPOINT | The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces
JAEGER_USER | Username to send as part of "Basic" authentication to the collector endpoint
JAEGER_PASSWORD | Password to send as part of "Basic" authentication to the collector endpoint
JAEGER_REPORTER_LOG_SPANS | Whether the reporter should also log the spans
JAEGER_REPORTER_MAX_QUEUE_SIZE | The reporter's maximum queue size
JAEGER_REPORTER_FLUSH_INTERVAL | The reporter's flush interval, with units, e.g. "500ms" or "2s" ([valid units][timeunits])
JAEGER_SAMPLER_TYPE | The sampler type
JAEGER_SAMPLER_PARAM | The sampler parameter (number)
JAEGER_SAMPLER_MANAGER_HOST_PORT | The HTTP endpoint when using the remote sampler, i.e. http://jaeger-agent:5778/sampling
JAEGER_SAMPLER_MAX_OPERATIONS | The maximum number of operations that the sampler will keep track of
JAEGER_SAMPLER_REFRESH_INTERVAL | How often the remotely controlled sampler will poll jaeger-agent for the appropriate sampling strategy, with units, e.g. "1m" or "30s" ([valid units][timeunits])
JAEGER_TAGS | A comma separated list of `name = value` tracer level tags, which get added to all reported spans. The value can also refer to an environment variable using the format `${envVarName:default}`, where the `:default` is optional, and identifies a value to be used if the environment variable cannot be found
JAEGER_DISABLED | Whether the tracer is disabled or not. If true, the default `opentracing.NoopTracer` is used.
JAEGER_RPC_METRICS | Whether to store RPC metrics
By default, the client sends traces via UDP to the agent at `localhost:6831`. Use `JAEGER_AGENT_HOST` and
`JAEGER_AGENT_PORT` to send UDP traces to a different `host:port`. If `JAEGER_ENDPOINT` is set, the client sends traces
to the endpoint via `HTTP`, making the `JAEGER_AGENT_HOST` and `JAEGER_AGENT_PORT` unused. If `JAEGER_ENDPOINT` is
secured, HTTP basic authentication can be performed by setting the `JAEGER_USER` and `JAEGER_PASSWORD` environment
variables.
### Closing the tracer via `io.Closer`
The constructor function for Jaeger Tracer returns the tracer itself and an `io.Closer` instance.
It is recommended to structure your `main()` so that it calls the `Close()` function on the closer
before exiting, e.g.
```go
tracer, closer, err := cfg.NewTracer(...)
defer closer.Close()
```
This is especially useful for command-line tools that enable tracing, as well as
for the long-running apps that support graceful shutdown. For example, if your deployment
system sends SIGTERM instead of killing the process and you trap that signal to do a graceful
exit, then having `defer closer.Closer()` ensures that all buffered spans are flushed.
### Metrics & Monitoring
The tracer emits a number of different metrics, defined in
[metrics.go](metrics.go). The monitoring backend is expected to support
tag-based metric names, e.g. instead of `statsd`-style string names
like `counters.my-service.jaeger.spans.started.sampled`, the metrics
are defined by a short name and a collection of key/value tags, for
example: `name:jaeger.traces, state:started, sampled:y`. See [metrics.go](./metrics.go)
file for the full list and descriptions of emitted metrics.
The monitoring backend is represented by the `metrics.Factory` interface from package
[`"github.com/uber/jaeger-lib/metrics"`](https://github.com/jaegertracing/jaeger-lib/tree/master/metrics). An implementation
of that interface can be passed as an option to either the Configuration object or the Tracer
constructor, for example:
```go
import (
"github.com/uber/jaeger-client-go/config"
"github.com/uber/jaeger-lib/metrics/prometheus"
)
metricsFactory := prometheus.New()
tracer, closer, err := config.Configuration{
ServiceName: "your-service-name",
}.NewTracer(
config.Metrics(metricsFactory),
)
```
By default, a no-op `metrics.NullFactory` is used.
### Logging
The tracer can be configured with an optional logger, which will be
used to log communication errors, or log spans if a logging reporter
option is specified in the configuration. The logging API is abstracted
by the [Logger](logger.go) interface. A logger instance implementing
this interface can be set on the `Config` object before calling the
`New` method.
Besides the [zap](https://github.com/uber-go/zap) implementation
bundled with this package there is also a [go-kit](https://github.com/go-kit/kit)
one in the [jaeger-lib](https://github.com/jaegertracing/jaeger-lib) repository.
## Instrumentation for Tracing
Since this tracer is fully compliant with OpenTracing API 1.0,
all code instrumentation should only use the API itself, as described
in the [opentracing-go](https://github.com/opentracing/opentracing-go) documentation.
## Features
### Reporters
A "reporter" is a component that receives the finished spans and reports
them to somewhere. Under normal circumstances, the Tracer
should use the default `RemoteReporter`, which sends the spans out of
process via configurable "transport". For testing purposes, one can
use an `InMemoryReporter` that accumulates spans in a buffer and
allows to retrieve them for later verification. Also available are
`NullReporter`, a no-op reporter that does nothing, a `LoggingReporter`
which logs all finished spans using their `String()` method, and a
`CompositeReporter` that can be used to combine more than one reporter
into one, e.g. to attach a logging reporter to the main remote reporter.
### Span Reporting Transports
The remote reporter uses "transports" to actually send the spans out
of process. Currently the supported transports include:
* [Jaeger Thrift](https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/agent.thrift) over UDP or HTTP,
* [Zipkin Thrift](https://github.com/jaegertracing/jaeger-idl/blob/master/thrift/zipkincore.thrift) over HTTP.
### Sampling
The tracer does not record all spans, but only those that have the
sampling bit set in the `flags`. When a new trace is started and a new
unique ID is generated, a sampling decision is made whether this trace
should be sampled. The sampling decision is propagated to all downstream
calls via the `flags` field of the trace context. The following samplers
are available:
1. `RemotelyControlledSampler` uses one of the other simpler samplers
and periodically updates it by polling an external server. This
allows dynamic control of the sampling strategies.
1. `ConstSampler` always makes the same sampling decision for all
trace IDs. it can be configured to either sample all traces, or
to sample none.
1. `ProbabilisticSampler` uses a fixed sampling rate as a probability
for a given trace to be sampled. The actual decision is made by
comparing the trace ID with a random number multiplied by the
sampling rate.
1. `RateLimitingSampler` can be used to allow only a certain fixed
number of traces to be sampled per second.
### Baggage Injection
The OpenTracing spec allows for [baggage][baggage], which are key value pairs that are added
to the span context and propagated throughout the trace. An external process can inject baggage
by setting the special HTTP Header `jaeger-baggage` on a request:
```sh
curl -H "jaeger-baggage: key1=value1, key2=value2" http://myhost.com
```
Baggage can also be programatically set inside your service:
```go
if span := opentracing.SpanFromContext(ctx); span != nil {
span.SetBaggageItem("key", "value")
}
```
Another service downstream of that can retrieve the baggage in a similar way:
```go
if span := opentracing.SpanFromContext(ctx); span != nil {
val := span.BaggageItem("key")
println(val)
}
```
### Debug Traces (Forced Sampling)
#### Programmatically
The OpenTracing API defines a `sampling.priority` standard tag that
can be used to affect the sampling of a span and its children:
```go
import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
span := opentracing.SpanFromContext(ctx)
ext.SamplingPriority.Set(span, 1)
```
#### Via HTTP Headers
Jaeger Tracer also understands a special HTTP Header `jaeger-debug-id`,
which can be set in the incoming request, e.g.
```sh
curl -H "jaeger-debug-id: some-correlation-id" http://myhost.com
```
When Jaeger sees this header in the request that otherwise has no
tracing context, it ensures that the new trace started for this
request will be sampled in the "debug" mode (meaning it should survive
all downsampling that might happen in the collection pipeline), and the
root span will have a tag as if this statement was executed:
```go
span.SetTag("jaeger-debug-id", "some-correlation-id")
```
This allows using Jaeger UI to find the trace by this tag.
### Zipkin HTTP B3 compatible header propagation
Jaeger Tracer supports Zipkin B3 Propagation HTTP headers, which are used
by a lot of Zipkin tracers. This means that you can use Jaeger in conjunction with e.g. [these OpenZipkin tracers](https://github.com/openzipkin).
However it is not the default propagation format, see [here](zipkin/README.md#NewZipkinB3HTTPHeaderPropagator) how to set it up.
## License
[Apache 2.0 License](LICENSE).
[doc-img]: https://godoc.org/github.com/uber/jaeger-client-go?status.svg
[doc]: https://godoc.org/github.com/uber/jaeger-client-go
[ci-img]: https://travis-ci.org/jaegertracing/jaeger-client-go.svg?branch=master
[ci]: https://travis-ci.org/jaegertracing/jaeger-client-go
[cov-img]: https://codecov.io/gh/jaegertracing/jaeger-client-go/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/jaegertracing/jaeger-client-go
[ot-img]: https://img.shields.io/badge/OpenTracing--1.0-enabled-blue.svg
[ot-url]: http://opentracing.io
[baggage]: https://github.com/opentracing/specification/blob/master/specification.md#set-a-baggage-item
[timeunits]: https://golang.org/pkg/time/#ParseDuration
+11
View File
@@ -0,0 +1,11 @@
# Release Process
1. Create a PR "Preparing for release X.Y.Z" against master branch
* Alter CHANGELOG.md from `<placeholder_version> (unreleased)` to `<X.Y.Z> (YYYY-MM-DD)`
* Update `JaegerClientVersion` in constants.go to `Go-X.Y.Z`
2. Create a release "Release X.Y.Z" on Github
* Create Tag `vX.Y.Z`
* Copy CHANGELOG.md into the release notes
3. Create a PR "Back to development" against master branch
* Add `<next_version> (unreleased)` to CHANGELOG.md
* Update `JaegerClientVersion` in constants.go to `Go-<next_version>dev`
+27 -3
View File
@@ -27,6 +27,8 @@ import (
"github.com/uber/jaeger-client-go/internal/baggage/remote"
throttler "github.com/uber/jaeger-client-go/internal/throttler/remote"
"github.com/uber/jaeger-client-go/rpcmetrics"
"github.com/uber/jaeger-client-go/transport"
"github.com/uber/jaeger-lib/metrics"
)
const defaultSamplingProbability = 0.001
@@ -108,6 +110,18 @@ type ReporterConfig struct {
// LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address
// Can be set by exporting an environment variable named JAEGER_AGENT_HOST / JAEGER_AGENT_PORT
LocalAgentHostPort string `yaml:"localAgentHostPort"`
// CollectorEndpoint instructs reporter to send spans to jaeger-collector at this URL
// Can be set by exporting an environment variable named JAEGER_ENDPOINT
CollectorEndpoint string `yaml:"collectorEndpoint"`
// User instructs reporter to include a user for basic http authentication when sending spans to jaeger-collector.
// Can be set by exporting an environment variable named JAEGER_USER
User string `yaml:"user"`
// Password instructs reporter to include a password for basic http authentication when sending spans to
// jaeger-collector. Can be set by exporting an environment variable named JAEGER_PASSWORD
Password string `yaml:"password"`
}
// BaggageRestrictionsConfig configures the baggage restrictions manager which can be used to whitelist
@@ -179,7 +193,7 @@ func (c Configuration) NewTracer(options ...Option) (opentracing.Tracer, io.Clos
if c.RPCMetrics {
Observer(
rpcmetrics.NewObserver(
opts.metrics.Namespace("jaeger-rpc", map[string]string{"component": "jaeger"}),
opts.metrics.Namespace(metrics.NSOptions{Name: "jaeger-rpc", Tags: map[string]string{"component": "jaeger"}}),
rpcmetrics.DefaultNameNormalizer,
),
)(&opts) // adds to c.observers
@@ -217,7 +231,9 @@ func (c Configuration) NewTracer(options ...Option) (opentracing.Tracer, io.Clos
jaeger.TracerOptions.Logger(opts.logger),
jaeger.TracerOptions.CustomHeaderKeys(c.Headers),
jaeger.TracerOptions.Gen128Bit(opts.gen128Bit),
jaeger.TracerOptions.PoolSpans(opts.poolSpans),
jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan),
jaeger.TracerOptions.MaxTagValueLength(opts.maxTagValueLength),
}
for _, tag := range opts.tags {
@@ -344,7 +360,7 @@ func (sc *SamplerConfig) NewSampler(
return nil, fmt.Errorf("Unknown sampler type %v", sc.Type)
}
// NewReporter instantiates a new reporter that submits spans to tcollector
// NewReporter instantiates a new reporter that submits spans to the collector
func (rc *ReporterConfig) NewReporter(
serviceName string,
metrics *jaeger.Metrics,
@@ -368,5 +384,13 @@ func (rc *ReporterConfig) NewReporter(
}
func (rc *ReporterConfig) newTransport() (jaeger.Transport, error) {
return jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0)
switch {
case rc.CollectorEndpoint != "" && rc.User != "" && rc.Password != "":
return transport.NewHTTPTransport(rc.CollectorEndpoint, transport.HTTPBatchSize(1),
transport.HTTPBasicAuth(rc.User, rc.Password)), nil
case rc.CollectorEndpoint != "":
return transport.NewHTTPTransport(rc.CollectorEndpoint, transport.HTTPBatchSize(1)), nil
default:
return jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0)
}
}
+32 -16
View File
@@ -16,12 +16,13 @@ package config
import (
"fmt"
"net/url"
"os"
"strconv"
"strings"
"time"
opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/uber/jaeger-client-go"
@@ -41,6 +42,9 @@ const (
envReporterMaxQueueSize = "JAEGER_REPORTER_MAX_QUEUE_SIZE"
envReporterFlushInterval = "JAEGER_REPORTER_FLUSH_INTERVAL"
envReporterLogSpans = "JAEGER_REPORTER_LOG_SPANS"
envEndpoint = "JAEGER_ENDPOINT"
envUser = "JAEGER_USER"
envPassword = "JAEGER_PASSWORD"
envAgentHost = "JAEGER_AGENT_HOST"
envAgentPort = "JAEGER_AGENT_PORT"
)
@@ -155,23 +159,35 @@ func reporterConfigFromEnv() (*ReporterConfig, error) {
}
}
host := jaeger.DefaultUDPSpanServerHost
if e := os.Getenv(envAgentHost); e != "" {
host = e
}
port := jaeger.DefaultUDPSpanServerPort
if e := os.Getenv(envAgentPort); e != "" {
if value, err := strconv.ParseInt(e, 10, 0); err == nil {
port = int(value)
} else {
return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envAgentPort, e)
if e := os.Getenv(envEndpoint); e != "" {
u, err := url.ParseRequestURI(e)
if err != nil {
return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envEndpoint, e)
}
rc.CollectorEndpoint = u.String()
user := os.Getenv(envUser)
pswd := os.Getenv(envPassword)
if user != "" && pswd == "" || user == "" && pswd != "" {
return nil, errors.Errorf("you must set %s and %s env vars together", envUser, envPassword)
}
rc.User = user
rc.Password = pswd
} else {
host := jaeger.DefaultUDPSpanServerHost
if e := os.Getenv(envAgentHost); e != "" {
host = e
}
}
// the side effect of this is that we are building the default value, even if none of the env vars
// were not explicitly passed
rc.LocalAgentHostPort = fmt.Sprintf("%s:%d", host, port)
port := jaeger.DefaultUDPSpanServerPort
if e := os.Getenv(envAgentPort); e != "" {
if value, err := strconv.ParseInt(e, 10, 0); err == nil {
port = int(value)
} else {
return nil, errors.Wrapf(err, "cannot parse env var %s=%s", envAgentPort, e)
}
}
rc.LocalAgentHostPort = fmt.Sprintf("%s:%d", host, port)
}
return rc, nil
}
+17 -1
View File
@@ -33,7 +33,9 @@ type Options struct {
contribObservers []jaeger.ContribObserver
observers []jaeger.Observer
gen128Bit bool
poolSpans bool
zipkinSharedRPCSpan bool
maxTagValueLength int
tags []opentracing.Tag
injectors map[interface{}]jaeger.Injector
extractors map[interface{}]jaeger.Extractor
@@ -77,7 +79,7 @@ func Observer(observer jaeger.Observer) Option {
}
}
// ContribObserver can be registered with the Tracer to recieve notifications
// ContribObserver can be registered with the Tracer to receive notifications
// about new spans.
func ContribObserver(observer jaeger.ContribObserver) Option {
return func(c *Options) {
@@ -92,6 +94,13 @@ func Gen128Bit(gen128Bit bool) Option {
}
}
// PoolSpans specifies whether to pool spans
func PoolSpans(poolSpans bool) Option {
return func(c *Options) {
c.poolSpans = poolSpans
}
}
// ZipkinSharedRPCSpan creates an option that enables sharing span ID between client
// and server spans a la zipkin. If false, client and server spans will be assigned
// different IDs.
@@ -101,6 +110,13 @@ func ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) Option {
}
}
// MaxTagValueLength can be provided to override the default max tag value length.
func MaxTagValueLength(maxTagValueLength int) Option {
return func(c *Options) {
c.maxTagValueLength = maxTagValueLength
}
}
// Tag creates an option that adds a tracer-level tag.
func Tag(key string, value interface{}) Option {
return func(c *Options) {
+4 -1
View File
@@ -16,7 +16,7 @@ package jaeger
const (
// JaegerClientVersion is the version of the client library reported as Span tag.
JaegerClientVersion = "Go-2.14.0"
JaegerClientVersion = "Go-2.16.0"
// JaegerClientVersionTagKey is the name of the tag used to report client version.
JaegerClientVersionTagKey = "jaeger.version"
@@ -82,4 +82,7 @@ const (
// DefaultUDPSpanServerPort is the default port to send the spans to, via UDP
DefaultUDPSpanServerPort = 6831
// DefaultMaxTagValueLength is the default max length of byte array or string allowed in the tag value.
DefaultMaxTagValueLength = 256
)
+1 -1
View File
@@ -198,7 +198,7 @@ func (c SpanContext) WithBaggageItem(key, value string) SpanContext {
// extract method, but now it returns a dummy context with only debugID filled in.
//
// See JaegerDebugHeader in constants.go
// See textMapPropagator#Extract
// See TextMapPropagator#Extract
func (c *SpanContext) isDebugIDContainerOnly() bool {
return !c.traceID.IsValid() && c.debugID != ""
}
+90
View File
@@ -0,0 +1,90 @@
hash: 92cc8f956428fc65bee07d809a752f34376aece141c934eff02aefa08d450b72
updated: 2019-03-23T18:26:09.960887-04:00
imports:
- name: github.com/beorn7/perks
version: 3a771d992973f24aa725d07868b467d1ddfceafb
subpackages:
- quantile
- name: github.com/codahale/hdrhistogram
version: 3a0bb77429bd3a61596f5e8a3172445844342120
- name: github.com/crossdock/crossdock-go
version: 049aabb0122b03bc9bd30cab8f3f91fb60166361
subpackages:
- assert
- require
- name: github.com/davecgh/go-spew
version: d8f796af33cc11cb798c1aaeb27a4ebc5099927d
subpackages:
- spew
- name: github.com/golang/protobuf
version: bbd03ef6da3a115852eaf24c8a1c46aeb39aa175
subpackages:
- proto
- name: github.com/matttproud/golang_protobuf_extensions
version: c12348ce28de40eed0136aa2b644d0ee0650e56c
subpackages:
- pbutil
- name: github.com/opentracing/opentracing-go
version: 659c90643e714681897ec2521c60567dd21da733
subpackages:
- ext
- harness
- log
- name: github.com/pkg/errors
version: ba968bfe8b2f7e042a574c888954fccecfa385b4
- name: github.com/pmezard/go-difflib
version: 792786c7400a136282c1664665ae0a8db921c6c2
subpackages:
- difflib
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
subpackages:
- prometheus
- name: github.com/prometheus/client_model
version: 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
subpackages:
- go
- name: github.com/prometheus/common
version: 38c53a9f4bfcd932d1b00bfc65e256a7fba6b37a
subpackages:
- expfmt
- internal/bitbucket.org/ww/goautoneg
- model
- name: github.com/prometheus/procfs
version: 780932d4fbbe0e69b84c34c20f5c8d0981e109ea
subpackages:
- internal/util
- nfs
- xfs
- name: github.com/stretchr/testify
version: f35b8ab0b5a2cef36673838d662e249dd9c94686
subpackages:
- assert
- require
- suite
- name: github.com/uber/jaeger-lib
version: 0e30338a695636fe5bcf7301e8030ce8dd2a8530
subpackages:
- metrics
- metrics/metricstest
- metrics/prometheus
- name: go.uber.org/atomic
version: 1ea20fb1cbb1cc08cbd0d913a96dead89aa18289
- name: go.uber.org/multierr
version: 3c4937480c32f4c13a875a1829af76c98ca3d40a
- name: go.uber.org/zap
version: ff33455a0e382e8a81d14dd7c922020b6b5e7982
subpackages:
- buffer
- internal/bufferpool
- internal/color
- internal/exit
- zapcore
- name: golang.org/x/net
version: 49bb7cea24b1df9410e1712aa6433dae904ff66a
subpackages:
- context
- context/ctxhttp
testImports:
- name: github.com/uber-go/atomic
version: 8474b86a5a6f79c443ce4b2992817ff32cf208b8
+22
View File
@@ -0,0 +1,22 @@
package: github.com/uber/jaeger-client-go
import:
- package: github.com/opentracing/opentracing-go
version: ^1.1
subpackages:
- ext
- log
- package: github.com/crossdock/crossdock-go
- package: github.com/uber/jaeger-lib
version: ^2.0.0
subpackages:
- metrics
- package: github.com/pkg/errors
version: ~0.8.0
testImport:
- package: github.com/stretchr/testify
subpackages:
- assert
- require
- suite
- package: github.com/prometheus/client_golang
version: v0.8.0
+2 -1
View File
@@ -38,7 +38,8 @@ type HeadersConfig struct {
TraceBaggageHeaderPrefix string `yaml:"traceBaggageHeaderPrefix"`
}
func (c *HeadersConfig) applyDefaults() *HeadersConfig {
// ApplyDefaults sets missing configuration keys to default values
func (c *HeadersConfig) ApplyDefaults() *HeadersConfig {
if c.JaegerBaggageHeader == "" {
c.JaegerBaggageHeader = JaegerBaggageHeader
}
+9 -9
View File
@@ -38,7 +38,7 @@ func BuildJaegerThrift(span *Span) *j.Span {
Flags: int32(span.context.flags),
StartTime: startTime,
Duration: duration,
Tags: buildTags(span.tags),
Tags: buildTags(span.tags, span.tracer.options.maxTagValueLength),
Logs: buildLogs(span.logs),
References: buildReferences(span.references),
}
@@ -55,7 +55,7 @@ func BuildJaegerProcessThrift(span *Span) *j.Process {
func buildJaegerProcessThrift(tracer *Tracer) *j.Process {
process := &j.Process{
ServiceName: tracer.serviceName,
Tags: buildTags(tracer.tags),
Tags: buildTags(tracer.tags, tracer.options.maxTagValueLength),
}
if tracer.process.UUID != "" {
process.Tags = append(process.Tags, &j.Tag{Key: TracerUUIDTagKey, VStr: &tracer.process.UUID, VType: j.TagType_STRING})
@@ -63,10 +63,10 @@ func buildJaegerProcessThrift(tracer *Tracer) *j.Process {
return process
}
func buildTags(tags []Tag) []*j.Tag {
func buildTags(tags []Tag, maxTagValueLength int) []*j.Tag {
jTags := make([]*j.Tag, 0, len(tags))
for _, tag := range tags {
jTag := buildTag(&tag)
jTag := buildTag(&tag, maxTagValueLength)
jTags = append(jTags, jTag)
}
return jTags
@@ -84,16 +84,16 @@ func buildLogs(logs []opentracing.LogRecord) []*j.Log {
return jLogs
}
func buildTag(tag *Tag) *j.Tag {
func buildTag(tag *Tag, maxTagValueLength int) *j.Tag {
jTag := &j.Tag{Key: tag.key}
switch value := tag.value.(type) {
case string:
vStr := truncateString(value)
vStr := truncateString(value, maxTagValueLength)
jTag.VStr = &vStr
jTag.VType = j.TagType_STRING
case []byte:
if len(value) > maxAnnotationLength {
value = value[:maxAnnotationLength]
if len(value) > maxTagValueLength {
value = value[:maxTagValueLength]
}
jTag.VBinary = value
jTag.VType = j.TagType_BINARY
@@ -150,7 +150,7 @@ func buildTag(tag *Tag) *j.Tag {
jTag.VBool = &vBool
jTag.VType = j.TagType_BOOL
default:
vStr := truncateString(stringify(value))
vStr := truncateString(stringify(value), maxTagValueLength)
jTag.VStr = &vStr
jTag.VType = j.TagType_STRING
}
+26 -26
View File
@@ -21,83 +21,83 @@ import (
// Metrics is a container of all stats emitted by Jaeger tracer.
type Metrics struct {
// Number of traces started by this tracer as sampled
TracesStartedSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=y"`
TracesStartedSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=y" help:"Number of traces started by this tracer as sampled"`
// Number of traces started by this tracer as not sampled
TracesStartedNotSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=n"`
TracesStartedNotSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=n" help:"Number of traces started by this tracer as not sampled"`
// Number of externally started sampled traces this tracer joined
TracesJoinedSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=y"`
TracesJoinedSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=y" help:"Number of externally started sampled traces this tracer joined"`
// Number of externally started not-sampled traces this tracer joined
TracesJoinedNotSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=n"`
TracesJoinedNotSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=n" help:"Number of externally started not-sampled traces this tracer joined"`
// Number of sampled spans started by this tracer
SpansStartedSampled metrics.Counter `metric:"started_spans" tags:"sampled=y"`
SpansStartedSampled metrics.Counter `metric:"started_spans" tags:"sampled=y" help:"Number of sampled spans started by this tracer"`
// Number of unsampled spans started by this tracer
SpansStartedNotSampled metrics.Counter `metric:"started_spans" tags:"sampled=n"`
SpansStartedNotSampled metrics.Counter `metric:"started_spans" tags:"sampled=n" help:"Number of unsampled spans started by this tracer"`
// Number of spans finished by this tracer
SpansFinished metrics.Counter `metric:"finished_spans"`
SpansFinished metrics.Counter `metric:"finished_spans" help:"Number of spans finished by this tracer"`
// Number of errors decoding tracing context
DecodingErrors metrics.Counter `metric:"span_context_decoding_errors"`
DecodingErrors metrics.Counter `metric:"span_context_decoding_errors" help:"Number of errors decoding tracing context"`
// Number of spans successfully reported
ReporterSuccess metrics.Counter `metric:"reporter_spans" tags:"result=ok"`
ReporterSuccess metrics.Counter `metric:"reporter_spans" tags:"result=ok" help:"Number of spans successfully reported"`
// Number of spans not reported due to a Sender failure
ReporterFailure metrics.Counter `metric:"reporter_spans" tags:"result=err"`
ReporterFailure metrics.Counter `metric:"reporter_spans" tags:"result=err" help:"Number of spans not reported due to a Sender failure"`
// Number of spans dropped due to internal queue overflow
ReporterDropped metrics.Counter `metric:"reporter_spans" tags:"result=dropped"`
ReporterDropped metrics.Counter `metric:"reporter_spans" tags:"result=dropped" help:"Number of spans dropped due to internal queue overflow"`
// Current number of spans in the reporter queue
ReporterQueueLength metrics.Gauge `metric:"reporter_queue_length"`
ReporterQueueLength metrics.Gauge `metric:"reporter_queue_length" help:"Current number of spans in the reporter queue"`
// Number of times the Sampler succeeded to retrieve sampling strategy
SamplerRetrieved metrics.Counter `metric:"sampler_queries" tags:"result=ok"`
SamplerRetrieved metrics.Counter `metric:"sampler_queries" tags:"result=ok" help:"Number of times the Sampler succeeded to retrieve sampling strategy"`
// Number of times the Sampler failed to retrieve sampling strategy
SamplerQueryFailure metrics.Counter `metric:"sampler_queries" tags:"result=err"`
SamplerQueryFailure metrics.Counter `metric:"sampler_queries" tags:"result=err" help:"Number of times the Sampler failed to retrieve sampling strategy"`
// Number of times the Sampler succeeded to retrieve and update sampling strategy
SamplerUpdated metrics.Counter `metric:"sampler_updates" tags:"result=ok"`
SamplerUpdated metrics.Counter `metric:"sampler_updates" tags:"result=ok" help:"Number of times the Sampler succeeded to retrieve and update sampling strategy"`
// Number of times the Sampler failed to update sampling strategy
SamplerUpdateFailure metrics.Counter `metric:"sampler_updates" tags:"result=err"`
SamplerUpdateFailure metrics.Counter `metric:"sampler_updates" tags:"result=err" help:"Number of times the Sampler failed to update sampling strategy"`
// Number of times baggage was successfully written or updated on spans.
BaggageUpdateSuccess metrics.Counter `metric:"baggage_updates" tags:"result=ok"`
BaggageUpdateSuccess metrics.Counter `metric:"baggage_updates" tags:"result=ok" help:"Number of times baggage was successfully written or updated on spans"`
// Number of times baggage failed to write or update on spans.
BaggageUpdateFailure metrics.Counter `metric:"baggage_updates" tags:"result=err"`
BaggageUpdateFailure metrics.Counter `metric:"baggage_updates" tags:"result=err" help:"Number of times baggage failed to write or update on spans"`
// Number of times baggage was truncated as per baggage restrictions.
BaggageTruncate metrics.Counter `metric:"baggage_truncations"`
BaggageTruncate metrics.Counter `metric:"baggage_truncations" help:"Number of times baggage was truncated as per baggage restrictions"`
// Number of times baggage restrictions were successfully updated.
BaggageRestrictionsUpdateSuccess metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=ok"`
BaggageRestrictionsUpdateSuccess metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=ok" help:"Number of times baggage restrictions were successfully updated"`
// Number of times baggage restrictions failed to update.
BaggageRestrictionsUpdateFailure metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=err"`
BaggageRestrictionsUpdateFailure metrics.Counter `metric:"baggage_restrictions_updates" tags:"result=err" help:"Number of times baggage restrictions failed to update"`
// Number of times debug spans were throttled.
ThrottledDebugSpans metrics.Counter `metric:"throttled_debug_spans"`
ThrottledDebugSpans metrics.Counter `metric:"throttled_debug_spans" help:"Number of times debug spans were throttled"`
// Number of times throttler successfully updated.
ThrottlerUpdateSuccess metrics.Counter `metric:"throttler_updates" tags:"result=ok"`
ThrottlerUpdateSuccess metrics.Counter `metric:"throttler_updates" tags:"result=ok" help:"Number of times throttler successfully updated"`
// Number of times throttler failed to update.
ThrottlerUpdateFailure metrics.Counter `metric:"throttler_updates" tags:"result=err"`
ThrottlerUpdateFailure metrics.Counter `metric:"throttler_updates" tags:"result=err" help:"Number of times throttler failed to update"`
}
// NewMetrics creates a new Metrics struct and initializes it.
func NewMetrics(factory metrics.Factory, globalTags map[string]string) *Metrics {
m := &Metrics{}
// TODO the namespace "jaeger" should be configurable (e.g. in all-in-one "jaeger-client" would make more sense)
metrics.Init(m, factory.Namespace("jaeger", nil), globalTags)
// TODO the namespace "jaeger" should be configurable
metrics.MustInit(m, factory.Namespace(metrics.NSOptions{Name: "jaeger"}).Namespace(metrics.NSOptions{Name: "tracer"}), globalTags)
return m
}
+24 -15
View File
@@ -51,15 +51,17 @@ type Extractor interface {
Extract(carrier interface{}) (SpanContext, error)
}
type textMapPropagator struct {
// TextMapPropagator is a combined Injector and Extractor for TextMap format
type TextMapPropagator struct {
headerKeys *HeadersConfig
metrics Metrics
encodeValue func(string) string
decodeValue func(string) string
}
func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator {
return &textMapPropagator{
// NewTextMapPropagator creates a combined Injector and Extractor for TextMap format
func NewTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *TextMapPropagator {
return &TextMapPropagator{
headerKeys: headerKeys,
metrics: metrics,
encodeValue: func(val string) string {
@@ -71,8 +73,9 @@ func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPr
}
}
func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator {
return &textMapPropagator{
// NewHTTPHeaderPropagator creates a combined Injector and Extractor for HTTPHeaders format
func NewHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *TextMapPropagator {
return &TextMapPropagator{
headerKeys: headerKeys,
metrics: metrics,
encodeValue: func(val string) string {
@@ -88,19 +91,22 @@ func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMa
}
}
type binaryPropagator struct {
// BinaryPropagator is a combined Injector and Extractor for Binary format
type BinaryPropagator struct {
tracer *Tracer
buffers sync.Pool
}
func newBinaryPropagator(tracer *Tracer) *binaryPropagator {
return &binaryPropagator{
// NewBinaryPropagator creates a combined Injector and Extractor for Binary format
func NewBinaryPropagator(tracer *Tracer) *BinaryPropagator {
return &BinaryPropagator{
tracer: tracer,
buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }},
}
}
func (p *textMapPropagator) Inject(
// Inject implements Injector of TextMapPropagator
func (p *TextMapPropagator) Inject(
sc SpanContext,
abstractCarrier interface{},
) error {
@@ -121,7 +127,8 @@ func (p *textMapPropagator) Inject(
return nil
}
func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
// Extract implements Extractor of TextMapPropagator
func (p *TextMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
textMapReader, ok := abstractCarrier.(opentracing.TextMapReader)
if !ok {
return emptyContext, opentracing.ErrInvalidCarrier
@@ -166,7 +173,8 @@ func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, e
return ctx, nil
}
func (p *binaryPropagator) Inject(
// Inject implements Injector of BinaryPropagator
func (p *BinaryPropagator) Inject(
sc SpanContext,
abstractCarrier interface{},
) error {
@@ -207,7 +215,8 @@ func (p *binaryPropagator) Inject(
return nil
}
func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
// Extract implements Extractor of BinaryPropagator
func (p *BinaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
carrier, ok := abstractCarrier.(io.Reader)
if !ok {
return emptyContext, opentracing.ErrInvalidCarrier
@@ -269,7 +278,7 @@ func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, er
// is converted to map[string]string { "key1" : "value1",
// "key2" : "value2",
// "key3" : "value3" }
func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]string {
func (p *TextMapPropagator) parseCommaSeparatedMap(value string) map[string]string {
baggage := make(map[string]string)
value, err := url.QueryUnescape(value)
if err != nil {
@@ -289,12 +298,12 @@ func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]stri
// Converts a baggage item key into an http header format,
// by prepending TraceBaggageHeaderPrefix and encoding the key string
func (p *textMapPropagator) addBaggageKeyPrefix(key string) string {
func (p *TextMapPropagator) addBaggageKeyPrefix(key string) string {
// TODO encodeBaggageKeyAsHeader add caching and escaping
return fmt.Sprintf("%v%v", p.headerKeys.TraceBaggageHeaderPrefix, key)
}
func (p *textMapPropagator) removeBaggageKeyPrefix(key string) string {
func (p *TextMapPropagator) removeBaggageKeyPrefix(key string) string {
// TODO decodeBaggageHeaderKey add caching and escaping
return key[len(p.headerKeys.TraceBaggageHeaderPrefix):]
}
+5
View File
@@ -0,0 +1,5 @@
An Observer that can be used to emit RPC metrics
================================================
It can be attached to the tracer during tracer construction.
See `ExampleObserver` function in [observer_test.go](./observer_test.go).
+1 -1
View File
@@ -34,7 +34,7 @@ type Metrics struct {
// RequestCountFailures is a counter of the number of times any failure has been observed.
RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"`
// RequestLatencySuccess is a latency histogram of succesful requests.
// RequestLatencySuccess is a latency histogram of successful requests.
RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"`
// RequestLatencyFailures is a latency histogram of failed requests.
+1
View File
@@ -511,6 +511,7 @@ func (s *RemotelyControlledSampler) updateSampler() {
res, err := s.manager.GetSamplingStrategy(s.serviceName)
if err != nil {
s.metrics.SamplerQueryFailure.Inc(1)
s.logger.Infof("Unable to query sampling strategy: %v", err)
return
}
s.Lock()
@@ -18,6 +18,8 @@ const CLIENT_SEND = "cs"
const CLIENT_RECV = "cr"
const SERVER_SEND = "ss"
const SERVER_RECV = "sr"
const MESSAGE_SEND = "ms"
const MESSAGE_RECV = "mr"
const WIRE_SEND = "ws"
const WIRE_RECV = "wr"
const CLIENT_SEND_FRAGMENT = "csf"
@@ -27,6 +29,7 @@ const SERVER_RECV_FRAGMENT = "srf"
const LOCAL_COMPONENT = "lc"
const CLIENT_ADDR = "ca"
const SERVER_ADDR = "sa"
const MESSAGE_ADDR = "ma"
func init() {
}
@@ -103,10 +103,12 @@ func (p *AnnotationType) UnmarshalText(text []byte) error {
// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web"
//
// Conventionally, when the service name isn't known, service_name = "unknown".
// - Ipv6: IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes()
type Endpoint struct {
Ipv4 int32 `thrift:"ipv4,1" json:"ipv4"`
Port int16 `thrift:"port,2" json:"port"`
ServiceName string `thrift:"service_name,3" json:"service_name"`
Ipv6 []byte `thrift:"ipv6,4" json:"ipv6,omitempty"`
}
func NewEndpoint() *Endpoint {
@@ -124,6 +126,16 @@ func (p *Endpoint) GetPort() int16 {
func (p *Endpoint) GetServiceName() string {
return p.ServiceName
}
var Endpoint_Ipv6_DEFAULT []byte
func (p *Endpoint) GetIpv6() []byte {
return p.Ipv6
}
func (p *Endpoint) IsSetIpv6() bool {
return p.Ipv6 != nil
}
func (p *Endpoint) Read(iprot thrift.TProtocol) error {
if _, err := iprot.ReadStructBegin(); err != nil {
return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
@@ -150,6 +162,10 @@ func (p *Endpoint) Read(iprot thrift.TProtocol) error {
if err := p.readField3(iprot); err != nil {
return err
}
case 4:
if err := p.readField4(iprot); err != nil {
return err
}
default:
if err := iprot.Skip(fieldTypeId); err != nil {
return err
@@ -192,6 +208,15 @@ func (p *Endpoint) readField3(iprot thrift.TProtocol) error {
return nil
}
func (p *Endpoint) readField4(iprot thrift.TProtocol) error {
if v, err := iprot.ReadBinary(); err != nil {
return thrift.PrependError("error reading field 4: ", err)
} else {
p.Ipv6 = v
}
return nil
}
func (p *Endpoint) Write(oprot thrift.TProtocol) error {
if err := oprot.WriteStructBegin("Endpoint"); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
@@ -205,6 +230,9 @@ func (p *Endpoint) Write(oprot thrift.TProtocol) error {
if err := p.writeField3(oprot); err != nil {
return err
}
if err := p.writeField4(oprot); err != nil {
return err
}
if err := oprot.WriteFieldStop(); err != nil {
return thrift.PrependError("write field stop error: ", err)
}
@@ -253,6 +281,21 @@ func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) {
return err
}
func (p *Endpoint) writeField4(oprot thrift.TProtocol) (err error) {
if p.IsSetIpv6() {
if err := oprot.WriteFieldBegin("ipv6", thrift.STRING, 4); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:ipv6: ", p), err)
}
if err := oprot.WriteBinary(p.Ipv6); err != nil {
return thrift.PrependError(fmt.Sprintf("%T.ipv6 (4) field write error: ", p), err)
}
if err := oprot.WriteFieldEnd(); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field end error 4:ipv6: ", p), err)
}
}
return err
}
func (p *Endpoint) String() string {
if p == nil {
return "<nil>"
@@ -707,6 +750,8 @@ func (p *BinaryAnnotation) String() string {
// this field non-atomically is implementation-specific.
//
// This field is i64 vs i32 to support spans longer than 35 minutes.
// - TraceIDHigh: Optional unique 8-byte additional identifier for a trace. If non zero, this
// means the trace uses 128 bit traceIds instead of 64 bit.
type Span struct {
TraceID int64 `thrift:"trace_id,1" json:"trace_id"`
// unused field # 2
@@ -719,6 +764,7 @@ type Span struct {
Debug bool `thrift:"debug,9" json:"debug,omitempty"`
Timestamp *int64 `thrift:"timestamp,10" json:"timestamp,omitempty"`
Duration *int64 `thrift:"duration,11" json:"duration,omitempty"`
TraceIDHigh *int64 `thrift:"trace_id_high,12" json:"trace_id_high,omitempty"`
}
func NewSpan() *Span {
@@ -777,6 +823,15 @@ func (p *Span) GetDuration() int64 {
}
return *p.Duration
}
var Span_TraceIDHigh_DEFAULT int64
func (p *Span) GetTraceIDHigh() int64 {
if !p.IsSetTraceIDHigh() {
return Span_TraceIDHigh_DEFAULT
}
return *p.TraceIDHigh
}
func (p *Span) IsSetParentID() bool {
return p.ParentID != nil
}
@@ -793,6 +848,10 @@ func (p *Span) IsSetDuration() bool {
return p.Duration != nil
}
func (p *Span) IsSetTraceIDHigh() bool {
return p.TraceIDHigh != nil
}
func (p *Span) Read(iprot thrift.TProtocol) error {
if _, err := iprot.ReadStructBegin(); err != nil {
return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
@@ -843,6 +902,10 @@ func (p *Span) Read(iprot thrift.TProtocol) error {
if err := p.readField11(iprot); err != nil {
return err
}
case 12:
if err := p.readField12(iprot); err != nil {
return err
}
default:
if err := iprot.Skip(fieldTypeId); err != nil {
return err
@@ -961,6 +1024,15 @@ func (p *Span) readField11(iprot thrift.TProtocol) error {
return nil
}
func (p *Span) readField12(iprot thrift.TProtocol) error {
if v, err := iprot.ReadI64(); err != nil {
return thrift.PrependError("error reading field 12: ", err)
} else {
p.TraceIDHigh = &v
}
return nil
}
func (p *Span) Write(oprot thrift.TProtocol) error {
if err := oprot.WriteStructBegin("Span"); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
@@ -992,6 +1064,9 @@ func (p *Span) Write(oprot thrift.TProtocol) error {
if err := p.writeField11(oprot); err != nil {
return err
}
if err := p.writeField12(oprot); err != nil {
return err
}
if err := oprot.WriteFieldStop(); err != nil {
return thrift.PrependError("write field stop error: ", err)
}
@@ -1142,6 +1217,21 @@ func (p *Span) writeField11(oprot thrift.TProtocol) (err error) {
return err
}
func (p *Span) writeField12(oprot thrift.TProtocol) (err error) {
if p.IsSetTraceIDHigh() {
if err := oprot.WriteFieldBegin("trace_id_high", thrift.I64, 12); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field begin error 12:trace_id_high: ", p), err)
}
if err := oprot.WriteI64(int64(*p.TraceIDHigh)); err != nil {
return thrift.PrependError(fmt.Sprintf("%T.trace_id_high (12) field write error: ", p), err)
}
if err := oprot.WriteFieldEnd(); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field end error 12:trace_id_high: ", p), err)
}
}
return err
}
func (p *Span) String() string {
if p == nil {
return "<nil>"
View File
+7
View File
@@ -0,0 +1,7 @@
# Apache Thrift
This is a partial copy of Apache Thrift v0.10 (https://github.com/apache/thrift/commit/b2a4d4ae21c789b689dd162deb819665567f481c).
It is vendored code to avoid compatibility issues introduced in Thrift v0.11.
See https://github.com/jaegertracing/jaeger-client-go/pull/303.
+32 -8
View File
@@ -17,6 +17,7 @@ package jaeger
import (
"fmt"
"io"
"math/rand"
"os"
"reflect"
"strconv"
@@ -50,6 +51,7 @@ type Tracer struct {
gen128Bit bool // whether to generate 128bit trace IDs
zipkinSharedRPCSpan bool
highTraceIDGenerator func() uint64 // custom high trace ID generator
maxTagValueLength int
// more options to come
}
// pool for Span objects
@@ -95,13 +97,13 @@ func NewTracer(
}
// register default injectors/extractors unless they are already provided via options
textPropagator := newTextMapPropagator(getDefaultHeadersConfig(), t.metrics)
textPropagator := NewTextMapPropagator(getDefaultHeadersConfig(), t.metrics)
t.addCodec(opentracing.TextMap, textPropagator, textPropagator)
httpHeaderPropagator := newHTTPHeaderPropagator(getDefaultHeadersConfig(), t.metrics)
httpHeaderPropagator := NewHTTPHeaderPropagator(getDefaultHeadersConfig(), t.metrics)
t.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator)
binaryPropagator := newBinaryPropagator(t)
binaryPropagator := NewBinaryPropagator(t)
t.addCodec(opentracing.Binary, binaryPropagator, binaryPropagator)
// TODO remove after TChannel supports OpenTracing
@@ -121,9 +123,18 @@ func NewTracer(
}
if t.randomNumber == nil {
rng := utils.NewRand(time.Now().UnixNano())
seedGenerator := utils.NewRand(time.Now().UnixNano())
pool := sync.Pool{
New: func() interface{} {
return rand.NewSource(seedGenerator.Int63())
},
}
t.randomNumber = func() uint64 {
return uint64(rng.Int63())
generator := pool.Get().(rand.Source)
number := uint64(generator.Int63())
pool.Put(generator)
return number
}
}
if t.timeNow == nil {
@@ -152,6 +163,9 @@ func NewTracer(
t.logger.Error("Overriding high trace ID generator but not generating " +
"128 bit trace IDs, consider enabling the \"Gen128Bit\" option")
}
if t.options.maxTagValueLength == 0 {
t.options.maxTagValueLength = DefaultMaxTagValueLength
}
t.process = Process{
Service: serviceName,
UUID: strconv.FormatUint(t.randomNumber(), 16),
@@ -194,6 +208,12 @@ func (t *Tracer) startSpanWithOptions(
options.StartTime = t.timeNow()
}
// Predicate whether the given span context is a valid reference
// which may be used as parent / debug ID / baggage items source
isValidReference := func(ctx SpanContext) bool {
return ctx.IsValid() || ctx.isDebugIDContainerOnly() || len(ctx.baggage) != 0
}
var references []Reference
var parent SpanContext
var hasParent bool // need this because `parent` is a value, not reference
@@ -205,7 +225,7 @@ func (t *Tracer) startSpanWithOptions(
reflect.ValueOf(ref.ReferencedContext)))
continue
}
if !(ctx.IsValid() || ctx.isDebugIDContainerOnly() || len(ctx.baggage) != 0) {
if !isValidReference(ctx) {
continue
}
references = append(references, Reference{Type: ref.Type, Context: ctx})
@@ -214,7 +234,7 @@ func (t *Tracer) startSpanWithOptions(
hasParent = ref.Type == opentracing.ChildOfRef
}
}
if !hasParent && parent.IsValid() {
if !hasParent && isValidReference(parent) {
// If ChildOfRef wasn't found but a FollowFromRef exists, use the context from
// the FollowFromRef as the parent
hasParent = true
@@ -299,7 +319,11 @@ func (t *Tracer) Extract(
carrier interface{},
) (opentracing.SpanContext, error) {
if extractor, ok := t.extractors[format]; ok {
return extractor.Extract(carrier)
spanCtx, err := extractor.Extract(carrier)
if err != nil {
return nil, err // ensure returned spanCtx is nil
}
return spanCtx, nil
}
return nil, opentracing.ErrUnsupportedFormat
}
+8 -2
View File
@@ -51,10 +51,10 @@ func (tracerOptions) CustomHeaderKeys(headerKeys *HeadersConfig) TracerOption {
if headerKeys == nil {
return
}
textPropagator := newTextMapPropagator(headerKeys.applyDefaults(), tracer.metrics)
textPropagator := NewTextMapPropagator(headerKeys.ApplyDefaults(), tracer.metrics)
tracer.addCodec(opentracing.TextMap, textPropagator, textPropagator)
httpHeaderPropagator := newHTTPHeaderPropagator(headerKeys.applyDefaults(), tracer.metrics)
httpHeaderPropagator := NewHTTPHeaderPropagator(headerKeys.ApplyDefaults(), tracer.metrics)
tracer.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator)
}
}
@@ -128,6 +128,12 @@ func (tracerOptions) HighTraceIDGenerator(highTraceIDGenerator func() uint64) Tr
}
}
func (tracerOptions) MaxTagValueLength(maxTagValueLength int) TracerOption {
return func(tracer *Tracer) {
tracer.options.maxTagValueLength = maxTagValueLength
}
}
func (tracerOptions) ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) TracerOption {
return func(tracer *Tracer) {
tracer.options.zipkinSharedRPCSpan = zipkinSharedRPCSpan
+23
View File
@@ -0,0 +1,23 @@
// 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 transport defines various transports that can be used with
// RemoteReporter to send spans out of process. Transport is responsible
// for serializing the spans into a specific format suitable for sending
// to the tracing backend. Examples may include Thrift over UDP, Thrift
// or JSON over HTTP, Thrift over Kafka, etc.
//
// Implementations are NOT required to be thread-safe; the RemoteReporter
// is expected to only call methods on the Transport from the same go-routine.
package transport
+163
View File
@@ -0,0 +1,163 @@
// 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 transport
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"github.com/uber/jaeger-client-go/thrift"
"github.com/uber/jaeger-client-go"
j "github.com/uber/jaeger-client-go/thrift-gen/jaeger"
)
// Default timeout for http request in seconds
const defaultHTTPTimeout = time.Second * 5
// HTTPTransport implements Transport by forwarding spans to a http server.
type HTTPTransport struct {
url string
client *http.Client
batchSize int
spans []*j.Span
process *j.Process
httpCredentials *HTTPBasicAuthCredentials
}
// HTTPBasicAuthCredentials stores credentials for HTTP basic auth.
type HTTPBasicAuthCredentials struct {
username string
password string
}
// HTTPOption sets a parameter for the HttpCollector
type HTTPOption func(c *HTTPTransport)
// HTTPTimeout sets maximum timeout for http request.
func HTTPTimeout(duration time.Duration) HTTPOption {
return func(c *HTTPTransport) { c.client.Timeout = duration }
}
// HTTPBatchSize sets the maximum batch size, after which a collect will be
// triggered. The default batch size is 100 spans.
func HTTPBatchSize(n int) HTTPOption {
return func(c *HTTPTransport) { c.batchSize = n }
}
// HTTPBasicAuth sets the credentials required to perform HTTP basic auth
func HTTPBasicAuth(username string, password string) HTTPOption {
return func(c *HTTPTransport) {
c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password}
}
}
// HTTPRoundTripper configures the underlying Transport on the *http.Client
// that is used
func HTTPRoundTripper(transport http.RoundTripper) HTTPOption {
return func(c *HTTPTransport) {
c.client.Transport = transport
}
}
// NewHTTPTransport returns a new HTTP-backend transport. url should be an http
// url of the collector to handle POST request, typically something like:
// http://hostname:14268/api/traces?format=jaeger.thrift
func NewHTTPTransport(url string, options ...HTTPOption) *HTTPTransport {
c := &HTTPTransport{
url: url,
client: &http.Client{Timeout: defaultHTTPTimeout},
batchSize: 100,
spans: []*j.Span{},
}
for _, option := range options {
option(c)
}
return c
}
// Append implements Transport.
func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) {
if c.process == nil {
c.process = jaeger.BuildJaegerProcessThrift(span)
}
jSpan := jaeger.BuildJaegerThrift(span)
c.spans = append(c.spans, jSpan)
if len(c.spans) >= c.batchSize {
return c.Flush()
}
return 0, nil
}
// Flush implements Transport.
func (c *HTTPTransport) Flush() (int, error) {
count := len(c.spans)
if count == 0 {
return 0, nil
}
err := c.send(c.spans)
c.spans = c.spans[:0]
return count, err
}
// Close implements Transport.
func (c *HTTPTransport) Close() error {
return nil
}
func (c *HTTPTransport) send(spans []*j.Span) error {
batch := &j.Batch{
Spans: spans,
Process: c.process,
}
body, err := serializeThrift(batch)
if err != nil {
return err
}
req, err := http.NewRequest("POST", c.url, body)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-thrift")
if c.httpCredentials != nil {
req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password)
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
return fmt.Errorf("error from collector: %d", resp.StatusCode)
}
return nil
}
func serializeThrift(obj thrift.TStruct) (*bytes.Buffer, error) {
t := thrift.NewTMemoryBuffer()
p := thrift.NewTBinaryProtocolTransport(t)
if err := obj.Write(p); err != nil {
return nil, err
}
return t.Buffer, nil
}
+17 -14
View File
@@ -27,9 +27,6 @@ import (
)
const (
// maxAnnotationLength is the max length of byte array or string allowed in the annotations
maxAnnotationLength = 256
// Zipkin UI does not work well with non-string tag values
allowPackedNumbers = false
)
@@ -51,13 +48,19 @@ func BuildZipkinThrift(s *Span) *z.Span {
if parentID != 0 {
ptrParentID = &parentID
}
traceIDHigh := int64(span.context.traceID.High)
var ptrTraceIDHigh *int64
if traceIDHigh != 0 {
ptrTraceIDHigh = &traceIDHigh
}
timestamp := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime)
duration := span.duration.Nanoseconds() / int64(time.Microsecond)
endpoint := &z.Endpoint{
ServiceName: span.tracer.serviceName,
Ipv4: int32(span.tracer.hostIPv4)}
thriftSpan := &z.Span{
TraceID: int64(span.context.traceID.Low), // TODO upgrade zipkin thrift and use TraceIdHigh
TraceID: int64(span.context.traceID.Low),
TraceIDHigh: ptrTraceIDHigh,
ID: int64(span.context.spanID),
ParentID: ptrParentID,
Name: span.operationName,
@@ -98,7 +101,7 @@ func buildAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.Annotation {
Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp),
Host: endpoint}
if content, err := spanlog.MaterializeWithJSON(log.Fields); err == nil {
anno.Value = truncateString(string(content))
anno.Value = truncateString(string(content), span.tracer.options.maxTagValueLength)
} else {
anno.Value = err.Error()
}
@@ -148,21 +151,21 @@ func buildBinaryAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.BinaryA
if _, ok := specialTagHandlers[tag.key]; ok {
continue
}
if anno := buildBinaryAnnotation(tag.key, tag.value, nil); anno != nil {
if anno := buildBinaryAnnotation(tag.key, tag.value, span.tracer.options.maxTagValueLength, nil); anno != nil {
annotations = append(annotations, anno)
}
}
return annotations
}
func buildBinaryAnnotation(key string, val interface{}, endpoint *z.Endpoint) *z.BinaryAnnotation {
func buildBinaryAnnotation(key string, val interface{}, maxTagValueLength int, endpoint *z.Endpoint) *z.BinaryAnnotation {
bann := &z.BinaryAnnotation{Key: key, Host: endpoint}
if value, ok := val.(string); ok {
bann.Value = []byte(truncateString(value))
bann.Value = []byte(truncateString(value, maxTagValueLength))
bann.AnnotationType = z.AnnotationType_STRING
} else if value, ok := val.([]byte); ok {
if len(value) > maxAnnotationLength {
value = value[:maxAnnotationLength]
if len(value) > maxTagValueLength {
value = value[:maxTagValueLength]
}
bann.Value = value
bann.AnnotationType = z.AnnotationType_BYTES
@@ -180,7 +183,7 @@ func buildBinaryAnnotation(key string, val interface{}, endpoint *z.Endpoint) *z
bann.AnnotationType = z.AnnotationType_BOOL
} else {
value := stringify(val)
bann.Value = []byte(truncateString(value))
bann.Value = []byte(truncateString(value, maxTagValueLength))
bann.AnnotationType = z.AnnotationType_STRING
}
return bann
@@ -193,12 +196,12 @@ func stringify(value interface{}) string {
return fmt.Sprintf("%+v", value)
}
func truncateString(value string) string {
func truncateString(value string, maxLength int) string {
// we ignore the problem of utf8 runes possibly being sliced in the middle,
// as it is rather expensive to iterate through each tag just to find rune
// boundaries.
if len(value) > maxAnnotationLength {
return value[:maxAnnotationLength]
if len(value) > maxLength {
return value[:maxLength]
}
return value
}
+51 -8
View File
@@ -14,14 +14,48 @@
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(name string, tags map[string]string) Counter
Timer(name string, tags map[string]string) Timer
Gauge(name string, tags map[string]string) Gauge
Counter(metric Options) Counter
Timer(metric TimerOptions) Timer
Gauge(metric Options) Gauge
Histogram(metric HistogramOptions) Histogram
// Namespace returns a nested metrics factory.
Namespace(name string, tags map[string]string) Factory
Namespace(scope NSOptions) Factory
}
// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge.
@@ -29,7 +63,16 @@ var NullFactory Factory = nullFactory{}
type nullFactory struct{}
func (nullFactory) Counter(name string, tags map[string]string) Counter { return NullCounter }
func (nullFactory) Timer(name string, tags map[string]string) Timer { return NullTimer }
func (nullFactory) Gauge(name string, tags map[string]string) Gauge { return NullGauge }
func (nullFactory) Namespace(name string, tags map[string]string) Factory { return NullFactory }
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 }
+28
View File
@@ -0,0 +1,28 @@
// 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) {}
+35
View File
@@ -0,0 +1,35 @@
// 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
}
-337
View File
@@ -1,337 +0,0 @@
// 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"
"sync"
"sync/atomic"
"time"
"github.com/codahale/hdrhistogram"
)
// 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 LocalBackend is a metrics provider which aggregates data in-vm, and
// allows exporting snapshots to shove the data into a remote collector
type LocalBackend struct {
cm sync.Mutex
gm sync.Mutex
tm sync.Mutex
counters map[string]*int64
gauges map[string]*int64
timers map[string]*localBackendTimer
stop chan struct{}
wg sync.WaitGroup
TagsSep string
TagKVSep string
}
// NewLocalBackend returns a new LocalBackend. The collectionInterval is the histogram
// time window for each timer.
func NewLocalBackend(collectionInterval time.Duration) *LocalBackend {
b := &LocalBackend{
counters: make(map[string]*int64),
gauges: make(map[string]*int64),
timers: make(map[string]*localBackendTimer),
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 *LocalBackend) Clear() {
b.cm.Lock()
defer b.cm.Unlock()
b.gm.Lock()
defer b.gm.Unlock()
b.tm.Lock()
defer b.tm.Unlock()
b.counters = make(map[string]*int64)
b.gauges = make(map[string]*int64)
b.timers = make(map[string]*localBackendTimer)
}
func (b *LocalBackend) 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 *LocalBackend) IncCounter(name string, tags map[string]string, delta int64) {
name = 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 *LocalBackend) UpdateGauge(name string, tags map[string]string, value int64) {
name = 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)
}
// RecordTimer records a timing duration
func (b *LocalBackend) RecordTimer(name string, tags map[string]string, d time.Duration) {
name = 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 *LocalBackend) 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 *LocalBackend) 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)
}
}
return
}
// Stop cleanly closes the background goroutine spawned by NewLocalBackend.
func (b *LocalBackend) Stop() {
close(b.stop)
b.wg.Wait()
}
// 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
}
type stats struct {
name string
tags map[string]string
localBackend *LocalBackend
}
type localTimer struct {
stats
}
func (l *localTimer) Record(d time.Duration) {
l.localBackend.RecordTimer(l.name, l.tags, d)
}
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)
}
// LocalFactory stats factory that creates metrics that are stored locally
type LocalFactory struct {
*LocalBackend
namespace string
tags map[string]string
}
// NewLocalFactory returns a new LocalMetricsFactory
func NewLocalFactory(collectionInterval time.Duration) *LocalFactory {
return &LocalFactory{
LocalBackend: NewLocalBackend(collectionInterval),
}
}
// appendTags adds the tags to the namespace tags and returns a combined map.
func (l *LocalFactory) 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 *LocalFactory) 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 *LocalFactory) Counter(name string, tags map[string]string) Counter {
return &localCounter{
stats{
name: l.newNamespace(name),
tags: l.appendTags(tags),
localBackend: l.LocalBackend,
},
}
}
// Timer returns a local stats timer.
func (l *LocalFactory) Timer(name string, tags map[string]string) Timer {
return &localTimer{
stats{
name: l.newNamespace(name),
tags: l.appendTags(tags),
localBackend: l.LocalBackend,
},
}
}
// Gauge returns a local stats gauge.
func (l *LocalFactory) Gauge(name string, tags map[string]string) Gauge {
return &localGauge{
stats{
name: l.newNamespace(name),
tags: l.appendTags(tags),
localBackend: l.LocalBackend,
},
}
}
// Namespace returns a new namespace.
func (l *LocalFactory) Namespace(name string, tags map[string]string) Factory {
return &LocalFactory{
namespace: l.newNamespace(name),
tags: l.appendTags(tags),
LocalBackend: l.LocalBackend,
}
}
+64 -12
View File
@@ -17,23 +17,29 @@ package metrics
import (
"fmt"
"reflect"
"strconv"
"strings"
)
// Init initializes the passed in metrics and initializes its fields using the passed in factory.
func Init(metrics interface{}, factory Factory, globalTags map[string]string) {
if err := initMetrics(metrics, factory, globalTags); err != nil {
panic(err.Error())
}
}
// initMetrics uses reflection to initialize a struct containing metrics fields
// 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.
func initMetrics(m interface{}, factory Factory, globalTags map[string]string) error {
//
// 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 Init, 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
@@ -42,6 +48,7 @@ func initMetrics(m interface{}, factory Factory, globalTags map[string]string) e
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()
@@ -50,6 +57,7 @@ func initMetrics(m interface{}, factory Factory, globalTags map[string]string) e
for k, v := range globalTags {
tags[k] = v
}
var buckets []float64
field := t.Field(i)
metric := field.Tag.Get("metric")
if metric == "" {
@@ -67,13 +75,57 @@ func initMetrics(m interface{}, factory Factory, globalTags map[string]string) e
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(metric, tags)
obj = factory.Counter(Options{
Name: metric,
Tags: tags,
Help: help,
})
} else if field.Type.AssignableTo(gaugePtrType) {
obj = factory.Gauge(metric, tags)
obj = factory.Gauge(Options{
Name: metric,
Tags: tags,
Help: help,
})
} else if field.Type.AssignableTo(timerPtrType) {
obj = factory.Timer(metric, tags)
// 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",