diff --git a/vendor/manifest b/vendor/manifest index 2f075ef3e..1bd43957d 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -298,12 +298,6 @@ "revision": "6a22caf2fd45d5e2119bfc3717e984f15a7eb7ee", "branch": "master" }, - { - "importpath": "github.com/tj/go-debug", - "repository": "https://github.com/tj/go-debug", - "revision": "ff4a55a20a86994118644bbddc6a216da193cc13", - "branch": "master" - }, { "importpath": "github.com/uber-go/atomic", "repository": "https://github.com/uber-go/atomic", @@ -311,14 +305,14 @@ "branch": "master" }, { - "importpath": "github.com/uber/jaeger-client-go", - "repository": "https://github.com/uber/jaeger-client-go", + "importpath": "github.com/jaegertracing/jaeger-client-go", + "repository": "https://github.com/jaegertracing/jaeger-client-go", "revision": "3ad49a1d839b517923a6fdac36d81cbf7b744f37", "branch": "master" }, { - "importpath": "github.com/uber/jaeger-lib/metrics", - "repository": "https://github.com/uber/jaeger-lib", + "importpath": "github.com/jaegertracing/jaeger-lib/metrics", + "repository": "https://github.com/jaegertracing/jaeger-lib", "revision": "21a3da6d66fe0e278072676fdc84cd4c9ccb9b67", "branch": "master", "path": "/metrics" @@ -458,7 +452,7 @@ { "importpath": "gopkg.in/h2non/bimg.v1", "repository": "https://gopkg.in/h2non/bimg.v1", - "revision": "45f8993550e71ee7b8001d40c681c6c9fa822357", + "revision": "02e621739c77c791d8c153f240b7a1f75b07816f", "branch": "master" }, { diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/CHANGELOG.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/CHANGELOG.md new file mode 100644 index 000000000..17014701f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/CHANGELOG.md @@ -0,0 +1,131 @@ +Changes by Version +================== + +2.9.1 (unreleased) +------------------ + +- nothing yet + + +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" diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/CONTRIBUTING.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/CONTRIBUTING.md new file mode 100644 index 000000000..bb46b5cb6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing to `jaeger-client-go` + +We'd love your help! If you would like to contribute code you can do so through GitHub +by forking the repository and sending a pull request into the `master` branch. + +## Getting Started + +This library uses [glide](https://github.com/Masterminds/glide) to manage dependencies. + +The library's import path is `github.com/uber/jaeger-client-go`. + +To get started: + +```bash +git submodule update --init --recursive +glide install +make test +``` + +## Making A Change + +*Before making any significant changes, please [open an +issue](https://github.com/uber/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](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Cutting a Release + +See [RELEASE.md](./RELEASE.md) + +## License + +By contributing your code, you agree to license your contribution under the terms of the [Apache 2.0 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 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. +``` + diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/LICENSE b/vendor/src/github.com/jaegertracing/jaeger-client-go/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/Makefile b/vendor/src/github.com/jaegertracing/jaeger-client-go/Makefile new file mode 100644 index 000000000..c8b0925b6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/Makefile @@ -0,0 +1,104 @@ +PROJECT_ROOT=github.com/uber/jaeger-client-go +PACKAGES := $(shell glide novendor | grep -v ./thrift-gen/...) +# all .go files that don't exist in hidden directories +ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor -e thrift-gen \ + -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: + 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) + @[ ! -s "$(FMT_LOG)" ] || (echo "Go Fmt Failures, run 'make fmt'" | cat - $(FMT_LOG) && false) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.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 + sed -i '' 's|"zipkincore"|"$(PROJECT_ROOT)/thrift-gen/zipkincore"|g' $(THRIFT_GEN_DIR)/agent/*.go + $(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 + rm -rf thrift-gen/*/*-remote + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/crossdock/thrift/ /data/idl/thrift/crossdock/tracetest.thrift + rm -rf crossdock/thrift/*/*-remote + +idl-submodule: + git submodule init + git submodule update + +thrift-image: + $(THRIFT) -version + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + go get github.com/golang/lint/golint + + +.PHONY: test_ci +test_ci: + @./scripts/cover.sh $(shell go list $(PACKAGES)) + make lint + diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/README.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/README.md new file mode 100644 index 000000000..3b8abb2d7 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/README.md @@ -0,0 +1,226 @@ +[![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 + +This is a client side library that implements an +[OpenTracing](http://opentracing.io) Tracer, +with Zipkin-compatible data model. + +## 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 dependencioes. 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). + +### Closing the tracer via `io.Closer` + +The constructor functions for Jaeger Tracer return 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.New(...) +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:traces, state:started, sampled:true`. + +The monitoring backend is represented by the +[StatsReporter](stats_reporter.go) interface. An implementation +of that interface should be passed to the `New` method during +tracer initialization: + +```go + stats := // create StatsReporter implementation + tracer := config.Tracing.New("your-service-name", stats) +``` + +By default, a no-op `NullStatsReporter` 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/uber/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 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 two supported transports are Thrift over UDP +and Thrift over TChannel. More transports will be added in the future. + +The only data format currently used is Zipkin Thrift 1.x span format, +which allows easy integration of the tracer with Zipkin backend. + +### 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](https://github.com/opentracing/specification/blob/master/specification.md#set-a-baggage-item), +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 by doing +the following + +```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/uber/jaeger-client-go.svg?branch=master +[ci]: https://travis-ci.org/uber/jaeger-client-go +[cov-img]: https://coveralls.io/repos/uber/jaeger-client-go/badge.svg?branch=master&service=github +[cov]: https://coveralls.io/github/uber/jaeger-client-go?branch=master +[ot-img]: https://img.shields.io/badge/OpenTracing--1.0-enabled-blue.svg +[ot-url]: http://opentracing.io diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/RELEASE.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/RELEASE.md new file mode 100644 index 000000000..115e49ab8 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/RELEASE.md @@ -0,0 +1,11 @@ +# Release Process + +1. Create a PR "Preparing for release X.Y.Z" against master branch + * Alter CHANGELOG.md from ` (unreleased)` to ` (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 ` (unreleased)` to CHANGELOG.md + * Update `JaegerClientVersion` in constants.go to `Go-dev` diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter.go new file mode 100644 index 000000000..1037ca0e8 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter.go @@ -0,0 +1,77 @@ +// 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 jaeger + +import ( + "github.com/opentracing/opentracing-go/log" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +// baggageSetter is an actor that can set a baggage value on a Span given certain +// restrictions (eg. maxValueLength). +type baggageSetter struct { + restrictionManager baggage.RestrictionManager + metrics *Metrics +} + +func newBaggageSetter(restrictionManager baggage.RestrictionManager, metrics *Metrics) *baggageSetter { + return &baggageSetter{ + restrictionManager: restrictionManager, + metrics: metrics, + } +} + +// (NB) span should hold the lock before making this call +func (s *baggageSetter) setBaggage(span *Span, key, value string) { + var truncated bool + var prevItem string + restriction := s.restrictionManager.GetRestriction(span.serviceName(), key) + if !restriction.KeyAllowed() { + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + s.metrics.BaggageUpdateFailure.Inc(1) + return + } + if len(value) > restriction.MaxValueLength() { + truncated = true + value = value[:restriction.MaxValueLength()] + s.metrics.BaggageTruncate.Inc(1) + } + prevItem = span.context.baggage[key] + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + span.context = span.context.WithBaggageItem(key, value) + s.metrics.BaggageUpdateSuccess.Inc(1) +} + +func (s *baggageSetter) logFields(span *Span, key, value, prevItem string, truncated, valid bool) { + if !span.context.IsSampled() { + return + } + fields := []log.Field{ + log.String("event", "baggage"), + log.String("key", key), + log.String("value", value), + } + if prevItem != "" { + fields = append(fields, log.String("override", "true")) + } + if truncated { + fields = append(fields, log.String("truncated", "true")) + } + if !valid { + fields = append(fields, log.String("invalid", "true")) + } + span.logFieldsNoLocking(fields...) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter_test.go new file mode 100644 index 000000000..0e48b6d9a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter_test.go @@ -0,0 +1,126 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +func withTracerAndMetrics(f func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory)) { + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, nil) + + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + f(tracer.(*Tracer), m, factory) +} + +func TestTruncateBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(5), metrics) + key := "key" + value := "01234567890" + expected := "01234" + + parent := tracer.StartSpan("parent").(*Span) + parent.context = parent.context.WithBaggageItem(key, value) + span := tracer.StartSpan("child", opentracing.ChildOf(parent.Context())).(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, expected, true, true, false) + assert.Equal(t, expected, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-truncate", + Value: 1, + }, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-update", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +type keyNotAllowedBaggageRestrictionManager struct{} + +func (m *keyNotAllowedBaggageRestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + return baggage.NewRestriction(false, 0) +} + +func TestInvalidBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(&keyNotAllowedBaggageRestrictionManager{}, metrics) + key := "key" + value := "value" + + span := tracer.StartSpan("span").(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, value, false, false, true) + assert.Empty(t, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-update", + Tags: map[string]string{"result": "err"}, + Value: 1, + }, + ) + }) +} + +func TestNotSampled(t *testing.T) { + withTracerAndMetrics(func(_ *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + tracer, closer := NewTracer("svc", NewConstSampler(false), NewNullReporter()) + defer closer.Close() + + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(10), metrics) + span := tracer.StartSpan("span").(*Span) + setter.setBaggage(span, "key", "value") + assert.Empty(t, span.logs, "No baggage fields should be created if span is not sampled") + }) +} + +func assertBaggageFields(t *testing.T, sp *Span, key, value string, override, truncated, invalid bool) { + require.Len(t, sp.logs, 1) + keys := map[string]struct{}{} + for _, field := range sp.logs[0].Fields { + keys[field.String()] = struct{}{} + } + assert.Contains(t, keys, "event:baggage") + assert.Contains(t, keys, "key:"+key) + assert.Contains(t, keys, "value:"+value) + if invalid { + assert.Contains(t, keys, "invalid:true") + } + if override { + assert.Contains(t, keys, "override:true") + } + if truncated { + assert.Contains(t, keys, "truncated:true") + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config.go new file mode 100644 index 000000000..10fa1c00f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config.go @@ -0,0 +1,287 @@ +// 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 config + +import ( + "errors" + "fmt" + "io" + "strings" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage/remote" + "github.com/uber/jaeger-client-go/rpcmetrics" +) + +const defaultSamplingProbability = 0.001 + +// Configuration configures and creates Jaeger Tracer +type Configuration struct { + Disabled bool `yaml:"disabled"` + Sampler *SamplerConfig `yaml:"sampler"` + Reporter *ReporterConfig `yaml:"reporter"` + Headers *jaeger.HeadersConfig `yaml:"headers"` + RPCMetrics bool `yaml:"rpc_metrics"` + BaggageRestrictions *BaggageRestrictionsConfig `yaml:"baggage_restrictions"` +} + +// SamplerConfig allows initializing a non-default sampler. All fields are optional. +type SamplerConfig struct { + // Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote + Type string `yaml:"type"` + + // Param is a value passed to the sampler. + // Valid values for Param field are: + // - for "const" sampler, 0 or 1 for always false/true respectively + // - for "probabilistic" sampler, a probability between 0 and 1 + // - for "rateLimiting" sampler, the number of spans per second + // - for "remote" sampler, param is the same as for "probabilistic" + // and indicates the initial sampling rate before the actual one + // is received from the mothership + Param float64 `yaml:"param"` + + // SamplingServerURL is the address of jaeger-agent's HTTP sampling server + SamplingServerURL string `yaml:"samplingServerURL"` + + // MaxOperations is the maximum number of operations that the sampler + // will keep track of. If an operation is not tracked, a default probabilistic + // sampler will be used rather than the per operation specific sampler. + MaxOperations int `yaml:"maxOperations"` + + // SamplingRefreshInterval controls how often the remotely controlled sampler will poll + // jaeger-agent for the appropriate sampling strategy. + SamplingRefreshInterval time.Duration `yaml:"samplingRefreshInterval"` +} + +// ReporterConfig configures the reporter. All fields are optional. +type ReporterConfig struct { + // QueueSize controls how many spans the reporter can keep in memory before it starts dropping + // new spans. The queue is continuously drained by a background go-routine, as fast as spans + // can be sent out of process. + QueueSize int `yaml:"queueSize"` + + // BufferFlushInterval controls how often the buffer is force-flushed, even if it's not full. + // It is generally not useful, as it only matters for very low traffic services. + BufferFlushInterval time.Duration + + // LogSpans, when true, enables LoggingReporter that runs in parallel with the main reporter + // and logs all submitted spans. Main Configuration.Logger must be initialized in the code + // for this option to have any effect. + LogSpans bool `yaml:"logSpans"` + + // LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address + LocalAgentHostPort string `yaml:"localAgentHostPort"` +} + +// BaggageRestrictionsConfig configures the baggage restrictions manager which can be used to whitelist +// certain baggage keys. All fields are optional. +type BaggageRestrictionsConfig struct { + // DenyBaggageOnInitializationFailure controls the startup failure mode of the baggage restriction + // manager. If true, the manager will not allow any baggage to be written until baggage restrictions have + // been retrieved from jaeger-agent. If false, the manager wil allow any baggage to be written until baggage + // restrictions have been retrieved from jaeger-agent. + DenyBaggageOnInitializationFailure bool `yaml:"denyBaggageOnInitializationFailure"` + + // HostPort is the hostPort of jaeger-agent's baggage restrictions server + HostPort string `yaml:"hostPort"` + + // RefreshInterval controls how often the baggage restriction manager will poll + // jaeger-agent for the most recent baggage restrictions. + RefreshInterval time.Duration `yaml:"refreshInterval"` +} + +type nullCloser struct{} + +func (*nullCloser) Close() error { return nil } + +// New creates a new Jaeger Tracer, and a closer func that can be used to flush buffers +// before shutdown. +func (c Configuration) New( + serviceName string, + options ...Option, +) (opentracing.Tracer, io.Closer, error) { + if serviceName == "" { + return nil, nil, errors.New("no service name provided") + } + if c.Disabled { + return &opentracing.NoopTracer{}, &nullCloser{}, nil + } + opts := applyOptions(options...) + tracerMetrics := jaeger.NewMetrics(opts.metrics, nil) + if c.RPCMetrics { + Observer( + rpcmetrics.NewObserver( + opts.metrics.Namespace("jaeger-rpc", map[string]string{"component": "jaeger"}), + rpcmetrics.DefaultNameNormalizer, + ), + )(&opts) // adds to c.observers + } + if c.Sampler == nil { + c.Sampler = &SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: defaultSamplingProbability, + } + } + if c.Reporter == nil { + c.Reporter = &ReporterConfig{} + } + + sampler, err := c.Sampler.NewSampler(serviceName, tracerMetrics) + if err != nil { + return nil, nil, err + } + + reporter := opts.reporter + if reporter == nil { + r, err := c.Reporter.NewReporter(serviceName, tracerMetrics, opts.logger) + if err != nil { + return nil, nil, err + } + reporter = r + } + + tracerOptions := []jaeger.TracerOption{ + jaeger.TracerOptions.Metrics(tracerMetrics), + jaeger.TracerOptions.Logger(opts.logger), + jaeger.TracerOptions.CustomHeaderKeys(c.Headers), + jaeger.TracerOptions.Gen128Bit(opts.gen128Bit), + jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan), + } + + for _, tag := range opts.tags { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value)) + } + + for _, obs := range opts.observers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Observer(obs)) + } + + for _, cobs := range opts.contribObservers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.ContribObserver(cobs)) + } + + if c.BaggageRestrictions != nil { + mgr := remote.NewRestrictionManager( + serviceName, + remote.Options.Metrics(tracerMetrics), + remote.Options.Logger(opts.logger), + remote.Options.HostPort(c.BaggageRestrictions.HostPort), + remote.Options.RefreshInterval(c.BaggageRestrictions.RefreshInterval), + remote.Options.DenyBaggageOnInitializationFailure( + c.BaggageRestrictions.DenyBaggageOnInitializationFailure, + ), + ) + tracerOptions = append(tracerOptions, jaeger.TracerOptions.BaggageRestrictionManager(mgr)) + } + + tracer, closer := jaeger.NewTracer( + serviceName, + sampler, + reporter, + tracerOptions...) + + return tracer, closer, nil +} + +// InitGlobalTracer creates a new Jaeger Tracer, and sets it as global OpenTracing Tracer. +// It returns a closer func that can be used to flush buffers before shutdown. +func (c Configuration) InitGlobalTracer( + serviceName string, + options ...Option, +) (io.Closer, error) { + if c.Disabled { + return &nullCloser{}, nil + } + tracer, closer, err := c.New(serviceName, options...) + if err != nil { + return nil, err + } + opentracing.InitGlobalTracer(tracer) + return closer, nil +} + +// NewSampler creates a new sampler based on the configuration +func (sc *SamplerConfig) NewSampler( + serviceName string, + metrics *jaeger.Metrics, +) (jaeger.Sampler, error) { + samplerType := strings.ToLower(sc.Type) + if samplerType == jaeger.SamplerTypeConst { + return jaeger.NewConstSampler(sc.Param != 0), nil + } + if samplerType == jaeger.SamplerTypeProbabilistic { + if sc.Param >= 0 && sc.Param <= 1.0 { + return jaeger.NewProbabilisticSampler(sc.Param) + } + return nil, fmt.Errorf( + "Invalid Param for probabilistic sampler: %v. Expecting value between 0 and 1", + sc.Param, + ) + } + if samplerType == jaeger.SamplerTypeRateLimiting { + return jaeger.NewRateLimitingSampler(sc.Param), nil + } + if samplerType == jaeger.SamplerTypeRemote || sc.Type == "" { + sc2 := *sc + sc2.Type = jaeger.SamplerTypeProbabilistic + initSampler, err := sc2.NewSampler(serviceName, nil) + if err != nil { + return nil, err + } + options := []jaeger.SamplerOption{ + jaeger.SamplerOptions.Metrics(metrics), + jaeger.SamplerOptions.InitialSampler(initSampler), + jaeger.SamplerOptions.SamplingServerURL(sc.SamplingServerURL), + } + if sc.MaxOperations != 0 { + options = append(options, jaeger.SamplerOptions.MaxOperations(sc.MaxOperations)) + } + if sc.SamplingRefreshInterval != 0 { + options = append(options, jaeger.SamplerOptions.SamplingRefreshInterval(sc.SamplingRefreshInterval)) + } + return jaeger.NewRemotelyControlledSampler(serviceName, options...), nil + } + return nil, fmt.Errorf("Unknown sampler type %v", sc.Type) +} + +// NewReporter instantiates a new reporter that submits spans to tcollector +func (rc *ReporterConfig) NewReporter( + serviceName string, + metrics *jaeger.Metrics, + logger jaeger.Logger, +) (jaeger.Reporter, error) { + sender, err := rc.newTransport() + if err != nil { + return nil, err + } + reporter := jaeger.NewRemoteReporter( + sender, + jaeger.ReporterOptions.QueueSize(rc.QueueSize), + jaeger.ReporterOptions.BufferFlushInterval(rc.BufferFlushInterval), + jaeger.ReporterOptions.Logger(logger), + jaeger.ReporterOptions.Metrics(metrics)) + if rc.LogSpans && logger != nil { + logger.Infof("Initializing logging reporter\n") + reporter = jaeger.NewCompositeReporter(jaeger.NewLoggingReporter(logger), reporter) + } + return reporter, err +} + +func (rc *ReporterConfig) newTransport() (jaeger.Transport, error) { + return jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config_test.go new file mode 100644 index 000000000..4ad9afda3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/config_test.go @@ -0,0 +1,259 @@ +// 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 config + +import ( + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" +) + +func TestNewSamplerConst(t *testing.T) { + constTests := []struct { + param float64 + decision bool + }{{1, true}, {0, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeConst, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + require.NoError(t, err) + s1, ok := s.(*jaeger.ConstSampler) + require.True(t, ok, "converted to constSampler") + require.Equal(t, tst.decision, s1.Decision, "decision") + } +} + +func TestNewSamplerProbabilistic(t *testing.T) { + constTests := []struct { + param float64 + error bool + }{{1.5, true}, {0.5, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeProbabilistic, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + if tst.error { + require.Error(t, err) + } else { + require.NoError(t, err) + _, ok := s.(*jaeger.ProbabilisticSampler) + require.True(t, ok, "converted to ProbabilisticSampler") + } + } +} + +func TestDefaultSampler(t *testing.T) { + cfg := Configuration{ + Sampler: &SamplerConfig{Type: "InvalidType"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestInvalidSamplerType(t *testing.T) { + cfg := &SamplerConfig{MaxOperations: 10} + s, err := cfg.NewSampler("x", jaeger.NewNullMetrics()) + require.NoError(t, err) + rcs, ok := s.(*jaeger.RemotelyControlledSampler) + require.True(t, ok, "converted to RemotelyControlledSampler") + rcs.Close() +} + +func TestDefaultConfig(t *testing.T) { + cfg := Configuration{} + _, _, err := cfg.New("", Metrics(metrics.NullFactory), Logger(log.NullLogger)) + require.EqualError(t, err, "no service name provided") + + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestDisabledFlag(t *testing.T) { + cfg := Configuration{Disabled: true} + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestNewReporterError(t *testing.T) { + cfg := Configuration{ + Reporter: &ReporterConfig{LocalAgentHostPort: "bad_local_agent"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestInitGlobalTracer(t *testing.T) { + // Save the existing GlobalTracer and replace after finishing function + prevTracer := opentracing.GlobalTracer() + defer opentracing.InitGlobalTracer(prevTracer) + noopTracer := opentracing.NoopTracer{} + + tests := []struct { + cfg Configuration + shouldErr bool + tracerChanged bool + }{ + { + cfg: Configuration{Disabled: true}, + shouldErr: false, + tracerChanged: false, + }, + { + cfg: Configuration{Sampler: &SamplerConfig{Type: "InvalidType"}}, + shouldErr: true, + tracerChanged: false, + }, + { + cfg: Configuration{ + Sampler: &SamplerConfig{ + Type: "remote", + SamplingRefreshInterval: 1, + }, + }, + shouldErr: false, + tracerChanged: true, + }, + { + cfg: Configuration{}, + shouldErr: false, + tracerChanged: true, + }, + } + for _, test := range tests { + opentracing.InitGlobalTracer(noopTracer) + _, err := test.cfg.InitGlobalTracer("testService") + if test.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if test.tracerChanged { + require.NotEqual(t, noopTracer, opentracing.GlobalTracer()) + } else { + require.Equal(t, noopTracer, opentracing.GlobalTracer()) + } + } +} + +func TestConfigWithReporter(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New("test", Reporter(r)) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test").Finish() + assert.Len(t, r.GetSpans(), 1) +} + +func TestConfigWithRPCMetrics(t *testing.T) { + metrics := metrics.NewLocalFactory(0) + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New( + "test", + Reporter(r), + Metrics(metrics), + ContribObserver(fakeContribObserver{}), + ) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test", ext.SpanKindRPCServer).Finish() + + testutils.AssertCounterMetrics(t, metrics, + testutils.ExpectedMetric{ + Name: "jaeger-rpc.requests", + Tags: map[string]string{"component": "jaeger", "endpoint": "test", "error": "false"}, + Value: 1, + }, + ) +} + +func TestBaggageRestrictionsConfig(t *testing.T) { + m := metrics.NewLocalFactory(0) + c := Configuration{ + BaggageRestrictions: &BaggageRestrictionsConfig{ + HostPort: "not:1929213", + RefreshInterval: time.Minute, + }, + } + _, closer, err := c.New( + "test", + Metrics(m), + ) + require.NoError(t, err) + defer closer.Close() + + metricName := "jaeger.baggage-restrictions-update" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := m.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, m, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) +} + +func TestConfigWithGen128Bit(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + tracer, closer, err := c.New("test", Gen128Bit(true)) + require.NoError(t, err) + defer closer.Close() + + span := tracer.StartSpan("test") + defer span.Finish() + traceID := span.Context().(jaeger.SpanContext).TraceID() + require.True(t, traceID.High != 0) + require.True(t, traceID.Low != 0) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/config/example_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/example_test.go new file mode 100644 index 000000000..befaae8d5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/example_test.go @@ -0,0 +1,84 @@ +// 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 config_test + +import ( + "log" + + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" + jaegercfg "github.com/uber/jaeger-client-go/config" + jaegerlog "github.com/uber/jaeger-client-go/log" +) + +func ExampleConfiguration_InitGlobalTracer_testing() { + // Sample configuration for testing. Use constant sampling to sample every trace + // and enable LogSpan to log every span via configured Logger. + cfg := jaegercfg.Configuration{ + Sampler: &jaegercfg.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &jaegercfg.ReporterConfig{ + LogSpans: true, + }, + } + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +func ExampleConfiguration_InitGlobalTracer_production() { + // Recommended configuration for production. + cfg := jaegercfg.Configuration{} + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options.go new file mode 100644 index 000000000..76486440a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options.go @@ -0,0 +1,113 @@ +// 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 config + +import ( + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +// Option is a function that sets some option on the client. +type Option func(c *Options) + +// Options control behavior of the client. +type Options struct { + metrics metrics.Factory + logger jaeger.Logger + reporter jaeger.Reporter + contribObservers []jaeger.ContribObserver + observers []jaeger.Observer + gen128Bit bool + zipkinSharedRPCSpan bool + tags []opentracing.Tag +} + +// Metrics creates an Option that initializes Metrics in the tracer, +// which is used to emit statistics about spans. +func Metrics(factory metrics.Factory) Option { + return func(c *Options) { + c.metrics = factory + } +} + +// Logger can be provided to log Reporter errors, as well as to log spans +// if Reporter.LogSpans is set to true. +func Logger(logger jaeger.Logger) Option { + return func(c *Options) { + c.logger = logger + } +} + +// Reporter can be provided explicitly to override the configuration. +// Useful for testing, e.g. by passing InMemoryReporter. +func Reporter(reporter jaeger.Reporter) Option { + return func(c *Options) { + c.reporter = reporter + } +} + +// Observer can be registered with the Tracer to receive notifications about new Spans. +func Observer(observer jaeger.Observer) Option { + return func(c *Options) { + c.observers = append(c.observers, observer) + } +} + +// ContribObserver can be registered with the Tracer to recieve notifications +// about new spans. +func ContribObserver(observer jaeger.ContribObserver) Option { + return func(c *Options) { + c.contribObservers = append(c.contribObservers, observer) + } +} + +// Gen128Bit specifies whether to generate 128bit trace IDs. +func Gen128Bit(gen128Bit bool) Option { + return func(c *Options) { + c.gen128Bit = gen128Bit + } +} + +// 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. +func ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) Option { + return func(c *Options) { + c.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +// Tag creates an option that adds a tracer-level tag. +func Tag(key string, value interface{}) Option { + return func(c *Options) { + c.tags = append(c.tags, opentracing.Tag{Key: key, Value: value}) + } +} + +func applyOptions(options ...Option) Options { + opts := Options{} + for _, option := range options { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = metrics.NullFactory + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + return opts +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options_test.go new file mode 100644 index 000000000..01f338026 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/config/options_test.go @@ -0,0 +1,72 @@ +// 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 config + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +func TestApplyOptions(t *testing.T) { + metricsFactory := metrics.NewLocalFactory(0) + observer := fakeObserver{} + contribObserver := fakeContribObserver{} + opts := applyOptions( + Metrics(metricsFactory), + Logger(jaeger.StdLogger), + Observer(observer), + ContribObserver(contribObserver), + Gen128Bit(true), + ZipkinSharedRPCSpan(true), + ) + assert.Equal(t, jaeger.StdLogger, opts.logger) + assert.Equal(t, metricsFactory, opts.metrics) + assert.Equal(t, []jaeger.Observer{observer}, opts.observers) + assert.Equal(t, []jaeger.ContribObserver{contribObserver}, opts.contribObservers) + assert.True(t, opts.gen128Bit) + assert.True(t, opts.zipkinSharedRPCSpan) +} + +func TestTraceTagOption(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test-service", Tag("tag-key", "tag-value")) + require.NoError(t, err) + defer closer.Close() + assert.Equal(t, opentracing.Tag{Key: "tag-key", Value: "tag-value"}, tracer.(*jaeger.Tracer).Tags()[0]) +} + +func TestApplyOptionsDefaults(t *testing.T) { + opts := applyOptions() + assert.Equal(t, jaeger.NullLogger, opts.logger) + assert.Equal(t, metrics.NullFactory, opts.metrics) +} + +type fakeObserver struct{} + +func (o fakeObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) jaeger.SpanObserver { + return nil +} + +type fakeContribObserver struct{} + +func (o fakeContribObserver) OnStartSpan(span opentracing.Span, operationName string, options opentracing.StartSpanOptions) (jaeger.ContribSpanObserver, bool) { + return nil, false +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/constants.go new file mode 100644 index 000000000..fda2daa86 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/constants.go @@ -0,0 +1,76 @@ +// 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 jaeger + +const ( + // JaegerClientVersion is the version of the client library reported as Span tag. + JaegerClientVersion = "Go-2.9.1dev" + + // JaegerClientVersionTagKey is the name of the tag used to report client version. + JaegerClientVersionTagKey = "jaeger.version" + + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader = "jaeger-debug-id" + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader = "jaeger-baggage" + + // TracerHostnameTagKey used to report host name of the process. + TracerHostnameTagKey = "hostname" + + // TracerIPTagKey used to report ip of the process. + TracerIPTagKey = "ip" + + // SamplerTypeTagKey reports which sampler was used on the root span. + SamplerTypeTagKey = "sampler.type" + + // SamplerParamTagKey reports the parameter of the sampler, like sampling probability. + SamplerParamTagKey = "sampler.param" + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName = "uber-trace-id" + + // TracerStateHeaderName is deprecated. + // Deprecated: use TraceContextHeaderName + TracerStateHeaderName = TraceContextHeaderName + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix = "uberctx-" + + // SamplerTypeConst is the type of sampler that always makes the same decision. + SamplerTypeConst = "const" + + // SamplerTypeRemote is the type of sampler that polls Jaeger agent for sampling strategy. + SamplerTypeRemote = "remote" + + // SamplerTypeProbabilistic is the type of sampler that samples traces + // with a certain fixed probability. + SamplerTypeProbabilistic = "probabilistic" + + // SamplerTypeRateLimiting is the type of sampler that samples + // only up to a fixed number of traces per second. + SamplerTypeRateLimiting = "ratelimiting" + + // SamplerTypeLowerBound is the type of sampler that samples + // only up to a fixed number of traces per second. + SamplerTypeLowerBound = "lowerbound" +) diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/constants_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/constants_test.go new file mode 100644 index 000000000..0beae1912 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/constants_test.go @@ -0,0 +1,29 @@ +// 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 jaeger + +import ( + "strings" + "testing" +) + +func TestHeaderConstants(t *testing.T) { + if TraceContextHeaderName != strings.ToLower(TraceContextHeaderName) { + t.Errorf("TraceContextHeaderName is not lower-case: %+v", TraceContextHeaderName) + } + if TraceBaggageHeaderPrefix != strings.ToLower(TraceBaggageHeaderPrefix) { + t.Errorf("TraceBaggageHeaderPrefix is not lower-case: %+v", TraceBaggageHeaderPrefix) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/context.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/context.go new file mode 100644 index 000000000..8b06173d9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/context.go @@ -0,0 +1,258 @@ +// 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 jaeger + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + flagSampled = byte(1) + flagDebug = byte(2) +) + +var ( + errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state") + errMalformedTracerStateString = errors.New("String does not match tracer state format") + + emptyContext = SpanContext{} +) + +// TraceID represents unique 128bit identifier of a trace +type TraceID struct { + High, Low uint64 +} + +// SpanID represents unique 64bit identifier of a span +type SpanID uint64 + +// SpanContext represents propagated span identity and state +type SpanContext struct { + // traceID represents globally unique ID of the trace. + // Usually generated as a random number. + traceID TraceID + + // spanID represents span ID that must be unique within its trace, + // but does not have to be globally unique. + spanID SpanID + + // parentID refers to the ID of the parent span. + // Should be 0 if the current span is a root span. + parentID SpanID + + // flags is a bitmap containing such bits as 'sampled' and 'debug'. + flags byte + + // Distributed Context baggage. The is a snapshot in time. + baggage map[string]string + + // debugID can be set to some correlation ID when the context is being + // extracted from a TextMap carrier. + // + // See JaegerDebugHeader in constants.go + debugID string +} + +// ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext +func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) { + for k, v := range c.baggage { + if !handler(k, v) { + break + } + } +} + +// IsSampled returns whether this trace was chosen for permanent storage +// by the sampling mechanism of the tracer. +func (c SpanContext) IsSampled() bool { + return (c.flags & flagSampled) == flagSampled +} + +// IsDebug indicates whether sampling was explicitly requested by the service. +func (c SpanContext) IsDebug() bool { + return (c.flags & flagDebug) == flagDebug +} + +// IsValid indicates whether this context actually represents a valid trace. +func (c SpanContext) IsValid() bool { + return c.traceID.IsValid() && c.spanID != 0 +} + +func (c SpanContext) String() string { + if c.traceID.High == 0 { + return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) + } + return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) +} + +// ContextFromString reconstructs the Context encoded in a string +func ContextFromString(value string) (SpanContext, error) { + var context SpanContext + if value == "" { + return emptyContext, errEmptyTracerStateString + } + parts := strings.Split(value, ":") + if len(parts) != 4 { + return emptyContext, errMalformedTracerStateString + } + var err error + if context.traceID, err = TraceIDFromString(parts[0]); err != nil { + return emptyContext, err + } + if context.spanID, err = SpanIDFromString(parts[1]); err != nil { + return emptyContext, err + } + if context.parentID, err = SpanIDFromString(parts[2]); err != nil { + return emptyContext, err + } + flags, err := strconv.ParseUint(parts[3], 10, 8) + if err != nil { + return emptyContext, err + } + context.flags = byte(flags) + return context, nil +} + +// TraceID returns the trace ID of this span context +func (c SpanContext) TraceID() TraceID { + return c.traceID +} + +// SpanID returns the span ID of this span context +func (c SpanContext) SpanID() SpanID { + return c.spanID +} + +// ParentID returns the parent span ID of this span context +func (c SpanContext) ParentID() SpanID { + return c.parentID +} + +// NewSpanContext creates a new instance of SpanContext +func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext { + flags := byte(0) + if sampled { + flags = flagSampled + } + return SpanContext{ + traceID: traceID, + spanID: spanID, + parentID: parentID, + flags: flags, + baggage: baggage} +} + +// CopyFrom copies data from ctx into this context, including span identity and baggage. +// TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing. +func (c *SpanContext) CopyFrom(ctx *SpanContext) { + c.traceID = ctx.traceID + c.spanID = ctx.spanID + c.parentID = ctx.parentID + c.flags = ctx.flags + if l := len(ctx.baggage); l > 0 { + c.baggage = make(map[string]string, l) + for k, v := range ctx.baggage { + c.baggage[k] = v + } + } else { + c.baggage = nil + } +} + +// WithBaggageItem creates a new context with an extra baggage item. +func (c SpanContext) WithBaggageItem(key, value string) SpanContext { + var newBaggage map[string]string + if c.baggage == nil { + newBaggage = map[string]string{key: value} + } else { + newBaggage = make(map[string]string, len(c.baggage)+1) + for k, v := range c.baggage { + newBaggage[k] = v + } + newBaggage[key] = value + } + // Use positional parameters so the compiler will help catch new fields. + return SpanContext{c.traceID, c.spanID, c.parentID, c.flags, newBaggage, ""} +} + +// isDebugIDContainerOnly returns true when the instance of the context is only +// used to return the debug/correlation ID from extract() method. This happens +// in the situation when "jaeger-debug-id" header is passed in the carrier to +// the extract() method, but the request otherwise has no span context in it. +// Previously this would've returned opentracing.ErrSpanContextNotFound from the +// extract method, but now it returns a dummy context with only debugID filled in. +// +// See JaegerDebugHeader in constants.go +// See textMapPropagator#Extract +func (c *SpanContext) isDebugIDContainerOnly() bool { + return !c.traceID.IsValid() && c.debugID != "" +} + +// ------- TraceID ------- + +func (t TraceID) String() string { + if t.High == 0 { + return fmt.Sprintf("%x", t.Low) + } + return fmt.Sprintf("%x%016x", t.High, t.Low) +} + +// TraceIDFromString creates a TraceID from a hexadecimal string +func TraceIDFromString(s string) (TraceID, error) { + var hi, lo uint64 + var err error + if len(s) > 32 { + return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s) + } else if len(s) > 16 { + hiLen := len(s) - 16 + if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil { + return TraceID{}, err + } + if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil { + return TraceID{}, err + } + } else { + if lo, err = strconv.ParseUint(s, 16, 64); err != nil { + return TraceID{}, err + } + } + return TraceID{High: hi, Low: lo}, nil +} + +// IsValid checks if the trace ID is valid, i.e. not zero. +func (t TraceID) IsValid() bool { + return t.High != 0 || t.Low != 0 +} + +// ------- SpanID ------- + +func (s SpanID) String() string { + return fmt.Sprintf("%x", uint64(s)) +} + +// SpanIDFromString creates a SpanID from a hexadecimal string +func SpanIDFromString(s string) (SpanID, error) { + if len(s) > 16 { + return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s) + } + id, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return SpanID(0), err + } + return SpanID(id), nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/context_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/context_test.go new file mode 100644 index 000000000..34dfc7457 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/context_test.go @@ -0,0 +1,110 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestContextFromString(t *testing.T) { + var err error + _, err = ContextFromString("") + assert.Error(t, err) + _, err = ContextFromString("abcd") + assert.Error(t, err) + _, err = ContextFromString("x:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:x:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:x:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901234:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901:1:1:1") + assert.NoError(t, err) + _, err = ContextFromString("01234_67890123456789012345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("0123456789012345678901_345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:0123456789012345:1:1") + assert.NoError(t, err) + _, err = ContextFromString("1:01234567890123456:1:1") + assert.Error(t, err) + ctx, err := ContextFromString("10000000000000001:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{High: 1, Low: 1}, ctx.traceID) + ctx, err = ContextFromString("1:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + ctx = NewSpanContext(TraceID{Low: 1}, 1, 1, true, nil) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + assert.Equal(t, "ff", SpanID(255).String()) + assert.Equal(t, "ff", TraceID{Low: 255}.String()) + assert.Equal(t, "ff00000000000000ff", TraceID{High: 255, Low: 255}.String()) + ctx = NewSpanContext(TraceID{High: 255, Low: 255}, SpanID(1), SpanID(1), false, nil) + assert.Equal(t, "ff00000000000000ff:1:1:0", ctx.String()) +} + +func TestSpanContext_WithBaggageItem(t *testing.T) { + var ctx SpanContext + ctx = ctx.WithBaggageItem("some-KEY", "Some-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage) + ctx = ctx.WithBaggageItem("some-KEY", "Some-Other-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Other-Value"}, ctx.baggage) +} + +func TestSpanContext_SampledDebug(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:3") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.True(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:0") + require.NoError(t, err) + assert.False(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) +} + +func TestSpanContext_CopyFrom(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + ctx2 := SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + // with baggage + ctx = ctx.WithBaggageItem("x", "y") + ctx2 = SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + assert.Equal(t, "y", ctx2.baggage["x"]) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/contrib_observer.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/contrib_observer.go new file mode 100644 index 000000000..4ce1881f3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/contrib_observer.go @@ -0,0 +1,56 @@ +// 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 jaeger + +import ( + opentracing "github.com/opentracing/opentracing-go" +) + +// ContribObserver can be registered with the Tracer to receive notifications +// about new Spans. Modelled after github.com/opentracing-contrib/go-observer. +type ContribObserver interface { + // Create and return a span observer. Called when a span starts. + // If the Observer is not interested in the given span, it must return (nil, false). + // E.g : + // func StartSpan(opName string, opts ...opentracing.StartSpanOption) { + // var sp opentracing.Span + // sso := opentracing.StartSpanOptions{} + // if spanObserver, ok := Observer.OnStartSpan(span, opName, sso); ok { + // // we have a valid SpanObserver + // } + // ... + // } + OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) +} + +// ContribSpanObserver is created by the Observer and receives notifications +// about other Span events. This interface is meant to match +// github.com/opentracing-contrib/go-observer, via duck typing, without +// directly importing the go-observer package. +type ContribSpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// wrapper observer for the old observers (see observer.go) +type oldObserver struct { + obs Observer +} + +func (o *oldObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) { + spanObserver := o.obs.OnStartSpan(operationName, options) + return spanObserver, spanObserver != nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/Dockerfile b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/Dockerfile new file mode 100644 index 000000000..ae8284f13 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/Dockerfile @@ -0,0 +1,4 @@ +FROM scratch +ADD crossdock / +CMD ["/crossdock"] +EXPOSE 8080-8082 diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/README.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/README.md new file mode 100644 index 000000000..2faf91ad3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/README.md @@ -0,0 +1,78 @@ +# Crossdock-based Integration Test Suite + +This package implements integration test suite for testing +interoperability between different Jaeger client libraries. + +## Actors + +There are five actors participating in any given test case, +the crossdock driver itself, a Client, and three Servers, S1-S3. + +### Driver + +The crossdock driver reads axis and test definitions from the YAML file, +generates permutations based on values listed for each axis, and +makes an HTTP request to the Client, passing it the selected value +for each axis. + +### Client + +Client runs as part of the `jaeger-client/go` image and orchestrates +the actual test case with the servers S1-S3. The incoming request +from the driver is expected to have parameters defined in +[client/constants.go](client/constants.go), which specify + + 1. The type of test to execute (only `trace` is currently supported) + 1. Whether the trace should be sampled or not + 1. For each of the servers S1-S3: + * the name of the server (same as docker image name, same as host name) + * the transport to send request to that server (http or TChannel) + * the type of client to use (e.g. in Python, `urllib2` vs. `requests`) + +The Client translates the parameters into a "call tree" instruction set, +and calls S1, which in turn calls S2 with the sub-set of instructions, +and so on. Upon receiving the response from S1, the Client validates the +response against the conditions of the test, and returns a summary result +to the crossdock driver, indicating a success of a failure of the test. +For the `trace` test type, the success conditions are that at all levels +the observed tracing spans have the same trace ID, the same sampling flag +equal to the input `sampled` parameter, and the same value of a baggage +item. The baggage item value is randomly selected by the client at the +start of each test. + +### Servers + +Servers represent examples of business services with Jaeger tracing enabled. +Servers must be implemented for each supported language, and potentially +multiple times for a given language depending on the framework used to build +the service, such as Flask vs. Tornado in Python. Each implementation of the +server may act as any of the S1-S3 servers in the test. Each server must +implement the `TracedService` interface from +[thrift/tracetest.thrift](thrift/tracetest.thrift): + + service TracedService { + TraceResponse startTrace(1: StartTraceRequest request) + TraceResponse joinTrace(1: JoinTraceRequest request) + } + + * In `startTrace` the server is supposed to ignore any trace it may have + received via inbound request and start a brand new trace, with the + sampling flag set appropriately, using `sampling.priority` tag, + see [Go server implementation](server/trace.go) for example. + * In `joinTrace` the server is supposed to respect the trace in the + inbound request and propagate it to the outbound downstream request. + +The response from the server contains the information about the current +tracing span it has observed (or started), including trace ID, sampling +flag, and the value of a baggage item. For S1 and S2 the response also +includes the response of the downstream server. + +## Running the tests + +The intended setup is that every commit to master branch of each of the client +libraries results in a build of a new docker image (or images, e.g. in Python). +When a new pull request is tested against one of the libraries, it will build +a new image from the modified version of the library, and use the existing +images for the other languages. The `docker-compose.yaml` file refers to those +images by name. + diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client.go new file mode 100644 index 000000000..16a9148a9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client.go @@ -0,0 +1,109 @@ +// 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 client + +import ( + "fmt" + "net" + "net/http" + "sync" + + "github.com/crossdock/crossdock-go" + + "github.com/uber/jaeger-client-go/crossdock/common" +) + +// Client is a controller for the tests +type Client struct { + ClientHostPort string + ServerPortHTTP string + ServerPortTChannel string + listener net.Listener + hostMapper func(service string) string +} + +// Start begins a blocking Crossdock client +func (c *Client) Start() error { + if err := c.Listen(); err != nil { + return err + } + return c.Serve() +} + +// AsyncStart begins a Crossdock client in the background, +// but does not return until it started serving. +func (c *Client) AsyncStart() error { + if err := c.Listen(); err != nil { + return err + } + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + c.Serve() + }() + started.Wait() + return nil +} + +// Listen initializes the server +func (c *Client) Listen() error { + c.setDefaultPort(&c.ClientHostPort, ":"+common.DefaultClientPortHTTP) + c.setDefaultPort(&c.ServerPortHTTP, common.DefaultServerPortHTTP) + c.setDefaultPort(&c.ServerPortTChannel, common.DefaultServerPortTChannel) + + behaviors := crossdock.Behaviors{ + behaviorTrace: c.trace, + } + + http.Handle("/", crossdock.Handler(behaviors, true)) + + listener, err := net.Listen("tcp", c.ClientHostPort) + if err != nil { + return err + } + c.listener = listener + c.ClientHostPort = listener.Addr().String() + return nil +} + +// Serve starts service crossdock traffic. This is a blocking call. +func (c *Client) Serve() error { + return http.Serve(c.listener, nil) +} + +// Close stops the client +func (c *Client) Close() error { + return c.listener.Close() +} + +// URL returns a URL that the client can be accessed on +func (c *Client) URL() string { + return fmt.Sprintf("http://%s/", c.ClientHostPort) +} + +func (c *Client) setDefaultPort(port *string, defaultPort string) { + if *port == "" { + *port = defaultPort + } +} + +func (c *Client) mapServiceToHost(service string) string { + mapper := c.hostMapper + if mapper == nil { + return service + } + return mapper(service) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client_test.go new file mode 100644 index 000000000..dad89e2fe --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client_test.go @@ -0,0 +1,109 @@ +// 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 client + +import ( + "net/url" + "testing" + + "github.com/crossdock/crossdock-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func TestCrossdock(t *testing.T) { + log.Enabled = false // enable when debugging tests + log.Printf("Starting crossdock test") + + var reporter jaeger.Reporter + if log.Enabled { + reporter = jaeger.NewLoggingReporter(jlog.StdLogger) + } else { + reporter = jaeger.NewNullReporter() + } + + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + reporter) + defer tCloser.Close() + + s := &server.Server{ + HostPortHTTP: "127.0.0.1:0", + HostPortTChannel: "127.0.0.1:0", + Tracer: tracer, + } + err := s.Start() + require.NoError(t, err) + defer s.Close() + + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: s.GetPortHTTP(), + ServerPortTChannel: s.GetPortTChannel(), + hostMapper: func(server string) string { return "localhost" }, + } + err = c.AsyncStart() + require.NoError(t, err) + defer c.Close() + + crossdock.Wait(t, s.URL(), 10) + crossdock.Wait(t, c.URL(), 10) + + behaviors := []struct { + name string + axes map[string][]string + }{ + { + name: behaviorTrace, + axes: map[string][]string{ + server1NameParam: {common.DefaultServiceName}, + sampledParam: {"true", "false"}, + server2NameParam: {common.DefaultServiceName}, + server2TransportParam: {transportHTTP, transportTChannel, transportDummy}, + server3NameParam: {common.DefaultServiceName}, + server3TransportParam: {transportHTTP, transportTChannel}, + }, + }, + } + + for _, bb := range behaviors { + for _, entry := range crossdock.Combinations(bb.axes) { + entryArgs := url.Values{} + for k, v := range entry { + entryArgs.Set(k, v) + } + // test via real HTTP call + crossdock.Call(t, c.URL(), bb.name, entryArgs) + } + } +} + +func TestHostMapper(t *testing.T) { + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: "8080", + ServerPortTChannel: "8081", + } + assert.Equal(t, "go", c.mapServiceToHost("go")) + c.hostMapper = func(server string) string { return "localhost" } + assert.Equal(t, "localhost", c.mapServiceToHost("go")) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/constants.go new file mode 100644 index 000000000..52d73f544 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/constants.go @@ -0,0 +1,43 @@ +// 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 client + +// Different parameter keys and values used by the system +const ( + // S1 instructions + sampledParam = "sampled" + server1NameParam = "s1name" + // S1->S2 instructions + server2NameParam = "s2name" + server2TransportParam = "s2transport" + // S2->S3 instructions + server3NameParam = "s3name" + server3TransportParam = "s3transport" + + transportHTTP = "http" + transportTChannel = "tchannel" + transportDummy = "dummy" + + behaviorTrace = "trace" + + // RoleS1 is the name of the role for server S1 + RoleS1 = "S1" + + // RoleS2 is the name of the role for server S2 + RoleS2 = "S2" + + // RoleS3 is the name of the role for server S3 + RoleS3 = "S3" +) diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/trace.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/trace.go new file mode 100644 index 000000000..cfb72416d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/trace.go @@ -0,0 +1,167 @@ +// 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 client + +import ( + "fmt" + "time" + + "github.com/crossdock/crossdock-go" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" +) + +func (c *Client) trace(t crossdock.T) { + sampled := str2bool(t.Param(sampledParam)) + baggage := randomBaggage() + + level1 := tracetest.NewStartTraceRequest() + level1.ServerRole = RoleS1 + level1.Sampled = sampled + level1.Baggage = baggage + server1 := t.Param(server1NameParam) + + level2 := tracetest.NewDownstream() + level2.ServiceName = t.Param(server2NameParam) + level2.ServerRole = RoleS2 + level2.Host = c.mapServiceToHost(level2.ServiceName) + level2.Port = c.transport2port(t.Param(server2TransportParam)) + level2.Transport = transport2transport(t.Param(server2TransportParam)) + level1.Downstream = level2 + + level3 := tracetest.NewDownstream() + level3.ServiceName = t.Param(server3NameParam) + level3.ServerRole = RoleS3 + level3.Host = c.mapServiceToHost(level3.ServiceName) + level3.Port = c.transport2port(t.Param(server3TransportParam)) + level3.Transport = transport2transport(t.Param(server3TransportParam)) + level2.Downstream = level3 + + server1host := c.mapServiceToHost(server1) + url := fmt.Sprintf("http://%s:%s/start_trace", server1host, c.ServerPortHTTP) + resp, err := common.PostJSON(context.Background(), url, level1) + if err != nil { + t.Errorf(err.Error()) + return + } + + for r := resp; r != nil; r = r.Downstream { + if r.NotImplementedError != "" { + t.Skipf(r.NotImplementedError) + log.Printf("SKIP: %s", r.NotImplementedError) + return + } + } + + traceID := resp.Span.TraceId + if traceID == "" { + t.Errorf("Trace ID is empty in S1(%s)", server1) + return + } + + success := validateTrace(t, level1.Downstream, resp, server1, 1, traceID, sampled, baggage) + if success { + t.Successf("trace checks out") + log.Printf("PASS") + } +} + +func validateTrace( + t crossdock.T, + target *tracetest.Downstream, + resp *tracetest.TraceResponse, + service string, + level int, + traceID string, + sampled bool, + baggage string) bool { + + success := true + if traceID != resp.Span.TraceId { + t.Errorf("Trace ID mismatch in S%d(%s): expected %s, received %s", + level, service, traceID, resp.Span.TraceId) + success = false + } + if baggage != resp.Span.Baggage { + t.Errorf("Baggage mismatch in S%d(%s): expected %s, received %s", + level, service, baggage, resp.Span.Baggage) + success = false + } + if sampled != resp.Span.Sampled { + t.Errorf("Sampled mismatch in S%d(%s): expected %t, received %t", + level, service, sampled, resp.Span.Sampled) + success = false + } + if target != nil { + if resp.Downstream == nil { + t.Errorf("Missing downstream in S%d(%s)", level, service) + success = false + } else { + success = validateTrace(t, target.Downstream, resp.Downstream, + target.Host, level+1, traceID, sampled, baggage) && success + } + } else if resp.Downstream != nil { + t.Errorf("Unexpected downstream in S%d(%s)", level, service) + success = false + } + return success +} + +func randomBaggage() string { + r := utils.NewRand(time.Now().UnixNano()) + n := uint64(r.Int63()) + return fmt.Sprintf("%x", n) +} + +func str2bool(v string) bool { + switch v { + case "true": + return true + case "false": + return false + default: + panic(v + " is not a Boolean") + } +} + +func (c *Client) transport2port(v string) string { + switch v { + case transportHTTP: + return c.ServerPortHTTP + case transportTChannel: + return c.ServerPortTChannel + case transportDummy: + return "9999" + default: + panic("Unknown protocol " + v) + } +} + +func transport2transport(v string) tracetest.Transport { + switch v { + case transportHTTP: + return tracetest.Transport_HTTP + case transportTChannel: + return tracetest.Transport_TCHANNEL + case transportDummy: + return tracetest.Transport_DUMMY + default: + panic("Unknown protocol " + v) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/constants.go new file mode 100644 index 000000000..f3c2837f2 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/constants.go @@ -0,0 +1,32 @@ +// 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 common + +const ( + // DefaultClientPortHTTP is the port where the client (controller) runs + DefaultClientPortHTTP = "8080" + + // DefaultServerPortHTTP is the port where HTTP server runs + DefaultServerPortHTTP = "8081" + + // DefaultServerPortTChannel is the port where TChannel server runs + DefaultServerPortTChannel = "8082" + + // DefaultServiceName is the service name used by TChannel server + DefaultServiceName = "go" + + // DefaultTracerServiceName is the service name used by the tracer + DefaultTracerServiceName = "crossdock-go" +) diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/json.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/json.go new file mode 100644 index 000000000..1b93544c6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/json.go @@ -0,0 +1,73 @@ +// 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 common + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" +) + +// PostJSON sends a POST request to `url` with body containing JSON-serialized `req`. +// It injects tracing span into the headers (if found in the context). +// It returns parsed TraceResponse, or error. +func PostJSON(ctx context.Context, url string, req interface{}) (*tracetest.TraceResponse, error) { + data, err := json.Marshal(req) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + + span, err := injectSpan(ctx, httpReq) + if span != nil { + defer span.Finish() + } + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(httpReq) + if err != nil { + return nil, err + } + + var result tracetest.TraceResponse + err = utils.ReadJSON(resp, &result) + return &result, err +} + +func injectSpan(ctx context.Context, req *http.Request) (opentracing.Span, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, nil + } + span = span.Tracer().StartSpan("post", opentracing.ChildOf(span.Context())) + ext.SpanKindRPCClient.Set(span) + c := opentracing.HTTPHeadersCarrier(req.Header) + err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, c) + return span, err +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/docker-compose.yml b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/docker-compose.yml new file mode 100644 index 000000000..bc0c85af3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/docker-compose.yml @@ -0,0 +1,71 @@ +version: '2' + +services: + crossdock: + image: crossdock/crossdock + links: + - test_driver + - go + - java + - python + + environment: + - WAIT_FOR=test_driver,go,java,python + - WAIT_FOR_TIMEOUT=60s + + - CALL_TIMEOUT=60s + + - AXIS_CLIENT=go + - AXIS_S1NAME=go,java,python + - AXIS_SAMPLED=true,false + - AXIS_S2NAME=go,java,python + - AXIS_S2TRANSPORT=http,tchannel + - AXIS_S3NAME=go,java,python + - AXIS_S3TRANSPORT=http,tchannel + + - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2transport,s3name,s3transport + + - AXIS_TESTDRIVER=test_driver + - AXIS_SERVICES=go + + - BEHAVIOR_ENDTOEND=testdriver,services + + - REPORT=compact + go: + build: . + ports: + - "8080-8082" + + java: + image: jaegertracing/xdock-java + ports: + - "8080-8082" + + python: + image: jaegertracing/xdock-py + ports: + - "8080:8082" + + cassandra: + image: "cassandra:3.9" + + test_driver: + image: jaegertracing/test-driver + links: + - cassandra + depends_on: + - cassandra + ports: + - "8080" + +# node: +# image: yarpc/yarpc-node +# ports: +# - "8080-8082" +# +# python-sync: +# image: yarpc/yarpc-python +# ports: +# - 8080 +# environment: +# - SYNC=1 diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler.go new file mode 100644 index 000000000..837c3235a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler.go @@ -0,0 +1,137 @@ +// 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 endtoend + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" +) + +var ( + defaultSamplerType = jaeger.SamplerTypeRemote + + endToEndConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: defaultSamplerType, + Param: 1.0, + SamplingServerURL: "http://test_driver:5778/sampling", + SamplingRefreshInterval: 5 * time.Second, + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + LocalAgentHostPort: "test_driver:5775", + }, + } +) + +/*Handler handles creating traces from a http request. + * + * json: { + * "type": "remote", + * "operation": "operationName", + * "count": 2, + * "tags": { + * "key": "value" + * } + * } + * + * Given the above json payload, the handler will use a tracer with the RemotelyControlledSampler + * to create 2 traces for "operationName" operation with the tags: {"key":"value"}. These traces + * are reported to the agent with the hostname "test_driver". + */ +type Handler struct { + sync.RWMutex + + tracers map[string]opentracing.Tracer +} + +type traceRequest struct { + Type string `json:"type"` + Operation string `json:"operation"` + Tags map[string]string `json:"tags"` + Count int `json:"count"` +} + +// NewHandler returns a Handler. +func NewHandler() *Handler { + return &Handler{ + tracers: make(map[string]opentracing.Tracer), + } +} + +// init initializes the handler with a tracer +func (h *Handler) init(cfg config.Configuration) error { + tracer, _, err := cfg.New(common.DefaultTracerServiceName) + if err != nil { + return err + } + h.tracers[cfg.Sampler.Type] = tracer + return nil +} + +func (h *Handler) getTracer(samplerType string) opentracing.Tracer { + if samplerType == "" { + samplerType = defaultSamplerType + } + h.Lock() + defer h.Unlock() + tracer, ok := h.tracers[samplerType] + if !ok { + endToEndConfig.Sampler.Type = samplerType + if err := h.init(endToEndConfig); err != nil { + log.Printf("Failed to create tracer: %s", err.Error()) + return nil + } + tracer, _ = h.tracers[samplerType] + } + return tracer +} + +// GenerateTraces creates traces given the parameters in the request. +func (h *Handler) GenerateTraces(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var req traceRequest + if err := decoder.Decode(&req); err != nil { + http.Error(w, fmt.Sprintf("JSON payload is invalid: %s", err.Error()), http.StatusBadRequest) + return + } + tracer := h.getTracer(req.Type) + if tracer == nil { + http.Error(w, "Tracer is not initialized", http.StatusInternalServerError) + return + } + generateTraces(tracer, &req) +} + +func generateTraces(tracer opentracing.Tracer, r *traceRequest) { + for i := 0; i < r.Count; i++ { + span := tracer.StartSpan(r.Operation) + for k, v := range r.Tags { + span.SetTag(k, v) + } + span.Finish() + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler_test.go new file mode 100644 index 000000000..48b4068f3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler_test.go @@ -0,0 +1,154 @@ +// 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 endtoend + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/log" +) + +var ( + testOperation = "testOperation" + testService = "testService" + + testConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: 1.0, + SamplingServerURL: "http://localhost:5778/sampling", + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + LocalAgentHostPort: "localhost:5775", + }, + } + + badConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: "INVALID_TYPE", + }, + } + + testTraceRequest = traceRequest{ + Type: jaeger.SamplerTypeConst, + Operation: testOperation, + Tags: map[string]string{ + "key": "value", + }, + Count: 2, + } + + testInvalidJSON = `bad_json` + + testTraceJSONRequest = ` + { + "type": "const", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` + + testInvalidTypeJSONRequest = ` + { + "type": "INVALID_TYPE", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` +) + +func newInMemoryTracer() (opentracing.Tracer, *jaeger.InMemoryReporter) { + inMemoryReporter := jaeger.NewInMemoryReporter() + tracer, _ := jaeger.NewTracer( + testService, + jaeger.NewConstSampler(true), + inMemoryReporter, + jaeger.TracerOptions.Metrics(jaeger.NewNullMetrics()), + jaeger.TracerOptions.Logger(log.NullLogger)) + return tracer, inMemoryReporter +} + +func TestInit(t *testing.T) { + handler := NewHandler() + err := handler.init(testConfig) + assert.NoError(t, err) +} + +func TestInitBadConfig(t *testing.T) { + handler := NewHandler() + err := handler.init(badConfig) + assert.Error(t, err) +} + +func TestGetTracer(t *testing.T) { + iTracer, _ := newInMemoryTracer() + handler := &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: iTracer}} + tracer := handler.getTracer(jaeger.SamplerTypeConst) + assert.Equal(t, iTracer, tracer) +} + +func TestGetTracerError(t *testing.T) { + handler := NewHandler() + tracer := handler.getTracer("INVALID_TYPE") + assert.Nil(t, tracer) +} + +func TestGenerateTraces(t *testing.T) { + tracer, _ := newInMemoryTracer() + + tests := []struct { + expectedCode int + json string + handler *Handler + }{ + {http.StatusOK, testTraceJSONRequest, &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: tracer}}}, + {http.StatusBadRequest, testInvalidJSON, NewHandler()}, + {http.StatusInternalServerError, testInvalidTypeJSONRequest, NewHandler()}, // Tracer failed to initialize + } + + for _, test := range tests { + req, err := http.NewRequest("POST", "/create_spans", bytes.NewBuffer([]byte(test.json))) + assert.NoError(t, err, "Failed to initialize request") + recorder := httptest.NewRecorder() + handlerFunc := http.HandlerFunc(test.handler.GenerateTraces) + handlerFunc.ServeHTTP(recorder, req) + + assert.Equal(t, test.expectedCode, recorder.Code) + } +} + +func TestGenerateTracesInternal(t *testing.T) { + tracer, reporter := newInMemoryTracer() + generateTraces(tracer, &testTraceRequest) + assert.Equal(t, 2, reporter.SpansSubmitted()) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/log/logger.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/log/logger.go new file mode 100644 index 000000000..44fd92557 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/log/logger.go @@ -0,0 +1,29 @@ +// 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 log + +import ( + real_log "log" +) + +// Enabled controls logging from crossdock tests. It is enabled in main.go, but off in unit tests. +var Enabled bool + +// Printf delegates to log.Printf if Enabled == true +func Printf(msg string, args ...interface{}) { + if Enabled { + real_log.Printf(msg, args) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/main.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/main.go new file mode 100644 index 000000000..6bf47708f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/main.go @@ -0,0 +1,54 @@ +// 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 main + +import ( + "io" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/client" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func main() { + log.Enabled = true + + tracer, tCloser := initTracer() + defer tCloser.Close() + + s := &server.Server{Tracer: tracer} + if err := s.Start(); err != nil { + panic(err.Error()) + } else { + defer s.Close() + } + client := &client.Client{} + if err := client.Start(); err != nil { + panic(err.Error()) + } +} + +func initTracer() (opentracing.Tracer, io.Closer) { + t, c := jaeger.NewTracer( + common.DefaultTracerServiceName, + jaeger.NewConstSampler(false), + jaeger.NewLoggingReporter(jlog.StdLogger)) + return t, c +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/rules.mk b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/rules.mk new file mode 100644 index 000000000..30ddfe47c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/rules.mk @@ -0,0 +1,25 @@ +XDOCK_YAML=crossdock/docker-compose.yml + +.PHONY: crossdock-linux-bin +crossdock-linux-bin: + CGO_ENABLED=0 GOOS=linux time go build -a -installsuffix cgo -o crossdock/crossdock ./crossdock + +.PHONY: crossdock +crossdock: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill go + docker-compose -f $(XDOCK_YAML) rm -f go + docker-compose -f $(XDOCK_YAML) build go + docker-compose -f $(XDOCK_YAML) run crossdock 2>&1 | tee run-crossdock.log + grep 'Tests passed!' run-crossdock.log + +.PHONY: crossdock-fresh +crossdock-fresh: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill + docker-compose -f $(XDOCK_YAML) rm --force + docker-compose -f $(XDOCK_YAML) pull + docker-compose -f $(XDOCK_YAML) build + docker-compose -f $(XDOCK_YAML) run crossdock + +.PHONE: crossdock-logs +crossdock-logs: + docker-compose -f $(XDOCK_YAML) logs diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/constants.go new file mode 100644 index 000000000..481263c81 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/constants.go @@ -0,0 +1,26 @@ +// 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 server + +import "errors" + +// BaggageKey is the key used to pass baggage item +const BaggageKey = "crossdock-baggage-key" + +var ( + errNoSpanObserved = errors.New("no span found in Context") + errUnrecognizedProtocol = errors.New("unrecognized protocol for downstream call") + errCannotStartInTChannel = errors.New("cannot start new trace in tchannel server") +) diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server.go new file mode 100644 index 000000000..a25b49d61 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server.go @@ -0,0 +1,164 @@ +// 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 server + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "strings" + "sync" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/tchannel-go" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/endtoend" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +// Server implements S1-S3 servers +type Server struct { + HostPortHTTP string + HostPortTChannel string + Tracer opentracing.Tracer + listener net.Listener + channel *tchannel.Channel + eHandler *endtoend.Handler +} + +// Start starts the test server called by the Client and other upstream servers. +func (s *Server) Start() error { + if s.HostPortHTTP == "" { + s.HostPortHTTP = ":" + common.DefaultServerPortHTTP + } + if s.HostPortTChannel == "" { + s.HostPortTChannel = ":" + common.DefaultServerPortTChannel + } + + if err := s.startTChannelServer(s.Tracer); err != nil { + return err + } + s.eHandler = endtoend.NewHandler() + + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { return }) // health check + mux.HandleFunc("/start_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewStartTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doStartTrace(req.(*tracetest.StartTraceRequest)) + }) + }) + mux.HandleFunc("/join_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewJoinTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doJoinTrace(ctx, req.(*tracetest.JoinTraceRequest)) + }) + }) + mux.HandleFunc("/create_traces", s.eHandler.GenerateTraces) + + listener, err := net.Listen("tcp", s.HostPortHTTP) + if err != nil { + return err + } + s.listener = listener + s.HostPortHTTP = listener.Addr().String() + + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + http.Serve(listener, mux) + }() + started.Wait() + log.Printf("Started http server at %s\n", s.HostPortHTTP) + return nil +} + +// URL returns URL of the HTTP server +func (s *Server) URL() string { + return fmt.Sprintf("http://%s/", s.HostPortHTTP) +} + +// Close stops the server +func (s *Server) Close() error { + return s.listener.Close() +} + +// GetPortHTTP returns the network port the server listens to. +func (s *Server) GetPortHTTP() string { + hostPort := s.HostPortHTTP + hostPortSplit := strings.Split(hostPort, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} + +// GetPortTChannel returns the actual port the server listens to +func (s *Server) GetPortTChannel() string { + hostPortSplit := strings.Split(s.HostPortTChannel, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} + +func (s *Server) handleJSON( + w http.ResponseWriter, + r *http.Request, + newReq func() interface{}, + handle func(ctx context.Context, req interface{}) (interface{}, error), +) { + spanCtx, err := s.Tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) + if err != nil && err != opentracing.ErrSpanContextNotFound { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusBadRequest) + return + } + span := s.Tracer.StartSpan("post", ext.RPCServerOption(spanCtx)) + ctx := opentracing.ContextWithSpan(context.Background(), span) + defer span.Finish() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server request: %s", string(body)) + req := newReq() + if err := json.Unmarshal(body, req); err != nil { + http.Error(w, fmt.Sprintf("Cannot parse request JSON: %+v. body=[%s]", err, string(body)), http.StatusBadRequest) + return + } + resp, err := handle(ctx, req) + if err != nil { + log.Printf("Handle error: %s", err.Error()) + http.Error(w, fmt.Sprintf("Execution error: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot marshall response to JSON: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server response: %s", string(json)) + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server_test.go new file mode 100644 index 000000000..247008a5c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server_test.go @@ -0,0 +1,87 @@ +// 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 server + +import ( + "fmt" + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func TestServerJSON(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + jaeger.NewNullReporter()) + defer tCloser.Close() + + s := &Server{HostPortHTTP: "127.0.0.1:0", HostPortTChannel: "127.0.0.1:0", Tracer: tracer} + err := s.Start() + require.NoError(t, err) + defer s.Close() + + req := tracetest.NewStartTraceRequest() + req.Sampled = true + req.Baggage = "Zoidberg" + req.Downstream = &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortHTTP(), + Transport: tracetest.Transport_HTTP, + Downstream: &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortTChannel(), + Transport: tracetest.Transport_TCHANNEL, + }, + } + + url := fmt.Sprintf("http://%s/start_trace", s.HostPortHTTP) + result, err := common.PostJSON(context.Background(), url, req) + + require.NoError(t, err) + log.Printf("response=%+v", &result) +} + +func TestObserveSpan(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(true), + jaeger.NewNullReporter()) + defer tCloser.Close() + + _, err := observeSpan(context.Background(), tracer) + assert.Error(t, err) + + span := tracer.StartSpan("hi") + span.SetBaggageItem(BaggageKey, "xyz") + ctx := opentracing.ContextWithSpan(context.Background(), span) + + s, err := observeSpan(ctx, tracer) + assert.NoError(t, err) + assert.True(t, s.Sampled) + traceID := span.Context().(jaeger.SpanContext).TraceID().String() + assert.Equal(t, traceID, s.TraceId) + assert.Equal(t, "xyz", s.Baggage) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/tchannel.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/tchannel.go new file mode 100644 index 000000000..a8b119633 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/tchannel.go @@ -0,0 +1,88 @@ +// 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 server + +import ( + "fmt" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/thrift" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func (s *Server) startTChannelServer(tracer opentracing.Tracer) error { + channelOpts := &tchannel.ChannelOptions{ + Tracer: tracer, + } + ch, err := tchannel.NewChannel("go", channelOpts) + if err != nil { + return err + } + server := thrift.NewServer(ch) + + s.channel = ch + + handler := tracetest.NewTChanTracedServiceServer(s) + server.Register(handler) + + if err := ch.ListenAndServe(s.HostPortTChannel); err != nil { + return err + } + s.HostPortTChannel = ch.PeerInfo().HostPort + log.Printf("Started tchannel server at %s\n", s.HostPortTChannel) + return nil +} + +// StartTrace implements StartTrace() of TChanTracedService +func (s *Server) StartTrace(ctx thrift.Context, request *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) { + return nil, errCannotStartInTChannel +} + +// JoinTrace implements JoinTrace() of TChanTracedService +func (s *Server) JoinTrace(ctx thrift.Context, request *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) { + log.Printf("tchannel server handling JoinTrace") + return s.prepareResponse(ctx, request.ServerRole, request.Downstream) +} + +func (s *Server) callDownstreamTChannel(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) { + req := &tracetest.JoinTraceRequest{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + + hostPort := fmt.Sprintf("%s:%s", target.Host, target.Port) + log.Printf("calling downstream '%s' over tchannel:%s", target.ServiceName, hostPort) + + channelOpts := &tchannel.ChannelOptions{ + Tracer: s.Tracer, + } + ch, err := tchannel.NewChannel("tchannel-client", channelOpts) + if err != nil { + return nil, err + } + + opts := &thrift.ClientOptions{HostPort: hostPort} + thriftClient := thrift.NewClient(ch, target.ServiceName, opts) + + client := tracetest.NewTChanTracedServiceClient(thriftClient) + ctx, cx := context.WithTimeout(ctx, time.Second) + defer cx() + return client.JoinTrace(thrift.Wrap(ctx), req) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/trace.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/trace.go new file mode 100644 index 000000000..7ff6e1433 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/trace.go @@ -0,0 +1,101 @@ +// 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 server + +import ( + "fmt" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func (s *Server) doStartTrace(req *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) { + span := s.Tracer.StartSpan(req.ServerRole) + if req.Sampled { + ext.SamplingPriority.Set(span, 1) + } + span.SetBaggageItem(BaggageKey, req.Baggage) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(context.Background(), span) + + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) doJoinTrace(ctx context.Context, req *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) { + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) prepareResponse(ctx context.Context, role string, reqDwn *tracetest.Downstream) (*tracetest.TraceResponse, error) { + observedSpan, err := observeSpan(ctx, s.Tracer) + if err != nil { + return nil, err + } + + resp := tracetest.NewTraceResponse() + resp.Span = observedSpan + + if reqDwn != nil { + downstreamResp, err := s.callDownstream(ctx, role, reqDwn) + if err != nil { + return nil, err + } + resp.Downstream = downstreamResp + } + + return resp, nil +} + +func (s *Server) callDownstream(ctx context.Context, role string, downstream *tracetest.Downstream) (*tracetest.TraceResponse, error) { + switch downstream.Transport { + case tracetest.Transport_HTTP: + return s.callDownstreamHTTP(ctx, downstream) + case tracetest.Transport_TCHANNEL: + return s.callDownstreamTChannel(ctx, downstream) + case tracetest.Transport_DUMMY: + return &tracetest.TraceResponse{NotImplementedError: "DUMMY transport not implemented"}, nil + default: + return nil, errUnrecognizedProtocol + } +} + +func (s *Server) callDownstreamHTTP(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) { + req := &tracetest.JoinTraceRequest{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + url := fmt.Sprintf("http://%s:%s/join_trace", target.Host, target.Port) + log.Printf("Calling downstream service '%s' at %s", target.ServiceName, url) + return common.PostJSON(ctx, url, req) +} + +func observeSpan(ctx context.Context, tracer opentracing.Tracer) (*tracetest.ObservedSpan, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, errNoSpanObserved + } + sc := span.Context().(jaeger.SpanContext) + observedSpan := tracetest.NewObservedSpan() + observedSpan.TraceId = sc.TraceID().String() + observedSpan.Sampled = sc.IsSampled() + observedSpan.Baggage = span.BaggageItem(BaggageKey) + return observedSpan, nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/constants.go new file mode 100644 index 000000000..06fee4093 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go new file mode 100644 index 000000000..2ec9bbda6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go @@ -0,0 +1,137 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package tracetest is generated code used to make or handle TChannel calls using Thrift. +package tracetest + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanTracedService is the interface that defines the server handler and client interface. +type TChanTracedService interface { + JoinTrace(ctx thrift.Context, request *JoinTraceRequest) (*TraceResponse, error) + StartTrace(ctx thrift.Context, request *StartTraceRequest) (*TraceResponse, error) +} + +// Implementation of a client and service handler. + +type tchanTracedServiceClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanTracedServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanTracedServiceClient { + return &tchanTracedServiceClient{ + thriftService, + client, + } +} + +// NewTChanTracedServiceClient creates a client that can be used to make remote calls. +func NewTChanTracedServiceClient(client thrift.TChanClient) TChanTracedService { + return NewTChanTracedServiceInheritedClient("TracedService", client) +} + +func (c *tchanTracedServiceClient) JoinTrace(ctx thrift.Context, request *JoinTraceRequest) (*TraceResponse, error) { + var resp TracedServiceJoinTraceResult + args := TracedServiceJoinTraceArgs{ + Request: request, + } + success, err := c.client.Call(ctx, c.thriftService, "joinTrace", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +func (c *tchanTracedServiceClient) StartTrace(ctx thrift.Context, request *StartTraceRequest) (*TraceResponse, error) { + var resp TracedServiceStartTraceResult + args := TracedServiceStartTraceArgs{ + Request: request, + } + success, err := c.client.Call(ctx, c.thriftService, "startTrace", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +type tchanTracedServiceServer struct { + handler TChanTracedService +} + +// NewTChanTracedServiceServer wraps a handler for TChanTracedService so it can be +// registered with a thrift.Server. +func NewTChanTracedServiceServer(handler TChanTracedService) thrift.TChanServer { + return &tchanTracedServiceServer{ + handler, + } +} + +func (s *tchanTracedServiceServer) Service() string { + return "TracedService" +} + +func (s *tchanTracedServiceServer) Methods() []string { + return []string{ + "joinTrace", + "startTrace", + } +} + +func (s *tchanTracedServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "joinTrace": + return s.handleJoinTrace(ctx, protocol) + case "startTrace": + return s.handleStartTrace(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanTracedServiceServer) handleJoinTrace(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req TracedServiceJoinTraceArgs + var res TracedServiceJoinTraceResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.JoinTrace(ctx, req.Request) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} + +func (s *tchanTracedServiceServer) handleStartTrace(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req TracedServiceStartTraceArgs + var res TracedServiceStartTraceResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.StartTrace(ctx, req.Request) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go new file mode 100644 index 000000000..c2a1e54a7 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go @@ -0,0 +1,747 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type TracedService interface { + // Parameters: + // - Request + StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) + // Parameters: + // - Request + JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) +} + +type TracedServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewTracedServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewTracedServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Request +func (p *TracedServiceClient) StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) { + if err = p.sendStartTrace(request); err != nil { + return + } + return p.recvStartTrace() +} + +func (p *TracedServiceClient) sendStartTrace(request *StartTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("startTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceStartTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvStartTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "startTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "startTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "startTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "startTrace failed: invalid message type") + return + } + result := TracedServiceStartTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +// Parameters: +// - Request +func (p *TracedServiceClient) JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) { + if err = p.sendJoinTrace(request); err != nil { + return + } + return p.recvJoinTrace() +} + +func (p *TracedServiceClient) sendJoinTrace(request *JoinTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("joinTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceJoinTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvJoinTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "joinTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "joinTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "joinTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "joinTrace failed: invalid message type") + return + } + result := TracedServiceJoinTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type TracedServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler TracedService +} + +func (p *TracedServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *TracedServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *TracedServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewTracedServiceProcessor(handler TracedService) *TracedServiceProcessor { + + self4 := &TracedServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["startTrace"] = &tracedServiceProcessorStartTrace{handler: handler} + self4.processorMap["joinTrace"] = &tracedServiceProcessorJoinTrace{handler: handler} + return self4 +} + +func (p *TracedServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type tracedServiceProcessorStartTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorStartTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceStartTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceStartTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.StartTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing startTrace: "+err2.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("startTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type tracedServiceProcessorJoinTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorJoinTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceJoinTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceJoinTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.JoinTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing joinTrace: "+err2.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("joinTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Request +type TracedServiceStartTraceArgs struct { + Request *StartTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceStartTraceArgs() *TracedServiceStartTraceArgs { + return &TracedServiceStartTraceArgs{} +} + +var TracedServiceStartTraceArgs_Request_DEFAULT *StartTraceRequest + +func (p *TracedServiceStartTraceArgs) GetRequest() *StartTraceRequest { + if !p.IsSetRequest() { + return TracedServiceStartTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceStartTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceStartTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &StartTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceStartTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceStartTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceStartTraceResult() *TracedServiceStartTraceResult { + return &TracedServiceStartTraceResult{} +} + +var TracedServiceStartTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceStartTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceStartTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceStartTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceStartTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceStartTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceResult(%+v)", *p) +} + +// Attributes: +// - Request +type TracedServiceJoinTraceArgs struct { + Request *JoinTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceJoinTraceArgs() *TracedServiceJoinTraceArgs { + return &TracedServiceJoinTraceArgs{} +} + +var TracedServiceJoinTraceArgs_Request_DEFAULT *JoinTraceRequest + +func (p *TracedServiceJoinTraceArgs) GetRequest() *JoinTraceRequest { + if !p.IsSetRequest() { + return TracedServiceJoinTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceJoinTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceJoinTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &JoinTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceJoinTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceJoinTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceJoinTraceResult() *TracedServiceJoinTraceResult { + return &TracedServiceJoinTraceResult{} +} + +var TracedServiceJoinTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceJoinTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceJoinTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceJoinTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceJoinTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceJoinTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceResult(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go new file mode 100644 index 000000000..267fa18dc --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go @@ -0,0 +1,1103 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type Transport int64 + +const ( + Transport_HTTP Transport = 0 + Transport_TCHANNEL Transport = 1 + Transport_DUMMY Transport = 2 +) + +func (p Transport) String() string { + switch p { + case Transport_HTTP: + return "HTTP" + case Transport_TCHANNEL: + return "TCHANNEL" + case Transport_DUMMY: + return "DUMMY" + } + return "" +} + +func TransportFromString(s string) (Transport, error) { + switch s { + case "HTTP": + return Transport_HTTP, nil + case "TCHANNEL": + return Transport_TCHANNEL, nil + case "DUMMY": + return Transport_DUMMY, nil + } + return Transport(0), fmt.Errorf("not a valid Transport string") +} + +func TransportPtr(v Transport) *Transport { return &v } + +func (p Transport) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *Transport) UnmarshalText(text []byte) error { + q, err := TransportFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - ServiceName +// - ServerRole +// - Host +// - Port +// - Transport +// - Downstream +type Downstream struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + ServerRole string `thrift:"serverRole,2,required" json:"serverRole"` + Host string `thrift:"host,3,required" json:"host"` + Port string `thrift:"port,4,required" json:"port"` + Transport Transport `thrift:"transport,5,required" json:"transport"` + Downstream *Downstream `thrift:"downstream,6" json:"downstream,omitempty"` +} + +func NewDownstream() *Downstream { + return &Downstream{} +} + +func (p *Downstream) GetServiceName() string { + return p.ServiceName +} + +func (p *Downstream) GetServerRole() string { + return p.ServerRole +} + +func (p *Downstream) GetHost() string { + return p.Host +} + +func (p *Downstream) GetPort() string { + return p.Port +} + +func (p *Downstream) GetTransport() Transport { + return p.Transport +} + +var Downstream_Downstream_DEFAULT Downstream + +func (p *Downstream) GetDownstream() Downstream { + if !p.IsSetDownstream() { + return Downstream_Downstream_DEFAULT + } + return *p.Downstream +} +func (p *Downstream) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *Downstream) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + var issetServerRole bool = false + var issetHost bool = false + var issetPort bool = false + var issetTransport bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetServerRole = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetHost = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetPort = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetTransport = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetHost { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Host is not set")) + } + if !issetPort { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Port is not set")) + } + if !issetTransport { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Transport is not set")) + } + return nil +} + +func (p *Downstream) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Downstream) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *Downstream) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Host = v + } + return nil +} + +func (p *Downstream) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Downstream) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + temp := Transport(v) + p.Transport = temp + } + return nil +} + +func (p *Downstream) readField6(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *Downstream) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Downstream"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Downstream) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Downstream) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:serverRole: ", p), err) + } + return err +} + +func (p *Downstream) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("host", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := oprot.WriteString(string(p.Host)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.host (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + return err +} + +func (p *Downstream) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.STRING, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:port: ", p), err) + } + if err := oprot.WriteString(string(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:port: ", p), err) + } + return err +} + +func (p *Downstream) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("transport", thrift.I32, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:transport: ", p), err) + } + if err := oprot.WriteI32(int32(p.Transport)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.transport (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:transport: ", p), err) + } + return err +} + +func (p *Downstream) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:downstream: ", p), err) + } + } + return err +} + +func (p *Downstream) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Downstream(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Sampled +// - Baggage +// - Downstream +type StartTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` + Downstream *Downstream `thrift:"downstream,4,required" json:"downstream"` +} + +func NewStartTraceRequest() *StartTraceRequest { + return &StartTraceRequest{} +} + +func (p *StartTraceRequest) GetServerRole() string { + return p.ServerRole +} + +func (p *StartTraceRequest) GetSampled() bool { + return p.Sampled +} + +func (p *StartTraceRequest) GetBaggage() string { + return p.Baggage +} + +var StartTraceRequest_Downstream_DEFAULT *Downstream + +func (p *StartTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return StartTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *StartTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *StartTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + var issetSampled bool = false + var issetBaggage bool = false + var issetDownstream bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetDownstream = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + if !issetDownstream { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Downstream is not set")) + } + return nil +} + +func (p *StartTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *StartTraceRequest) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *StartTraceRequest) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *StartTraceRequest) readField4(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *StartTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("StartTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + 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) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *StartTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:downstream: ", p), err) + } + return err +} + +func (p *StartTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("StartTraceRequest(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Downstream +type JoinTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Downstream *Downstream `thrift:"downstream,2" json:"downstream,omitempty"` +} + +func NewJoinTraceRequest() *JoinTraceRequest { + return &JoinTraceRequest{} +} + +func (p *JoinTraceRequest) GetServerRole() string { + return p.ServerRole +} + +var JoinTraceRequest_Downstream_DEFAULT *Downstream + +func (p *JoinTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return JoinTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *JoinTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *JoinTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + return nil +} + +func (p *JoinTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *JoinTraceRequest) readField2(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *JoinTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("JoinTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *JoinTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *JoinTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *JoinTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("JoinTraceRequest(%+v)", *p) +} + +// Attributes: +// - TraceId +// - Sampled +// - Baggage +type ObservedSpan struct { + TraceId string `thrift:"traceId,1,required" json:"traceId"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` +} + +func NewObservedSpan() *ObservedSpan { + return &ObservedSpan{} +} + +func (p *ObservedSpan) GetTraceId() string { + return p.TraceId +} + +func (p *ObservedSpan) GetSampled() bool { + return p.Sampled +} + +func (p *ObservedSpan) GetBaggage() string { + return p.Baggage +} +func (p *ObservedSpan) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceId bool = false + var issetSampled bool = false + var issetBaggage bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceId = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceId is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + return nil +} + +func (p *ObservedSpan) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceId = v + } + return nil +} + +func (p *ObservedSpan) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *ObservedSpan) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *ObservedSpan) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ObservedSpan"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ObservedSpan) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceId", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceId: ", p), err) + } + if err := oprot.WriteString(string(p.TraceId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceId (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceId: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *ObservedSpan) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ObservedSpan(%+v)", *p) +} + +// Each server must include the information about the span it observed. +// It can only be omitted from the response if notImplementedError field is not empty. +// If the server was instructed to make a downstream call, it must embed the +// downstream response in its own response. +// +// Attributes: +// - Span +// - Downstream +// - NotImplementedError +type TraceResponse struct { + Span *ObservedSpan `thrift:"span,1" json:"span,omitempty"` + Downstream *TraceResponse `thrift:"downstream,2" json:"downstream,omitempty"` + NotImplementedError string `thrift:"notImplementedError,3,required" json:"notImplementedError"` +} + +func NewTraceResponse() *TraceResponse { + return &TraceResponse{} +} + +var TraceResponse_Span_DEFAULT *ObservedSpan + +func (p *TraceResponse) GetSpan() *ObservedSpan { + if !p.IsSetSpan() { + return TraceResponse_Span_DEFAULT + } + return p.Span +} + +var TraceResponse_Downstream_DEFAULT TraceResponse + +func (p *TraceResponse) GetDownstream() TraceResponse { + if !p.IsSetDownstream() { + return TraceResponse_Downstream_DEFAULT + } + return *p.Downstream +} + +func (p *TraceResponse) GetNotImplementedError() string { + return p.NotImplementedError +} +func (p *TraceResponse) IsSetSpan() bool { + return p.Span != nil +} + +func (p *TraceResponse) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *TraceResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetNotImplementedError bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetNotImplementedError = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetNotImplementedError { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field NotImplementedError is not set")) + } + return nil +} + +func (p *TraceResponse) readField1(iprot thrift.TProtocol) error { + p.Span = &ObservedSpan{} + if err := p.Span.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Span), err) + } + return nil +} + +func (p *TraceResponse) readField2(iprot thrift.TProtocol) error { + p.Downstream = &TraceResponse{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *TraceResponse) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.NotImplementedError = v + } + return nil +} + +func (p *TraceResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("TraceResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TraceResponse) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetSpan() { + if err := oprot.WriteFieldBegin("span", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:span: ", p), err) + } + if err := p.Span.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Span), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:span: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("notImplementedError", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:notImplementedError: ", p), err) + } + if err := oprot.WriteString(string(p.NotImplementedError)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.notImplementedError (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:notImplementedError: ", p), err) + } + return err +} + +func (p *TraceResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TraceResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/doc.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/doc.go new file mode 100644 index 000000000..4f5549033 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/doc.go @@ -0,0 +1,24 @@ +// 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 jaeger implements an OpenTracing (http://opentracing.io) Tracer. +It is currently using Zipkin-compatible data model and can be directly +itegrated with Zipkin backend (http://zipkin.io). + +For integration instructions please refer to the README: + +https://github.com/uber/jaeger-client-go/blob/master/README.md +*/ +package jaeger diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.lock b/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.lock new file mode 100644 index 000000000..912d2c54d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.lock @@ -0,0 +1,75 @@ +hash: 4abcf83a509c003112c27a131eafde7d4fa11aeb17decb43ac0746593890ff85 +updated: 2017-08-22T14:21:39.649697305-04:00 +imports: +- name: github.com/apache/thrift + version: b2a4d4ae21c789b689dd162deb819665567f481c + subpackages: + - lib/go/thrift +- name: github.com/codahale/hdrhistogram + version: f8ad88b59a584afeee9d334eff879b104439117b +- name: github.com/crossdock/crossdock-go + version: 049aabb0122b03bc9bd30cab8f3f91fb60166361 + subpackages: + - assert + - require +- name: github.com/davecgh/go-spew + version: adab96458c51a58dc1783b3335dcce5461522e75 + subpackages: + - spew +- name: github.com/opentracing/opentracing-go + version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 + subpackages: + - ext + - log +- name: github.com/pmezard/go-difflib + version: 792786c7400a136282c1664665ae0a8db921c6c2 + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require + - suite +- name: github.com/uber-go/atomic + version: 0c9e689d64f004564b79d9a663634756df322902 +- name: github.com/uber/jaeger-lib + version: 575c678b2873b62aaadd0fa5817a2aeb3155dc4d + subpackages: + - metrics + - metrics/testutils +- name: github.com/uber/tchannel-go + version: a7ad9ecb640b5f10a0395b38d6319175172b3ab2 + subpackages: + - atomic + - internal/argreader + - relay + - thrift + - thrift/gen-go/meta + - tnet + - tos + - trace/thrift/gen-go/tcollector + - trand + - typed +- name: go.uber.org/atomic + version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf +- name: go.uber.org/zap + version: 9cabc84638b70e564c3dab2766efcb1ded2aac9f + subpackages: + - buffer + - internal/bufferpool + - internal/color + - internal/exit + - internal/multierror + - zapcore +- name: golang.org/x/net + version: f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f + subpackages: + - bpf + - context + - context/ctxhttp + - internal/iana + - internal/socket + - ipv4 + - ipv6 +testImports: [] diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.yaml b/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.yaml new file mode 100644 index 000000000..fa30bded8 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/glide.yaml @@ -0,0 +1,34 @@ +package: github.com/uber/jaeger-client-go +import: +- package: github.com/apache/thrift + version: ">=0.9.3, <0.11.0" + subpackages: + - lib/go/thrift +- package: github.com/opentracing/opentracing-go + version: ^1 + subpackages: + - ext + - log +- package: golang.org/x/net + subpackages: + - context +- package: github.com/uber/tchannel-go + version: ^1.1.0 + subpackages: + - atomic + - thrift + - thrift/gen-go/meta + - tnet + - trace/thrift/gen-go/tcollector + - typed +- package: github.com/stretchr/testify + version: ^1.1.3 + subpackages: + - assert + - require + - suite +- package: github.com/crossdock/crossdock-go +- package: github.com/uber/jaeger-lib + version: ^1.0.0 + subpackages: + - metrics diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/header.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/header.go new file mode 100644 index 000000000..19c2c055b --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/header.go @@ -0,0 +1,64 @@ +// 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 jaeger + +// HeadersConfig contains the values for the header keys that Jaeger will use. +// These values may be either custom or default depending on whether custom +// values were provided via a configuration. +type HeadersConfig struct { + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader string `yaml:"jaegerDebugHeader"` + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader string `yaml:"jaegerBaggageHeader"` + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName string `yaml:"TraceContextHeaderName"` + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix string `yaml:"traceBaggageHeaderPrefix"` +} + +func (c *HeadersConfig) applyDefaults() *HeadersConfig { + if c.JaegerBaggageHeader == "" { + c.JaegerBaggageHeader = JaegerBaggageHeader + } + if c.JaegerDebugHeader == "" { + c.JaegerDebugHeader = JaegerDebugHeader + } + if c.TraceBaggageHeaderPrefix == "" { + c.TraceBaggageHeaderPrefix = TraceBaggageHeaderPrefix + } + if c.TraceContextHeaderName == "" { + c.TraceContextHeaderName = TraceContextHeaderName + } + return c +} + +func getDefaultHeadersConfig() *HeadersConfig { + return &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/header_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/header_test.go new file mode 100644 index 000000000..d6a481a84 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/header_test.go @@ -0,0 +1,50 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetDefaultOrCustom(t *testing.T) { + assert.Equal(t, (&HeadersConfig{}).applyDefaults(), getDefaultHeadersConfig()) + assert.Equal(t, (&HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + }).applyDefaults(), &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) + + customHeaders := &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: "custom-jaeger-baggage-header", + TraceContextHeaderName: "custom-tracer-state-header-name", + TraceBaggageHeaderPrefix: "custom-tracer-baggage-header-prefix", + } + assert.Equal(t, customHeaders.applyDefaults(), customHeaders) +} + +func TestGetDefaultHeadersConfig(t *testing.T) { + assert.Equal(t, getDefaultHeadersConfig(), &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/options.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/options.go new file mode 100644 index 000000000..745729319 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/options.go @@ -0,0 +1,101 @@ +// 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 remote + +import ( + "time" + + "github.com/uber/jaeger-client-go" +) + +const ( + defaultMaxValueLength = 2048 + defaultRefreshInterval = time.Minute + defaultHostPort = "localhost:5778" +) + +// Option is a function that sets some option on the RestrictionManager +type Option func(options *options) + +// Options is a factory for all available options +var Options options + +type options struct { + denyBaggageOnInitializationFailure bool + metrics *jaeger.Metrics + logger jaeger.Logger + hostPort string + refreshInterval time.Duration +} + +// DenyBaggageOnInitializationFailure creates an Option that determines the startup failure mode of RestrictionManager. +// If DenyBaggageOnInitializationFailure is true, RestrictionManager will not allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +// If DenyBaggageOnInitializationFailure is false, RestrictionManager will allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +func (options) DenyBaggageOnInitializationFailure(b bool) Option { + return func(o *options) { + o.denyBaggageOnInitializationFailure = b + } +} + +// Metrics creates an Option that initializes Metrics on the RestrictionManager, which is used to emit statistics. +func (options) Metrics(m *jaeger.Metrics) Option { + return func(o *options) { + o.metrics = m + } +} + +// Logger creates an Option that sets the logger used by the RestrictionManager. +func (options) Logger(logger jaeger.Logger) Option { + return func(o *options) { + o.logger = logger + } +} + +// HostPort creates an Option that sets the hostPort of the local agent that contains the baggage restrictions. +func (options) HostPort(hostPort string) Option { + return func(o *options) { + o.hostPort = hostPort + } +} + +// RefreshInterval creates an Option that sets how often the RestrictionManager will poll local agent for +// the baggage restrictions. +func (options) RefreshInterval(refreshInterval time.Duration) Option { + return func(o *options) { + o.refreshInterval = refreshInterval + } +} + +func applyOptions(o ...Option) options { + opts := options{} + for _, option := range o { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = jaeger.NewNullMetrics() + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + if opts.hostPort == "" { + opts.hostPort = defaultHostPort + } + if opts.refreshInterval == 0 { + opts.refreshInterval = defaultRefreshInterval + } + return opts +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager.go new file mode 100644 index 000000000..a56515aca --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager.go @@ -0,0 +1,157 @@ +// 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 remote + +import ( + "fmt" + "net/url" + "sync" + "time" + + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" + "github.com/uber/jaeger-client-go/utils" +) + +type httpBaggageRestrictionManagerProxy struct { + url string +} + +func newHTTPBaggageRestrictionManagerProxy(hostPort, serviceName string) *httpBaggageRestrictionManagerProxy { + v := url.Values{} + v.Set("service", serviceName) + return &httpBaggageRestrictionManagerProxy{ + url: fmt.Sprintf("http://%s/baggageRestrictions?%s", hostPort, v.Encode()), + } +} + +func (s *httpBaggageRestrictionManagerProxy) GetBaggageRestrictions(serviceName string) ([]*thrift.BaggageRestriction, error) { + var out []*thrift.BaggageRestriction + if err := utils.GetJSON(s.url, &out); err != nil { + return nil, err + } + return out, nil +} + +// RestrictionManager manages baggage restrictions by retrieving baggage restrictions from agent +type RestrictionManager struct { + options + + mux sync.RWMutex + serviceName string + restrictions map[string]*baggage.Restriction + thriftProxy thrift.BaggageRestrictionManager + pollStopped sync.WaitGroup + stopPoll chan struct{} + invalidRestriction *baggage.Restriction + validRestriction *baggage.Restriction + + // Determines if the manager has successfully retrieved baggage restrictions from agent + initialized bool +} + +// NewRestrictionManager returns a BaggageRestrictionManager that polls the agent for the latest +// baggage restrictions. +func NewRestrictionManager(serviceName string, options ...Option) *RestrictionManager { + // TODO there is a developing use case where a single tracer can generate traces on behalf of many services. + // restrictionsMap will need to exist per service + opts := applyOptions(options...) + m := &RestrictionManager{ + serviceName: serviceName, + options: opts, + restrictions: make(map[string]*baggage.Restriction), + thriftProxy: newHTTPBaggageRestrictionManagerProxy(opts.hostPort, serviceName), + stopPoll: make(chan struct{}), + invalidRestriction: baggage.NewRestriction(false, 0), + validRestriction: baggage.NewRestriction(true, defaultMaxValueLength), + } + m.pollStopped.Add(1) + go m.pollManager() + return m +} + +// isReady returns true if the manager has retrieved baggage restrictions from the remote source. +func (m *RestrictionManager) isReady() bool { + m.mux.RLock() + defer m.mux.RUnlock() + return m.initialized +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *RestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + m.mux.RLock() + defer m.mux.RUnlock() + if !m.initialized { + if m.denyBaggageOnInitializationFailure { + return m.invalidRestriction + } + return m.validRestriction + } + if restriction, ok := m.restrictions[key]; ok { + return restriction + } + return m.invalidRestriction +} + +// Close stops remote polling and closes the RemoteRestrictionManager. +func (m *RestrictionManager) Close() error { + close(m.stopPoll) + m.pollStopped.Wait() + return nil +} + +func (m *RestrictionManager) pollManager() { + defer m.pollStopped.Done() + // attempt to initialize baggage restrictions + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to initialize baggage restrictions: %s", err.Error())) + } + ticker := time.NewTicker(m.refreshInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to update baggage restrictions: %s", err.Error())) + } + case <-m.stopPoll: + return + } + } +} + +func (m *RestrictionManager) updateRestrictions() error { + restrictions, err := m.thriftProxy.GetBaggageRestrictions(m.serviceName) + if err != nil { + m.metrics.BaggageRestrictionsUpdateFailure.Inc(1) + return err + } + newRestrictions := m.parseRestrictions(restrictions) + m.metrics.BaggageRestrictionsUpdateSuccess.Inc(1) + m.mux.Lock() + defer m.mux.Unlock() + m.initialized = true + m.restrictions = newRestrictions + return nil +} + +func (m *RestrictionManager) parseRestrictions(restrictions []*thrift.BaggageRestriction) map[string]*baggage.Restriction { + setters := make(map[string]*baggage.Restriction, len(restrictions)) + for _, restriction := range restrictions { + setters[restriction.BaggageKey] = baggage.NewRestriction(true, int(restriction.MaxValueLength)) + } + return setters +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go new file mode 100644 index 000000000..6d5b865fd --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go @@ -0,0 +1,220 @@ +// 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 remote + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" +) + +const ( + service = "svc" + expectedKey = "key" + expectedSize = 10 +) + +var ( + testRestrictions = []*thrift.BaggageRestriction{ + {BaggageKey: expectedKey, MaxValueLength: int32(expectedSize)}, + } +) + +var _ io.Closer = new(RestrictionManager) // API check + +type baggageHandler struct { + returnError *atomic.Bool + restrictions []*thrift.BaggageRestriction +} + +func (h *baggageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if h.returnError.Load() { + w.WriteHeader(http.StatusInternalServerError) + } else { + bytes, _ := json.Marshal(h.restrictions) + w.Header().Add("Content-Type", "application/json") + w.Write(bytes) + } +} + +func (h *baggageHandler) setReturnError(b bool) { + h.returnError.Store(b) +} + +func withHTTPServer( + restrictions []*thrift.BaggageRestriction, + f func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ), +) { + factory := metrics.NewLocalFactory(0) + m := jaeger.NewMetrics(factory, nil) + + handler := &baggageHandler{returnError: atomic.NewBool(true), restrictions: restrictions} + server := httptest.NewServer(handler) + defer server.Close() + + f(m, factory, handler, server) +} + +func TestNewRemoteRestrictionManager(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + handler.setReturnError(false) + mgr := NewRestrictionManager( + service, + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + defer mgr.Close() + + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + + badKey := "bad-key" + restriction = mgr.GetRestriction(service, badKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-restrictions-update", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +func TestDenyBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.DenyBaggageOnInitializationFailure(true), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(m), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + metricName := "jaeger.baggage-restrictions-update" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := factory.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) + + // DenyBaggageOnInitializationFailure should not allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + // have the http server return restrictions + handler.setReturnError(false) + mgr.updateRestrictions() + + // Wait until manager retrieves baggage restrictions + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction = mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + }) +} + +func TestAllowBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.RefreshInterval(time.Millisecond), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + // AllowBaggageOnInitializationFailure should allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, 2048), restriction) + }) +} + +func getHostPort(t *testing.T, s string) string { + u, err := url.Parse(s) + require.NoError(t, err, "Failed to parse url") + return u.Host +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager.go new file mode 100644 index 000000000..c16a5c566 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager.go @@ -0,0 +1,71 @@ +// 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 baggage + +const ( + defaultMaxValueLength = 2048 +) + +// Restriction determines whether a baggage key is allowed and contains any restrictions on the baggage value. +type Restriction struct { + keyAllowed bool + maxValueLength int +} + +// NewRestriction returns a new Restriction. +func NewRestriction(keyAllowed bool, maxValueLength int) *Restriction { + return &Restriction{ + keyAllowed: keyAllowed, + maxValueLength: maxValueLength, + } +} + +// KeyAllowed returns whether the baggage key for this restriction is allowed. +func (r *Restriction) KeyAllowed() bool { + return r.keyAllowed +} + +// MaxValueLength returns the max length for the baggage value. +func (r *Restriction) MaxValueLength() int { + return r.maxValueLength +} + +// RestrictionManager keeps track of valid baggage keys and their restrictions. The manager +// will return a Restriction for a specific baggage key which will determine whether the baggage +// key is allowed for the current service and any other applicable restrictions on the baggage +// value. +type RestrictionManager interface { + GetRestriction(service, key string) *Restriction +} + +// DefaultRestrictionManager allows any baggage key. +type DefaultRestrictionManager struct { + defaultRestriction *Restriction +} + +// NewDefaultRestrictionManager returns a DefaultRestrictionManager. +func NewDefaultRestrictionManager(maxValueLength int) *DefaultRestrictionManager { + if maxValueLength == 0 { + maxValueLength = defaultMaxValueLength + } + return &DefaultRestrictionManager{ + defaultRestriction: &Restriction{keyAllowed: true, maxValueLength: maxValueLength}, + } +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *DefaultRestrictionManager) GetRestriction(service, key string) *Restriction { + return m.defaultRestriction +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager_test.go new file mode 100644 index 000000000..b91d866d5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager_test.go @@ -0,0 +1,29 @@ +// 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 baggage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var _ RestrictionManager = &DefaultRestrictionManager{} + +func TestDefaultRestrictionManager(t *testing.T) { + mgr := NewDefaultRestrictionManager(0) + restriction := mgr.GetRestriction("svc", "key") + assert.EqualValues(t, NewRestriction(true, 2048), restriction) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/spanlog/json.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/spanlog/json.go new file mode 100644 index 000000000..0e10b8a5a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/internal/spanlog/json.go @@ -0,0 +1,81 @@ +// 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 spanlog + +import ( + "encoding/json" + "fmt" + + "github.com/opentracing/opentracing-go/log" +) + +type fieldsAsMap map[string]string + +// MaterializeWithJSON converts log Fields into JSON string +// TODO refactor into pluggable materializer +func MaterializeWithJSON(logFields []log.Field) ([]byte, error) { + fields := fieldsAsMap(make(map[string]string, len(logFields))) + for _, field := range logFields { + field.Marshal(fields) + } + if event, ok := fields["event"]; ok && len(fields) == 1 { + return []byte(event), nil + } + return json.Marshal(fields) +} + +func (ml fieldsAsMap) EmitString(key, value string) { + ml[key] = value +} + +func (ml fieldsAsMap) EmitBool(key string, value bool) { + ml[key] = fmt.Sprintf("%t", value) +} + +func (ml fieldsAsMap) EmitInt(key string, value int) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt32(key string, value int32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt64(key string, value int64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint32(key string, value uint32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint64(key string, value uint64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitFloat32(key string, value float32) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitFloat64(key string, value float64) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitObject(key string, value interface{}) { + ml[key] = fmt.Sprintf("%+v", value) +} + +func (ml fieldsAsMap) EmitLazyLogger(value log.LazyLogger) { + value(ml) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/interop.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/interop.go new file mode 100644 index 000000000..8402d087c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/interop.go @@ -0,0 +1,55 @@ +// 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 jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// TODO this file should not be needed after TChannel PR. + +type formatKey int + +// SpanContextFormat is a constant used as OpenTracing Format. +// Requires *SpanContext as carrier. +// This format is intended for interop with TChannel or other Zipkin-like tracers. +const SpanContextFormat formatKey = iota + +type jaegerTraceContextPropagator struct { + tracer *Tracer +} + +func (p *jaegerTraceContextPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.CopyFrom(&ctx) + return nil +} + +func (p *jaegerTraceContextPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + ctx := new(SpanContext) + ctx.CopyFrom(carrier) + return *ctx, nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_tag.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_tag.go new file mode 100644 index 000000000..868b2a5b5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_tag.go @@ -0,0 +1,84 @@ +// 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 jaeger + +import ( + "fmt" + + "github.com/opentracing/opentracing-go/log" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type tags []*j.Tag + +// ConvertLogsToJaegerTags converts log Fields into jaeger tags. +func ConvertLogsToJaegerTags(logFields []log.Field) []*j.Tag { + fields := tags(make([]*j.Tag, 0, len(logFields))) + for _, field := range logFields { + field.Marshal(&fields) + } + return fields +} + +func (t *tags) EmitString(key, value string) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &value}) +} + +func (t *tags) EmitBool(key string, value bool) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_BOOL, VBool: &value}) +} + +func (t *tags) EmitInt(key string, value int) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt32(key string, value int32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt64(key string, value int64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &value}) +} + +func (t *tags) EmitUint32(key string, value uint32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitUint64(key string, value uint64) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitFloat32(key string, value float32) { + vDouble := float64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &vDouble}) +} + +func (t *tags) EmitFloat64(key string, value float64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &value}) +} + +func (t *tags) EmitObject(key string, value interface{}) { + vStr := fmt.Sprintf("%+v", value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &vStr}) +} + +func (t *tags) EmitLazyLogger(value log.LazyLogger) { + value(t) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span.go new file mode 100644 index 000000000..338a34582 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span.go @@ -0,0 +1,172 @@ +// 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 jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// BuildJaegerThrift builds jaeger span based on internal span. +func BuildJaegerThrift(span *Span) *j.Span { + startTime := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime) + duration := span.duration.Nanoseconds() / int64(time.Microsecond) + jaegerSpan := &j.Span{ + TraceIdLow: int64(span.context.traceID.Low), + TraceIdHigh: int64(span.context.traceID.High), + SpanId: int64(span.context.spanID), + ParentSpanId: int64(span.context.parentID), + OperationName: span.operationName, + Flags: int32(span.context.flags), + StartTime: startTime, + Duration: duration, + Tags: buildTags(span.tags), + Logs: buildLogs(span.logs), + References: buildReferences(span.references), + } + return jaegerSpan +} + +// BuildJaegerProcessThrift creates a thrift Process type. +func BuildJaegerProcessThrift(span *Span) *j.Process { + return buildJaegerProcessThrift(span.tracer) +} + +func buildJaegerProcessThrift(tracer *Tracer) *j.Process { + process := &j.Process{ + ServiceName: tracer.serviceName, + Tags: buildTags(tracer.tags), + } + return process +} + +func buildTags(tags []Tag) []*j.Tag { + jTags := make([]*j.Tag, 0, len(tags)) + for _, tag := range tags { + jTag := buildTag(&tag) + jTags = append(jTags, jTag) + } + return jTags +} + +func buildLogs(logs []opentracing.LogRecord) []*j.Log { + jLogs := make([]*j.Log, 0, len(logs)) + for _, log := range logs { + jLog := &j.Log{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Fields: ConvertLogsToJaegerTags(log.Fields), + } + jLogs = append(jLogs, jLog) + } + return jLogs +} + +func buildTag(tag *Tag) *j.Tag { + jTag := &j.Tag{Key: tag.key} + switch value := tag.value.(type) { + case string: + vStr := truncateString(value) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + case []byte: + if len(value) > maxAnnotationLength { + value = value[:maxAnnotationLength] + } + jTag.VBinary = value + jTag.VType = j.TagType_BINARY + case int: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case float32: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case float64: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case bool: + vBool := value + jTag.VBool = &vBool + jTag.VType = j.TagType_BOOL + default: + vStr := truncateString(stringify(value)) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + } + return jTag +} + +func buildReferences(references []Reference) []*j.SpanRef { + retMe := make([]*j.SpanRef, 0, len(references)) + for _, ref := range references { + if ref.Type == opentracing.ChildOfRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_CHILD_OF)) + } else if ref.Type == opentracing.FollowsFromRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_FOLLOWS_FROM)) + } + } + return retMe +} + +func spanRef(ctx SpanContext, refType j.SpanRefType) *j.SpanRef { + return &j.SpanRef{ + RefType: refType, + TraceIdLow: int64(ctx.traceID.Low), + TraceIdHigh: int64(ctx.traceID.High), + SpanId: int64(ctx.spanID), + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span_test.go new file mode 100644 index 000000000..995d0ddba --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span_test.go @@ -0,0 +1,388 @@ +// 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 jaeger + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +var ( + someString = "str" + someBool = true + someLong = int64(123) + someDouble = float64(123) + someBinary = []byte("hello") + someSlice = []string{"a"} + someSliceString = "[a]" +) + +func TestBuildJaegerThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("sp1").(*Span) + ext.SpanKindRPCServer.Set(sp1) + ext.PeerService.Set(sp1, "svc") + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + ext.SpanKindRPCClient.Set(sp2) + sp2.Finish() + sp1.Finish() + + jaegerSpan1 := BuildJaegerThrift(sp1) + jaegerSpan2 := BuildJaegerThrift(sp2) + assert.Equal(t, "sp1", jaegerSpan1.OperationName) + assert.Equal(t, "sp2", jaegerSpan2.OperationName) + assert.EqualValues(t, 0, jaegerSpan1.ParentSpanId) + assert.Equal(t, jaegerSpan1.SpanId, jaegerSpan2.ParentSpanId) + assert.Len(t, jaegerSpan1.Tags, 4) + tag := findTag(jaegerSpan1, SamplerTypeTagKey) + assert.Equal(t, SamplerTypeConst, *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCServerEnum), *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.PeerService)) + assert.Equal(t, "svc", *tag.VStr) + assert.Empty(t, jaegerSpan1.References) + + assert.Len(t, jaegerSpan2.References, 1) + assert.Equal(t, j.SpanRefType_CHILD_OF, jaegerSpan2.References[0].RefType) + assert.EqualValues(t, jaegerSpan1.TraceIdLow, jaegerSpan2.References[0].TraceIdLow) + assert.EqualValues(t, jaegerSpan1.TraceIdHigh, jaegerSpan2.References[0].TraceIdHigh) + assert.EqualValues(t, jaegerSpan1.SpanId, jaegerSpan2.References[0].SpanId) + tag = findTag(jaegerSpan2, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCClientEnum), *tag.VStr) +} + +func TestBuildJaegerProcessThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("sp1").(*Span) + sp.Finish() + + process := BuildJaegerProcessThrift(sp) + assert.Equal(t, process.ServiceName, "DOOP") + require.Len(t, process.Tags, 3) + assert.NotNil(t, findJaegerTag("jaeger.version", process.Tags)) + assert.NotNil(t, findJaegerTag("hostname", process.Tags)) + assert.NotNil(t, findJaegerTag("ip", process.Tags)) +} + +func TestBuildLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + errString := "error" + + tests := []struct { + field log.Field + logFunc func(sp opentracing.Span) + expected []*j.Tag + expectedTimestamp int64 + disableSampling bool + }{ + {field: log.String("event", someString), expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.String("k", someString), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.Bool("k", someBool), expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}}, + {field: log.Int("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Float32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Float64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Error(errors.New(errString)), expected: []*j.Tag{{Key: "error", VType: j.TagType_STRING, VStr: &errString}}}, + {field: log.Object("k", someSlice), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}}, + { + field: log.Lazy(func(fv log.Encoder) { + fv.EmitBool("k", someBool) + }), + expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: []*j.Tag{ + {Key: "error", VType: j.TagType_STRING, VStr: getStringPtr("non-even keyValues len: 1")}, + {Key: "function", VType: j.TagType_STRING, VStr: getStringPtr("LogKV")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent(someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload(someString, "payload") + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString}) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString, Payload: "payload"}) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: []log.Field{log.String("event", someString)}, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + Payload: "payload", + }, + }, + }) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + field: log.String("event", someString), + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + }, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if test.field != (log.Field{}) { + sp.LogFields(test.field) + } + jaegerSpan := BuildJaegerThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(jaegerSpan.Logs), testName) + continue + } + assert.Equal(t, 1, len(jaegerSpan.Logs), testName) + compareTagSlices(t, test.expected, jaegerSpan.Logs[0].GetFields(), testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, jaegerSpan.Logs[0].Timestamp, testName) + } + } +} + +func TestBuildTags(t *testing.T) { + tests := []struct { + tag Tag + expected *j.Tag + }{ + {tag: Tag{key: "k", value: someString}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someString}}, + {tag: Tag{key: "k", value: int(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: float32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: float64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: someBool}, expected: &j.Tag{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + {tag: Tag{key: "k", value: someBinary}, expected: &j.Tag{Key: "k", VType: j.TagType_BINARY, VBinary: someBinary}}, + {tag: Tag{key: "k", value: someSlice}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + actual := buildTags([]Tag{test.tag}) + assert.Len(t, actual, 1) + compareTags(t, test.expected, actual[0], testName) + } +} + +func TestBuildReferences(t *testing.T) { + references := []Reference{ + {Type: opentracing.ChildOfRef, Context: SpanContext{traceID: TraceID{High: 1, Low: 1}, spanID: SpanID(1)}}, + {Type: opentracing.FollowsFromRef, Context: SpanContext{traceID: TraceID{High: 2, Low: 2}, spanID: SpanID(2)}}, + } + spanRefs := buildReferences(references) + assert.Len(t, spanRefs, 2) + assert.Equal(t, j.SpanRefType_CHILD_OF, spanRefs[0].RefType) + assert.EqualValues(t, 1, spanRefs[0].SpanId) + assert.EqualValues(t, 1, spanRefs[0].TraceIdHigh) + assert.EqualValues(t, 1, spanRefs[0].TraceIdLow) + + assert.Equal(t, j.SpanRefType_FOLLOWS_FROM, spanRefs[1].RefType) + assert.EqualValues(t, 2, spanRefs[1].SpanId) + assert.EqualValues(t, 2, spanRefs[1].TraceIdHigh) + assert.EqualValues(t, 2, spanRefs[1].TraceIdLow) +} + +func TestJaegerSpanBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + jaegerSpan := BuildJaegerThrift(sp) + require.Len(t, jaegerSpan.Logs, 1) + fields := jaegerSpan.Logs[0].Fields + require.Len(t, fields, 3) + assertJaegerTag(t, fields, "event", "baggage") + assertJaegerTag(t, fields, "key", "auth.token") + assertJaegerTag(t, fields, "value", "token") +} + +func assertJaegerTag(t *testing.T, tags []*j.Tag, key string, value string) { + tag := findJaegerTag(key, tags) + require.NotNil(t, tag) + assert.Equal(t, value, tag.GetVStr()) +} + +func getStringPtr(s string) *string { + return &s +} + +func findTag(span *j.Span, key string) *j.Tag { + for _, s := range span.Tags { + if s.Key == key { + return s + } + } + return nil +} + +func findJaegerTag(key string, tags []*j.Tag) *j.Tag { + for _, tag := range tags { + if tag.Key == key { + return tag + } + } + return nil +} + +func compareTagSlices(t *testing.T, expectedTags, actualTags []*j.Tag, testName string) { + assert.Equal(t, len(expectedTags), len(actualTags)) + for _, expectedTag := range expectedTags { + actualTag := findJaegerTag(expectedTag.Key, actualTags) + compareTags(t, expectedTag, actualTag, testName) + } +} + +func compareTags(t *testing.T, expected, actual *j.Tag, testName string) { + if expected == nil && actual == nil { + return + } + if expected == nil || actual == nil { + assert.Fail(t, "one of the tags is nil", testName) + return + } + assert.Equal(t, expected.Key, actual.Key, testName) + assert.Equal(t, expected.VType, actual.VType, testName) + switch expected.VType { + case j.TagType_STRING: + assert.Equal(t, *expected.VStr, *actual.VStr, testName) + case j.TagType_LONG: + assert.Equal(t, *expected.VLong, *actual.VLong, testName) + case j.TagType_DOUBLE: + assert.Equal(t, *expected.VDouble, *actual.VDouble, testName) + case j.TagType_BOOL: + assert.Equal(t, *expected.VBool, *actual.VBool, testName) + case j.TagType_BINARY: + assert.Equal(t, expected.VBinary, actual.VBinary, testName) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger.go new file mode 100644 index 000000000..3e2d98869 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger.go @@ -0,0 +1,51 @@ +// 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 log + +import "log" + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that delegates to default `log` package +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger_test.go new file mode 100644 index 000000000..a20580a65 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/logger_test.go @@ -0,0 +1,26 @@ +// 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 log + +import ( + "testing" +) + +func TestLogger(t *testing.T) { + for _, logger := range []Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field.go new file mode 100644 index 000000000..a7f6683a3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field.go @@ -0,0 +1,60 @@ +// 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 zap + +import ( + "context" + "fmt" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Trace creates a field that extracts tracing information from a context and +// includes it under the "trace" key. +// +// Because the opentracing APIs don't expose this information, the returned +// zap.Field is a no-op for contexts that don't contain a span or contain a +// non-Jaeger span. +func Trace(ctx context.Context) zapcore.Field { + if ctx == nil { + return zap.Skip() + } + return zap.Object("trace", trace{ctx}) +} + +type trace struct { + ctx context.Context +} + +func (t trace) MarshalLogObject(enc zapcore.ObjectEncoder) error { + span := opentracing.SpanFromContext(t.ctx) + if span == nil { + return nil + } + j, ok := span.Context().(jaeger.SpanContext) + if !ok { + return nil + } + if !j.IsValid() { + return fmt.Errorf("invalid span: %v", j.SpanID()) + } + enc.AddString("span", j.SpanID().String()) + enc.AddString("trace", j.TraceID().String()) + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field_test.go new file mode 100644 index 000000000..ee367b479 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/field_test.go @@ -0,0 +1,64 @@ +// 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 zap + +import ( + "context" + "testing" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestTraceField(t *testing.T) { + assert.Equal(t, zap.Skip(), Trace(nil), "Expected Trace of a nil context to be a no-op.") + + withTracedContext(func(ctx context.Context) { + enc := zapcore.NewMapObjectEncoder() + Trace(ctx).AddTo(enc) + + logged, ok := enc.Fields["trace"].(map[string]interface{}) + require.True(t, ok, "Expected trace to be a map.") + + // We could extract the span from the context and assert specific IDs, + // but that just copies the production code. Instead, just assert that + // the keys we expect are present. + keys := make(map[string]struct{}, len(logged)) + for k := range logged { + keys[k] = struct{}{} + } + assert.Equal( + t, + map[string]struct{}{"span": {}, "trace": {}}, + keys, + "Expected to log span and trace IDs.", + ) + }) +} + +func withTracedContext(f func(ctx context.Context)) { + tracer, closer := jaeger.NewTracer( + "serviceName", jaeger.NewConstSampler(true), jaeger.NewNullReporter(), + ) + defer closer.Close() + + ctx := opentracing.ContextWithSpan(context.Background(), tracer.StartSpan("test")) + f(ctx) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger.go new file mode 100644 index 000000000..f05e568ee --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger.go @@ -0,0 +1,39 @@ +// 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 zap + +import ( + "go.uber.org/zap" +) + +// Logger is an adapter from zap Logger to jaeger-lib Logger. +type Logger struct { + logger *zap.SugaredLogger +} + +// NewLogger creates a new Logger. +func NewLogger(logger *zap.Logger) *Logger { + return &Logger{logger: logger.Sugar()} +} + +// Error logs a message at error priority +func (l *Logger) Error(msg string) { + l.logger.Error(msg) +} + +// Infof logs a message at info priority +func (l *Logger) Infof(msg string, args ...interface{}) { + l.logger.Infof(msg, args...) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger_test.go new file mode 100644 index 000000000..325ea1cad --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/log/zap/logger_test.go @@ -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 zap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestLogger(t *testing.T) { + buf := &bytes.Buffer{} + encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "key"}) + logger := NewLogger(zap.New(zapcore.NewCore(encoder, zapcore.AddSync(buf), zapcore.InfoLevel))) + logger.Infof("Hi %s %d", "there", 5) + assert.Equal(t, buf.String(), "Hi there 5\n") + buf.Reset() + logger.Error("Bad wolf") + assert.Equal(t, buf.String(), "Bad wolf\n") +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/logger.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/logger.go new file mode 100644 index 000000000..d4f0b5019 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/logger.go @@ -0,0 +1,53 @@ +// 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 jaeger + +import "log" + +// NB This will be deprecated in 3.0.0, please use jaeger-client-go/log/logger instead. + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that delegates to default `log` package +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/logger_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/logger_test.go new file mode 100644 index 000000000..519c86a8a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/logger_test.go @@ -0,0 +1,40 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/uber/jaeger-client-go/log" +) + +func TestLogger(t *testing.T) { + for _, logger := range []Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} + +func TestCompatibility(t *testing.T) { + for _, logger := range []log.Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } + + for _, logger := range []Logger{log.StdLogger, log.NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics.go new file mode 100644 index 000000000..93f244788 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics.go @@ -0,0 +1,100 @@ +// 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 jaeger + +import ( + "github.com/uber/jaeger-lib/metrics" +) + +// 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"` + + // Number of traces started by this tracer as not sampled + TracesStartedNotSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=n"` + + // Number of externally started sampled traces this tracer joined + TracesJoinedSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=y"` + + // Number of externally started not-sampled traces this tracer joined + TracesJoinedNotSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=n"` + + // Number of sampled spans started by this tracer + SpansStarted metrics.Counter `metric:"spans" tags:"group=lifecycle,state=started"` + + // Number of sampled spans finished by this tracer + SpansFinished metrics.Counter `metric:"spans" tags:"group=lifecycle,state=finished"` + + // Number of sampled spans started by this tracer + SpansSampled metrics.Counter `metric:"spans" tags:"group=sampling,sampled=y"` + + // Number of not-sampled spans started by this tracer + SpansNotSampled metrics.Counter `metric:"spans" tags:"group=sampling,sampled=n"` + + // Number of errors decoding tracing context + DecodingErrors metrics.Counter `metric:"decoding-errors"` + + // Number of spans successfully reported + ReporterSuccess metrics.Counter `metric:"reporter-spans" tags:"state=success"` + + // Number of spans in failed attempts to report + ReporterFailure metrics.Counter `metric:"reporter-spans" tags:"state=failure"` + + // Number of spans dropped due to internal queue overflow + ReporterDropped metrics.Counter `metric:"reporter-spans" tags:"state=dropped"` + + // Current number of spans in the reporter queue + ReporterQueueLength metrics.Gauge `metric:"reporter-queue"` + + // Number of times the Sampler succeeded to retrieve sampling strategy + SamplerRetrieved metrics.Counter `metric:"sampler" tags:"state=retrieved"` + + // Number of times the Sampler succeeded to retrieve and update sampling strategy + SamplerUpdated metrics.Counter `metric:"sampler" tags:"state=updated"` + + // Number of times the Sampler failed to update sampling strategy + SamplerUpdateFailure metrics.Counter `metric:"sampler" tags:"state=failure,phase=updating"` + + // Number of times the Sampler failed to retrieve sampling strategy + SamplerQueryFailure metrics.Counter `metric:"sampler" tags:"state=failure,phase=query"` + + // Number of times baggage was successfully written or updated on spans. + BaggageUpdateSuccess metrics.Counter `metric:"baggage-update" tags:"result=ok"` + + // Number of times baggage failed to write or update on spans. + BaggageUpdateFailure metrics.Counter `metric:"baggage-update" tags:"result=err"` + + // Number of times baggage was truncated as per baggage restrictions. + BaggageTruncate metrics.Counter `metric:"baggage-truncate"` + + // Number of times baggage restrictions were successfully updated. + BaggageRestrictionsUpdateSuccess metrics.Counter `metric:"baggage-restrictions-update" tags:"result=ok"` + + // Number of times baggage restrictions failed to update. + BaggageRestrictionsUpdateFailure metrics.Counter `metric:"baggage-restrictions-update" tags:"result=err"` +} + +// NewMetrics creates a new Metrics struct and initializes it. +func NewMetrics(factory metrics.Factory, globalTags map[string]string) *Metrics { + m := &Metrics{} + metrics.Init(m, factory.Namespace("jaeger", nil), globalTags) + return m +} + +// NewNullMetrics creates a new Metrics struct that won't report any metrics. +func NewNullMetrics() *Metrics { + return NewMetrics(metrics.NullFactory, nil) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics_test.go new file mode 100644 index 000000000..1a0274eb0 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/metrics_test.go @@ -0,0 +1,50 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func TestNewMetrics(t *testing.T) { + tags := map[string]string{"lib": "jaeger"} + + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, tags) + + require.NotNil(t, m.SpansSampled, "counter not initialized") + require.NotNil(t, m.ReporterQueueLength, "gauge not initialized") + + m.SpansSampled.Inc(1) + m.ReporterQueueLength.Update(11) + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.spans", + Tags: map[string]string{"group": "sampling", "lib": "jaeger", "sampled": "y"}, + Value: 1, + }, + ) + testutils.AssertGaugeMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.reporter-queue", + Tags: map[string]string{"lib": "jaeger"}, + Value: 11, + }, + ) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/observer.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/observer.go new file mode 100644 index 000000000..7bbd02889 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/observer.go @@ -0,0 +1,88 @@ +// 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 jaeger + +import opentracing "github.com/opentracing/opentracing-go" + +// Observer can be registered with the Tracer to receive notifications about +// new Spans. +// +// Deprecated: use jaeger.ContribObserver instead. +type Observer interface { + OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver +} + +// SpanObserver is created by the Observer and receives notifications about +// other Span events. +// +// Deprecated: use jaeger.ContribSpanObserver instead. +type SpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// compositeObserver is a dispatcher to other observers +type compositeObserver struct { + observers []ContribObserver +} + +// compositeSpanObserver is a dispatcher to other span observers +type compositeSpanObserver struct { + observers []ContribSpanObserver +} + +// noopSpanObserver is used when there are no observers registered +// on the Tracer or none of them returns span observers from OnStartSpan. +var noopSpanObserver = &compositeSpanObserver{} + +func (o *compositeObserver) append(contribObserver ContribObserver) { + o.observers = append(o.observers, contribObserver) +} + +func (o *compositeObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) ContribSpanObserver { + var spanObservers []ContribSpanObserver + for _, obs := range o.observers { + spanObs, ok := obs.OnStartSpan(sp, operationName, options) + if ok { + if spanObservers == nil { + spanObservers = make([]ContribSpanObserver, 0, len(o.observers)) + } + spanObservers = append(spanObservers, spanObs) + } + } + if len(spanObservers) == 0 { + return noopSpanObserver + } + return &compositeSpanObserver{observers: spanObservers} +} + +func (o *compositeSpanObserver) OnSetOperationName(operationName string) { + for _, obs := range o.observers { + obs.OnSetOperationName(operationName) + } +} + +func (o *compositeSpanObserver) OnSetTag(key string, value interface{}) { + for _, obs := range o.observers { + obs.OnSetTag(key, value) + } +} + +func (o *compositeSpanObserver) OnFinish(options opentracing.FinishOptions) { + for _, obs := range o.observers { + obs.OnFinish(options) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/observer_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/observer_test.go new file mode 100644 index 000000000..84c8d0b9d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/observer_test.go @@ -0,0 +1,109 @@ +// 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 jaeger + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestEmptyObserver(t *testing.T) { + tracer, closer := NewTracer("test", NewConstSampler(true), NewInMemoryReporter()) + defer closer.Close() + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + s.Finish() + assert.Equal(t, s.(*Span).observer, noopSpanObserver) +} + +func TestObservers(t *testing.T) { + tracer, closer := NewTracer( + "test", + NewConstSampler(true), + NewInMemoryReporter(), + TracerOptions.Observer(testObserver{}), + TracerOptions.Observer(testObserver{}), + ) + defer closer.Close() + + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + + forEachObs := func(f func(so *testSpanObserver)) { + observers := s.(*Span).observer.(*compositeSpanObserver).observers + assert.Len(t, observers, 2) + for _, so := range observers { + f(so.(*testSpanObserver)) + } + } + + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + }, + }, *so) + }) + + s.SetOperationName("test2") + s.SetTag("bender", "rodriguez") + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test2", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + "bender": "rodriguez", + }, + }, *so) + }) + + s.Finish() + forEachObs(func(so *testSpanObserver) { + assert.True(t, so.finished) + }) +} + +type testObserver struct{} + +type testSpanObserver struct { + operationName string + tags map[string]interface{} + finished bool +} + +func (o testObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver { + tags := make(map[string]interface{}) + for k, v := range options.Tags { + tags[k] = v + } + return &testSpanObserver{ + operationName: operationName, + tags: tags, + } +} + +func (o *testSpanObserver) OnSetOperationName(operationName string) { + o.operationName = operationName +} + +func (o *testSpanObserver) OnSetTag(key string, value interface{}) { + o.tags[key] = value +} + +func (o *testSpanObserver) OnFinish(options opentracing.FinishOptions) { + o.finished = true +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation.go new file mode 100644 index 000000000..abca67a3c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation.go @@ -0,0 +1,300 @@ +// 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 jaeger + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "net/url" + "strings" + "sync" + + opentracing "github.com/opentracing/opentracing-go" +) + +// Injector is responsible for injecting SpanContext instances in a manner suitable +// for propagation via a format-specific "carrier" object. Typically the +// injection will take place across an RPC boundary, but message queues and +// other IPC mechanisms are also reasonable places to use an Injector. +type Injector interface { + // Inject takes `SpanContext` and injects it into `carrier`. The actual type + // of `carrier` depends on the `format` passed to `Tracer.Inject()`. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if injection fails. + Inject(ctx SpanContext, carrier interface{}) error +} + +// Extractor is responsible for extracting SpanContext instances from a +// format-specific "carrier" object. Typically the extraction will take place +// on the server side of an RPC boundary, but message queues and other IPC +// mechanisms are also reasonable places to use an Extractor. +type Extractor interface { + // Extract decodes a SpanContext instance from the given `carrier`, + // or (nil, opentracing.ErrSpanContextNotFound) if no context could + // be found in the `carrier`. + Extract(carrier interface{}) (SpanContext, error) +} + +type textMapPropagator struct { + headerKeys *HeadersConfig + metrics Metrics + encodeValue func(string) string + decodeValue func(string) string +} + +func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return val + }, + decodeValue: func(val string) string { + return val + }, + } +} + +func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return url.QueryEscape(val) + }, + decodeValue: func(val string) string { + // ignore decoding errors, cannot do anything about them + if v, err := url.QueryUnescape(val); err == nil { + return v + } + return val + }, + } +} + +type binaryPropagator struct { + tracer *Tracer + buffers sync.Pool +} + +func newBinaryPropagator(tracer *Tracer) *binaryPropagator { + return &binaryPropagator{ + tracer: tracer, + buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}, + } +} + +func (p *textMapPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Do not encode the string with trace context to avoid accidental double-encoding + // if people are using opentracing < 0.10.0. Our colon-separated representation + // of the trace context is already safe for HTTP headers. + textMapWriter.Set(p.headerKeys.TraceContextHeaderName, sc.String()) + for k, v := range sc.baggage { + safeKey := p.addBaggageKeyPrefix(k) + safeVal := p.encodeValue(v) + textMapWriter.Set(safeKey, safeVal) + } + return nil +} + +func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + var baggage map[string]string + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + if key == p.headerKeys.TraceContextHeaderName { + var err error + safeVal := p.decodeValue(value) + if ctx, err = ContextFromString(safeVal); err != nil { + return err + } + } else if key == p.headerKeys.JaegerDebugHeader { + ctx.debugID = p.decodeValue(value) + } else if key == p.headerKeys.JaegerBaggageHeader { + if baggage == nil { + baggage = make(map[string]string) + } + for k, v := range p.parseCommaSeparatedMap(value) { + baggage[k] = v + } + } else if strings.HasPrefix(key, p.headerKeys.TraceBaggageHeaderPrefix) { + if baggage == nil { + baggage = make(map[string]string) + } + safeKey := p.removeBaggageKeyPrefix(key) + safeVal := p.decodeValue(value) + baggage[safeKey] = safeVal + } + return nil + }) + if err != nil { + p.metrics.DecodingErrors.Inc(1) + return emptyContext, err + } + if !ctx.traceID.IsValid() && ctx.debugID == "" && len(baggage) == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + ctx.baggage = baggage + return ctx, nil +} + +func (p *binaryPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(io.Writer) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Handle the tracer context + if err := binary.Write(carrier, binary.BigEndian, sc.traceID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.spanID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.parentID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.flags); err != nil { + return err + } + + // Handle the baggage items + if err := binary.Write(carrier, binary.BigEndian, int32(len(sc.baggage))); err != nil { + return err + } + for k, v := range sc.baggage { + if err := binary.Write(carrier, binary.BigEndian, int32(len(k))); err != nil { + return err + } + io.WriteString(carrier, k) + if err := binary.Write(carrier, binary.BigEndian, int32(len(v))); err != nil { + return err + } + io.WriteString(carrier, v) + } + + return nil +} + +func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(io.Reader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + + if err := binary.Read(carrier, binary.BigEndian, &ctx.traceID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.spanID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.parentID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.flags); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + + // Handle the baggage items + var numBaggage int32 + if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if iNumBaggage := int(numBaggage); iNumBaggage > 0 { + ctx.baggage = make(map[string]string, iNumBaggage) + buf := p.buffers.Get().(*bytes.Buffer) + defer p.buffers.Put(buf) + + var keyLen, valLen int32 + for i := 0; i < iNumBaggage; i++ { + if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(keyLen)) + if n, err := io.CopyN(buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + key := buf.String() + + if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(valLen)) + if n, err := io.CopyN(buf, carrier, int64(valLen)); err != nil || int32(n) != valLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + ctx.baggage[key] = buf.String() + } + } + + return ctx, nil +} + +// Converts a comma separated key value pair list into a map +// e.g. key1=value1, key2=value2, key3 = value3 +// is converted to map[string]string { "key1" : "value1", +// "key2" : "value2", +// "key3" : "value3" } +func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]string { + baggage := make(map[string]string) + value, err := url.QueryUnescape(value) + if err != nil { + log.Printf("Unable to unescape %s, %v", value, err) + return baggage + } + for _, kvpair := range strings.Split(value, ",") { + kv := strings.Split(strings.TrimSpace(kvpair), "=") + if len(kv) == 2 { + baggage[kv[0]] = kv[1] + } else { + log.Printf("Malformed value passed in for %s", p.headerKeys.JaegerBaggageHeader) + } + } + return baggage +} + +// 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 { + // TODO encodeBaggageKeyAsHeader add caching and escaping + return fmt.Sprintf("%v%v", p.headerKeys.TraceBaggageHeaderPrefix, key) +} + +func (p *textMapPropagator) removeBaggageKeyPrefix(key string) string { + // TODO decodeBaggageHeaderKey add caching and escaping + return key[len(p.headerKeys.TraceBaggageHeaderPrefix):] +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation_test.go new file mode 100644 index 000000000..0cc627534 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/propagation_test.go @@ -0,0 +1,268 @@ +// 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 jaeger + +import ( + "bytes" + "net/http" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func initMetrics() (*metrics.LocalFactory, *Metrics) { + factory := metrics.NewLocalFactory(0) + return factory, NewMetrics(factory, nil) +} + +func TestSpanPropagator(t *testing.T) { + const op = "test" + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics), TracerOptions.ZipkinSharedRPCSpan(true)) + + mapc := opentracing.TextMapCarrier(make(map[string]string)) + httpc := opentracing.HTTPHeadersCarrier(http.Header{}) + tests := []struct { + format, carrier, formatName interface{} + }{ + {SpanContextFormat, new(SpanContext), "TraceContextFormat"}, + {opentracing.Binary, new(bytes.Buffer), "Binary"}, + {opentracing.TextMap, mapc, "TextMap"}, + {opentracing.HTTPHeaders, httpc, "HTTPHeaders"}, + } + + sp := tracer.StartSpan(op) + sp.SetTag("x", "y") // to avoid later comparing nil vs. [] + sp.SetBaggageItem("foo", "bar") + for _, test := range tests { + // starting normal child to extract its serialized context + child := tracer.StartSpan(op, opentracing.ChildOf(sp.Context())) + err := tracer.Inject(child.Context(), test.format, test.carrier) + assert.NoError(t, err) + // Note: we're not finishing the above span + childCtx, err := tracer.Extract(test.format, test.carrier) + assert.NoError(t, err) + child = tracer.StartSpan(test.formatName.(string), ext.RPCServerOption(childCtx)) + child.SetTag("x", "y") // to avoid later comparing nil vs. [] + child.Finish() + } + sp.Finish() + closer.Close() + + otSpans := reporter.GetSpans() + require.Equal(t, len(tests)+1, len(otSpans), "unexpected number of spans reporter") + + spans := make([]*Span, len(otSpans)) + for i, s := range otSpans { + spans[i] = s.(*Span) + } + + // The last span is the original one. + exp, spans := spans[len(spans)-1], spans[:len(spans)-1] + exp.duration = time.Duration(123) + exp.startTime = time.Time{}.Add(1) + require.Len(t, exp.logs, 1) // The parent span should have baggage logs + fields := exp.logs[0].Fields + require.Len(t, fields, 3) + require.Equal(t, "event", fields[0].Key()) + require.Equal(t, "baggage", fields[0].Value().(string)) + require.Equal(t, "key", fields[1].Key()) + require.Equal(t, "foo", fields[1].Value().(string)) + require.Equal(t, "value", fields[2].Key()) + require.Equal(t, "bar", fields[2].Value().(string)) + + if exp.context.ParentID() != 0 { + t.Fatalf("Root span's ParentID %d is not 0", exp.context.ParentID()) + } + + expTags := exp.tags[2:] // skip two sampler.xxx tags + for i, sp := range spans { + formatName := sp.operationName + if a, e := sp.context.ParentID(), exp.context.SpanID(); a != e { + t.Fatalf("%d: ParentID %d does not match expectation %d", i, a, e) + } else { + // Prepare for comparison. + sp.context.spanID, sp.context.parentID = exp.context.SpanID(), 0 + sp.duration, sp.startTime = exp.duration, exp.startTime + } + assert.Equal(t, exp.context, sp.context, formatName) + assert.Equal(t, "span.kind", sp.tags[0].key) + assert.Equal(t, expTags, sp.tags[1:] /*skip span.kind tag*/, formatName) + assert.Empty(t, sp.logs, formatName) + // Override collections to avoid tripping comparison on different pointers + sp.context = exp.context + sp.tags = exp.tags + sp.logs = exp.logs + sp.operationName = op + sp.references = exp.references + // Compare the rest of the fields + assert.Equal(t, exp, sp, formatName) + } + + testutils.AssertCounterMetrics(t, metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 1 + 2*len(tests)}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 1 + 2*len(tests)}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 1 + len(tests)}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "joined", "sampled": "y"}, Value: len(tests)}, + }...) +} + +func TestSpanIntegrityAfterSerialize(t *testing.T) { + serializedString := "f6c385a2c57ed8d7:b04a90b7723bdc:76c385a2c57ed8d7:1" + + context, err := ContextFromString(serializedString) + require.NoError(t, err) + require.True(t, context.traceID.Low > (uint64(1)<<63)) + require.True(t, int64(context.traceID.Low) < 0) + + newSerializedString := context.String() + require.Equal(t, serializedString, newSerializedString) +} + +func TestDecodingError(t *testing.T) { + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics)) + defer closer.Close() + + badHeader := "x.x.x.x" + httpHeader := http.Header{} + httpHeader.Add(TraceContextHeaderName, badHeader) + tmc := opentracing.HTTPHeadersCarrier(httpHeader) + _, err := tracer.Extract(opentracing.HTTPHeaders, tmc) + assert.Error(t, err) + + testutils.AssertCounterMetrics(t, metricsFactory, testutils.ExpectedMetric{Name: "jaeger.decoding-errors", Value: 1}) +} + +func TestBaggagePropagationHTTP(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + assert.Equal(t, "12345", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + sp1.SetBaggageItem("Some_Key", "98:765") + assert.Equal(t, "98:765", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + + h := http.Header{} + h.Add("header1", "value1") // make sure this does not get unmarshalled as baggage + err := tracer.Inject(sp1.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + // check that colon : was encoded as %3A + assert.Equal(t, "98%3A765", h.Get(TraceBaggageHeaderPrefix+"Some_Key"), "headers: %+v", h) + + sp2, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.Equal(t, map[string]string{"some_key": "98:765"}, sp2.(SpanContext).baggage) +} + +func TestJaegerBaggageHeader(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + h.Add(JaegerBaggageHeader, "key1=value1, key 2=value two") + + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + + sp := tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + + assert.Equal(t, "value1", sp.BaggageItem("key1")) + assert.Equal(t, "value two", sp.BaggageItem("key 2")) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) +} + +func TestParseCommaSeperatedMap(t *testing.T) { + var testcases = []struct { + in string + out map[string]string + }{ + {"hobbit=Bilbo Baggins", map[string]string{"hobbit": "Bilbo Baggins"}}, + {"hobbit=Bilbo Baggins, dwarf= Thrain", map[string]string{"hobbit": "Bilbo Baggins", "dwarf": " Thrain"}}, + {"kevin spacey=actor", map[string]string{"kevin spacey": "actor"}}, + {"kevin%20spacey=se7en%3Aactor", map[string]string{"kevin spacey": "se7en:actor"}}, + {"key1=, key2=", map[string]string{"key1": "", "key2": ""}}, + {"malformed", map[string]string{}}, + {"malformed, string", map[string]string{}}, + {"another malformed string", map[string]string{}}, + } + + for _, testcase := range testcases { + m := (&textMapPropagator{ + headerKeys: getDefaultHeadersConfig(), + }).parseCommaSeparatedMap(testcase.in) + assert.Equal(t, testcase.out, m) + } +} + +func TestDebugCorrelationID(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + h.Add(JaegerDebugHeader, "value1") + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.EqualValues(t, 0, ctx.(SpanContext).parentID) + assert.EqualValues(t, "value1", ctx.(SpanContext).debugID) + sp := tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + assert.EqualValues(t, 0, sp.context.parentID) + assert.True(t, sp.context.traceID.IsValid()) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + tagFound := false + for _, tag := range sp.tags { + if tag.key == JaegerDebugHeader { + assert.Equal(t, "value1", tag.value) + tagFound = true + } + } + assert.True(t, tagFound) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/reference.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/reference.go new file mode 100644 index 000000000..5646e78bb --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/reference.go @@ -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 jaeger + +import "github.com/opentracing/opentracing-go" + +// Reference represents a causal reference to other Spans (via their SpanContext). +type Reference struct { + Type opentracing.SpanReferenceType + Context SpanContext +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter.go new file mode 100644 index 000000000..3acc4be7d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter.go @@ -0,0 +1,262 @@ +// 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 jaeger + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/log" +) + +// Reporter is called by the tracer when a span is completed to report the span to the tracing collector. +type Reporter interface { + // Report submits a new span to collectors, possibly asynchronously and/or with buffering. + Report(span *Span) + + // Close does a clean shutdown of the reporter, flushing any traces that may be buffered in memory. + Close() +} + +// ------------------------------ + +type nullReporter struct{} + +// NewNullReporter creates a no-op reporter that ignores all reported spans. +func NewNullReporter() Reporter { + return &nullReporter{} +} + +// Report implements Report() method of Reporter by doing nothing. +func (r *nullReporter) Report(span *Span) { + // no-op +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *nullReporter) Close() { + // no-op +} + +// ------------------------------ + +type loggingReporter struct { + logger Logger +} + +// NewLoggingReporter creates a reporter that logs all reported spans to provided logger. +func NewLoggingReporter(logger Logger) Reporter { + return &loggingReporter{logger} +} + +// Report implements Report() method of Reporter by logging the span to the logger. +func (r *loggingReporter) Report(span *Span) { + r.logger.Infof("Reporting span %+v", span) +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *loggingReporter) Close() { + // no-op +} + +// ------------------------------ + +// InMemoryReporter is used for testing, and simply collects spans in memory. +type InMemoryReporter struct { + spans []opentracing.Span + lock sync.Mutex +} + +// NewInMemoryReporter creates a reporter that stores spans in memory. +// NOTE: the Tracer should be created with options.PoolSpans = false. +func NewInMemoryReporter() *InMemoryReporter { + return &InMemoryReporter{ + spans: make([]opentracing.Span, 0, 10), + } +} + +// Report implements Report() method of Reporter by storing the span in the buffer. +func (r *InMemoryReporter) Report(span *Span) { + r.lock.Lock() + r.spans = append(r.spans, span) + r.lock.Unlock() +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *InMemoryReporter) Close() { + // no-op +} + +// SpansSubmitted returns the number of spans accumulated in the buffer. +func (r *InMemoryReporter) SpansSubmitted() int { + r.lock.Lock() + defer r.lock.Unlock() + return len(r.spans) +} + +// GetSpans returns accumulated spans as a copy of the buffer. +func (r *InMemoryReporter) GetSpans() []opentracing.Span { + r.lock.Lock() + defer r.lock.Unlock() + copied := make([]opentracing.Span, len(r.spans)) + copy(copied, r.spans) + return copied +} + +// Reset clears all accumulated spans. +func (r *InMemoryReporter) Reset() { + r.lock.Lock() + defer r.lock.Unlock() + r.spans = nil +} + +// ------------------------------ + +type compositeReporter struct { + reporters []Reporter +} + +// NewCompositeReporter creates a reporter that ignores all reported spans. +func NewCompositeReporter(reporters ...Reporter) Reporter { + return &compositeReporter{reporters: reporters} +} + +// Report implements Report() method of Reporter by delegating to each underlying reporter. +func (r *compositeReporter) Report(span *Span) { + for _, reporter := range r.reporters { + reporter.Report(span) + } +} + +// Close implements Close() method of Reporter by closing each underlying reporter. +func (r *compositeReporter) Close() { + for _, reporter := range r.reporters { + reporter.Close() + } +} + +// ------------------------------ + +const ( + defaultQueueSize = 100 + defaultBufferFlushInterval = 10 * time.Second +) + +type remoteReporter struct { + // must be first in the struct because `sync/atomic` expects 64-bit alignment. + // Cf. https://github.com/uber/jaeger-client-go/issues/155, https://goo.gl/zW7dgq + queueLength int64 + + reporterOptions + sender Transport + queue chan *Span + queueDrained sync.WaitGroup + flushSignal chan *sync.WaitGroup +} + +// NewRemoteReporter creates a new reporter that sends spans out of process by means of Sender +func NewRemoteReporter(sender Transport, opts ...ReporterOption) Reporter { + options := reporterOptions{} + for _, option := range opts { + option(&options) + } + if options.bufferFlushInterval <= 0 { + options.bufferFlushInterval = defaultBufferFlushInterval + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.queueSize <= 0 { + options.queueSize = defaultQueueSize + } + reporter := &remoteReporter{ + reporterOptions: options, + sender: sender, + flushSignal: make(chan *sync.WaitGroup), + queue: make(chan *Span, options.queueSize), + } + go reporter.processQueue() + return reporter +} + +// Report implements Report() method of Reporter. +// It passes the span to a background go-routine for submission to Jaeger. +func (r *remoteReporter) Report(span *Span) { + select { + case r.queue <- span: + atomic.AddInt64(&r.queueLength, 1) + default: + r.metrics.ReporterDropped.Inc(1) + } +} + +// Close implements Close() method of Reporter by waiting for the queue to be drained. +func (r *remoteReporter) Close() { + r.queueDrained.Add(1) + close(r.queue) + r.queueDrained.Wait() + r.sender.Close() +} + +// processQueue reads spans from the queue, converts them to Thrift, and stores them in an internal buffer. +// When the buffer length reaches batchSize, it is flushed by submitting the accumulated spans to Jaeger. +// Buffer also gets flushed automatically every batchFlushInterval seconds, just in case the tracer stopped +// reporting new spans. +func (r *remoteReporter) processQueue() { + timer := time.NewTicker(r.bufferFlushInterval) + for { + select { + case span, ok := <-r.queue: + if ok { + atomic.AddInt64(&r.queueLength, -1) + if flushed, err := r.sender.Append(span); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(fmt.Sprintf("error reporting span %q: %s", span.OperationName(), err.Error())) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + // to reduce the number of gauge stats, we only emit queue length on flush + r.metrics.ReporterQueueLength.Update(atomic.LoadInt64(&r.queueLength)) + } + } else { + // queue closed + timer.Stop() + r.flush() + r.queueDrained.Done() + return + } + case <-timer.C: + r.flush() + case wg := <-r.flushSignal: // for testing + r.flush() + wg.Done() + } + } +} + +// flush causes the Sender to flush its accumulated spans and clear the buffer +func (r *remoteReporter) flush() { + if flushed, err := r.sender.Flush(); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(err.Error()) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_options.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_options.go new file mode 100644 index 000000000..65012d701 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_options.go @@ -0,0 +1,69 @@ +// 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 jaeger + +import ( + "time" +) + +// ReporterOption is a function that sets some option on the reporter. +type ReporterOption func(c *reporterOptions) + +// ReporterOptions is a factory for all available ReporterOption's +var ReporterOptions reporterOptions + +// reporterOptions control behavior of the reporter. +type reporterOptions struct { + // queueSize is the size of internal queue where reported spans are stored before they are processed in the background + queueSize int + // bufferFlushInterval is how often the buffer is force-flushed, even if it's not full + bufferFlushInterval time.Duration + // logger is used to log errors of span submissions + logger Logger + // metrics is used to record runtime stats + metrics *Metrics +} + +// QueueSize creates a ReporterOption that sets the size of the internal queue where +// spans are stored before they are processed. +func (reporterOptions) QueueSize(queueSize int) ReporterOption { + return func(r *reporterOptions) { + r.queueSize = queueSize + } +} + +// Metrics creates a ReporterOption that initializes Metrics in the reporter, +// which is used to record runtime statistics. +func (reporterOptions) Metrics(metrics *Metrics) ReporterOption { + return func(r *reporterOptions) { + r.metrics = metrics + } +} + +// BufferFlushInterval creates a ReporterOption that sets how often the queue +// is force-flushed. +func (reporterOptions) BufferFlushInterval(bufferFlushInterval time.Duration) ReporterOption { + return func(r *reporterOptions) { + r.bufferFlushInterval = bufferFlushInterval + } +} + +// Logger creates a ReporterOption that initializes the logger used to log +// errors of span submissions. +func (reporterOptions) Logger(logger Logger) ReporterOption { + return func(r *reporterOptions) { + r.logger = logger + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_test.go new file mode 100644 index 000000000..77ce0f86b --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/reporter_test.go @@ -0,0 +1,272 @@ +// 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 jaeger + +import ( + "io" + "sort" + "strings" + "sync" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/testutils" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type reporterSuite struct { + suite.Suite + tracer opentracing.Tracer + closer io.Closer + serviceName string + reporter *remoteReporter + collector *fakeSender + metricsFactory *metrics.LocalFactory +} + +func (s *reporterSuite) SetupTest() { + s.metricsFactory = metrics.NewLocalFactory(0) + metrics := NewMetrics(s.metricsFactory, nil) + s.serviceName = "DOOP" + s.collector = &fakeSender{} + s.reporter = NewRemoteReporter( + s.collector, ReporterOptions.Metrics(metrics), + ).(*remoteReporter) + + s.tracer, s.closer = NewTracer( + "reporter-test-service", + NewConstSampler(true), + s.reporter, + TracerOptions.Metrics(metrics)) + s.NotNil(s.tracer) +} + +func (s *reporterSuite) TearDownTest() { + s.closer.Close() + s.tracer = nil + s.reporter = nil + s.collector = nil +} + +func TestReporter(t *testing.T) { + suite.Run(t, new(reporterSuite)) +} + +func (s *reporterSuite) flushReporter() { + // Wait for reporter queue to add spans to buffer. We could've called reporter.Close(), + // but then it fails when the test suite calls close on it again (via tracer's Closer). + time.Sleep(5 * time.Millisecond) + + var wg sync.WaitGroup + wg.Add(1) + s.reporter.flushSignal <- &wg + wg.Wait() +} + +func (s *reporterSuite) TestRootSpanTags() { + s.metricsFactory.Clear() + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, s.serviceName) + sp.Finish() + s.flushReporter() + s.Equal(1, len(s.collector.Spans())) + span := s.collector.Spans()[0] + s.Len(span.tags, 4) + s.EqualValues("server", span.tags[2].value, "span.kind should be server") + + mTestutils.AssertCounterMetrics(s.T(), s.metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter-spans", + Tags: map[string]string{"state": "success"}, + Value: 1, + }, + ) +} + +func (s *reporterSuite) TestClientSpan() { + s.metricsFactory.Clear() + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, s.serviceName) + sp2 := s.tracer.StartSpan("get_last_name", opentracing.ChildOf(sp.Context())) + ext.SpanKindRPCClient.Set(sp2) + ext.PeerService.Set(sp2, s.serviceName) + sp2.Finish() + sp.Finish() + s.flushReporter() + s.Equal(2, len(s.collector.Spans())) + span := s.collector.Spans()[0] // child span is reported first + s.EqualValues(span.context.spanID, sp2.(*Span).context.spanID) + s.Len(span.tags, 2) + s.EqualValues("client", span.tags[0].value, "span.kind should be client") + + mTestutils.AssertCounterMetrics(s.T(), s.metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter-spans", + Tags: map[string]string{"state": "success"}, + Value: 2, + }, + ) +} + +func (s *reporterSuite) TestTagsAndEvents() { + sp := s.tracer.StartSpan("get_name") + sp.LogEvent("hello") + sp.LogEvent(strings.Repeat("long event ", 30)) + expected := []string{"long", "ping", "awake", "awake", "one", "two", "three", "bite me", + SamplerParamTagKey, SamplerTypeTagKey, "does not compute"} + sp.SetTag("long", strings.Repeat("x", 300)) + sp.SetTag("ping", "pong") + sp.SetTag("awake", true) + sp.SetTag("awake", false) + sp.SetTag("one", 1) + sp.SetTag("two", int32(2)) + sp.SetTag("three", int64(3)) + sp.SetTag("bite me", []byte{1}) + sp.SetTag("does not compute", sp) // should be converted to string + sp.Finish() + s.flushReporter() + s.Equal(1, len(s.collector.Spans())) + span := s.collector.Spans()[0] + s.Equal(2, len(span.logs), "expecting two logs") + s.Equal(len(expected), len(span.tags), + "expecting %d tags", len(expected)) + tags := []string{} + for _, tag := range span.tags { + tags = append(tags, string(tag.key)) + } + sort.Strings(expected) + sort.Strings(tags) + s.Equal(expected, tags, "expecting %d tags", len(expected)) + + s.NotNil(findDomainLog(span, "hello"), "expecting 'hello' log: %+v", span.logs) +} + +func TestUDPReporter(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + testRemoteReporter(t, + func(m *Metrics) (Transport, error) { + return NewUDPTransport(agent.SpanServerAddr(), 0) + }, + func() []*j.Batch { + return agent.GetJaegerBatches() + }) +} + +func testRemoteReporter( + t *testing.T, + factory func(m *Metrics) (Transport, error), + getBatches func() []*j.Batch, +) { + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + sender, err := factory(metrics) + require.NoError(t, err) + reporter := NewRemoteReporter(sender, ReporterOptions.Metrics(metrics)).(*remoteReporter) + + tracer, closer := NewTracer( + "reporter-test-service", + NewConstSampler(true), + reporter, + TracerOptions.Metrics(metrics)) + + span := tracer.StartSpan("leela") + ext.SpanKindRPCClient.Set(span) + ext.PeerService.Set(span, "downstream") + span.Finish() + closer.Close() // close the tracer, which also closes and flushes the reporter + // however, in case of UDP reporter it's fire and forget, so we need to wait a bit + time.Sleep(5 * time.Millisecond) + + batches := getBatches() + require.Equal(t, 1, len(batches)) + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, "leela", batches[0].Spans[0].OperationName) + assert.Equal(t, "reporter-test-service", batches[0].Process.ServiceName) + tag := findJaegerTag("peer.service", batches[0].Spans[0].Tags) + assert.NotNil(t, tag) + assert.Equal(t, "downstream", *tag.VStr) + + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.reporter-spans", Tags: map[string]string{"state": "success"}, Value: 1}, + {Name: "jaeger.reporter-spans", Tags: map[string]string{"state": "failure"}, Value: 0}, + }...) +} + +func (s *reporterSuite) TestMemoryReporterReport() { + sp := s.tracer.StartSpan("leela") + ext.PeerService.Set(sp, s.serviceName) + reporter := NewInMemoryReporter() + reporter.Report(sp.(*Span)) + s.Equal(1, reporter.SpansSubmitted(), "expected number of spans submitted") + reporter.Close() +} + +type fakeSender struct { + spans []*Span + mutex sync.Mutex +} + +func (s *fakeSender) Append(span *Span) (int, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.spans = append(s.spans, span) + return 1, nil +} + +func (s *fakeSender) Flush() (int, error) { + return 0, nil +} + +func (s *fakeSender) Close() error { return nil } + +func (s *fakeSender) Spans() []*Span { + s.mutex.Lock() + defer s.mutex.Unlock() + res := make([]*Span, len(s.spans)) + copy(res, s.spans) + return res +} + +func findDomainLog(span *Span, key string) *opentracing.LogRecord { + for _, log := range span.logs { + if log.Fields[0].Value().(string) == key { + return &log + } + } + return nil +} + +func findDomainTag(span *Span, key string) *Tag { + for _, tag := range span.tags { + if tag.key == key { + return &tag + } + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/README.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/README.md new file mode 100644 index 000000000..879948e9c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/README.md @@ -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). diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/doc.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/doc.go new file mode 100644 index 000000000..51aa11b35 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/doc.go @@ -0,0 +1,16 @@ +// 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 rpcmetrics implements an Observer that can be used to emit RPC metrics. +package rpcmetrics diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints.go new file mode 100644 index 000000000..30555243d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints.go @@ -0,0 +1,63 @@ +// 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 rpcmetrics + +import "sync" + +// normalizedEndpoints is a cache for endpointName -> safeName mappings. +type normalizedEndpoints struct { + names map[string]string + maxSize int + defaultName string + normalizer NameNormalizer + mux sync.RWMutex +} + +func newNormalizedEndpoints(maxSize int, normalizer NameNormalizer) *normalizedEndpoints { + return &normalizedEndpoints{ + maxSize: maxSize, + normalizer: normalizer, + names: make(map[string]string, maxSize), + } +} + +// normalize looks up the name in the cache, if not found it uses normalizer +// to convert the name to a safe name. If called with more than maxSize unique +// names it returns "" for all other names beyond those already cached. +func (n *normalizedEndpoints) normalize(name string) string { + n.mux.RLock() + norm, ok := n.names[name] + l := len(n.names) + n.mux.RUnlock() + if ok { + return norm + } + if l >= n.maxSize { + return "" + } + return n.normalizeWithLock(name) +} + +func (n *normalizedEndpoints) normalizeWithLock(name string) string { + norm := n.normalizer.Normalize(name) + n.mux.Lock() + defer n.mux.Unlock() + // cache may have grown while we were not holding the lock + if len(n.names) >= n.maxSize { + return "" + } + n.names[name] = norm + return norm +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints_test.go new file mode 100644 index 000000000..8a5b4e530 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints_test.go @@ -0,0 +1,43 @@ +// 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 rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalizedEndpoints(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + + assertLen := func(l int) { + n.mux.RLock() + defer n.mux.RUnlock() + assert.Len(t, n.names, l) + } + + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "one translation") + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "cache hit") + assertLen(1) + assert.Equal(t, "", n.normalize("xys"), "cache overflow") + assertLen(1) +} + +func TestNormalizedEndpointsDoubleLocking(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "fill out the cache") + assert.Equal(t, "", n.normalizeWithLock("xys"), "cache overflow") +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics.go new file mode 100644 index 000000000..ab8d74c29 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics.go @@ -0,0 +1,124 @@ +// 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 rpcmetrics + +import ( + "sync" + + "github.com/uber/jaeger-lib/metrics" +) + +const ( + otherEndpointsPlaceholder = "other" + endpointNameMetricTag = "endpoint" +) + +// Metrics is a collection of metrics for an endpoint describing +// throughput, success, errors, and performance. +type Metrics struct { + // RequestCountSuccess is a counter of the total number of successes. + RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"` + + // 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 metrics.Timer `metric:"request_latency" tags:"error=false"` + + // RequestLatencyFailures is a latency histogram of failed requests. + RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"` + + // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299 + HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"` + + // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399 + HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"` + + // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499 + HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"` + + // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599 + HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"` +} + +func (m *Metrics) recordHTTPStatusCode(statusCode uint16) { + if statusCode >= 200 && statusCode < 300 { + m.HTTPStatusCode2xx.Inc(1) + } else if statusCode >= 300 && statusCode < 400 { + m.HTTPStatusCode3xx.Inc(1) + } else if statusCode >= 400 && statusCode < 500 { + m.HTTPStatusCode4xx.Inc(1) + } else if statusCode >= 500 && statusCode < 600 { + m.HTTPStatusCode5xx.Inc(1) + } +} + +// MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name. +// Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped +// to a generic endpoint name "other". +type MetricsByEndpoint struct { + metricsFactory metrics.Factory + endpoints *normalizedEndpoints + metricsByEndpoint map[string]*Metrics + mux sync.RWMutex +} + +func newMetricsByEndpoint( + metricsFactory metrics.Factory, + normalizer NameNormalizer, + maxNumberOfEndpoints int, +) *MetricsByEndpoint { + return &MetricsByEndpoint{ + metricsFactory: metricsFactory, + endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer), + metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other" + } +} + +func (m *MetricsByEndpoint) get(endpoint string) *Metrics { + safeName := m.endpoints.normalize(endpoint) + if safeName == "" { + safeName = otherEndpointsPlaceholder + } + m.mux.RLock() + met := m.metricsByEndpoint[safeName] + m.mux.RUnlock() + if met != nil { + return met + } + + return m.getWithWriteLock(safeName) +} + +// split to make easier to test +func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics { + m.mux.Lock() + defer m.mux.Unlock() + + // it is possible that the name has been already registered after we released + // the read lock and before we grabbed the write lock, so check for that. + if met, ok := m.metricsByEndpoint[safeName]; ok { + return met + } + + // it would be nice to create the struct before locking, since Init() is somewhat + // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics. + met := &Metrics{} + tags := map[string]string{endpointNameMetricTag: safeName} + metrics.Init(met, m.metricsFactory, tags) + + m.metricsByEndpoint[safeName] = met + return met +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics_test.go new file mode 100644 index 000000000..292ec943f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics_test.go @@ -0,0 +1,61 @@ +// 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 rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +// E.g. tags("key", "value", "key", "value") +func tags(kv ...string) map[string]string { + m := make(map[string]string) + for i := 0; i < len(kv)-1; i += 2 { + m[kv[i]] = kv[i+1] + } + return m +} + +func endpointTags(endpoint string, kv ...string) map[string]string { + return tags(append([]string{"endpoint", endpoint}, kv...)...) +} + +func TestMetricsByEndpoint(t *testing.T) { + met := metrics.NewLocalFactory(0) + mbe := newMetricsByEndpoint(met, DefaultNameNormalizer, 2) + + m1 := mbe.get("abc1") + m2 := mbe.get("abc1") // from cache + m2a := mbe.getWithWriteLock("abc1") // from cache in double-checked lock + assert.Equal(t, m1, m2) + assert.Equal(t, m1, m2a) + + m3 := mbe.get("abc3") + m4 := mbe.get("overflow") + m5 := mbe.get("overflow2") + + for _, m := range []*Metrics{m1, m2, m2a, m3, m4, m5} { + m.RequestCountSuccess.Inc(1) + } + + testutils.AssertCounterMetrics(t, met, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc1", "error", "false"), Value: 3}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc3", "error", "false"), Value: 1}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("other", "error", "false"), Value: 2}, + ) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer.go new file mode 100644 index 000000000..148d84b3a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer.go @@ -0,0 +1,101 @@ +// 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 rpcmetrics + +// NameNormalizer is used to convert the endpoint names to strings +// that can be safely used as tags in the metrics. +type NameNormalizer interface { + Normalize(name string) string +} + +// DefaultNameNormalizer converts endpoint names so that they contain only characters +// from the safe charset [a-zA-Z0-9-./_]. All other characters are replaced with '-'. +var DefaultNameNormalizer = &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Range{From: 'A', To: 'Z'}, + &Range{From: '0', To: '9'}, + &Char{'-'}, + &Char{'_'}, + &Char{'/'}, + &Char{'.'}, + }, + Replacement: '-', +} + +// SimpleNameNormalizer uses a set of safe character sets. +type SimpleNameNormalizer struct { + SafeSets []SafeCharacterSet + Replacement byte +} + +// SafeCharacterSet determines if the given character is "safe" +type SafeCharacterSet interface { + IsSafe(c byte) bool +} + +// Range implements SafeCharacterSet +type Range struct { + From, To byte +} + +// IsSafe implements SafeCharacterSet +func (r *Range) IsSafe(c byte) bool { + return c >= r.From && c <= r.To +} + +// Char implements SafeCharacterSet +type Char struct { + Val byte +} + +// IsSafe implements SafeCharacterSet +func (ch *Char) IsSafe(c byte) bool { + return c == ch.Val +} + +// Normalize checks each character in the string against SafeSets, +// and if it's not safe substitutes it with Replacement. +func (n *SimpleNameNormalizer) Normalize(name string) string { + var retMe []byte + nameBytes := []byte(name) + for i, b := range nameBytes { + if n.safeByte(b) { + if retMe != nil { + retMe[i] = b + } + } else { + if retMe == nil { + retMe = make([]byte, len(nameBytes)) + copy(retMe[0:i], nameBytes[0:i]) + } + retMe[i] = n.Replacement + } + } + if retMe == nil { + return name + } + return string(retMe) +} + +// safeByte checks if b against all safe charsets. +func (n *SimpleNameNormalizer) safeByte(b byte) bool { + for i := range n.SafeSets { + if n.SafeSets[i].IsSafe(b) { + return true + } + } + return false +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer_test.go new file mode 100644 index 000000000..a93c17165 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer_test.go @@ -0,0 +1,34 @@ +// 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 rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleNameNormalizer(t *testing.T) { + n := &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Char{'-'}, + }, + Replacement: '-', + } + assert.Equal(t, "ab-cd", n.Normalize("ab-cd"), "all valid") + assert.Equal(t, "ab-cd", n.Normalize("ab.cd"), "single mismatch") + assert.Equal(t, "a--cd", n.Normalize("aB-cd"), "range letter mismatch") +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer.go new file mode 100644 index 000000000..eca5ff6f3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer.go @@ -0,0 +1,171 @@ +// 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 rpcmetrics + +import ( + "strconv" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/jaeger-lib/metrics" + + jaeger "github.com/uber/jaeger-client-go" +) + +const defaultMaxNumberOfEndpoints = 200 + +// Observer is an observer that can emit RPC metrics. +type Observer struct { + metricsByEndpoint *MetricsByEndpoint +} + +// NewObserver creates a new observer that can emit RPC metrics. +func NewObserver(metricsFactory metrics.Factory, normalizer NameNormalizer) *Observer { + return &Observer{ + metricsByEndpoint: newMetricsByEndpoint( + metricsFactory, + normalizer, + defaultMaxNumberOfEndpoints, + ), + } +} + +// OnStartSpan creates a new Observer for the span. +func (o *Observer) OnStartSpan( + operationName string, + options opentracing.StartSpanOptions, +) jaeger.SpanObserver { + return NewSpanObserver(o.metricsByEndpoint, operationName, options) +} + +// SpanKind identifies the span as inboud, outbound, or internal +type SpanKind int + +const ( + // Local span kind + Local SpanKind = iota + // Inbound span kind + Inbound + // Outbound span kind + Outbound +) + +// SpanObserver collects RPC metrics +type SpanObserver struct { + metricsByEndpoint *MetricsByEndpoint + operationName string + startTime time.Time + mux sync.Mutex + kind SpanKind + httpStatusCode uint16 + err bool +} + +// NewSpanObserver creates a new SpanObserver that can emit RPC metrics. +func NewSpanObserver( + metricsByEndpoint *MetricsByEndpoint, + operationName string, + options opentracing.StartSpanOptions, +) *SpanObserver { + so := &SpanObserver{ + metricsByEndpoint: metricsByEndpoint, + operationName: operationName, + startTime: options.StartTime, + } + for k, v := range options.Tags { + so.handleTagInLock(k, v) + } + return so +} + +// handleTags watches for special tags +// - SpanKind +// - HttpStatusCode +// - Error +func (so *SpanObserver) handleTagInLock(key string, value interface{}) { + if key == string(ext.SpanKind) { + if v, ok := value.(ext.SpanKindEnum); ok { + value = string(v) + } + if v, ok := value.(string); ok { + if v == string(ext.SpanKindRPCClientEnum) { + so.kind = Outbound + } else if v == string(ext.SpanKindRPCServerEnum) { + so.kind = Inbound + } + } + return + } + if key == string(ext.HTTPStatusCode) { + if v, ok := value.(uint16); ok { + so.httpStatusCode = v + } else if v, ok := value.(int); ok { + so.httpStatusCode = uint16(v) + } else if v, ok := value.(string); ok { + if vv, err := strconv.Atoi(v); err == nil { + so.httpStatusCode = uint16(vv) + } + } + return + } + if key == string(ext.Error) { + if v, ok := value.(bool); ok { + so.err = v + } else if v, ok := value.(string); ok { + if vv, err := strconv.ParseBool(v); err == nil { + so.err = vv + } + } + return + } +} + +// OnFinish emits the RPC metrics. It only has an effect when operation name +// is not blank, and the span kind is an RPC server. +func (so *SpanObserver) OnFinish(options opentracing.FinishOptions) { + so.mux.Lock() + defer so.mux.Unlock() + + if so.operationName == "" || so.kind != Inbound { + return + } + + mets := so.metricsByEndpoint.get(so.operationName) + latency := options.FinishTime.Sub(so.startTime) + if so.err { + mets.RequestCountFailures.Inc(1) + mets.RequestLatencyFailures.Record(latency) + } else { + mets.RequestCountSuccess.Inc(1) + mets.RequestLatencySuccess.Record(latency) + } + mets.recordHTTPStatusCode(so.httpStatusCode) +} + +// OnSetOperationName records new operation name. +func (so *SpanObserver) OnSetOperationName(operationName string) { + so.mux.Lock() + so.operationName = operationName + so.mux.Unlock() +} + +// OnSetTag implements SpanObserver +func (so *SpanObserver) OnSetTag(key string, value interface{}) { + so.mux.Lock() + so.handleTagInLock(key, value) + so.mux.Unlock() +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer_test.go new file mode 100644 index 000000000..c2c31ae07 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer_test.go @@ -0,0 +1,177 @@ +// 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 rpcmetrics + +import ( + "fmt" + "testing" + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + u "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/opentracing/opentracing-go/ext" + jaeger "github.com/uber/jaeger-client-go" +) + +func ExampleObserver() { + metricsFactory := metrics.NewLocalFactory(0) + metricsObserver := NewObserver( + metricsFactory, + DefaultNameNormalizer, + ) + tracer, closer := jaeger.NewTracer( + "serviceName", + jaeger.NewConstSampler(true), + jaeger.NewInMemoryReporter(), + jaeger.TracerOptions.Observer(metricsObserver), + ) + defer closer.Close() + + span := tracer.StartSpan("test", ext.SpanKindRPCServer) + span.Finish() + + c, _ := metricsFactory.Snapshot() + fmt.Printf("requests (success): %d\n", c["requests|endpoint=test|error=false"]) + fmt.Printf("requests (failure): %d\n", c["requests|endpoint=test|error=true"]) + // Output: + // requests (success): 1 + // requests (failure): 0 +} + +type testTracer struct { + metrics *metrics.LocalFactory + tracer opentracing.Tracer +} + +func withTestTracer(runTest func(tt *testTracer)) { + sampler := jaeger.NewConstSampler(true) + reporter := jaeger.NewInMemoryReporter() + metrics := metrics.NewLocalFactory(time.Minute) + observer := NewObserver(metrics, DefaultNameNormalizer) + tracer, closer := jaeger.NewTracer( + "test", + sampler, + reporter, + jaeger.TracerOptions.Observer(observer)) + defer closer.Close() + runTest(&testTracer{ + metrics: metrics, + tracer: tracer, + }) +} + +func TestObserver(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + ts := time.Now() + finishOptions := opentracing.FinishOptions{ + FinishTime: ts.Add(50 * time.Millisecond), + } + + testCases := []struct { + name string + tag opentracing.Tag + opNameOverride string + err bool + }{ + {name: "local-span", tag: opentracing.Tag{Key: "x", Value: "y"}}, + {name: "get-user", tag: ext.SpanKindRPCServer}, + {name: "get-user", tag: ext.SpanKindRPCServer, opNameOverride: "get-user-override"}, + {name: "get-user", tag: ext.SpanKindRPCServer, err: true}, + {name: "get-user-client", tag: ext.SpanKindRPCClient}, + } + + for _, testCase := range testCases { + span := testTracer.tracer.StartSpan( + testCase.name, + testCase.tag, + opentracing.StartTime(ts), + ) + if testCase.opNameOverride != "" { + span.SetOperationName(testCase.opNameOverride) + } + if testCase.err { + ext.Error.Set(span, true) + } + span.FinishWithOptions(finishOptions) + } + + u.AssertCounterMetrics(t, + testTracer.metrics, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("local-span", "error", "false"), Value: 0}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "true"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-override", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-client", "error", "false"), Value: 0}, + ) + // TODO something wrong with string generation, .P99 should not be appended to the tag + // as a result we cannot use u.AssertGaugeMetrics + _, g := testTracer.metrics.Snapshot() + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=false.P99"]) + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=true.P99"]) + }) +} + +func TestTags(t *testing.T) { + type tagTestCase struct { + key string + value interface{} + metrics []u.ExpectedMetric + } + + testCases := []tagTestCase{ + {key: "something", value: 42, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "false")}, + }}, + {key: "error", value: true, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + {key: "error", value: "true", metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + } + + for i := 2; i <= 5; i++ { + values := []interface{}{ + i * 100, + uint16(i * 100), + fmt.Sprintf("%d00", i), + } + for _, v := range values { + testCases = append(testCases, tagTestCase{ + key: "http.status_code", value: v, metrics: []u.ExpectedMetric{ + {Name: "http_requests", Value: 1, Tags: tags("status_code", fmt.Sprintf("%dxx", i))}, + }, + }) + } + } + + for _, tc := range testCases { + testCase := tc // capture loop var + for i := range testCase.metrics { + testCase.metrics[i].Tags["endpoint"] = "span" + } + t.Run(fmt.Sprintf("%s-%v", testCase.key, testCase.value), func(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + span := testTracer.tracer.StartSpan("span", ext.SpanKindRPCServer) + span.SetTag(testCase.key, testCase.value) + span.Finish() + u.AssertCounterMetrics(t, testTracer.metrics, testCase.metrics...) + }) + }) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler.go new file mode 100644 index 000000000..f2e8c994e --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler.go @@ -0,0 +1,531 @@ +// 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 jaeger + +import ( + "fmt" + "math" + "net/url" + "sync" + "time" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + defaultSamplingServerURL = "http://localhost:5778/sampling" + defaultSamplingRefreshInterval = time.Minute + defaultMaxOperations = 2000 +) + +// Sampler decides whether a new trace should be sampled or not. +type Sampler interface { + // IsSampled decides whether a trace with given `id` and `operation` + // should be sampled. This function will also return the tags that + // can be used to identify the type of sampling that was applied to + // the root span. Most simple samplers would return two tags, + // sampler.type and sampler.param, similar to those used in the Configuration + IsSampled(id TraceID, operation string) (sampled bool, tags []Tag) + + // Close does a clean shutdown of the sampler, stopping any background + // go-routines it may have started. + Close() + + // Equal checks if the `other` sampler is functionally equivalent + // to this sampler. + // TODO remove this function. This function is used to determine if 2 samplers are equivalent + // which does not bode well with the adaptive sampler which has to create all the composite samplers + // for the comparison to occur. This is expensive to do if only one sampler has changed. + Equal(other Sampler) bool +} + +// ----------------------- + +// ConstSampler is a sampler that always makes the same decision. +type ConstSampler struct { + Decision bool + tags []Tag +} + +// NewConstSampler creates a ConstSampler. +func NewConstSampler(sample bool) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeConst}, + {key: SamplerParamTagKey, value: sample}, + } + return &ConstSampler{Decision: sample, tags: tags} +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ConstSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.Decision, s.tags +} + +// Close implements Close() of Sampler. +func (s *ConstSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ConstSampler) Equal(other Sampler) bool { + if o, ok := other.(*ConstSampler); ok { + return s.Decision == o.Decision + } + return false +} + +// ----------------------- + +// ProbabilisticSampler is a sampler that randomly samples a certain percentage +// of traces. +type ProbabilisticSampler struct { + samplingRate float64 + samplingBoundary uint64 + tags []Tag +} + +const maxRandomNumber = ^(uint64(1) << 63) // i.e. 0x7fffffffffffffff + +// NewProbabilisticSampler creates a sampler that randomly samples a certain percentage of traces specified by the +// samplingRate, in the range between 0.0 and 1.0. +// +// It relies on the fact that new trace IDs are 63bit random numbers themselves, thus making the sampling decision +// without generating a new random number, but simply calculating if traceID < (samplingRate * 2^63). +// TODO remove the error from this function for next major release +func NewProbabilisticSampler(samplingRate float64) (*ProbabilisticSampler, error) { + if samplingRate < 0.0 || samplingRate > 1.0 { + return nil, fmt.Errorf("Sampling Rate must be between 0.0 and 1.0, received %f", samplingRate) + } + return newProbabilisticSampler(samplingRate), nil +} + +func newProbabilisticSampler(samplingRate float64) *ProbabilisticSampler { + samplingRate = math.Max(0.0, math.Min(samplingRate, 1.0)) + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeProbabilistic}, + {key: SamplerParamTagKey, value: samplingRate}, + } + return &ProbabilisticSampler{ + samplingRate: samplingRate, + samplingBoundary: uint64(float64(maxRandomNumber) * samplingRate), + tags: tags, + } +} + +// SamplingRate returns the sampling probability this sampled was constructed with. +func (s *ProbabilisticSampler) SamplingRate() float64 { + return s.samplingRate +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.samplingBoundary >= id.Low, s.tags +} + +// Close implements Close() of Sampler. +func (s *ProbabilisticSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ProbabilisticSampler) Equal(other Sampler) bool { + if o, ok := other.(*ProbabilisticSampler); ok { + return s.samplingBoundary == o.samplingBoundary + } + return false +} + +// ----------------------- + +type rateLimitingSampler struct { + maxTracesPerSecond float64 + rateLimiter utils.RateLimiter + tags []Tag +} + +// NewRateLimitingSampler creates a sampler that samples at most maxTracesPerSecond. The distribution of sampled +// traces follows burstiness of the service, i.e. a service with uniformly distributed requests will have those +// requests sampled uniformly as well, but if requests are bursty, especially sub-second, then a number of +// sequential requests can be sampled each second. +func NewRateLimitingSampler(maxTracesPerSecond float64) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeRateLimiting}, + {key: SamplerParamTagKey, value: maxTracesPerSecond}, + } + return &rateLimitingSampler{ + maxTracesPerSecond: maxTracesPerSecond, + rateLimiter: utils.NewRateLimiter(maxTracesPerSecond, math.Max(maxTracesPerSecond, 1.0)), + tags: tags, + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *rateLimitingSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.rateLimiter.CheckCredit(1.0), s.tags +} + +func (s *rateLimitingSampler) Close() { + // nothing to do +} + +func (s *rateLimitingSampler) Equal(other Sampler) bool { + if o, ok := other.(*rateLimitingSampler); ok { + return s.maxTracesPerSecond == o.maxTracesPerSecond + } + return false +} + +// ----------------------- + +// GuaranteedThroughputProbabilisticSampler is a sampler that leverages both probabilisticSampler and +// rateLimitingSampler. The rateLimitingSampler is used as a guaranteed lower bound sampler such that +// every operation is sampled at least once in a time interval defined by the lowerBound. ie a lowerBound +// of 1.0 / (60 * 10) will sample an operation at least once every 10 minutes. +// +// The probabilisticSampler is given higher priority when tags are emitted, ie. if IsSampled() for both +// samplers return true, the tags for probabilisticSampler will be used. +type GuaranteedThroughputProbabilisticSampler struct { + probabilisticSampler *ProbabilisticSampler + lowerBoundSampler Sampler + tags []Tag + samplingRate float64 + lowerBound float64 +} + +// NewGuaranteedThroughputProbabilisticSampler returns a delegating sampler that applies both +// probabilisticSampler and rateLimitingSampler. +func NewGuaranteedThroughputProbabilisticSampler( + lowerBound, samplingRate float64, +) (*GuaranteedThroughputProbabilisticSampler, error) { + return newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate), nil +} + +func newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate float64) *GuaranteedThroughputProbabilisticSampler { + s := &GuaranteedThroughputProbabilisticSampler{ + lowerBoundSampler: NewRateLimitingSampler(lowerBound), + lowerBound: lowerBound, + } + s.setProbabilisticSampler(samplingRate) + return s +} + +func (s *GuaranteedThroughputProbabilisticSampler) setProbabilisticSampler(samplingRate float64) { + if s.probabilisticSampler == nil || s.samplingRate != samplingRate { + s.probabilisticSampler = newProbabilisticSampler(samplingRate) + s.samplingRate = s.probabilisticSampler.SamplingRate() + s.tags = []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeLowerBound}, + {key: SamplerParamTagKey, value: s.samplingRate}, + } + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + if sampled, tags := s.probabilisticSampler.IsSampled(id, operation); sampled { + s.lowerBoundSampler.IsSampled(id, operation) + return true, tags + } + sampled, _ := s.lowerBoundSampler.IsSampled(id, operation) + return sampled, s.tags +} + +// Close implements Close() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Close() { + s.probabilisticSampler.Close() + s.lowerBoundSampler.Close() +} + +// Equal implements Equal() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + return false +} + +// this function should only be called while holding a Write lock +func (s *GuaranteedThroughputProbabilisticSampler) update(lowerBound, samplingRate float64) { + s.setProbabilisticSampler(samplingRate) + if s.lowerBound != lowerBound { + s.lowerBoundSampler = NewRateLimitingSampler(lowerBound) + s.lowerBound = lowerBound + } +} + +// ----------------------- + +type adaptiveSampler struct { + sync.RWMutex + + samplers map[string]*GuaranteedThroughputProbabilisticSampler + defaultSampler *ProbabilisticSampler + lowerBound float64 + maxOperations int +} + +// NewAdaptiveSampler returns a delegating sampler that applies both probabilisticSampler and +// rateLimitingSampler via the guaranteedThroughputProbabilisticSampler. This sampler keeps track of all +// operations and delegates calls to the respective guaranteedThroughputProbabilisticSampler. +func NewAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) (Sampler, error) { + return newAdaptiveSampler(strategies, maxOperations), nil +} + +func newAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) Sampler { + samplers := make(map[string]*GuaranteedThroughputProbabilisticSampler) + for _, strategy := range strategies.PerOperationStrategies { + sampler := newGuaranteedThroughputProbabilisticSampler( + strategies.DefaultLowerBoundTracesPerSecond, + strategy.ProbabilisticSampling.SamplingRate, + ) + samplers[strategy.Operation] = sampler + } + return &adaptiveSampler{ + samplers: samplers, + defaultSampler: newProbabilisticSampler(strategies.DefaultSamplingProbability), + lowerBound: strategies.DefaultLowerBoundTracesPerSecond, + maxOperations: maxOperations, + } +} + +func (s *adaptiveSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + sampler, ok := s.samplers[operation] + if ok { + defer s.RUnlock() + return sampler.IsSampled(id, operation) + } + s.RUnlock() + s.Lock() + defer s.Unlock() + + // Check if sampler has already been created + sampler, ok = s.samplers[operation] + if ok { + return sampler.IsSampled(id, operation) + } + // Store only up to maxOperations of unique ops. + if len(s.samplers) >= s.maxOperations { + return s.defaultSampler.IsSampled(id, operation) + } + newSampler := newGuaranteedThroughputProbabilisticSampler(s.lowerBound, s.defaultSampler.SamplingRate()) + s.samplers[operation] = newSampler + return newSampler.IsSampled(id, operation) +} + +func (s *adaptiveSampler) Close() { + s.Lock() + defer s.Unlock() + for _, sampler := range s.samplers { + sampler.Close() + } + s.defaultSampler.Close() +} + +func (s *adaptiveSampler) Equal(other Sampler) bool { + // NB The Equal() function is overly expensive for adaptiveSampler since it's composed of multiple + // samplers which all need to be initialized before this function can be called for a comparison. + // Therefore, adaptiveSampler uses the update() function to only alter the samplers that need + // changing. Hence this function always returns false so that the update function can be called. + // Once the Equal() function is removed from the Sampler API, this will no longer be needed. + return false +} + +func (s *adaptiveSampler) update(strategies *sampling.PerOperationSamplingStrategies) { + s.Lock() + defer s.Unlock() + for _, strategy := range strategies.PerOperationStrategies { + operation := strategy.Operation + samplingRate := strategy.ProbabilisticSampling.SamplingRate + lowerBound := strategies.DefaultLowerBoundTracesPerSecond + if sampler, ok := s.samplers[operation]; ok { + sampler.update(lowerBound, samplingRate) + } else { + sampler := newGuaranteedThroughputProbabilisticSampler( + lowerBound, + samplingRate, + ) + s.samplers[operation] = sampler + } + } + s.lowerBound = strategies.DefaultLowerBoundTracesPerSecond + if s.defaultSampler.SamplingRate() != strategies.DefaultSamplingProbability { + s.defaultSampler = newProbabilisticSampler(strategies.DefaultSamplingProbability) + } +} + +// ----------------------- + +// RemotelyControlledSampler is a delegating sampler that polls a remote server +// for the appropriate sampling strategy, constructs a corresponding sampler and +// delegates to it for sampling decisions. +type RemotelyControlledSampler struct { + sync.RWMutex + samplerOptions + + serviceName string + timer *time.Ticker + manager sampling.SamplingManager + pollStopped sync.WaitGroup +} + +type httpSamplingManager struct { + serverURL string +} + +func (s *httpSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + var out sampling.SamplingStrategyResponse + v := url.Values{} + v.Set("service", serviceName) + if err := utils.GetJSON(s.serverURL+"?"+v.Encode(), &out); err != nil { + return nil, err + } + return &out, nil +} + +// NewRemotelyControlledSampler creates a sampler that periodically pulls +// the sampling strategy from an HTTP sampling server (e.g. jaeger-agent). +func NewRemotelyControlledSampler( + serviceName string, + opts ...SamplerOption, +) *RemotelyControlledSampler { + options := applySamplerOptions(opts...) + sampler := &RemotelyControlledSampler{ + samplerOptions: options, + serviceName: serviceName, + timer: time.NewTicker(options.samplingRefreshInterval), + manager: &httpSamplingManager{serverURL: options.samplingServerURL}, + } + + go sampler.pollController() + return sampler +} + +func applySamplerOptions(opts ...SamplerOption) samplerOptions { + options := samplerOptions{} + for _, option := range opts { + option(&options) + } + if options.sampler == nil { + options.sampler = newProbabilisticSampler(0.001) + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.maxOperations <= 0 { + options.maxOperations = defaultMaxOperations + } + if options.samplingServerURL == "" { + options.samplingServerURL = defaultSamplingServerURL + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.samplingRefreshInterval <= 0 { + options.samplingRefreshInterval = defaultSamplingRefreshInterval + } + return options +} + +// IsSampled implements IsSampled() of Sampler. +func (s *RemotelyControlledSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + defer s.RUnlock() + return s.sampler.IsSampled(id, operation) +} + +// Close implements Close() of Sampler. +func (s *RemotelyControlledSampler) Close() { + s.RLock() + s.timer.Stop() + s.RUnlock() + + s.pollStopped.Wait() +} + +// Equal implements Equal() of Sampler. +func (s *RemotelyControlledSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + if o, ok := other.(*RemotelyControlledSampler); ok { + s.RLock() + o.RLock() + defer s.RUnlock() + defer o.RUnlock() + return s.sampler.Equal(o.sampler) + } + return false +} + +func (s *RemotelyControlledSampler) pollController() { + // in unit tests we re-assign the timer Ticker, so need to lock to avoid data races + s.Lock() + timer := s.timer + s.Unlock() + + for range timer.C { + s.updateSampler() + } + s.pollStopped.Add(1) +} + +func (s *RemotelyControlledSampler) updateSampler() { + res, err := s.manager.GetSamplingStrategy(s.serviceName) + if err != nil { + s.metrics.SamplerQueryFailure.Inc(1) + return + } + s.Lock() + defer s.Unlock() + + s.metrics.SamplerRetrieved.Inc(1) + if strategies := res.GetOperationSampling(); strategies != nil { + s.updateAdaptiveSampler(strategies) + } else { + err = s.updateRateLimitingOrProbabilisticSampler(res) + } + if err != nil { + s.metrics.SamplerUpdateFailure.Inc(1) + s.logger.Infof("Unable to handle sampling strategy response %+v. Got error: %v", res, err) + return + } + s.metrics.SamplerUpdated.Inc(1) +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies) { + if adaptiveSampler, ok := s.sampler.(*adaptiveSampler); ok { + adaptiveSampler.update(strategies) + } else { + s.sampler = newAdaptiveSampler(strategies, s.maxOperations) + } +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateRateLimitingOrProbabilisticSampler(res *sampling.SamplingStrategyResponse) error { + var newSampler Sampler + if probabilistic := res.GetProbabilisticSampling(); probabilistic != nil { + newSampler = newProbabilisticSampler(probabilistic.SamplingRate) + } else if rateLimiting := res.GetRateLimitingSampling(); rateLimiting != nil { + newSampler = NewRateLimitingSampler(float64(rateLimiting.MaxTracesPerSecond)) + } else { + return fmt.Errorf("Unsupported sampling strategy type %v", res.GetStrategyType()) + } + if !s.sampler.Equal(newSampler) { + s.sampler = newSampler + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_options.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_options.go new file mode 100644 index 000000000..75d28a561 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_options.go @@ -0,0 +1,81 @@ +// 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 jaeger + +import ( + "time" +) + +// SamplerOption is a function that sets some option on the sampler +type SamplerOption func(options *samplerOptions) + +// SamplerOptions is a factory for all available SamplerOption's +var SamplerOptions samplerOptions + +type samplerOptions struct { + metrics *Metrics + maxOperations int + sampler Sampler + logger Logger + samplingServerURL string + samplingRefreshInterval time.Duration +} + +// Metrics creates a SamplerOption that initializes Metrics on the sampler, +// which is used to emit statistics. +func (samplerOptions) Metrics(m *Metrics) SamplerOption { + return func(o *samplerOptions) { + o.metrics = m + } +} + +// MaxOperations creates a SamplerOption that sets the maximum number of +// operations the sampler will keep track of. +func (samplerOptions) MaxOperations(maxOperations int) SamplerOption { + return func(o *samplerOptions) { + o.maxOperations = maxOperations + } +} + +// InitialSampler creates a SamplerOption that sets the initial sampler +// to use before a remote sampler is created and used. +func (samplerOptions) InitialSampler(sampler Sampler) SamplerOption { + return func(o *samplerOptions) { + o.sampler = sampler + } +} + +// Logger creates a SamplerOption that sets the logger used by the sampler. +func (samplerOptions) Logger(logger Logger) SamplerOption { + return func(o *samplerOptions) { + o.logger = logger + } +} + +// SamplingServerURL creates a SamplerOption that sets the sampling server url +// of the local agent that contains the sampling strategies. +func (samplerOptions) SamplingServerURL(samplingServerURL string) SamplerOption { + return func(o *samplerOptions) { + o.samplingServerURL = samplingServerURL + } +} + +// SamplingRefreshInterval creates a SamplerOption that sets how often the +// sampler will poll local agent for the appropriate sampling strategy. +func (samplerOptions) SamplingRefreshInterval(samplingRefreshInterval time.Duration) SamplerOption { + return func(o *samplerOptions) { + o.samplingRefreshInterval = samplingRefreshInterval + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_test.go new file mode 100644 index 000000000..d17072542 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/sampler_test.go @@ -0,0 +1,703 @@ +// 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 jaeger + +import ( + "errors" + "fmt" + "runtime" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + testOperationName = "op" + testFirstTimeOperationName = "firstTimeOp" + + testDefaultSamplingProbability = 0.5 + testMaxID = uint64(1) << 62 + testDefaultMaxOperations = 10 +) + +var ( + testProbabilisticExpectedTags = []Tag{ + {"sampler.type", "probabilistic"}, + {"sampler.param", 0.5}, + } + testLowerBoundExpectedTags = []Tag{ + {"sampler.type", "lowerbound"}, + {"sampler.param", 0.5}, + } +) + +func TestSamplerTags(t *testing.T) { + prob, err := NewProbabilisticSampler(0.1) + require.NoError(t, err) + rate := NewRateLimitingSampler(0.1) + remote := &RemotelyControlledSampler{} + remote.sampler = NewConstSampler(true) + tests := []struct { + sampler Sampler + typeTag string + paramTag interface{} + }{ + {NewConstSampler(true), "const", true}, + {NewConstSampler(false), "const", false}, + {prob, "probabilistic", 0.1}, + {rate, "ratelimiting", 0.1}, + {remote, "const", true}, + } + for _, test := range tests { + _, tags := test.sampler.IsSampled(TraceID{}, testOperationName) + count := 0 + for _, tag := range tags { + if tag.key == SamplerTypeTagKey { + assert.Equal(t, test.typeTag, tag.value) + count++ + } + if tag.key == SamplerParamTagKey { + assert.Equal(t, test.paramTag, tag.value) + count++ + } + } + assert.Equal(t, 2, count) + } +} + +func TestApplySamplerOptions(t *testing.T) { + options := applySamplerOptions() + sampler, ok := options.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.Equal(t, 0.001, sampler.samplingRate) + + assert.NotNil(t, options.logger) + assert.NotZero(t, options.maxOperations) + assert.NotEmpty(t, options.samplingServerURL) + assert.NotNil(t, options.metrics) + assert.NotZero(t, options.samplingRefreshInterval) +} + +func TestProbabilisticSamplerErrors(t *testing.T) { + _, err := NewProbabilisticSampler(-0.1) + assert.Error(t, err) + _, err = NewProbabilisticSampler(1.1) + assert.Error(t, err) +} + +func TestProbabilisticSampler(t *testing.T) { + sampler, _ := NewProbabilisticSampler(0.5) + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampler2, _ := NewProbabilisticSampler(0.5) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(NewConstSampler(true))) +} + +func TestProbabilisticSamplerPerformance(t *testing.T) { + t.Skip("Skipped performance test") + sampler, _ := NewProbabilisticSampler(0.01) + rand := utils.NewRand(8736823764) + var count uint64 + for i := 0; i < 100000000; i++ { + id := TraceID{Low: uint64(rand.Int63())} + if sampled, _ := sampler.IsSampled(id, testOperationName); sampled { + count++ + } + } + println("Sampled:", count, "rate=", float64(count)/float64(100000000)) + // Sampled: 999829 rate= 0.009998290 +} + +func TestRateLimitingSampler(t *testing.T) { + sampler := NewRateLimitingSampler(2) + sampler2 := NewRateLimitingSampler(2) + sampler3 := NewRateLimitingSampler(3) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(sampler3)) + assert.False(t, sampler.Equal(NewConstSampler(false))) + + sampler = NewRateLimitingSampler(2) + sampled, _ := sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) + + sampler = NewRateLimitingSampler(0.1) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) +} + +func TestGuaranteedThroughputProbabilisticSamplerUpdate(t *testing.T) { + samplingRate := 0.5 + lowerBound := 2.0 + sampler, err := NewGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate) + assert.NoError(t, err) + + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, samplingRate, sampler.samplingRate) + + newSamplingRate := 0.6 + newLowerBound := 1.0 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newSamplingRate, sampler.samplingRate) + + newSamplingRate = 1.1 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, 1.0, sampler.samplingRate) +} + +func TestAdaptiveSampler(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: testDefaultSamplingProbability}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + defer sampler.Close() + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testLowerBoundExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + + // This operation is seen for the first time by the sampler + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestAdaptiveSamplerErrors(t *testing.T) { + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: -0.1}, + }, + }, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 0.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) + + strategies.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate = 1.1 + sampler, err = NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 1.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) +} + +func TestAdaptiveSamplerUpdate(t *testing.T) { + samplingRate := 0.1 + lowerBound := 2.0 + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: samplingRate}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: lowerBound, + PerOperationStrategies: samplingRates, + } + + s, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + + sampler, ok := s.(*adaptiveSampler) + assert.True(t, ok) + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, testDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 1) + + // Update the sampler with new sampling rates + newSamplingRate := 0.2 + newLowerBound := 3.0 + newDefaultSamplingProbability := 0.1 + newSamplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + { + Operation: testFirstTimeOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + } + strategies = &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: newDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: newLowerBound, + PerOperationStrategies: newSamplingRates, + } + + sampler.update(strategies) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 2) +} + +func initAgent(t *testing.T) (*testutils.MockAgent, *RemotelyControlledSampler, *metrics.LocalFactory) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + initialSampler, _ := NewProbabilisticSampler(0.001) + sampler := NewRemotelyControlledSampler( + "client app", + SamplerOptions.Metrics(metrics), + SamplerOptions.SamplingServerURL("http://"+agent.SamplingServerAddr()), + SamplerOptions.MaxOperations(testDefaultMaxOperations), + SamplerOptions.InitialSampler(initialSampler), + SamplerOptions.Logger(log.NullLogger), + SamplerOptions.SamplingRefreshInterval(time.Minute), + ) + sampler.Close() // stop timer-based updates, we want to call them manually + + return agent, sampler, metricsFactory +} + +func TestRemotelyControlledSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, testDefaultSamplingProbability)) + remoteSampler.updateSampler() + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.sampler", Tags: map[string]string{"state": "retrieved"}, Value: 1}, + {Name: "jaeger.sampler", Tags: map[string]string{"state": "updated"}, Value: 1}, + }...) + _, ok = remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, remoteSampler.sampler, "Sampler should have been updated") + + sampled, tags := remoteSampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = remoteSampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + remoteSampler.sampler = initSampler + c := make(chan time.Time) + remoteSampler.Lock() + remoteSampler.timer = &time.Ticker{C: c} + remoteSampler.Unlock() + go remoteSampler.pollController() + + c <- time.Now() // force update based on timer + time.Sleep(10 * time.Millisecond) + remoteSampler.Close() + + _, ok = remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, remoteSampler.sampler, "Sampler should have been updated from timer") + + assert.True(t, remoteSampler.Equal(remoteSampler)) +} + +func generateTags(key string, value float64) []Tag { + return []Tag{ + {"sampler.type", key}, + {"sampler.param", value}, + } +} + +func TestRemotelyControlledSampler_updateSampler(t *testing.T) { + tests := []struct { + probabilities map[string]float64 + defaultProbability float64 + expectedDefaultProbability float64 + expectedTags []Tag + }{ + { + probabilities: map[string]float64{testOperationName: 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: generateTags("probabilistic", 1.0), + }, + { + probabilities: map[string]float64{testOperationName: testDefaultSamplingProbability}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{ + testOperationName: testDefaultSamplingProbability, + testFirstTimeOperationName: testDefaultSamplingProbability, + }, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: 1.1, + expectedDefaultProbability: 1.0, + expectedTags: generateTags("probabilistic", 1.0), + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + res := &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + OperationSampling: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: test.defaultProbability, + DefaultLowerBoundTracesPerSecond: 0.001, + }, + } + for opName, prob := range test.probabilities { + res.OperationSampling.PerOperationStrategies = append(res.OperationSampling.PerOperationStrategies, + &sampling.OperationSamplingStrategy{ + Operation: opName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: prob, + }, + }, + ) + } + + agent.AddSamplingStrategy("client app", res) + sampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler" + "|state=updated", Value: 1, + }, + ) + + s, ok := sampler.sampler.(*adaptiveSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, sampler.sampler, "Sampler should have been updated") + assert.Equal(t, test.expectedDefaultProbability, s.defaultSampler.SamplingRate()) + + // First call is always sampled + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, test.expectedTags, tags) + }) + } +} + +func TestMaxOperations(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 0.1}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, 1) + assert.NoError(t, err) + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID - 10}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestSamplerQueryError(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + // override the actual handler + sampler.manager = &fakeSamplingManager{} + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + sampler.Close() // stop timer-based updates, we want to call them manually + + sampler.updateSampler() + assert.Equal(t, initSampler, sampler.sampler, "Sampler should not have been updated due to query error") + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"phase": "query", "state": "failure"}, + Value: 1, + }, + ) +} + +type fakeSamplingManager struct{} + +func (c *fakeSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + return nil, errors.New("query error") +} + +func TestRemotelyControlledSampler_updateSamplerFromAdaptiveSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + remoteSampler.Close() // stop timer-based updates, we want to call them manually + + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + } + + adaptiveSampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.5)) + remoteSampler.updateSampler() + + // Sampler should have been updated to probabilistic + _, ok := remoteSampler.sampler.(*ProbabilisticSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 1)) + remoteSampler.updateSampler() + + // Sampler should have been updated to ratelimiting + _, ok = remoteSampler.sampler.(*rateLimitingSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + // Update existing adaptive sampler + agent.AddSamplingStrategy("client app", &sampling.SamplingStrategyResponse{OperationSampling: strategies}) + remoteSampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"state": "retrieved"}, + Value: 3, + }, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"state": "updated"}, + Value: 3, + }, + ) +} + +func TestRemotelyControlledSampler_updateRateLimitingOrProbabilisticSampler(t *testing.T) { + probabilisticSampler, err := NewProbabilisticSampler(0.002) + require.NoError(t, err) + otherProbabilisticSampler, err := NewProbabilisticSampler(0.003) + require.NoError(t, err) + maxProbabilisticSampler, err := NewProbabilisticSampler(1.0) + require.NoError(t, err) + + rateLimitingSampler := NewRateLimitingSampler(2) + otherRateLimitingSampler := NewRateLimitingSampler(3) + + testCases := []struct { + res *sampling.SamplingStrategyResponse + initSampler Sampler + expectedSampler Sampler + shouldErr bool + referenceEquivalence bool + caption string + }{ + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 1.5), + initSampler: probabilisticSampler, + expectedSampler: maxProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "invalid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.002), + initSampler: probabilisticSampler, + expectedSampler: probabilisticSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.003), + initSampler: probabilisticSampler, + expectedSampler: otherProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 2), + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged rate limiting strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 3), + initSampler: rateLimitingSampler, + expectedSampler: otherRateLimitingSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid rate limiting strategy", + }, + { + res: &sampling.SamplingStrategyResponse{}, + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: true, + referenceEquivalence: true, + caption: "invalid strategy", + }, + } + + for _, tc := range testCases { + testCase := tc // capture loop var + t.Run(testCase.caption, func(t *testing.T) { + remoteSampler := &RemotelyControlledSampler{samplerOptions: samplerOptions{sampler: testCase.initSampler}} + err := remoteSampler.updateRateLimitingOrProbabilisticSampler(testCase.res) + if testCase.shouldErr { + require.Error(t, err) + } + if testCase.referenceEquivalence { + assert.Equal(t, testCase.expectedSampler, remoteSampler.sampler) + } else { + assert.True(t, testCase.expectedSampler.Equal(remoteSampler.sampler)) + } + }) + } +} + +func getSamplingStrategyResponse(strategyType sampling.SamplingStrategyType, value float64) *sampling.SamplingStrategyResponse { + if strategyType == sampling.SamplingStrategyType_PROBABILISTIC { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: value, + }, + } + } + if strategyType == sampling.SamplingStrategyType_RATE_LIMITING { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int16(value), + }, + } + } + return nil +} + +func TestAdaptiveSampler_lockRaceCondition(t *testing.T) { + agent, remoteSampler, _ := initAgent(t) + defer agent.Close() + remoteSampler.Close() // stop timer-based updates, we want to call them manually + + numOperations := 1000 + adaptiveSampler, err := NewAdaptiveSampler( + &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 1, + }, + 2000, + ) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(2) + + // Start 2 go routines that will simulate simultaneous calls to IsSampled + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "a") + }() + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "b") + }() +} + +func isSampled(t *testing.T, remoteSampler *RemotelyControlledSampler, numOperations int, operationNamePrefix string) { + for i := 0; i < numOperations; i++ { + runtime.Gosched() + sampled, _ := remoteSampler.IsSampled(TraceID{}, fmt.Sprintf("%s%d", operationNamePrefix, i)) + assert.True(t, sampled) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/cover.sh b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/cover.sh new file mode 100644 index 000000000..d0fafbdba --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/cover.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -e + +COVER=.cover +ROOT_PKG=github.com/uber/jaeger-client-go/ + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +# If a package directory has a .nocover file, don't count it when calculating +# coverage. +filter="" +for pkg in "$@"; do + if [[ -f "$GOPATH/src/$pkg/.nocover" ]]; then + if [[ -n "$filter" ]]; then + filter="$filter, " + fi + filter="\"$pkg\": true" + fi +done + + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | . + ["'"$pkg"'"] + | map + ( select(startswith("'"$ROOT_PKG"'")) + | select(contains("/vendor/") | not) + | select(in({'"$filter"'}) | not) + ) + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + args="" + if [[ -n "$coverpkg" ]]; then + args="-coverprofile $COVER/cover.${i}.out" # -coverpkg $coverpkg" + fi + + echo go test -v -race "$pkg" + go test $args -v -race "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicense.py b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicense.py new file mode 100644 index 000000000..5374d36fa --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicense.py @@ -0,0 +1,120 @@ +from __future__ import ( + absolute_import, print_function, division, unicode_literals +) + +import logging +import re +import sys +from datetime import datetime + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +CURRENT_YEAR = datetime.today().year + +MIT_LICENSE_BLOB = """Copyright (c) %d Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.""" % CURRENT_YEAR + +LICENSE_BLOB = """Copyright (c) %d 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.""" % CURRENT_YEAR + +MIT_LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in MIT_LICENSE_BLOB.split('\n') +] + +LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n') +] + +COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I) + + +def update_go_license(name, force=False): + with open(name) as f: + orig_lines = list(f) + lines = list(orig_lines) + + current_header = ''.join(lines[0:len(MIT_LICENSE_BLOB_LINES_GO)]) + mit_header = ''.join(MIT_LICENSE_BLOB_LINES_GO) + if current_header == mit_header: + lines = lines[len(MIT_LICENSE_BLOB_LINES_GO)+1:] + + found = False + changed = False + for i, line in enumerate(lines[:5]): + m = COPYRIGHT_RE.search(line) + if not m: + continue + + found = True + year = int(m.group(1)) + if year == CURRENT_YEAR: + break + + new_line = COPYRIGHT_RE.sub('Copyright (c) %d' % CURRENT_YEAR, line) + assert line != new_line, ('Could not change year in: %s' % line) + lines[i] = new_line + changed = True + break + + if not found: + if 'Code generated by' in lines[0]: + lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO + else: + lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n'] + changed = True + + if changed: + with open(name, 'w') as f: + for line in lines: + f.write(line) + print(name) + + +def main(): + if len(sys.argv) == 1: + print('USAGE: %s FILE ...' % sys.argv[0]) + sys.exit(1) + + for name in sys.argv[1:]: + if name.endswith('.go'): + try: + update_go_license(name) + except Exception as error: + logger.error('Failed to process file %s', name) + logger.exception(error) + raise error + else: + raise NotImplementedError('Unsupported file type: %s' % name) + + +if __name__ == "__main__": + main() diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicenses.sh b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicenses.sh new file mode 100644 index 000000000..fa97364e8 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicenses.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +python scripts/updateLicense.py $(git ls-files "*\.go" | grep -v thrift-gen | grep -v tracetest) diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/span.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/span.go new file mode 100644 index 000000000..132fb7217 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/span.go @@ -0,0 +1,242 @@ +// 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 jaeger + +import ( + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +// Span implements opentracing.Span +type Span struct { + sync.RWMutex + + tracer *Tracer + + context SpanContext + + // The name of the "operation" this span is an instance of. + // Known as a "span name" in some implementations. + operationName string + + // firstInProcess, if true, indicates that this span is the root of the (sub)tree + // of spans in the current process. In other words it's true for the root spans, + // and the ingress spans when the process joins another trace. + firstInProcess bool + + // startTime is the timestamp indicating when the span began, with microseconds precision. + startTime time.Time + + // duration returns duration of the span with microseconds precision. + // Zero value means duration is unknown. + duration time.Duration + + // tags attached to this span + tags []Tag + + // The span's "micro-log" + logs []opentracing.LogRecord + + // references for this span + references []Reference + + observer ContribSpanObserver +} + +// Tag is a simple key value wrapper. +// TODO deprecate in the next major release, use opentracing.Tag instead. +type Tag struct { + key string + value interface{} +} + +// SetOperationName sets or changes the operation name. +func (s *Span) SetOperationName(operationName string) opentracing.Span { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.operationName = operationName + } + s.observer.OnSetOperationName(operationName) + return s +} + +// SetTag implements SetTag() of opentracing.Span +func (s *Span) SetTag(key string, value interface{}) opentracing.Span { + s.observer.OnSetTag(key, value) + if key == string(ext.SamplingPriority) && setSamplingPriority(s, value) { + return s + } + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.setTagNoLocking(key, value) + } + return s +} + +func (s *Span) setTagNoLocking(key string, value interface{}) { + s.tags = append(s.tags, Tag{key: key, value: value}) +} + +// LogFields implements opentracing.Span API +func (s *Span) LogFields(fields ...log.Field) { + s.Lock() + defer s.Unlock() + if !s.context.IsSampled() { + return + } + s.logFieldsNoLocking(fields...) +} + +// this function should only be called while holding a Write lock +func (s *Span) logFieldsNoLocking(fields ...log.Field) { + lr := opentracing.LogRecord{ + Fields: fields, + Timestamp: time.Now(), + } + s.appendLog(lr) +} + +// LogKV implements opentracing.Span API +func (s *Span) LogKV(alternatingKeyValues ...interface{}) { + s.RLock() + sampled := s.context.IsSampled() + s.RUnlock() + if !sampled { + return + } + fields, err := log.InterleavedKVToFields(alternatingKeyValues...) + if err != nil { + s.LogFields(log.Error(err), log.String("function", "LogKV")) + return + } + s.LogFields(fields...) +} + +// LogEvent implements opentracing.Span API +func (s *Span) LogEvent(event string) { + s.Log(opentracing.LogData{Event: event}) +} + +// LogEventWithPayload implements opentracing.Span API +func (s *Span) LogEventWithPayload(event string, payload interface{}) { + s.Log(opentracing.LogData{Event: event, Payload: payload}) +} + +// Log implements opentracing.Span API +func (s *Span) Log(ld opentracing.LogData) { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + if ld.Timestamp.IsZero() { + ld.Timestamp = s.tracer.timeNow() + } + s.appendLog(ld.ToLogRecord()) + } +} + +// this function should only be called while holding a Write lock +func (s *Span) appendLog(lr opentracing.LogRecord) { + // TODO add logic to limit number of logs per span (issue #46) + s.logs = append(s.logs, lr) +} + +// SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext +func (s *Span) SetBaggageItem(key, value string) opentracing.Span { + s.Lock() + defer s.Unlock() + s.tracer.setBaggage(s, key, value) + return s +} + +// BaggageItem implements BaggageItem() of opentracing.SpanContext +func (s *Span) BaggageItem(key string) string { + s.RLock() + defer s.RUnlock() + return s.context.baggage[key] +} + +// Finish implements opentracing.Span API +func (s *Span) Finish() { + s.FinishWithOptions(opentracing.FinishOptions{}) +} + +// FinishWithOptions implements opentracing.Span API +func (s *Span) FinishWithOptions(options opentracing.FinishOptions) { + if options.FinishTime.IsZero() { + options.FinishTime = s.tracer.timeNow() + } + s.observer.OnFinish(options) + s.Lock() + if s.context.IsSampled() { + s.duration = options.FinishTime.Sub(s.startTime) + // Note: bulk logs are not subject to maxLogsPerSpan limit + if options.LogRecords != nil { + s.logs = append(s.logs, options.LogRecords...) + } + for _, ld := range options.BulkLogData { + s.logs = append(s.logs, ld.ToLogRecord()) + } + } + s.Unlock() + // call reportSpan even for non-sampled traces, to return span to the pool + s.tracer.reportSpan(s) +} + +// Context implements opentracing.Span API +func (s *Span) Context() opentracing.SpanContext { + return s.context +} + +// Tracer implements opentracing.Span API +func (s *Span) Tracer() opentracing.Tracer { + return s.tracer +} + +func (s *Span) String() string { + s.RLock() + defer s.RUnlock() + return s.context.String() +} + +// OperationName allows retrieving current operation name. +func (s *Span) OperationName() string { + s.RLock() + defer s.RUnlock() + return s.operationName +} + +func (s *Span) serviceName() string { + return s.tracer.serviceName +} + +func setSamplingPriority(s *Span, value interface{}) bool { + s.Lock() + defer s.Unlock() + if val, ok := value.(uint16); ok { + if val > 0 { + s.context.flags = s.context.flags | flagDebug | flagSampled + } else { + s.context.flags = s.context.flags & (^flagSampled) + } + return true + } + return false +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/span_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/span_test.go new file mode 100644 index 000000000..471ffac03 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/span_test.go @@ -0,0 +1,90 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBaggageIterator(t *testing.T) { + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + sp1.SetBaggageItem("Some-other-key", "42") + expectedBaggage := map[string]string{"Some_Key": "12345", "Some-other-key": "42"} + assertBaggage(t, sp1, expectedBaggage) + assertBaggageRecords(t, sp1, expectedBaggage) + + b := extractBaggage(sp1, false) // break out early + assert.Equal(t, 1, len(b), "only one baggage item should be extracted") + + sp2 := tracer.StartSpan("s2", opentracing.ChildOf(sp1.Context())).(*Span) + assertBaggage(t, sp2, expectedBaggage) // child inherits the same baggage + require.Len(t, sp2.logs, 0) // child doesn't inherit the baggage logs +} + +func assertBaggageRecords(t *testing.T, sp *Span, expected map[string]string) { + require.Len(t, sp.logs, len(expected)) + for _, logRecord := range sp.logs { + require.Len(t, logRecord.Fields, 3) + require.Equal(t, "event:baggage", logRecord.Fields[0].String()) + key := logRecord.Fields[1].Value().(string) + value := logRecord.Fields[2].Value().(string) + + require.Contains(t, expected, key) + assert.Equal(t, expected[key], value) + } +} + +func assertBaggage(t *testing.T, sp opentracing.Span, expected map[string]string) { + b := extractBaggage(sp, true) + assert.Equal(t, expected, b) +} + +func extractBaggage(sp opentracing.Span, allItems bool) map[string]string { + b := make(map[string]string) + sp.Context().ForeachBaggageItem(func(k, v string) bool { + b[k] = v + return allItems + }) + return b +} + +func TestSpanProperties(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + assert.Equal(t, tracer, sp1.Tracer()) + assert.NotNil(t, sp1.Context()) +} + +func TestSpanOperationName(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetOperationName("s2") + sp1.Finish() + + assert.Equal(t, "s2", sp1.OperationName()) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent.go new file mode 100644 index 000000000..6fa54380d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent.go @@ -0,0 +1,189 @@ +// 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 testutils + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "sync/atomic" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +// StartMockAgent runs a mock representation of jaeger-agent. +// This function returns a started server. +func StartMockAgent() (*MockAgent, error) { + transport, err := NewTUDPServerTransport("127.0.0.1:0") + if err != nil { + return nil, err + } + + samplingManager := newSamplingManager() + samplingHandler := &samplingHandler{manager: samplingManager} + samplingServer := httptest.NewServer(samplingHandler) + + agent := &MockAgent{ + transport: transport, + samplingMgr: samplingManager, + samplingSrv: samplingServer, + } + + var started sync.WaitGroup + started.Add(1) + go agent.serve(&started) + started.Wait() + + return agent, nil +} + +// Close stops the serving of traffic +func (s *MockAgent) Close() { + atomic.StoreUint32(&s.serving, 0) + s.transport.Close() + s.samplingSrv.Close() +} + +// MockAgent is a mock representation of Jaeger Agent. +// It receives spans over UDP, and has an HTTP endpoint for sampling strategies. +type MockAgent struct { + transport *TUDPTransport + jaegerBatches []*jaeger.Batch + mutex sync.Mutex + serving uint32 + samplingMgr *samplingManager + samplingSrv *httptest.Server +} + +// SpanServerAddr returns the UDP host:port where MockAgent listens for spans +func (s *MockAgent) SpanServerAddr() string { + return s.transport.Addr().String() +} + +// SpanServerClient returns a UDP client that can be used to send spans to the MockAgent +func (s *MockAgent) SpanServerClient() (agent.Agent, error) { + return utils.NewAgentClientUDP(s.SpanServerAddr(), 0) +} + +// SamplingServerAddr returns the host:port of HTTP server exposing sampling strategy endpoint +func (s *MockAgent) SamplingServerAddr() string { + return s.samplingSrv.Listener.Addr().String() +} + +func (s *MockAgent) serve(started *sync.WaitGroup) { + handler := agent.NewAgentProcessor(s) + protocolFact := thrift.NewTCompactProtocolFactory() + buf := make([]byte, utils.UDPPacketMaxLength, utils.UDPPacketMaxLength) + trans := thrift.NewTMemoryBufferLen(utils.UDPPacketMaxLength) + + atomic.StoreUint32(&s.serving, 1) + started.Done() + for s.IsServing() { + n, err := s.transport.Read(buf) + if err == nil { + trans.Write(buf[:n]) + protocol := protocolFact.GetProtocol(trans) + handler.Process(protocol, protocol) + } + } +} + +// EmitZipkinBatch is deprecated, use EmitBatch +func (s *MockAgent) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + // TODO remove this for 3.0.0 + return errors.New("Not implemented") +} + +// GetZipkinSpans is deprecated use GetJaegerBatches +func (s *MockAgent) GetZipkinSpans() []*zipkincore.Span { + return nil +} + +// ResetZipkinSpans is deprecated use ResetJaegerBatches +func (s *MockAgent) ResetZipkinSpans() {} + +// EmitBatch implements EmitBatch() of TChanSamplingManagerServer +func (s *MockAgent) EmitBatch(batch *jaeger.Batch) (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = append(s.jaegerBatches, batch) + return err +} + +// IsServing indicates whether the server is currently serving traffic +func (s *MockAgent) IsServing() bool { + return atomic.LoadUint32(&s.serving) == 1 +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *MockAgent) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.samplingMgr.AddSamplingStrategy(service, strategy) +} + +// GetJaegerBatches returns accumulated Jaeger batches +func (s *MockAgent) GetJaegerBatches() []*jaeger.Batch { + s.mutex.Lock() + defer s.mutex.Unlock() + n := len(s.jaegerBatches) + batches := make([]*jaeger.Batch, n, n) + copy(batches, s.jaegerBatches) + return batches +} + +// ResetJaegerBatches discards accumulated Jaeger batches +func (s *MockAgent) ResetJaegerBatches() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = nil +} + +type samplingHandler struct { + manager *samplingManager +} + +func (h *samplingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + services := r.URL.Query()["service"] + if len(services) == 0 { + http.Error(w, "'service' parameter is empty", http.StatusBadRequest) + return + } + if len(services) > 1 { + http.Error(w, "'service' parameter must occur only once", http.StatusBadRequest) + return + } + resp, err := h.manager.GetSamplingStrategy(services[0]) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving strategy: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, "Cannot marshall Thrift to JSON", http.StatusInternalServerError) + return + } + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent_test.go new file mode 100644 index 000000000..4afa83877 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent_test.go @@ -0,0 +1,93 @@ +// 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 testutils + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +func TestMockAgentSpanServer(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + client, err := mockAgent.SpanServerClient() + require.NoError(t, err) + + for i := 1; i < 5; i++ { + batch := &jaeger.Batch{Process: &jaeger.Process{ServiceName: "svc"}} + spans := make([]*jaeger.Span, i, i) + for j := 0; j < i; j++ { + spans[j] = jaeger.NewSpan() + spans[j].OperationName = fmt.Sprintf("span-%d", j) + } + batch.Spans = spans + + err = client.EmitBatch(batch) + assert.NoError(t, err) + + for k := 0; k < 100; k++ { + time.Sleep(time.Millisecond) + batches := mockAgent.GetJaegerBatches() + if len(batches) > 0 && len(batches[0].Spans) == i { + break + } + } + batches := mockAgent.GetJaegerBatches() + require.NotEmpty(t, len(batches)) + require.Equal(t, i, len(batches[0].Spans)) + for j := 0; j < i; j++ { + assert.Equal(t, fmt.Sprintf("span-%d", j), batches[0].Spans[j].OperationName) + } + mockAgent.ResetJaegerBatches() + } +} + +func TestMockAgentSamplingManager(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/", nil) + require.Error(t, err, "no 'service' parameter") + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=a&service=b", nil) + require.Error(t, err, "Too many 'service' parameters") + + var resp sampling.SamplingStrategyResponse + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=something", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_PROBABILISTIC, resp.StrategyType) + + mockAgent.AddSamplingStrategy("service123", &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: 123, + }, + }) + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=service123", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_RATE_LIMITING, resp.StrategyType) + require.NotNil(t, resp.RateLimitingSampling) + assert.EqualValues(t, 123, resp.RateLimitingSampling.MaxTracesPerSecond) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/sampling_manager.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/sampling_manager.go new file mode 100644 index 000000000..6352c510f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/sampling_manager.go @@ -0,0 +1,53 @@ +// 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 testutils + +import ( + "sync" + + "github.com/uber/jaeger-client-go/thrift-gen/sampling" +) + +func newSamplingManager() *samplingManager { + return &samplingManager{ + sampling: make(map[string]*sampling.SamplingStrategyResponse), + } +} + +type samplingManager struct { + sampling map[string]*sampling.SamplingStrategyResponse + mutex sync.Mutex +} + +// GetSamplingStrategy implements handler method of sampling.SamplingManager +func (s *samplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + if strategy, ok := s.sampling[serviceName]; ok { + return strategy, nil + } + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: 0.01, + }}, nil +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *samplingManager) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.sampling[service] = strategy +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport.go new file mode 100644 index 000000000..84daa134d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport.go @@ -0,0 +1,106 @@ +// 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 testutils + +import ( + "bytes" + "net" + "sync/atomic" + + "github.com/apache/thrift/lib/go/thrift" +) + +const ( + // used in RemainingBytes() + maxRemainingBytes = ^uint64(0) +) + +// TUDPTransport does UDP as a thrift.TTransport (read-only, write functions not implemented). +type TUDPTransport struct { + conn *net.UDPConn + addr net.Addr + writeBuf bytes.Buffer + closed uint32 +} + +// NewTUDPServerTransport creates a net.UDPConn-backed TTransport for Thrift servers +// It will listen for incoming udp packets on the specified host/port +// Example: +// trans, err := utils.NewTUDPClientTransport("localhost:9001") +func NewTUDPServerTransport(hostPort string) (*TUDPTransport, error) { + addr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + conn, err := net.ListenUDP(addr.Network(), addr) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + return &TUDPTransport{addr: conn.LocalAddr(), conn: conn}, nil +} + +// Open does nothing as connection is opened on creation +// Required to maintain thrift.TTransport interface +func (p *TUDPTransport) Open() error { + return nil +} + +// Conn retrieves the underlying net.UDPConn +func (p *TUDPTransport) Conn() *net.UDPConn { + return p.conn +} + +// IsOpen returns true if the connection is open +func (p *TUDPTransport) IsOpen() bool { + return p.conn != nil && atomic.LoadUint32(&p.closed) == 0 +} + +// Close closes the connection +func (p *TUDPTransport) Close() error { + if p.conn != nil && atomic.CompareAndSwapUint32(&p.closed, 0, 1) { + return p.conn.Close() + } + return nil +} + +// Addr returns the address that the transport is listening on or writing to +func (p *TUDPTransport) Addr() net.Addr { + return p.addr +} + +// Read reads one UDP packet and puts it in the specified buf +func (p *TUDPTransport) Read(buf []byte) (int, error) { + if !p.IsOpen() { + return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") + } + n, err := p.conn.Read(buf) + return n, thrift.NewTTransportExceptionFromError(err) +} + +// RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we +// do not know how many bytes we have left. +func (p *TUDPTransport) RemainingBytes() uint64 { + return maxRemainingBytes +} + +// Write writes specified buf to the write buffer +func (p *TUDPTransport) Write(buf []byte) (int, error) { + return 0, thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Write not implemented") +} + +// Flush flushes the write buffer as one udp packet +func (p *TUDPTransport) Flush() error { + return thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Flush not implemented") +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport_test.go new file mode 100644 index 000000000..14708b545 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport_test.go @@ -0,0 +1,67 @@ +// 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 testutils + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUDPTransport(t *testing.T) { + server, err := NewTUDPServerTransport("127.0.0.1:0") + require.NoError(t, err) + defer server.Close() + + assert.NoError(t, server.Open()) + assert.True(t, server.IsOpen()) + assert.NotNil(t, server.Conn()) + + c := make(chan []byte) + defer close(c) + + go serveOnce(t, server, c) + + destAddr, err := net.ResolveUDPAddr("udp", server.Addr().String()) + require.NoError(t, err) + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + require.NoError(t, err) + defer connUDP.Close() + + n, err := connUDP.Write([]byte("test")) + assert.NoError(t, err) + assert.Equal(t, 4, n) + + select { + case data := <-c: + assert.Equal(t, "test", string(data)) + case <-time.After(time.Second * 1): + t.Error("Server did not respond in time") + } +} + +func serveOnce(t *testing.T, transport *TUDPTransport, c chan []byte) { + b := make([]byte, 65000, 65000) + n, err := transport.Read(b) + if err == nil { + c <- b[:n] + } else { + panic("Server failed to read: " + err.Error()) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/agent.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/agent.go new file mode 100644 index 000000000..a192cb6f4 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/agent.go @@ -0,0 +1,410 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ + +type Agent interface { + // Parameters: + // - Spans + EmitZipkinBatch(spans []*zipkincore.Span) (err error) + // Parameters: + // - Batch + EmitBatch(batch *jaeger.Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *AgentClient) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + if err = p.sendEmitZipkinBatch(spans); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitZipkinBatch(spans []*zipkincore.Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitZipkinBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *jaeger.Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *jaeger.Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self0 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self0.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler} + self0.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self0 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x1 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x1.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x1 + +} + +type agentProcessorEmitZipkinBatch struct { + handler Agent +} + +func (p *agentProcessorEmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitZipkinBatch(args.Spans); err2 != nil { + return true, err2 + } + return true, nil +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type AgentEmitZipkinBatchArgs struct { + Spans []*zipkincore.Span `thrift:"spans,1" json:"spans"` +} + +func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs { + return &AgentEmitZipkinBatchArgs{} +} + +func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span { + return p.Spans +} +func (p *AgentEmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*zipkincore.Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem2 := &zipkincore.Span{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Spans = append(p.Spans, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *AgentEmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *jaeger.Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch + +func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &jaeger.Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/constants.go new file mode 100644 index 000000000..369d016ea --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/constants.go @@ -0,0 +1,21 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/ttypes.go new file mode 100644 index 000000000..f377c2276 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/ttypes.go @@ -0,0 +1,19 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ +var GoUnusedProtection__ int diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go new file mode 100644 index 000000000..1931cd72c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go @@ -0,0 +1,435 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type BaggageRestrictionManager interface { + // getBaggageRestrictions retrieves the baggage restrictions for a specific service. + // Usually, baggageRestrictions apply to all services however there may be situations + // where a baggageKey might only be allowed to be set by a specific service. + // + // Parameters: + // - ServiceName + GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) +} + +type BaggageRestrictionManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewBaggageRestrictionManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewBaggageRestrictionManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// getBaggageRestrictions retrieves the baggage restrictions for a specific service. +// Usually, baggageRestrictions apply to all services however there may be situations +// where a baggageKey might only be allowed to be set by a specific service. +// +// Parameters: +// - ServiceName +func (p *BaggageRestrictionManagerClient) GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) { + if err = p.sendGetBaggageRestrictions(serviceName); err != nil { + return + } + return p.recvGetBaggageRestrictions() +} + +func (p *BaggageRestrictionManagerClient) sendGetBaggageRestrictions(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.CALL, p.SeqId); err != nil { + return + } + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *BaggageRestrictionManagerClient) recvGetBaggageRestrictions() (value []*BaggageRestriction, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getBaggageRestrictions" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getBaggageRestrictions failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getBaggageRestrictions failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getBaggageRestrictions failed: invalid message type") + return + } + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type BaggageRestrictionManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler BaggageRestrictionManager +} + +func (p *BaggageRestrictionManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *BaggageRestrictionManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *BaggageRestrictionManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewBaggageRestrictionManagerProcessor(handler BaggageRestrictionManager) *BaggageRestrictionManagerProcessor { + + self2 := &BaggageRestrictionManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self2.processorMap["getBaggageRestrictions"] = &baggageRestrictionManagerProcessorGetBaggageRestrictions{handler: handler} + return self2 +} + +func (p *BaggageRestrictionManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x3.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x3 + +} + +type baggageRestrictionManagerProcessorGetBaggageRestrictions struct { + handler BaggageRestrictionManager +} + +func (p *baggageRestrictionManagerProcessorGetBaggageRestrictions) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + var retval []*BaggageRestriction + var err2 error + if retval, err2 = p.handler.GetBaggageRestrictions(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getBaggageRestrictions: "+err2.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type BaggageRestrictionManagerGetBaggageRestrictionsArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsArgs() *BaggageRestrictionManagerGetBaggageRestrictionsArgs { + return &BaggageRestrictionManagerGetBaggageRestrictionsArgs{} +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) GetServiceName() string { + return p.ServiceName +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsArgs(%+v)", *p) +} + +// Attributes: +// - Success +type BaggageRestrictionManagerGetBaggageRestrictionsResult struct { + Success []*BaggageRestriction `thrift:"success,0" json:"success,omitempty"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsResult() *BaggageRestrictionManagerGetBaggageRestrictionsResult { + return &BaggageRestrictionManagerGetBaggageRestrictionsResult{} +} + +var BaggageRestrictionManagerGetBaggageRestrictionsResult_Success_DEFAULT []*BaggageRestriction + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) GetSuccess() []*BaggageRestriction { + return p.Success +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BaggageRestriction, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem4 := &BaggageRestriction{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Success = append(p.Success, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsResult(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/constants.go new file mode 100644 index 000000000..6668424a5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/ttypes.go new file mode 100644 index 000000000..be442fbd9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/baggage/ttypes.go @@ -0,0 +1,154 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - BaggageKey +// - MaxValueLength +type BaggageRestriction struct { + BaggageKey string `thrift:"baggageKey,1,required" json:"baggageKey"` + MaxValueLength int32 `thrift:"maxValueLength,2,required" json:"maxValueLength"` +} + +func NewBaggageRestriction() *BaggageRestriction { + return &BaggageRestriction{} +} + +func (p *BaggageRestriction) GetBaggageKey() string { + return p.BaggageKey +} + +func (p *BaggageRestriction) GetMaxValueLength() int32 { + return p.MaxValueLength +} +func (p *BaggageRestriction) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetBaggageKey bool = false + var issetMaxValueLength bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetBaggageKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetMaxValueLength = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetBaggageKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field BaggageKey is not set")) + } + if !issetMaxValueLength { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxValueLength is not set")) + } + return nil +} + +func (p *BaggageRestriction) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.BaggageKey = v + } + return nil +} + +func (p *BaggageRestriction) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.MaxValueLength = v + } + return nil +} + +func (p *BaggageRestriction) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BaggageRestriction"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestriction) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggageKey", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:baggageKey: ", p), err) + } + if err := oprot.WriteString(string(p.BaggageKey)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggageKey (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:baggageKey: ", p), err) + } + return err +} + +func (p *BaggageRestriction) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxValueLength", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:maxValueLength: ", p), err) + } + if err := oprot.WriteI32(int32(p.MaxValueLength)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxValueLength (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:maxValueLength: ", p), err) + } + return err +} + +func (p *BaggageRestriction) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestriction(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/agent.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/agent.go new file mode 100644 index 000000000..db6cac9fc --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/agent.go @@ -0,0 +1,242 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Agent interface { + // Parameters: + // - Batch + EmitBatch(batch *Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self6 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self6.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self6 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x7.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x7 + +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *Batch + +func (p *AgentEmitBatchArgs) GetBatch() *Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/constants.go new file mode 100644 index 000000000..250474222 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/ttypes.go new file mode 100644 index 000000000..b5ddfa645 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/jaeger/ttypes.go @@ -0,0 +1,1838 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type TagType int64 + +const ( + TagType_STRING TagType = 0 + TagType_DOUBLE TagType = 1 + TagType_BOOL TagType = 2 + TagType_LONG TagType = 3 + TagType_BINARY TagType = 4 +) + +func (p TagType) String() string { + switch p { + case TagType_STRING: + return "STRING" + case TagType_DOUBLE: + return "DOUBLE" + case TagType_BOOL: + return "BOOL" + case TagType_LONG: + return "LONG" + case TagType_BINARY: + return "BINARY" + } + return "" +} + +func TagTypeFromString(s string) (TagType, error) { + switch s { + case "STRING": + return TagType_STRING, nil + case "DOUBLE": + return TagType_DOUBLE, nil + case "BOOL": + return TagType_BOOL, nil + case "LONG": + return TagType_LONG, nil + case "BINARY": + return TagType_BINARY, nil + } + return TagType(0), fmt.Errorf("not a valid TagType string") +} + +func TagTypePtr(v TagType) *TagType { return &v } + +func (p TagType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *TagType) UnmarshalText(text []byte) error { + q, err := TagTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +type SpanRefType int64 + +const ( + SpanRefType_CHILD_OF SpanRefType = 0 + SpanRefType_FOLLOWS_FROM SpanRefType = 1 +) + +func (p SpanRefType) String() string { + switch p { + case SpanRefType_CHILD_OF: + return "CHILD_OF" + case SpanRefType_FOLLOWS_FROM: + return "FOLLOWS_FROM" + } + return "" +} + +func SpanRefTypeFromString(s string) (SpanRefType, error) { + switch s { + case "CHILD_OF": + return SpanRefType_CHILD_OF, nil + case "FOLLOWS_FROM": + return SpanRefType_FOLLOWS_FROM, nil + } + return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") +} + +func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } + +func (p SpanRefType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SpanRefType) UnmarshalText(text []byte) error { + q, err := SpanRefTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - Key +// - VType +// - VStr +// - VDouble +// - VBool +// - VLong +// - VBinary +type Tag struct { + Key string `thrift:"key,1,required" json:"key"` + VType TagType `thrift:"vType,2,required" json:"vType"` + VStr *string `thrift:"vStr,3" json:"vStr,omitempty"` + VDouble *float64 `thrift:"vDouble,4" json:"vDouble,omitempty"` + VBool *bool `thrift:"vBool,5" json:"vBool,omitempty"` + VLong *int64 `thrift:"vLong,6" json:"vLong,omitempty"` + VBinary []byte `thrift:"vBinary,7" json:"vBinary,omitempty"` +} + +func NewTag() *Tag { + return &Tag{} +} + +func (p *Tag) GetKey() string { + return p.Key +} + +func (p *Tag) GetVType() TagType { + return p.VType +} + +var Tag_VStr_DEFAULT string + +func (p *Tag) GetVStr() string { + if !p.IsSetVStr() { + return Tag_VStr_DEFAULT + } + return *p.VStr +} + +var Tag_VDouble_DEFAULT float64 + +func (p *Tag) GetVDouble() float64 { + if !p.IsSetVDouble() { + return Tag_VDouble_DEFAULT + } + return *p.VDouble +} + +var Tag_VBool_DEFAULT bool + +func (p *Tag) GetVBool() bool { + if !p.IsSetVBool() { + return Tag_VBool_DEFAULT + } + return *p.VBool +} + +var Tag_VLong_DEFAULT int64 + +func (p *Tag) GetVLong() int64 { + if !p.IsSetVLong() { + return Tag_VLong_DEFAULT + } + return *p.VLong +} + +var Tag_VBinary_DEFAULT []byte + +func (p *Tag) GetVBinary() []byte { + return p.VBinary +} +func (p *Tag) IsSetVStr() bool { + return p.VStr != nil +} + +func (p *Tag) IsSetVDouble() bool { + return p.VDouble != nil +} + +func (p *Tag) IsSetVBool() bool { + return p.VBool != nil +} + +func (p *Tag) IsSetVLong() bool { + return p.VLong != nil +} + +func (p *Tag) IsSetVBinary() bool { + return p.VBinary != nil +} + +func (p *Tag) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetKey bool = false + var issetVType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetVType = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) + } + if !issetVType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")) + } + return nil +} + +func (p *Tag) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *Tag) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + temp := TagType(v) + p.VType = temp + } + return nil +} + +func (p *Tag) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.VStr = &v + } + return nil +} + +func (p *Tag) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.VDouble = &v + } + return nil +} + +func (p *Tag) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.VBool = &v + } + return nil +} + +func (p *Tag) readField6(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 6: ", err) + } else { + p.VLong = &v + } + return nil +} + +func (p *Tag) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.VBinary = v + } + return nil +} + +func (p *Tag) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Tag"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Tag) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *Tag) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("vType", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) + } + if err := oprot.WriteI32(int32(p.VType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) + } + return err +} + +func (p *Tag) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetVStr() { + if err := oprot.WriteFieldBegin("vStr", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) + } + if err := oprot.WriteString(string(*p.VStr)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) + } + } + return err +} + +func (p *Tag) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetVDouble() { + if err := oprot.WriteFieldBegin("vDouble", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.VDouble)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) + } + } + return err +} + +func (p *Tag) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetVBool() { + if err := oprot.WriteFieldBegin("vBool", thrift.BOOL, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) + } + if err := oprot.WriteBool(bool(*p.VBool)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) + } + } + return err +} + +func (p *Tag) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetVLong() { + if err := oprot.WriteFieldBegin("vLong", thrift.I64, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) + } + if err := oprot.WriteI64(int64(*p.VLong)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) + } + } + return err +} + +func (p *Tag) writeField7(oprot thrift.TProtocol) (err error) { + if p.IsSetVBinary() { + if err := oprot.WriteFieldBegin("vBinary", thrift.STRING, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) + } + if err := oprot.WriteBinary(p.VBinary); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) + } + } + return err +} + +func (p *Tag) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Tag(%+v)", *p) +} + +// Attributes: +// - Timestamp +// - Fields +type Log struct { + Timestamp int64 `thrift:"timestamp,1,required" json:"timestamp"` + Fields []*Tag `thrift:"fields,2,required" json:"fields"` +} + +func NewLog() *Log { + return &Log{} +} + +func (p *Log) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Log) GetFields() []*Tag { + return p.Fields +} +func (p *Log) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTimestamp bool = false + var issetFields bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTimestamp = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetFields = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTimestamp { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) + } + if !issetFields { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")) + } + return nil +} + +func (p *Log) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Log) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Fields = tSlice + for i := 0; i < size; i++ { + _elem0 := &Tag{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Fields = append(p.Fields, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Log) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Log"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Log) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Log) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("fields", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Fields)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Fields { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) + } + return err +} + +func (p *Log) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Log(%+v)", *p) +} + +// Attributes: +// - RefType +// - TraceIdLow +// - TraceIdHigh +// - SpanId +type SpanRef struct { + RefType SpanRefType `thrift:"refType,1,required" json:"refType"` + TraceIdLow int64 `thrift:"traceIdLow,2,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,3,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,4,required" json:"spanId"` +} + +func NewSpanRef() *SpanRef { + return &SpanRef{} +} + +func (p *SpanRef) GetRefType() SpanRefType { + return p.RefType +} + +func (p *SpanRef) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *SpanRef) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *SpanRef) GetSpanId() int64 { + return p.SpanId +} +func (p *SpanRef) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetRefType bool = false + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetRefType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetSpanId = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetRefType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + return nil +} + +func (p *SpanRef) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SpanRefType(v) + p.RefType = temp + } + return nil +} + +func (p *SpanRef) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *SpanRef) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *SpanRef) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *SpanRef) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SpanRef"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + 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) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SpanRef) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("refType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) + } + if err := oprot.WriteI32(int32(p.RefType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) + } + return err +} + +func (p *SpanRef) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) + } + return err +} + +func (p *SpanRef) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) + } + return err +} + +func (p *SpanRef) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) + } + return err +} + +func (p *SpanRef) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SpanRef(%+v)", *p) +} + +// Attributes: +// - TraceIdLow +// - TraceIdHigh +// - SpanId +// - ParentSpanId +// - OperationName +// - References +// - Flags +// - StartTime +// - Duration +// - Tags +// - Logs +type Span struct { + TraceIdLow int64 `thrift:"traceIdLow,1,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,2,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,3,required" json:"spanId"` + ParentSpanId int64 `thrift:"parentSpanId,4,required" json:"parentSpanId"` + OperationName string `thrift:"operationName,5,required" json:"operationName"` + References []*SpanRef `thrift:"references,6" json:"references,omitempty"` + Flags int32 `thrift:"flags,7,required" json:"flags"` + StartTime int64 `thrift:"startTime,8,required" json:"startTime"` + Duration int64 `thrift:"duration,9,required" json:"duration"` + Tags []*Tag `thrift:"tags,10" json:"tags,omitempty"` + Logs []*Log `thrift:"logs,11" json:"logs,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *Span) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *Span) GetSpanId() int64 { + return p.SpanId +} + +func (p *Span) GetParentSpanId() int64 { + return p.ParentSpanId +} + +func (p *Span) GetOperationName() string { + return p.OperationName +} + +var Span_References_DEFAULT []*SpanRef + +func (p *Span) GetReferences() []*SpanRef { + return p.References +} + +func (p *Span) GetFlags() int32 { + return p.Flags +} + +func (p *Span) GetStartTime() int64 { + return p.StartTime +} + +func (p *Span) GetDuration() int64 { + return p.Duration +} + +var Span_Tags_DEFAULT []*Tag + +func (p *Span) GetTags() []*Tag { + return p.Tags +} + +var Span_Logs_DEFAULT []*Log + +func (p *Span) GetLogs() []*Log { + return p.Logs +} +func (p *Span) IsSetReferences() bool { + return p.References != nil +} + +func (p *Span) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Span) IsSetLogs() bool { + return p.Logs != 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) + } + + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + var issetParentSpanId bool = false + var issetOperationName bool = false + var issetFlags bool = false + var issetStartTime bool = false + var issetDuration bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetSpanId = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetParentSpanId = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetOperationName = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + issetFlags = true + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + issetStartTime = true + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + issetDuration = true + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + if !issetParentSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")) + } + if !issetOperationName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")) + } + if !issetFlags { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")) + } + if !issetStartTime { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")) + } + if !issetDuration { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *Span) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ParentSpanId = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.OperationName = v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*SpanRef, 0, size) + p.References = tSlice + for i := 0; i < size; i++ { + _elem1 := &SpanRef{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.References = append(p.References, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.Flags = v + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 8: ", err) + } else { + p.StartTime = v + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Duration = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem2 := &Tag{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Tags = append(p.Tags, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Log, 0, size) + p.Logs = tSlice + for i := 0; i < size; i++ { + _elem3 := &Log{} + if err := _elem3.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) + } + p.Logs = append(p.Logs, _elem3) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + 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) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) + } + return err +} + +func (p *Span) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("parentSpanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.ParentSpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operationName", thrift.STRING, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) + } + if err := oprot.WriteString(string(p.OperationName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetReferences() { + if err := oprot.WriteFieldBegin("references", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.References)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.References { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) + } + } + return err +} + +func (p *Span) writeField7(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("flags", thrift.I32, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) + } + if err := oprot.WriteI32(int32(p.Flags)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("startTime", thrift.I64, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) + } + if err := oprot.WriteI64(int64(p.StartTime)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) + } + if err := oprot.WriteI64(int64(p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetLogs() { + if err := oprot.WriteFieldBegin("logs", thrift.LIST, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Logs)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Logs { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - ServiceName +// - Tags +type Process struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + Tags []*Tag `thrift:"tags,2" json:"tags,omitempty"` +} + +func NewProcess() *Process { + return &Process{} +} + +func (p *Process) GetServiceName() string { + return p.ServiceName +} + +var Process_Tags_DEFAULT []*Tag + +func (p *Process) GetTags() []*Tag { + return p.Tags +} +func (p *Process) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Process) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *Process) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Process) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem4 := &Tag{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Tags = append(p.Tags, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Process) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Process"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Process) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Process) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) + } + } + return err +} + +func (p *Process) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Process(%+v)", *p) +} + +// Attributes: +// - Process +// - Spans +type Batch struct { + Process *Process `thrift:"process,1,required" json:"process"` + Spans []*Span `thrift:"spans,2,required" json:"spans"` +} + +func NewBatch() *Batch { + return &Batch{} +} + +var Batch_Process_DEFAULT *Process + +func (p *Batch) GetProcess() *Process { + if !p.IsSetProcess() { + return Batch_Process_DEFAULT + } + return p.Process +} + +func (p *Batch) GetSpans() []*Span { + return p.Spans +} +func (p *Batch) IsSetProcess() bool { + return p.Process != nil +} + +func (p *Batch) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetProcess bool = false + var issetSpans bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetProcess = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSpans = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetProcess { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")) + } + if !issetSpans { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")) + } + return nil +} + +func (p *Batch) readField1(iprot thrift.TProtocol) error { + p.Process = &Process{} + if err := p.Process.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) + } + return nil +} + +func (p *Batch) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem5 := &Span{} + if err := _elem5.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem5), err) + } + p.Spans = append(p.Spans, _elem5) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Batch) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Batch"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Batch) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("process", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) + } + if err := p.Process.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) + } + return err +} + +func (p *Batch) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) + } + return err +} + +func (p *Batch) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Batch(%+v)", *p) +} + +// Attributes: +// - Ok +type BatchSubmitResponse struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewBatchSubmitResponse() *BatchSubmitResponse { + return &BatchSubmitResponse{} +} + +func (p *BatchSubmitResponse) GetOk() bool { + return p.Ok +} +func (p *BatchSubmitResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *BatchSubmitResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *BatchSubmitResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BatchSubmitResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BatchSubmitResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *BatchSubmitResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/constants.go new file mode 100644 index 000000000..728988b83 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/samplingmanager.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/samplingmanager.go new file mode 100644 index 000000000..563e2b4c9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/samplingmanager.go @@ -0,0 +1,410 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type SamplingManager interface { + // Parameters: + // - ServiceName + GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) +} + +type SamplingManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSamplingManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSamplingManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - ServiceName +func (p *SamplingManagerClient) GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) { + if err = p.sendGetSamplingStrategy(serviceName); err != nil { + return + } + return p.recvGetSamplingStrategy() +} + +func (p *SamplingManagerClient) sendGetSamplingStrategy(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getSamplingStrategy", thrift.CALL, p.SeqId); err != nil { + return + } + args := SamplingManagerGetSamplingStrategyArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SamplingManagerClient) recvGetSamplingStrategy() (value *SamplingStrategyResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getSamplingStrategy" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getSamplingStrategy failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getSamplingStrategy failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error1 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error2 error + error2, err = error1.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error2 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getSamplingStrategy failed: invalid message type") + return + } + result := SamplingManagerGetSamplingStrategyResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type SamplingManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler SamplingManager +} + +func (p *SamplingManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SamplingManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SamplingManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSamplingManagerProcessor(handler SamplingManager) *SamplingManagerProcessor { + + self3 := &SamplingManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self3.processorMap["getSamplingStrategy"] = &samplingManagerProcessorGetSamplingStrategy{handler: handler} + return self3 +} + +func (p *SamplingManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x4.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x4 + +} + +type samplingManagerProcessorGetSamplingStrategy struct { + handler SamplingManager +} + +func (p *samplingManagerProcessorGetSamplingStrategy) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SamplingManagerGetSamplingStrategyArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SamplingManagerGetSamplingStrategyResult{} + var retval *SamplingStrategyResponse + var err2 error + if retval, err2 = p.handler.GetSamplingStrategy(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getSamplingStrategy: "+err2.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getSamplingStrategy", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type SamplingManagerGetSamplingStrategyArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewSamplingManagerGetSamplingStrategyArgs() *SamplingManagerGetSamplingStrategyArgs { + return &SamplingManagerGetSamplingStrategyArgs{} +} + +func (p *SamplingManagerGetSamplingStrategyArgs) GetServiceName() string { + return p.ServiceName +} +func (p *SamplingManagerGetSamplingStrategyArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyArgs(%+v)", *p) +} + +// Attributes: +// - Success +type SamplingManagerGetSamplingStrategyResult struct { + Success *SamplingStrategyResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewSamplingManagerGetSamplingStrategyResult() *SamplingManagerGetSamplingStrategyResult { + return &SamplingManagerGetSamplingStrategyResult{} +} + +var SamplingManagerGetSamplingStrategyResult_Success_DEFAULT *SamplingStrategyResponse + +func (p *SamplingManagerGetSamplingStrategyResult) GetSuccess() *SamplingStrategyResponse { + if !p.IsSetSuccess() { + return SamplingManagerGetSamplingStrategyResult_Success_DEFAULT + } + return p.Success +} +func (p *SamplingManagerGetSamplingStrategyResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) readField0(iprot thrift.TProtocol) error { + p.Success = &SamplingStrategyResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyResult(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/ttypes.go new file mode 100644 index 000000000..3e831af4a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/sampling/ttypes.go @@ -0,0 +1,873 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type SamplingStrategyType int64 + +const ( + SamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0 + SamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1 +) + +func (p SamplingStrategyType) String() string { + switch p { + case SamplingStrategyType_PROBABILISTIC: + return "PROBABILISTIC" + case SamplingStrategyType_RATE_LIMITING: + return "RATE_LIMITING" + } + return "" +} + +func SamplingStrategyTypeFromString(s string) (SamplingStrategyType, error) { + switch s { + case "PROBABILISTIC": + return SamplingStrategyType_PROBABILISTIC, nil + case "RATE_LIMITING": + return SamplingStrategyType_RATE_LIMITING, nil + } + return SamplingStrategyType(0), fmt.Errorf("not a valid SamplingStrategyType string") +} + +func SamplingStrategyTypePtr(v SamplingStrategyType) *SamplingStrategyType { return &v } + +func (p SamplingStrategyType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SamplingStrategyType) UnmarshalText(text []byte) error { + q, err := SamplingStrategyTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - SamplingRate +type ProbabilisticSamplingStrategy struct { + SamplingRate float64 `thrift:"samplingRate,1,required" json:"samplingRate"` +} + +func NewProbabilisticSamplingStrategy() *ProbabilisticSamplingStrategy { + return &ProbabilisticSamplingStrategy{} +} + +func (p *ProbabilisticSamplingStrategy) GetSamplingRate() float64 { + return p.SamplingRate +} +func (p *ProbabilisticSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetSamplingRate bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetSamplingRate = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetSamplingRate { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SamplingRate is not set")) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.SamplingRate = v + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ProbabilisticSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("samplingRate", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:samplingRate: ", p), err) + } + if err := oprot.WriteDouble(float64(p.SamplingRate)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.samplingRate (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:samplingRate: ", p), err) + } + return err +} + +func (p *ProbabilisticSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ProbabilisticSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - MaxTracesPerSecond +type RateLimitingSamplingStrategy struct { + MaxTracesPerSecond int16 `thrift:"maxTracesPerSecond,1,required" json:"maxTracesPerSecond"` +} + +func NewRateLimitingSamplingStrategy() *RateLimitingSamplingStrategy { + return &RateLimitingSamplingStrategy{} +} + +func (p *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int16 { + return p.MaxTracesPerSecond +} +func (p *RateLimitingSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetMaxTracesPerSecond bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetMaxTracesPerSecond = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetMaxTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxTracesPerSecond is not set")) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.MaxTracesPerSecond = v + } + return nil +} + +func (p *RateLimitingSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("RateLimitingSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxTracesPerSecond", thrift.I16, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:maxTracesPerSecond: ", p), err) + } + if err := oprot.WriteI16(int16(p.MaxTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxTracesPerSecond (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:maxTracesPerSecond: ", p), err) + } + return err +} + +func (p *RateLimitingSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("RateLimitingSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - Operation +// - ProbabilisticSampling +type OperationSamplingStrategy struct { + Operation string `thrift:"operation,1,required" json:"operation"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2,required" json:"probabilisticSampling"` +} + +func NewOperationSamplingStrategy() *OperationSamplingStrategy { + return &OperationSamplingStrategy{} +} + +func (p *OperationSamplingStrategy) GetOperation() string { + return p.Operation +} + +var OperationSamplingStrategy_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *OperationSamplingStrategy) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return OperationSamplingStrategy_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} +func (p *OperationSamplingStrategy) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *OperationSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOperation bool = false + var issetProbabilisticSampling bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOperation = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetProbabilisticSampling = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOperation { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Operation is not set")) + } + if !issetProbabilisticSampling { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ProbabilisticSampling is not set")) + } + return nil +} + +func (p *OperationSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Operation = v + } + return nil +} + +func (p *OperationSamplingStrategy) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *OperationSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("OperationSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *OperationSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operation", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:operation: ", p), err) + } + if err := oprot.WriteString(string(p.Operation)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operation (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:operation: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("OperationSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - DefaultSamplingProbability +// - DefaultLowerBoundTracesPerSecond +// - PerOperationStrategies +// - DefaultUpperBoundTracesPerSecond +type PerOperationSamplingStrategies struct { + DefaultSamplingProbability float64 `thrift:"defaultSamplingProbability,1,required" json:"defaultSamplingProbability"` + DefaultLowerBoundTracesPerSecond float64 `thrift:"defaultLowerBoundTracesPerSecond,2,required" json:"defaultLowerBoundTracesPerSecond"` + PerOperationStrategies []*OperationSamplingStrategy `thrift:"perOperationStrategies,3,required" json:"perOperationStrategies"` + DefaultUpperBoundTracesPerSecond *float64 `thrift:"defaultUpperBoundTracesPerSecond,4" json:"defaultUpperBoundTracesPerSecond,omitempty"` +} + +func NewPerOperationSamplingStrategies() *PerOperationSamplingStrategies { + return &PerOperationSamplingStrategies{} +} + +func (p *PerOperationSamplingStrategies) GetDefaultSamplingProbability() float64 { + return p.DefaultSamplingProbability +} + +func (p *PerOperationSamplingStrategies) GetDefaultLowerBoundTracesPerSecond() float64 { + return p.DefaultLowerBoundTracesPerSecond +} + +func (p *PerOperationSamplingStrategies) GetPerOperationStrategies() []*OperationSamplingStrategy { + return p.PerOperationStrategies +} + +var PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT float64 + +func (p *PerOperationSamplingStrategies) GetDefaultUpperBoundTracesPerSecond() float64 { + if !p.IsSetDefaultUpperBoundTracesPerSecond() { + return PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT + } + return *p.DefaultUpperBoundTracesPerSecond +} +func (p *PerOperationSamplingStrategies) IsSetDefaultUpperBoundTracesPerSecond() bool { + return p.DefaultUpperBoundTracesPerSecond != nil +} + +func (p *PerOperationSamplingStrategies) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetDefaultSamplingProbability bool = false + var issetDefaultLowerBoundTracesPerSecond bool = false + var issetPerOperationStrategies bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetDefaultSamplingProbability = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetDefaultLowerBoundTracesPerSecond = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetPerOperationStrategies = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetDefaultSamplingProbability { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultSamplingProbability is not set")) + } + if !issetDefaultLowerBoundTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultLowerBoundTracesPerSecond is not set")) + } + if !issetPerOperationStrategies { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field PerOperationStrategies is not set")) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.DefaultSamplingProbability = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.DefaultLowerBoundTracesPerSecond = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField3(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*OperationSamplingStrategy, 0, size) + p.PerOperationStrategies = tSlice + for i := 0; i < size; i++ { + _elem0 := &OperationSamplingStrategy{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.PerOperationStrategies = append(p.PerOperationStrategies, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.DefaultUpperBoundTracesPerSecond = &v + } + return nil +} + +func (p *PerOperationSamplingStrategies) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("PerOperationSamplingStrategies"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + 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) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultSamplingProbability", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:defaultSamplingProbability: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultSamplingProbability)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultSamplingProbability (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:defaultSamplingProbability: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultLowerBoundTracesPerSecond", thrift.DOUBLE, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultLowerBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultLowerBoundTracesPerSecond (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("perOperationStrategies", thrift.LIST, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:perOperationStrategies: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.PerOperationStrategies)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.PerOperationStrategies { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:perOperationStrategies: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetDefaultUpperBoundTracesPerSecond() { + if err := oprot.WriteFieldBegin("defaultUpperBoundTracesPerSecond", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.DefaultUpperBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultUpperBoundTracesPerSecond (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + } + return err +} + +func (p *PerOperationSamplingStrategies) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("PerOperationSamplingStrategies(%+v)", *p) +} + +// Attributes: +// - StrategyType +// - ProbabilisticSampling +// - RateLimitingSampling +// - OperationSampling +type SamplingStrategyResponse struct { + StrategyType SamplingStrategyType `thrift:"strategyType,1,required" json:"strategyType"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2" json:"probabilisticSampling,omitempty"` + RateLimitingSampling *RateLimitingSamplingStrategy `thrift:"rateLimitingSampling,3" json:"rateLimitingSampling,omitempty"` + OperationSampling *PerOperationSamplingStrategies `thrift:"operationSampling,4" json:"operationSampling,omitempty"` +} + +func NewSamplingStrategyResponse() *SamplingStrategyResponse { + return &SamplingStrategyResponse{} +} + +func (p *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType { + return p.StrategyType +} + +var SamplingStrategyResponse_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return SamplingStrategyResponse_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} + +var SamplingStrategyResponse_RateLimitingSampling_DEFAULT *RateLimitingSamplingStrategy + +func (p *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy { + if !p.IsSetRateLimitingSampling() { + return SamplingStrategyResponse_RateLimitingSampling_DEFAULT + } + return p.RateLimitingSampling +} + +var SamplingStrategyResponse_OperationSampling_DEFAULT *PerOperationSamplingStrategies + +func (p *SamplingStrategyResponse) GetOperationSampling() *PerOperationSamplingStrategies { + if !p.IsSetOperationSampling() { + return SamplingStrategyResponse_OperationSampling_DEFAULT + } + return p.OperationSampling +} +func (p *SamplingStrategyResponse) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetRateLimitingSampling() bool { + return p.RateLimitingSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetOperationSampling() bool { + return p.OperationSampling != nil +} + +func (p *SamplingStrategyResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetStrategyType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetStrategyType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + 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 + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetStrategyType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StrategyType is not set")) + } + return nil +} + +func (p *SamplingStrategyResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SamplingStrategyType(v) + p.StrategyType = temp + } + return nil +} + +func (p *SamplingStrategyResponse) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField3(iprot thrift.TProtocol) error { + p.RateLimitingSampling = &RateLimitingSamplingStrategy{} + if err := p.RateLimitingSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.RateLimitingSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField4(iprot thrift.TProtocol) error { + p.OperationSampling = &PerOperationSamplingStrategies{} + if err := p.OperationSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.OperationSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SamplingStrategyResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + 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) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingStrategyResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("strategyType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:strategyType: ", p), err) + } + if err := oprot.WriteI32(int32(p.StrategyType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.strategyType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:strategyType: ", p), err) + } + return err +} + +func (p *SamplingStrategyResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetProbabilisticSampling() { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetRateLimitingSampling() { + if err := oprot.WriteFieldBegin("rateLimitingSampling", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:rateLimitingSampling: ", p), err) + } + if err := p.RateLimitingSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.RateLimitingSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:rateLimitingSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetOperationSampling() { + if err := oprot.WriteFieldBegin("operationSampling", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:operationSampling: ", p), err) + } + if err := p.OperationSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.OperationSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:operationSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingStrategyResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/constants.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/constants.go new file mode 100644 index 000000000..dafd2b8e9 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/constants.go @@ -0,0 +1,32 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +const CLIENT_SEND = "cs" +const CLIENT_RECV = "cr" +const SERVER_SEND = "ss" +const SERVER_RECV = "sr" +const WIRE_SEND = "ws" +const WIRE_RECV = "wr" +const CLIENT_SEND_FRAGMENT = "csf" +const CLIENT_RECV_FRAGMENT = "crf" +const SERVER_SEND_FRAGMENT = "ssf" +const SERVER_RECV_FRAGMENT = "srf" +const LOCAL_COMPONENT = "lc" +const CLIENT_ADDR = "ca" +const SERVER_ADDR = "sa" + +func init() { +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/ttypes.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/ttypes.go new file mode 100644 index 000000000..50c7a9944 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/ttypes.go @@ -0,0 +1,1247 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type AnnotationType int64 + +const ( + AnnotationType_BOOL AnnotationType = 0 + AnnotationType_BYTES AnnotationType = 1 + AnnotationType_I16 AnnotationType = 2 + AnnotationType_I32 AnnotationType = 3 + AnnotationType_I64 AnnotationType = 4 + AnnotationType_DOUBLE AnnotationType = 5 + AnnotationType_STRING AnnotationType = 6 +) + +func (p AnnotationType) String() string { + switch p { + case AnnotationType_BOOL: + return "BOOL" + case AnnotationType_BYTES: + return "BYTES" + case AnnotationType_I16: + return "I16" + case AnnotationType_I32: + return "I32" + case AnnotationType_I64: + return "I64" + case AnnotationType_DOUBLE: + return "DOUBLE" + case AnnotationType_STRING: + return "STRING" + } + return "" +} + +func AnnotationTypeFromString(s string) (AnnotationType, error) { + switch s { + case "BOOL": + return AnnotationType_BOOL, nil + case "BYTES": + return AnnotationType_BYTES, nil + case "I16": + return AnnotationType_I16, nil + case "I32": + return AnnotationType_I32, nil + case "I64": + return AnnotationType_I64, nil + case "DOUBLE": + return AnnotationType_DOUBLE, nil + case "STRING": + return AnnotationType_STRING, nil + } + return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") +} + +func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } + +func (p AnnotationType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *AnnotationType) UnmarshalText(text []byte) error { + q, err := AnnotationTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Indicates the network context of a service recording an annotation with two +// exceptions. +// +// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, +// the endpoint indicates the source or destination of an RPC. This exception +// allows zipkin to display network context of uninstrumented services, or +// clients such as web browsers. +// +// Attributes: +// - Ipv4: IPv4 host address packed into 4 bytes. +// +// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 +// - Port: IPv4 port +// +// Note: this is to be treated as an unsigned integer, so watch for negatives. +// +// Conventionally, when the port isn't known, port = 0. +// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" +// +// Conventionally, when the service name isn't known, service_name = "unknown". +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"` +} + +func NewEndpoint() *Endpoint { + return &Endpoint{} +} + +func (p *Endpoint) GetIpv4() int32 { + return p.Ipv4 +} + +func (p *Endpoint) GetPort() int16 { + return p.Port +} + +func (p *Endpoint) GetServiceName() string { + return p.ServiceName +} +func (p *Endpoint) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Endpoint) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ipv4 = v + } + return nil +} + +func (p *Endpoint) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Endpoint) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.ServiceName = 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) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Endpoint) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) + } + if err := oprot.WriteI32(int32(p.Ipv4)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) + } + return err +} + +func (p *Endpoint) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.I16, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) + } + if err := oprot.WriteI16(int16(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) + } + return err +} + +func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("service_name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) + } + return err +} + +func (p *Endpoint) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Endpoint(%+v)", *p) +} + +// An annotation is similar to a log statement. It includes a host field which +// allows these events to be attributed properly, and also aggregatable. +// +// Attributes: +// - Timestamp: Microseconds from epoch. +// +// This value should use the most precise value possible. For example, +// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. +// - Value +// - Host: Always the host that recorded the event. By specifying the host you allow +// rollup of all events (such as client requests to a service) by IP address. +type Annotation struct { + Timestamp int64 `thrift:"timestamp,1" json:"timestamp"` + Value string `thrift:"value,2" json:"value"` + Host *Endpoint `thrift:"host,3" json:"host,omitempty"` +} + +func NewAnnotation() *Annotation { + return &Annotation{} +} + +func (p *Annotation) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Annotation) GetValue() string { + return p.Value +} + +var Annotation_Host_DEFAULT *Endpoint + +func (p *Annotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return Annotation_Host_DEFAULT + } + return p.Host +} +func (p *Annotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *Annotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Annotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Annotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *Annotation) readField3(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *Annotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Annotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Annotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Annotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteString(string(p.Value)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *Annotation) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + } + return err +} + +func (p *Annotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Annotation(%+v)", *p) +} + +// Binary annotations are tags applied to a Span to give it context. For +// example, a binary annotation of "http.uri" could the path to a resource in a +// RPC call. +// +// Binary annotations of type STRING are always queryable, though more a +// historical implementation detail than a structural concern. +// +// Binary annotations can repeat, and vary on the host. Similar to Annotation, +// the host indicates who logged the event. This allows you to tell the +// difference between the client and server side of the same key. For example, +// the key "http.uri" might be different on the client and server side due to +// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, +// you can see the different points of view, which often help in debugging. +// +// Attributes: +// - Key +// - Value +// - AnnotationType +// - Host: The host that recorded tag, which allows you to differentiate between +// multiple tags with the same key. There are two exceptions to this. +// +// When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or +// destination of an RPC. This exception allows zipkin to display network +// context of uninstrumented services, or clients such as web browsers. +type BinaryAnnotation struct { + Key string `thrift:"key,1" json:"key"` + Value []byte `thrift:"value,2" json:"value"` + AnnotationType AnnotationType `thrift:"annotation_type,3" json:"annotation_type"` + Host *Endpoint `thrift:"host,4" json:"host,omitempty"` +} + +func NewBinaryAnnotation() *BinaryAnnotation { + return &BinaryAnnotation{} +} + +func (p *BinaryAnnotation) GetKey() string { + return p.Key +} + +func (p *BinaryAnnotation) GetValue() []byte { + return p.Value +} + +func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { + return p.AnnotationType +} + +var BinaryAnnotation_Host_DEFAULT *Endpoint + +func (p *BinaryAnnotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return BinaryAnnotation_Host_DEFAULT + } + return p.Host +} +func (p *BinaryAnnotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *BinaryAnnotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + 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 + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BinaryAnnotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *BinaryAnnotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *BinaryAnnotation) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + temp := AnnotationType(v) + p.AnnotationType = temp + } + return nil +} + +func (p *BinaryAnnotation) readField4(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *BinaryAnnotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BinaryAnnotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + 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) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BinaryAnnotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteBinary(p.Value); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotation_type", thrift.I32, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) + } + if err := oprot.WriteI32(int32(p.AnnotationType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) + } + } + return err +} + +func (p *BinaryAnnotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BinaryAnnotation(%+v)", *p) +} + +// A trace is a series of spans (often RPC calls) which form a latency tree. +// +// The root span is where trace_id = id and parent_id = Nil. The root span is +// usually the longest interval in the trace, starting with a SERVER_RECV +// annotation and ending with a SERVER_SEND. +// +// Attributes: +// - TraceID +// - Name: Span name in lowercase, rpc method for example +// +// Conventionally, when the span name isn't known, name = "unknown". +// - ID +// - ParentID +// - Annotations +// - BinaryAnnotations +// - Debug +// - Timestamp: Microseconds from epoch of the creation of this span. +// +// This value should be set directly by instrumentation, using the most +// precise value possible. For example, gettimeofday or syncing nanoTime +// against a tick of currentTimeMillis. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this via Annotation.timestamp. +// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. +// +// This field is optional for compatibility with old data: first-party span +// stores are expected to support this at time of introduction. +// - Duration: Measurement of duration in microseconds, used to support queries. +// +// This value should be set directly, where possible. Doing so encourages +// precise measurement decoupled from problems of clocks, such as skew or NTP +// updates causing time to move backwards. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this by subtracting Annotation.timestamp. +// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. +// +// If this field is persisted as unset, zipkin will continue to work, except +// duration query support will be implementation-specific. Similarly, setting +// this field non-atomically is implementation-specific. +// +// This field is i64 vs i32 to support spans longer than 35 minutes. +type Span struct { + TraceID int64 `thrift:"trace_id,1" json:"trace_id"` + // unused field # 2 + Name string `thrift:"name,3" json:"name"` + ID int64 `thrift:"id,4" json:"id"` + ParentID *int64 `thrift:"parent_id,5" json:"parent_id,omitempty"` + Annotations []*Annotation `thrift:"annotations,6" json:"annotations"` + // unused field # 7 + BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" json:"binary_annotations"` + Debug bool `thrift:"debug,9" json:"debug,omitempty"` + Timestamp *int64 `thrift:"timestamp,10" json:"timestamp,omitempty"` + Duration *int64 `thrift:"duration,11" json:"duration,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceID() int64 { + return p.TraceID +} + +func (p *Span) GetName() string { + return p.Name +} + +func (p *Span) GetID() int64 { + return p.ID +} + +var Span_ParentID_DEFAULT int64 + +func (p *Span) GetParentID() int64 { + if !p.IsSetParentID() { + return Span_ParentID_DEFAULT + } + return *p.ParentID +} + +func (p *Span) GetAnnotations() []*Annotation { + return p.Annotations +} + +func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { + return p.BinaryAnnotations +} + +var Span_Debug_DEFAULT bool = false + +func (p *Span) GetDebug() bool { + return p.Debug +} + +var Span_Timestamp_DEFAULT int64 + +func (p *Span) GetTimestamp() int64 { + if !p.IsSetTimestamp() { + return Span_Timestamp_DEFAULT + } + return *p.Timestamp +} + +var Span_Duration_DEFAULT int64 + +func (p *Span) GetDuration() int64 { + if !p.IsSetDuration() { + return Span_Duration_DEFAULT + } + return *p.Duration +} +func (p *Span) IsSetParentID() bool { + return p.ParentID != nil +} + +func (p *Span) IsSetDebug() bool { + return p.Debug != Span_Debug_DEFAULT +} + +func (p *Span) IsSetTimestamp() bool { + return p.Timestamp != nil +} + +func (p *Span) IsSetDuration() bool { + return p.Duration != 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) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceID = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Name = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ID = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.ParentID = &v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Annotation, 0, size) + p.Annotations = tSlice + for i := 0; i < size; i++ { + _elem0 := &Annotation{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Annotations = append(p.Annotations, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BinaryAnnotation, 0, size) + p.BinaryAnnotations = tSlice + for i := 0; i < size; i++ { + _elem1 := &BinaryAnnotation{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Debug = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 10: ", err) + } else { + p.Timestamp = &v + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 11: ", err) + } else { + p.Duration = &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) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("trace_id", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) + } + if err := oprot.WriteString(string(p.Name)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("id", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) + } + if err := oprot.WriteI64(int64(p.ID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetParentID() { + if err := oprot.WriteFieldBegin("parent_id", thrift.I64, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) + } + if err := oprot.WriteI64(int64(*p.ParentID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) + } + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotations", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Annotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Annotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("binary_annotations", thrift.LIST, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.BinaryAnnotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if p.IsSetDebug() { + if err := oprot.WriteFieldBegin("debug", thrift.BOOL, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) + } + if err := oprot.WriteBool(bool(p.Debug)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) + } + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTimestamp() { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetDuration() { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - Ok +type Response struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewResponse() *Response { + return &Response{} +} + +func (p *Response) GetOk() bool { + return p.Ok +} +func (p *Response) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *Response) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *Response) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Response"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Response) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *Response) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Response(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go new file mode 100644 index 000000000..bcc54f36e --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go @@ -0,0 +1,446 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type ZipkinCollector interface { + // Parameters: + // - Spans + SubmitZipkinBatch(spans []*Span) (r []*Response, err error) +} + +type ZipkinCollectorClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *ZipkinCollectorClient) SubmitZipkinBatch(spans []*Span) (r []*Response, err error) { + if err = p.sendSubmitZipkinBatch(spans); err != nil { + return + } + return p.recvSubmitZipkinBatch() +} + +func (p *ZipkinCollectorClient) sendSubmitZipkinBatch(spans []*Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("submitZipkinBatch", thrift.CALL, p.SeqId); err != nil { + return + } + args := ZipkinCollectorSubmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *ZipkinCollectorClient) recvSubmitZipkinBatch() (value []*Response, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "submitZipkinBatch" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submitZipkinBatch failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submitZipkinBatch failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submitZipkinBatch failed: invalid message type") + return + } + result := ZipkinCollectorSubmitZipkinBatchResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type ZipkinCollectorProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler ZipkinCollector +} + +func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { + + self4 := &ZipkinCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler: handler} + return self4 +} + +func (p *ZipkinCollectorProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type zipkinCollectorProcessorSubmitZipkinBatch struct { + handler ZipkinCollector +} + +func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := ZipkinCollectorSubmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := ZipkinCollectorSubmitZipkinBatchResult{} + var retval []*Response + var err2 error + if retval, err2 = p.handler.SubmitZipkinBatch(args.Spans); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: "+err2.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type ZipkinCollectorSubmitZipkinBatchArgs struct { + Spans []*Span `thrift:"spans,1" json:"spans"` +} + +func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { + return &ZipkinCollectorSubmitZipkinBatchArgs{} +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { + return p.Spans +} +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem6 := &Span{} + if err := _elem6.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem6), err) + } + p.Spans = append(p.Spans, _elem6) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Success +type ZipkinCollectorSubmitZipkinBatchResult struct { + Success []*Response `thrift:"success,0" json:"success,omitempty"` +} + +func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { + return &ZipkinCollectorSubmitZipkinBatchResult{} +} + +var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response + +func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { + return p.Success +} +func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Response, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem7 := &Response{} + if err := _elem7.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem7), err) + } + p.Success = append(p.Success, _elem7) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer.go new file mode 100644 index 000000000..1a71364ab --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer.go @@ -0,0 +1,387 @@ +// 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 jaeger + +import ( + "fmt" + "io" + "os" + "reflect" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +// Tracer implements opentracing.Tracer. +type Tracer struct { + serviceName string + hostIPv4 uint32 // this is for zipkin endpoint conversion + + sampler Sampler + reporter Reporter + metrics Metrics + logger log.Logger + + timeNow func() time.Time + randomNumber func() uint64 + + options struct { + poolSpans bool + gen128Bit bool // whether to generate 128bit trace IDs + zipkinSharedRPCSpan bool + // more options to come + } + // pool for Span objects + spanPool sync.Pool + + injectors map[interface{}]Injector + extractors map[interface{}]Extractor + + observer compositeObserver + + tags []Tag + + baggageRestrictionManager baggage.RestrictionManager + baggageSetter *baggageSetter +} + +// NewTracer creates Tracer implementation that reports tracing to Jaeger. +// The returned io.Closer can be used in shutdown hooks to ensure that the internal +// queue of the Reporter is drained and all buffered spans are submitted to collectors. +func NewTracer( + serviceName string, + sampler Sampler, + reporter Reporter, + options ...TracerOption, +) (opentracing.Tracer, io.Closer) { + t := &Tracer{ + serviceName: serviceName, + sampler: sampler, + reporter: reporter, + injectors: make(map[interface{}]Injector), + extractors: make(map[interface{}]Extractor), + metrics: *NewNullMetrics(), + spanPool: sync.Pool{New: func() interface{} { + return &Span{} + }}, + } + + for _, option := range options { + option(t) + } + + // register default injectors/extractors unless they are already provided via options + textPropagator := newTextMapPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + + binaryPropagator := newBinaryPropagator(t) + t.addCodec(opentracing.Binary, binaryPropagator, binaryPropagator) + + // TODO remove after TChannel supports OpenTracing + interopPropagator := &jaegerTraceContextPropagator{tracer: t} + t.addCodec(SpanContextFormat, interopPropagator, interopPropagator) + + zipkinPropagator := &zipkinPropagator{tracer: t} + t.addCodec(ZipkinSpanFormat, zipkinPropagator, zipkinPropagator) + + if t.baggageRestrictionManager != nil { + t.baggageSetter = newBaggageSetter(t.baggageRestrictionManager, &t.metrics) + } else { + t.baggageSetter = newBaggageSetter(baggage.NewDefaultRestrictionManager(0), &t.metrics) + } + if t.randomNumber == nil { + rng := utils.NewRand(time.Now().UnixNano()) + t.randomNumber = func() uint64 { + return uint64(rng.Int63()) + } + } + if t.timeNow == nil { + t.timeNow = time.Now + } + if t.logger == nil { + t.logger = log.NullLogger + } + // Set tracer-level tags + t.tags = append(t.tags, Tag{key: JaegerClientVersionTagKey, value: JaegerClientVersion}) + if hostname, err := os.Hostname(); err == nil { + t.tags = append(t.tags, Tag{key: TracerHostnameTagKey, value: hostname}) + } + if ip, err := utils.HostIP(); err == nil { + t.tags = append(t.tags, Tag{key: TracerIPTagKey, value: ip.String()}) + t.hostIPv4 = utils.PackIPAsUint32(ip) + } else { + t.logger.Error("Unable to determine this host's IP address: " + err.Error()) + } + + return t, t +} + +// addCodec adds registers injector and extractor for given propagation format if not already defined. +func (t *Tracer) addCodec(format interface{}, injector Injector, extractor Extractor) { + if _, ok := t.injectors[format]; !ok { + t.injectors[format] = injector + } + if _, ok := t.extractors[format]; !ok { + t.extractors[format] = extractor + } +} + +// StartSpan implements StartSpan() method of opentracing.Tracer. +func (t *Tracer) StartSpan( + operationName string, + options ...opentracing.StartSpanOption, +) opentracing.Span { + sso := opentracing.StartSpanOptions{} + for _, o := range options { + o.Apply(&sso) + } + return t.startSpanWithOptions(operationName, sso) +} + +func (t *Tracer) startSpanWithOptions( + operationName string, + options opentracing.StartSpanOptions, +) opentracing.Span { + if options.StartTime.IsZero() { + options.StartTime = t.timeNow() + } + + var references []Reference + var parent SpanContext + var hasParent bool // need this because `parent` is a value, not reference + for _, ref := range options.References { + ctx, ok := ref.ReferencedContext.(SpanContext) + if !ok { + t.logger.Error(fmt.Sprintf( + "Reference contains invalid type of SpanReference: %s", + reflect.ValueOf(ref.ReferencedContext))) + continue + } + if !(ctx.IsValid() || ctx.isDebugIDContainerOnly() || len(ctx.baggage) != 0) { + continue + } + references = append(references, Reference{Type: ref.Type, Context: ctx}) + if !hasParent { + parent = ctx + hasParent = ref.Type == opentracing.ChildOfRef + } + } + if !hasParent && parent.IsValid() { + // If ChildOfRef wasn't found but a FollowFromRef exists, use the context from + // the FollowFromRef as the parent + hasParent = true + } + + rpcServer := false + if v, ok := options.Tags[ext.SpanKindRPCServer.Key]; ok { + rpcServer = (v == ext.SpanKindRPCServerEnum || v == string(ext.SpanKindRPCServerEnum)) + } + + var samplerTags []Tag + var ctx SpanContext + newTrace := false + if !hasParent || !parent.IsValid() { + newTrace = true + ctx.traceID.Low = t.randomID() + if t.options.gen128Bit { + ctx.traceID.High = t.randomID() + } + ctx.spanID = SpanID(ctx.traceID.Low) + ctx.parentID = 0 + ctx.flags = byte(0) + if hasParent && parent.isDebugIDContainerOnly() { + ctx.flags |= (flagSampled | flagDebug) + samplerTags = []Tag{{key: JaegerDebugHeader, value: parent.debugID}} + } else if sampled, tags := t.sampler.IsSampled(ctx.traceID, operationName); sampled { + ctx.flags |= flagSampled + samplerTags = tags + } + } else { + ctx.traceID = parent.traceID + if rpcServer && t.options.zipkinSharedRPCSpan { + // Support Zipkin's one-span-per-RPC model + ctx.spanID = parent.spanID + ctx.parentID = parent.parentID + } else { + ctx.spanID = SpanID(t.randomID()) + ctx.parentID = parent.spanID + } + ctx.flags = parent.flags + } + if hasParent { + // copy baggage items + if l := len(parent.baggage); l > 0 { + ctx.baggage = make(map[string]string, len(parent.baggage)) + for k, v := range parent.baggage { + ctx.baggage[k] = v + } + } + } + + sp := t.newSpan() + sp.context = ctx + sp.observer = t.observer.OnStartSpan(sp, operationName, options) + return t.startSpanInternal( + sp, + operationName, + options.StartTime, + samplerTags, + options.Tags, + newTrace, + rpcServer, + references, + ) +} + +// Inject implements Inject() method of opentracing.Tracer +func (t *Tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error { + c, ok := ctx.(SpanContext) + if !ok { + return opentracing.ErrInvalidSpanContext + } + if injector, ok := t.injectors[format]; ok { + return injector.Inject(c, carrier) + } + return opentracing.ErrUnsupportedFormat +} + +// Extract implements Extract() method of opentracing.Tracer +func (t *Tracer) Extract( + format interface{}, + carrier interface{}, +) (opentracing.SpanContext, error) { + if extractor, ok := t.extractors[format]; ok { + return extractor.Extract(carrier) + } + return nil, opentracing.ErrUnsupportedFormat +} + +// Close releases all resources used by the Tracer and flushes any remaining buffered spans. +func (t *Tracer) Close() error { + t.reporter.Close() + t.sampler.Close() + if mgr, ok := t.baggageRestrictionManager.(io.Closer); ok { + mgr.Close() + } + return nil +} + +// Tags returns a slice of tracer-level tags. +func (t *Tracer) Tags() []opentracing.Tag { + tags := make([]opentracing.Tag, len(t.tags)) + for i, tag := range t.tags { + tags[i] = opentracing.Tag{Key: tag.key, Value: tag.value} + } + return tags +} + +// newSpan returns an instance of a clean Span object. +// If options.PoolSpans is true, the spans are retrieved from an object pool. +func (t *Tracer) newSpan() *Span { + if !t.options.poolSpans { + return &Span{} + } + sp := t.spanPool.Get().(*Span) + sp.context = emptyContext + sp.tracer = nil + sp.tags = nil + sp.logs = nil + return sp +} + +func (t *Tracer) startSpanInternal( + sp *Span, + operationName string, + startTime time.Time, + internalTags []Tag, + tags opentracing.Tags, + newTrace bool, + rpcServer bool, + references []Reference, +) *Span { + sp.tracer = t + sp.operationName = operationName + sp.startTime = startTime + sp.duration = 0 + sp.references = references + sp.firstInProcess = rpcServer || sp.context.parentID == 0 + if len(tags) > 0 || len(internalTags) > 0 { + sp.tags = make([]Tag, len(internalTags), len(tags)+len(internalTags)) + copy(sp.tags, internalTags) + for k, v := range tags { + sp.observer.OnSetTag(k, v) + if k == string(ext.SamplingPriority) && setSamplingPriority(sp, v) { + continue + } + sp.setTagNoLocking(k, v) + } + } + // emit metrics + t.metrics.SpansStarted.Inc(1) + if sp.context.IsSampled() { + t.metrics.SpansSampled.Inc(1) + if newTrace { + // We cannot simply check for parentID==0 because in Zipkin model the + // server-side RPC span has the exact same trace/span/parent IDs as the + // calling client-side span, but obviously the server side span is + // no longer a root span of the trace. + t.metrics.TracesStartedSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedSampled.Inc(1) + } + } else { + t.metrics.SpansNotSampled.Inc(1) + if newTrace { + t.metrics.TracesStartedNotSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedNotSampled.Inc(1) + } + } + return sp +} + +func (t *Tracer) reportSpan(sp *Span) { + t.metrics.SpansFinished.Inc(1) + if sp.context.IsSampled() { + t.reporter.Report(sp) + } + if t.options.poolSpans { + t.spanPool.Put(sp) + } +} + +// randomID generates a random trace/span ID, using tracer.random() generator. +// It never returns 0. +func (t *Tracer) randomID() uint64 { + val := t.randomNumber() + for val == 0 { + val = t.randomNumber() + } + return val +} + +// (NB) span should hold the lock before making this call +func (t *Tracer) setBaggage(sp *Span, key, value string) { + t.baggageSetter.setBaggage(sp, key, value) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_options.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_options.go new file mode 100644 index 000000000..46f05282f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_options.go @@ -0,0 +1,140 @@ +// 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 jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +// TracerOption is a function that sets some option on the tracer +type TracerOption func(tracer *Tracer) + +// TracerOptions is a factory for all available TracerOption's +var TracerOptions tracerOptions + +type tracerOptions struct{} + +// Metrics creates a TracerOption that initializes Metrics on the tracer, +// which is used to emit statistics. +func (tracerOptions) Metrics(m *Metrics) TracerOption { + return func(tracer *Tracer) { + tracer.metrics = *m + } +} + +// Logger creates a TracerOption that gives the tracer a Logger. +func (tracerOptions) Logger(logger Logger) TracerOption { + return func(tracer *Tracer) { + tracer.logger = logger + } +} + +func (tracerOptions) CustomHeaderKeys(headerKeys *HeadersConfig) TracerOption { + return func(tracer *Tracer) { + if headerKeys == nil { + return + } + textPropagator := newTextMapPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + } +} + +// TimeNow creates a TracerOption that gives the tracer a function +// used to generate timestamps for spans. +func (tracerOptions) TimeNow(timeNow func() time.Time) TracerOption { + return func(tracer *Tracer) { + tracer.timeNow = timeNow + } +} + +// RandomNumber creates a TracerOption that gives the tracer +// a thread-safe random number generator function for generating trace IDs. +func (tracerOptions) RandomNumber(randomNumber func() uint64) TracerOption { + return func(tracer *Tracer) { + tracer.randomNumber = randomNumber + } +} + +// PoolSpans creates a TracerOption that tells the tracer whether it should use +// an object pool to minimize span allocations. +// This should be used with care, only if the service is not running any async tasks +// that can access parent spans after those spans have been finished. +func (tracerOptions) PoolSpans(poolSpans bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.poolSpans = poolSpans + } +} + +// Deprecated: HostIPv4 creates a TracerOption that identifies the current service/process. +// If not set, the factory method will obtain the current IP address. +// The TracerOption is deprecated; the tracer will attempt to automatically detect the IP. +func (tracerOptions) HostIPv4(hostIPv4 uint32) TracerOption { + return func(tracer *Tracer) { + tracer.hostIPv4 = hostIPv4 + } +} + +func (tracerOptions) Injector(format interface{}, injector Injector) TracerOption { + return func(tracer *Tracer) { + tracer.injectors[format] = injector + } +} + +func (tracerOptions) Extractor(format interface{}, extractor Extractor) TracerOption { + return func(tracer *Tracer) { + tracer.extractors[format] = extractor + } +} + +func (t tracerOptions) Observer(observer Observer) TracerOption { + return t.ContribObserver(&oldObserver{obs: observer}) +} + +func (tracerOptions) ContribObserver(observer ContribObserver) TracerOption { + return func(tracer *Tracer) { + tracer.observer.append(observer) + } +} + +func (tracerOptions) Gen128Bit(gen128Bit bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.gen128Bit = gen128Bit + } +} + +func (tracerOptions) ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +func (tracerOptions) Tag(key string, value interface{}) TracerOption { + return func(tracer *Tracer) { + tracer.tags = append(tracer.tags, Tag{key: key, value: value}) + } +} + +func (tracerOptions) BaggageRestrictionManager(mgr baggage.RestrictionManager) TracerOption { + return func(tracer *Tracer) { + tracer.baggageRestrictionManager = mgr + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_test.go new file mode 100644 index 000000000..d38d671e2 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/tracer_test.go @@ -0,0 +1,365 @@ +// 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 jaeger + +import ( + "io" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +type tracerSuite struct { + suite.Suite + tracer opentracing.Tracer + closer io.Closer + metricsFactory *metrics.LocalFactory +} + +func (s *tracerSuite) SetupTest() { + s.metricsFactory = metrics.NewLocalFactory(0) + metrics := NewMetrics(s.metricsFactory, nil) + + s.tracer, s.closer = NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + TracerOptions.ZipkinSharedRPCSpan(true), + TracerOptions.BaggageRestrictionManager(baggage.NewDefaultRestrictionManager(0)), + ) + s.NotNil(s.tracer) +} + +func (s *tracerSuite) TearDownTest() { + if s.tracer != nil { + s.closer.Close() + s.tracer = nil + } +} + +func TestTracerSuite(t *testing.T) { + suite.Run(t, new(tracerSuite)) +} + +func (s *tracerSuite) TestBeginRootSpan() { + s.metricsFactory.Clear() + startTime := time.Now() + s.tracer.(*Tracer).timeNow = func() time.Time { return startTime } + someID := uint64(12345) + s.tracer.(*Tracer).randomNumber = func() uint64 { return someID } + + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer-service") + s.NotNil(sp) + ss := sp.(*Span) + s.NotNil(ss.tracer, "Tracer must be referenced from span") + s.Equal("get_name", ss.operationName) + s.Len(ss.tags, 4, "Span should have 2 sampler tags, span.kind tag and peer.service tag") + s.EqualValues(Tag{key: "span.kind", value: ext.SpanKindRPCServerEnum}, ss.tags[2], "Span must be server-side") + s.EqualValues(Tag{key: "peer.service", value: "peer-service"}, ss.tags[3], "Client is 'peer-service'") + + s.EqualValues(someID, ss.context.traceID.Low) + s.EqualValues(0, ss.context.parentID) + + s.Equal(startTime, ss.startTime) + + sp.Finish() + s.NotNil(ss.duration) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + }...) +} + +func (s *tracerSuite) TestStartRootSpanWithOptions() { + ts := time.Now() + sp := s.tracer.StartSpan("get_address", opentracing.StartTime(ts)) + ss := sp.(*Span) + s.Equal("get_address", ss.operationName) + s.Equal(ts, ss.startTime) +} + +func (s *tracerSuite) TestStartChildSpan() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("get_address") + sp2 := s.tracer.StartSpan("get_street", opentracing.ChildOf(sp1.Context())) + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 2}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 2}, + }...) +} + +type nonJaegerSpanContext struct{} + +func (c nonJaegerSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +func (s *tracerSuite) TestStartSpanWithMultipleReferences() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan("B") + sp3 := s.tracer.StartSpan("C") + sp4 := s.tracer.StartSpan( + "D", + opentracing.ChildOf(sp1.Context()), + opentracing.ChildOf(sp2.Context()), + opentracing.FollowsFrom(sp3.Context()), + opentracing.FollowsFrom(nonJaegerSpanContext{}), + opentracing.FollowsFrom(SpanContext{}), // Empty span context should be excluded + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp4.(*Span).context.parentID) + sp4.Finish() + s.NotNil(sp4.(*Span).duration) + sp3.Finish() + sp2.Finish() + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 4}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 4}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 4}, + }...) + assert.Len(s.T(), sp4.(*Span).references, 3) +} + +func (s *tracerSuite) TestStartSpanWithOnlyFollowFromReference() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan( + "B", + opentracing.FollowsFrom(sp1.Context()), + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 2}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 2}, + }...) + assert.Len(s.T(), sp2.(*Span).references, 1) +} + +func (s *tracerSuite) TestTraceStartedOrJoinedMetrics() { + tests := []struct { + sampled bool + label string + }{ + {true, "y"}, + {false, "n"}, + } + for _, test := range tests { + s.metricsFactory.Clear() + s.tracer.(*Tracer).sampler = NewConstSampler(test.sampled) + sp1 := s.tracer.StartSpan("parent", ext.RPCServerOption(nil)) + sp2 := s.tracer.StartSpan("child1", opentracing.ChildOf(sp1.Context())) + sp3 := s.tracer.StartSpan("child2", ext.RPCServerOption(sp2.Context())) + s.Equal(sp2.(*Span).context.spanID, sp3.(*Span).context.spanID) + s.Equal(sp2.(*Span).context.parentID, sp3.(*Span).context.parentID) + sp3.Finish() + sp2.Finish() + sp1.Finish() + s.Equal(test.sampled, sp1.Context().(SpanContext).IsSampled()) + s.Equal(test.sampled, sp2.Context().(SpanContext).IsSampled()) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": test.label}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 3}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "started"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "joined"}, Value: 1}, + }...) + } +} + +func (s *tracerSuite) TestSetOperationName() { + sp1 := s.tracer.StartSpan("get_address") + sp1.SetOperationName("get_street") + s.Equal("get_street", sp1.(*Span).operationName) +} + +func (s *tracerSuite) TestSamplerEffects() { + s.tracer.(*Tracer).sampler = NewConstSampler(true) + sp := s.tracer.StartSpan("test") + flags := sp.(*Span).context.flags + s.EqualValues(flagSampled, flags&flagSampled) + + s.tracer.(*Tracer).sampler = NewConstSampler(false) + sp = s.tracer.StartSpan("test") + flags = sp.(*Span).context.flags + s.EqualValues(0, flags&flagSampled) +} + +func (s *tracerSuite) TestRandomIDNotZero() { + val := uint64(0) + s.tracer.(*Tracer).randomNumber = func() (r uint64) { + r = val + val++ + return + } + sp := s.tracer.StartSpan("get_name").(*Span) + s.EqualValues(TraceID{Low: 1}, sp.context.traceID) + + rng := utils.NewRand(0) + rng.Seed(1) // for test coverage +} + +func TestTracerOptions(t *testing.T) { + t1, e := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00") + assert.NoError(t, e) + + timeNow := func() time.Time { + return t1 + } + rnd := func() uint64 { + return 1 + } + + openTracer, closer := NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Logger(log.StdLogger), + TracerOptions.TimeNow(timeNow), + TracerOptions.RandomNumber(rnd), + TracerOptions.PoolSpans(true), + TracerOptions.Tag("tag_key", "tag_value"), + ) + defer closer.Close() + + tracer := openTracer.(*Tracer) + assert.Equal(t, log.StdLogger, tracer.logger) + assert.Equal(t, t1, tracer.timeNow()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) // always 1 + assert.Equal(t, true, tracer.options.poolSpans) + assert.Equal(t, opentracing.Tag{Key: "tag_key", Value: "tag_value"}, tracer.Tags()[0]) +} + +func TestInjectorExtractorOptions(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), + TracerOptions.Injector("dummy", &dummyPropagator{}), + TracerOptions.Extractor("dummy", &dummyPropagator{}), + ) + defer tc.Close() + + sp := tracer.StartSpan("x") + c := &dummyCarrier{} + err := tracer.Inject(sp.Context(), "dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + err = tracer.Inject(sp.Context(), "dummy", c) + assert.NoError(t, err) + assert.True(t, c.ok) + + c.ok = false + _, err = tracer.Extract("dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + _, err = tracer.Extract("dummy", c) + assert.Equal(t, opentracing.ErrSpanContextNotFound, err) + c.ok = true + _, err = tracer.Extract("dummy", c) + assert.NoError(t, err) +} + +func TestEmptySpanContextAsParent(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter()) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + ctx := span.Context().(SpanContext) + assert.True(t, ctx.traceID.IsValid()) + assert.True(t, ctx.IsValid()) +} + +func TestGen128Bit(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.Gen128Bit(true)) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + defer span.Finish() + traceID := span.Context().(SpanContext).TraceID() + assert.True(t, traceID.High != 0) + assert.True(t, traceID.Low != 0) +} + +func TestZipkinSharedRPCSpan(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(false)) + + sp1 := tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 := tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + assert.NotEqual(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + sp2.Finish() + sp1.Finish() + tc.Close() + + tracer, tc = NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + + sp1 = tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 = tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + assert.Equal(t, sp1.(*Span).context.parentID, sp2.(*Span).context.parentID) + sp2.Finish() + sp1.Finish() + tc.Close() +} + +type dummyPropagator struct{} +type dummyCarrier struct { + ok bool +} + +func (p *dummyPropagator) Inject(ctx SpanContext, carrier interface{}) error { + c, ok := carrier.(*dummyCarrier) + if !ok { + return opentracing.ErrInvalidCarrier + } + c.ok = true + return nil +} + +func (p *dummyPropagator) Extract(carrier interface{}) (SpanContext, error) { + c, ok := carrier.(*dummyCarrier) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if c.ok { + return emptyContext, nil + } + return emptyContext, opentracing.ErrSpanContextNotFound +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport.go new file mode 100644 index 000000000..c5f5b1955 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport.go @@ -0,0 +1,38 @@ +// 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 jaeger + +import ( + "io" +) + +// Transport abstracts the method of sending spans out of process. +// Implementations are NOT required to be thread-safe; the RemoteReporter +// is expected to only call methods on the Transport from the same go-routine. +type Transport interface { + // Append converts the span to the wire representation and adds it + // to sender's internal buffer. If the buffer exceeds its designated + // size, the transport should call Flush() and return the number of spans + // flushed, otherwise return 0. If error is returned, the returned number + // of spans is treated as failed span, and reported to metrics accordingly. + Append(span *Span) (int, error) + + // Flush submits the internal buffer to the remote server. It returns the + // number of spans flushed. If error is returned, the returned number of + // spans is treated as failed span, and reported to metrics accordingly. + Flush() (int, error) + + io.Closer +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/doc.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/doc.go new file mode 100644 index 000000000..6b961fb63 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/doc.go @@ -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 diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http.go new file mode 100644 index 000000000..7714ccca3 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http.go @@ -0,0 +1,155 @@ +// 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/apache/thrift/lib/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} + } +} + +// 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 +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http_test.go new file mode 100644 index 000000000..cf15c1816 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/http_test.go @@ -0,0 +1,156 @@ +// 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 ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +func TestHTTPTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Bender" + httpPassword := "Rodriguez" + sender := NewHTTPTransport( + "http://localhost:10001/api/v1/spans", + HTTPBatchSize(1), + HTTPBasicAuth(httpUsername, httpPassword), + ) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + defer closer.Close() + + span := tracer.StartSpan("root") + span.Finish() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.getBatches()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotBatch := server.getBatches()[0] + assert.Len(t, gotBatch.Spans, 1) + assert.Equal(t, "test", gotBatch.Process.ServiceName) + gotSpan := gotBatch.Spans[0] + assert.Equal(t, "root", gotSpan.OperationName) + assert.EqualValues(t, srcSpanCtx.TraceID().High, gotSpan.TraceIdHigh) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceIdLow) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.SpanId) + assert.Equal(t, + &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, + server.authCredentials[0], + ) +} + +func TestHTTPOptions(t *testing.T) { + sender := NewHTTPTransport( + "some url", + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + ) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) +} + +type httpServer struct { + t *testing.T + batches []*j.Batch + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) getBatches() []*j.Batch { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.batches +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +// TODO this function and zipkin/http_test.go#newHTTPServer look like twins lost at birth +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + batches: make([]*j.Batch, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + batch := &j.Batch{} + if err = batch.Read(transport); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.batches = append(server.batches, batch) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10001", nil) + }() + + return server +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/doc.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/doc.go new file mode 100644 index 000000000..64abb539e --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/doc.go @@ -0,0 +1,17 @@ +// 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 zipkin provides various Transports that can be used +// with RemoteReporter for submitting traces to Zipkin backend. +package zipkin diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/example_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/example_test.go new file mode 100644 index 000000000..3c928d31d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/example_test.go @@ -0,0 +1,46 @@ +// 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 zipkin_test + +import ( + "log" + + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + jlog "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/transport/zipkin" +) + +func ExampleNewHTTPTransport() { + // assume this is your main() + + transport, err := zipkin.NewHTTPTransport( + "http://localhost:9411/api/v1/spans", + zipkin.HTTPBatchSize(10), + zipkin.HTTPLogger(jlog.StdLogger), + ) + if err != nil { + log.Fatalf("Cannot initialize Zipkin HTTP transport: %v", err) + } + tracer, closer := jaeger.NewTracer( + "my-service-name", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(transport, nil), + ) + defer closer.Close() + opentracing.InitGlobalTracer(tracer) + + // initialize servers +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http.go new file mode 100644 index 000000000..ba583dd84 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http.go @@ -0,0 +1,166 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "bytes" + "net/http" + "time" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// 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 { + logger jaeger.Logger + url string + client *http.Client + batchSize int + batch []*zipkincore.Span + 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) + +// HTTPLogger sets the logger used to report errors in the collection +// process. By default, a no-op logger is used, i.e. no errors are logged +// anywhere. It's important to set this option in a production service. +func HTTPLogger(logger jaeger.Logger) HTTPOption { + return func(c *HTTPTransport) { c.logger = logger } +} + +// 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} + } +} + +// NewHTTPTransport returns a new HTTP-backend transport. url should be an http +// url to handle post request, typically something like: +// http://hostname:9411/api/v1/spans +func NewHTTPTransport(url string, options ...HTTPOption) (*HTTPTransport, error) { + c := &HTTPTransport{ + logger: log.NullLogger, + url: url, + client: &http.Client{Timeout: defaultHTTPTimeout}, + batchSize: 100, + batch: []*zipkincore.Span{}, + } + + for _, option := range options { + option(c) + } + return c, nil +} + +// Append implements Transport. +func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) { + zSpan := jaeger.BuildZipkinThrift(span) + c.batch = append(c.batch, zSpan) + if len(c.batch) >= c.batchSize { + return c.Flush() + } + return 0, nil +} + +// Flush implements Transport. +func (c *HTTPTransport) Flush() (int, error) { + count := len(c.batch) + if count == 0 { + return 0, nil + } + err := c.send(c.batch) + c.batch = c.batch[:0] + return count, err +} + +// Close implements Transport. +func (c *HTTPTransport) Close() error { + return nil +} + +func httpSerialize(spans []*zipkincore.Span) (*bytes.Buffer, error) { + t := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(t) + if err := p.WriteListBegin(thrift.STRUCT, len(spans)); err != nil { + return nil, err + } + for _, s := range spans { + if err := s.Write(p); err != nil { + return nil, err + } + } + if err := p.WriteListEnd(); err != nil { + return nil, err + } + return t.Buffer, nil +} + +func (c *HTTPTransport) send(spans []*zipkincore.Span) error { + body, err := httpSerialize(spans) + 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) + } + + _, err = c.client.Do(req) + + return err +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http_test.go new file mode 100644 index 000000000..425fbc23f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport/zipkin/http_test.go @@ -0,0 +1,178 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http_test.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +func TestHttpTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Aphex" + httpPassword := "Twin" + sender, err := NewHTTPTransport("http://localhost:10000/api/v1/spans", HTTPBasicAuth(httpUsername, httpPassword)) + require.NoError(t, err) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + + span := tracer.StartSpan("root") + span.Finish() + + closer.Close() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.spans()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotSpan := server.spans()[0] + assert.Equal(t, "root", gotSpan.Name) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceID) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.ID) + assert.Equal(t, &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, server.authCredentials[0]) +} + +func TestHTTPOptions(t *testing.T) { + sender, err := NewHTTPTransport( + "some url", + HTTPLogger(log.StdLogger), + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + HTTPBasicAuth("urundai", "kuzhambu"), + ) + require.NoError(t, err) + assert.Equal(t, log.StdLogger, sender.logger) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) + assert.Equal(t, "urundai", sender.httpCredentials.username) + assert.Equal(t, "kuzhambu", sender.httpCredentials.password) +} + +type httpServer struct { + t *testing.T + zipkinSpans []*zipkincore.Span + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) spans() []*zipkincore.Span { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.zipkinSpans +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + zipkinSpans: make([]*zipkincore.Span, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + _, size, err := transport.ReadListBegin() + if err != nil { + t.Error(err) + return + } + var spans []*zipkincore.Span + for i := 0; i < size; i++ { + zs := &zipkincore.Span{} + if err = zs.Read(transport); err != nil { + t.Error(err) + return + } + spans = append(spans, zs) + } + if err := transport.ReadListEnd(); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.zipkinSpans = append(server.zipkinSpans, spans...) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10000", nil) + }() + + return server +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp.go new file mode 100644 index 000000000..cb83cdf9b --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp.go @@ -0,0 +1,132 @@ +// 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 jaeger + +import ( + "errors" + + "github.com/apache/thrift/lib/go/thrift" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// Empirically obtained constant for how many bytes in the message are used for envelope. +// The total datagram size is: +// sizeof(Span) * numSpans + processByteSize + emitBatchOverhead <= maxPacketSize +// There is a unit test `TestEmitBatchOverhead` that validates this number. +// Note that due to the use of Compact Thrift protocol, overhead grows with the number of spans +// in the batch, because the length of the list is encoded as varint32, as well as SeqId. +const emitBatchOverhead = 30 + +const defaultUDPSpanServerHostPort = "localhost:6831" + +var errSpanTooLarge = errors.New("Span is too large") + +type udpSender struct { + client *utils.AgentClientUDP + maxPacketSize int // max size of datagram in bytes + maxSpanBytes int // max number of bytes to record spans (excluding envelope) in the datagram + byteBufferSize int // current number of span bytes accumulated in the buffer + spanBuffer []*j.Span // spans buffered before a flush + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span + thriftProtocol thrift.TProtocol + process *j.Process + processByteSize int +} + +// NewUDPTransport creates a reporter that submits spans to jaeger-agent +func NewUDPTransport(hostPort string, maxPacketSize int) (Transport, error) { + if len(hostPort) == 0 { + hostPort = defaultUDPSpanServerHostPort + } + if maxPacketSize == 0 { + maxPacketSize = utils.UDPPacketMaxLength + } + + protocolFactory := thrift.NewTCompactProtocolFactory() + + // Each span is first written to thriftBuffer to determine its size in bytes. + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + thriftProtocol := protocolFactory.GetProtocol(thriftBuffer) + + client, err := utils.NewAgentClientUDP(hostPort, maxPacketSize) + if err != nil { + return nil, err + } + + sender := &udpSender{ + client: client, + maxSpanBytes: maxPacketSize - emitBatchOverhead, + thriftBuffer: thriftBuffer, + thriftProtocol: thriftProtocol} + return sender, nil +} + +func (s *udpSender) calcSizeOfSerializedThrift(thriftStruct thrift.TStruct) int { + s.thriftBuffer.Reset() + thriftStruct.Write(s.thriftProtocol) + return s.thriftBuffer.Len() +} + +func (s *udpSender) Append(span *Span) (int, error) { + if s.process == nil { + s.process = BuildJaegerProcessThrift(span) + s.processByteSize = s.calcSizeOfSerializedThrift(s.process) + s.byteBufferSize += s.processByteSize + } + jSpan := BuildJaegerThrift(span) + spanSize := s.calcSizeOfSerializedThrift(jSpan) + if spanSize > s.maxSpanBytes { + return 1, errSpanTooLarge + } + + s.byteBufferSize += spanSize + if s.byteBufferSize <= s.maxSpanBytes { + s.spanBuffer = append(s.spanBuffer, jSpan) + if s.byteBufferSize < s.maxSpanBytes { + return 0, nil + } + return s.Flush() + } + // the latest span did not fit in the buffer + n, err := s.Flush() + s.spanBuffer = append(s.spanBuffer, jSpan) + s.byteBufferSize = spanSize + s.processByteSize + return n, err +} + +func (s *udpSender) Flush() (int, error) { + n := len(s.spanBuffer) + if n == 0 { + return 0, nil + } + err := s.client.EmitBatch(&j.Batch{Process: s.process, Spans: s.spanBuffer}) + s.resetBuffers() + + return n, err +} + +func (s *udpSender) Close() error { + return s.client.Close() +} + +func (s *udpSender) resetBuffers() { + for i := range s.spanBuffer { + s.spanBuffer[i] = nil + } + s.spanBuffer = s.spanBuffer[:0] + s.byteBufferSize = s.processByteSize +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp_test.go new file mode 100644 index 000000000..99ef1c22f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/transport_udp_test.go @@ -0,0 +1,221 @@ +// 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 jaeger + +import ( + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift-gen/agent" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +var ( + testTracer, _ = NewTracer("svcName", NewConstSampler(false), NewNullReporter()) + jaegerTracer = testTracer.(*Tracer) +) + +func getThriftSpanByteLength(t *testing.T, span *Span) int { + jSpan := BuildJaegerThrift(span) + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := jSpan.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func getThriftProcessByteLengthFromTracer(t *testing.T, tracer *Tracer) int { + process := buildJaegerProcessThrift(tracer) + return getThriftProcessByteLength(t, process) +} + +func getThriftProcessByteLength(t *testing.T, process *j.Process) int { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := process.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func TestEmitBatchOverhead(t *testing.T) { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := agent.NewAgentClientFactory(transport, protocolFactory) + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + tests := []int{1, 2, 14, 15, 377, 500, 65000, 0xFFFF} + for i, n := range tests { + transport.Reset() + batch := make([]*j.Span, n) + processTags := make([]*j.Tag, n) + for x := 0; x < n; x++ { + batch[x] = BuildJaegerThrift(span) + processTags[x] = &j.Tag{} + } + process := &j.Process{ServiceName: "svcName", Tags: processTags} + client.SeqId = -2 // this causes the longest encoding of varint32 as 5 bytes + err := client.EmitBatch(&j.Batch{Process: process, Spans: batch}) + processSize := getThriftProcessByteLength(t, process) + require.NoError(t, err) + overhead := transport.Len() - n*spanSize - processSize + assert.True(t, overhead <= emitBatchOverhead, + "test %d, n=%d, expected overhead %d <= %d", i, n, overhead, emitBatchOverhead) + t.Logf("span count: %d, overhead: %d", n, overhead) + } +} + +func TestUDPSenderFlush(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), 5*spanSize+processSize+emitBatchOverhead) + require.NoError(t, err) + udpSender := sender.(*udpSender) + + // test empty flush + n, err := sender.Flush() + require.NoError(t, err) + assert.Equal(t, 0, n) + + // test early flush + n, err = sender.Append(span) + require.NoError(t, err) + assert.Equal(t, 0, n, "span should be in buffer, not flushed") + buffer := udpSender.spanBuffer + require.Equal(t, 1, len(buffer), "span should be in buffer, not flushed") + assert.Equal(t, BuildJaegerThrift(span), buffer[0], "span should be in buffer, not flushed") + + n, err = sender.Flush() + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, 0, len(udpSender.spanBuffer), "buffer should become empty") + assert.Equal(t, processSize, udpSender.byteBufferSize, "buffer size counter should be equal to the processSize") + assert.Nil(t, buffer[0], "buffer should not keep reference to the span") + + for i := 0; i < 10000; i++ { + batches := agent.GetJaegerBatches() + if len(batches) > 0 { + break + } + time.Sleep(1 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, 1, len(batches), "agent should have received the batch") + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, span.operationName, batches[0].Spans[0].OperationName) +} + +func TestUDPSenderAppend(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + tests := []struct { + bufferSizeOffset int + expectFlush bool + expectSpansFlushed int + expectBatchesFlushed int + manualFlush bool + expectSpansFlushed2 int + expectBatchesFlushed2 int + description string + }{ + {1, false, 0, 0, true, 5, 1, "in test: buffer bigger than 5 spans"}, + {0, true, 5, 1, false, 0, 0, "in test: buffer fits exactly 5 spans"}, + {-1, true, 4, 1, true, 1, 1, "in test: buffer smaller than 5 spans"}, + } + + for _, test := range tests { + bufferSize := 5*spanSize + test.bufferSizeOffset + processSize + emitBatchOverhead + sender, err := NewUDPTransport(agent.SpanServerAddr(), bufferSize) + require.NoError(t, err, test.description) + + agent.ResetJaegerBatches() + for i := 0; i < 5; i++ { + n, err := sender.Append(span) + require.NoError(t, err, test.description) + if i < 4 { + assert.Equal(t, 0, n, test.description) + } else { + assert.Equal(t, test.expectSpansFlushed, n, test.description) + } + } + if test.expectFlush { + time.Sleep(5 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed, len(batches), test.description) + var spans []*j.Span + if test.expectBatchesFlushed > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + + if test.manualFlush { + agent.ResetJaegerBatches() + n, err := sender.Flush() + require.NoError(t, err, test.description) + assert.Equal(t, test.expectSpansFlushed2, n, test.description) + + time.Sleep(5 * time.Millisecond) + batches = agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed2, len(batches), test.description) + spans = []*j.Span{} + if test.expectBatchesFlushed2 > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed2, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed2; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + } + + } +} + +func TestUDPSenderHugeSpan(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), spanSize/2+emitBatchOverhead) + require.NoError(t, err) + + n, err := sender.Append(span) + assert.Equal(t, errSpanTooLarge, err) + assert.Equal(t, 1, n) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/build-crossdock.sh b/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/build-crossdock.sh new file mode 100644 index 000000000..8e98d0811 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/build-crossdock.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +make crossdock + +export REPO=jaegertracing/xdock-go +export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi) +export TAG=`if [ "$BRANCH" == "master" ]; then echo "latest"; else echo "${BRANCH///}"; fi` +echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, REPO=$REPO, PR=$PR, BRANCH=$BRANCH, TAG=$TAG" + +# Only push the docker container to Docker Hub for master branch +if [[ "$BRANCH" == "master" && "$TRAVIS_SECURE_ENV_VARS" == "true" ]]; then echo 'upload to Docker Hub'; else echo 'skip docker upload for PR'; exit 0; fi + +docker login -u $DOCKER_USER -p $DOCKER_PASS + +set -x + +docker build -f crossdock/Dockerfile -t $REPO:$COMMIT . + +docker tag $REPO:$COMMIT $REPO:$TAG +docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER +docker push $REPO diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/install-crossdock-deps.sh b/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/install-crossdock-deps.sh new file mode 100644 index 000000000..f2e15fe7a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/travis/install-crossdock-deps.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +docker version + +# Install docker-compose +sudo rm -f /usr/local/bin/docker-compose +curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose +chmod +x docker-compose +sudo mv docker-compose /usr/local/bin +docker-compose version diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json.go new file mode 100644 index 000000000..237211f82 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json.go @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// GetJSON makes an HTTP call to the specified URL and parses the returned JSON into `out`. +func GetJSON(url string, out interface{}) error { + resp, err := http.Get(url) + if err != nil { + return err + } + return ReadJSON(resp, out) +} + +// ReadJSON reads JSON from http.Response and parses it into `out` +func ReadJSON(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body) + } + + if out == nil { + io.Copy(ioutil.Discard, resp.Body) + return nil + } + + decoder := json.NewDecoder(resp.Body) + return decoder.Decode(out) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json_test.go new file mode 100644 index 000000000..6ee984a9a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/http_json_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testJSONStruct struct { + Name string + Age int +} + +func TestGetJSON(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.Write([]byte("{\"name\": \"Bender\", \"age\": 3}")) + })) + defer server.Close() + + var s testJSONStruct + err := GetJSON(server.URL, &s) + require.NoError(t, err) + + assert.Equal(t, "Bender", s.Name) + assert.Equal(t, 3, s.Age) +} + +func TestGetJSONErrors(t *testing.T) { + var s testJSONStruct + err := GetJSON("localhost:0", &s) + assert.Error(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "some error", http.StatusInternalServerError) + })) + defer server.Close() + + err = GetJSON(server.URL, &s) + assert.Error(t, err) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/localip.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/localip.go new file mode 100644 index 000000000..b51af7713 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/localip.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "errors" + "net" +) + +// This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go + +// scoreAddr scores how likely the given addr is to be a remote address and returns the +// IP to use when listening. Any address which receives a negative score should not be used. +// Scores are calculated as: +// -1 for any unknown IP addresses. +// +300 for IPv4 addresses +// +100 for non-local addresses, extra +100 for "up" interaces. +func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) { + var ip net.IP + if netAddr, ok := addr.(*net.IPNet); ok { + ip = netAddr.IP + } else if netIP, ok := addr.(*net.IPAddr); ok { + ip = netIP.IP + } else { + return -1, nil + } + + var score int + if ip.To4() != nil { + score += 300 + } + if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() { + score += 100 + if iface.Flags&net.FlagUp != 0 { + score += 100 + } + } + return score, ip +} + +// HostIP tries to find an IP that can be used by other machines to reach this machine. +func HostIP() (net.IP, error) { + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + bestScore := -1 + var bestIP net.IP + // Select the highest scoring IP as the best IP. + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + // Skip this interface if there is an error. + continue + } + + for _, addr := range addrs { + score, ip := scoreAddr(iface, addr) + if score > bestScore { + bestScore = score + bestIP = ip + } + } + } + + if bestScore == -1 { + return nil, errors.New("no addresses to listen on") + } + + return bestIP, nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rand.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rand.go new file mode 100644 index 000000000..9875f7f55 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rand.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "math/rand" + "sync" +) + +// lockedSource allows a random number generator to be used by multiple goroutines concurrently. +// The code is very similar to math/rand.lockedSource, which is unfortunately not exposed. +type lockedSource struct { + mut sync.Mutex + src rand.Source +} + +// NewRand returns a rand.Rand that is threadsafe. +func NewRand(seed int64) *rand.Rand { + return rand.New(&lockedSource{src: rand.NewSource(seed)}) +} + +func (r *lockedSource) Int63() (n int64) { + r.mut.Lock() + n = r.src.Int63() + r.mut.Unlock() + return +} + +// Seed implements Seed() of Source +func (r *lockedSource) Seed(seed int64) { + r.mut.Lock() + r.src.Seed(seed) + r.mut.Unlock() +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter.go new file mode 100644 index 000000000..1b8db9758 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "sync" + "time" +) + +// RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. +type RateLimiter interface { + CheckCredit(itemCost float64) bool +} + +type rateLimiter struct { + sync.Mutex + + creditsPerSecond float64 + balance float64 + maxBalance float64 + lastTick time.Time + + timeNow func() time.Time +} + +// NewRateLimiter creates a new rate limiter based on leaky bucket algorithm, formulated in terms of a +// credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional +// to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost +// of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" +// and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. +// +// This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the +// max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message +// to determine if the message is within the rate limit. +// +// It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput +// as bytes/second, and calling CheckCredit() with the actual message size. +func NewRateLimiter(creditsPerSecond, maxBalance float64) RateLimiter { + return &rateLimiter{ + creditsPerSecond: creditsPerSecond, + balance: maxBalance, + maxBalance: maxBalance, + lastTick: time.Now(), + timeNow: time.Now} +} + +func (b *rateLimiter) CheckCredit(itemCost float64) bool { + b.Lock() + defer b.Unlock() + // calculate how much time passed since the last tick, and update current tick + currentTime := b.timeNow() + elapsedTime := currentTime.Sub(b.lastTick) + b.lastTick = currentTime + // calculate how much credit have we accumulated since the last tick + b.balance += elapsedTime.Seconds() * b.creditsPerSecond + if b.balance > b.maxBalance { + b.balance = b.maxBalance + } + // if we have enough credits to pay for current item, then reduce balance and allow + if b.balance >= itemCost { + b.balance -= itemCost + return true + } + return false +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter_test.go new file mode 100644 index 000000000..a075afb42 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/rate_limiter_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter(t *testing.T) { + limiter := NewRateLimiter(2.0, 2.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 250ms forward, not enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second / 4) + } + assert.False(t, limiter.CheckCredit(1.0)) + // move time 500ms forward, now enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second/4 + time.Second/2) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(5 * time.Second) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} + +func TestMaxBalance(t *testing.T) { + limiter := NewRateLimiter(0.1, 1.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + // on initialization, should have enough credits for 1 message + assert.True(t, limiter.CheckCredit(1.0)) + + // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second * 20) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/udp_client.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/udp_client.go new file mode 100644 index 000000000..2374686e5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/udp_client.go @@ -0,0 +1,98 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "errors" + "fmt" + "io" + "net" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// UDPPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent +const UDPPacketMaxLength = 65000 + +// AgentClientUDP is a UDP client to Jaeger agent that implements agent.Agent interface. +type AgentClientUDP struct { + agent.Agent + io.Closer + + connUDP *net.UDPConn + client *agent.AgentClient + maxPacketSize int // max size of datagram in bytes + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span +} + +// NewAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP. +func NewAgentClientUDP(hostPort string, maxPacketSize int) (*AgentClientUDP, error) { + if maxPacketSize == 0 { + maxPacketSize = UDPPacketMaxLength + } + + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := agent.NewAgentClientFactory(thriftBuffer, protocolFactory) + + destAddr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, err + } + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + if err != nil { + return nil, err + } + if err := connUDP.SetWriteBuffer(maxPacketSize); err != nil { + return nil, err + } + + clientUDP := &AgentClientUDP{ + connUDP: connUDP, + client: client, + maxPacketSize: maxPacketSize, + thriftBuffer: thriftBuffer} + return clientUDP, nil +} + +// EmitZipkinBatch implements EmitZipkinBatch() of Agent interface +func (a *AgentClientUDP) EmitZipkinBatch(spans []*zipkincore.Span) error { + return errors.New("Not implemented") +} + +// EmitBatch implements EmitBatch() of Agent interface +func (a *AgentClientUDP) EmitBatch(batch *jaeger.Batch) error { + a.thriftBuffer.Reset() + a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages + if err := a.client.EmitBatch(batch); err != nil { + return err + } + if a.thriftBuffer.Len() > a.maxPacketSize { + return fmt.Errorf("Data does not fit within one UDP packet; size %d, max %d, spans %d", + a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans)) + } + _, err := a.connUDP.Write(a.thriftBuffer.Bytes()) + return err +} + +// Close implements Close() of io.Closer and closes the underlying UDP connection. +func (a *AgentClientUDP) Close() error { + return a.connUDP.Close() +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils.go new file mode 100644 index 000000000..ac3c325d1 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils.go @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "encoding/binary" + "errors" + "net" + "strconv" + "strings" + "time" +) + +var ( + // ErrEmptyIP an error for empty ip strings + ErrEmptyIP = errors.New("empty string given for ip") + + // ErrNotHostColonPort an error for invalid host port string + ErrNotHostColonPort = errors.New("expecting host:port") + + // ErrNotFourOctets an error for the wrong number of octets after splitting a string + ErrNotFourOctets = errors.New("Wrong number of octets") +) + +// ParseIPToUint32 converts a string ip (e.g. "x.y.z.w") to an uint32 +func ParseIPToUint32(ip string) (uint32, error) { + if ip == "" { + return 0, ErrEmptyIP + } + + if ip == "localhost" { + return 127<<24 | 1, nil + } + + octets := strings.Split(ip, ".") + if len(octets) != 4 { + return 0, ErrNotFourOctets + } + + var intIP uint32 + for i := 0; i < 4; i++ { + octet, err := strconv.Atoi(octets[i]) + if err != nil { + return 0, err + } + intIP = (intIP << 8) | uint32(octet) + } + + return intIP, nil +} + +// ParsePort converts port number from string to uin16 +func ParsePort(portString string) (uint16, error) { + port, err := strconv.ParseUint(portString, 10, 16) + return uint16(port), err +} + +// PackIPAsUint32 packs an IPv4 as uint32 +func PackIPAsUint32(ip net.IP) uint32 { + if ipv4 := ip.To4(); ipv4 != nil { + return binary.BigEndian.Uint32(ipv4) + } + return 0 +} + +// TimeToMicrosecondsSinceEpochInt64 converts Go time.Time to a long +// representing time since epoch in microseconds, which is used expected +// in the Jaeger spans encoded as Thrift. +func TimeToMicrosecondsSinceEpochInt64(t time.Time) int64 { + // ^^^ Passing time.Time by value is faster than passing a pointer! + // BenchmarkTimeByValue-8 2000000000 1.37 ns/op + // BenchmarkTimeByPtr-8 2000000000 1.98 ns/op + + return t.UnixNano() / 1000 +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils_test.go new file mode 100644 index 000000000..1df8e0b35 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/utils/utils_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetLocalIP(t *testing.T) { + ip, _ := HostIP() + assert.NotNil(t, ip, "assert we have an ip") +} + +func TestParseIPToUint32(t *testing.T) { + tests := []struct { + in string + out uint32 + err error + }{ + {"1.2.3.4", 1<<24 | 2<<16 | 3<<8 | 4, nil}, + {"127.0.0.1", 127<<24 | 1, nil}, + {"localhost", 127<<24 | 1, nil}, + {"127.xxx.0.1", 0, nil}, + {"", 0, ErrEmptyIP}, + {"hostname", 0, ErrNotFourOctets}, + } + + for _, test := range tests { + intIP, err := ParseIPToUint32(test.in) + if test.err != nil { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.out, intIP) + } + + } +} + +func TestParsePort(t *testing.T) { + tests := []struct { + in string + out uint16 + err bool + }{ + {"123", 123, false}, + {"77777", 0, true}, // too large for 16bit + {"bad-wolf", 0, true}, + } + for _, test := range tests { + p, err := ParsePort(test.in) + if test.err { + assert.Error(t, err) + } else { + assert.Equal(t, test.out, p) + } + } +} + +func TestPackIPAsUint32(t *testing.T) { + ipv6a := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 1, 2, 3, 4} + ipv6b := net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + assert.NotNil(t, ipv6a) + + tests := []struct { + in net.IP + out uint32 + }{ + {net.IPv4(1, 2, 3, 4), 1<<24 | 2<<16 | 3<<8 | 4}, + {ipv6a, 1<<24 | 2<<16 | 3<<8 | 4}, // IPv6 but convertible to IPv4 + {ipv6b, 0}, + } + for _, test := range tests { + ip := PackIPAsUint32(test.in) + assert.Equal(t, test.out, ip) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin.go new file mode 100644 index 000000000..636952b7f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin.go @@ -0,0 +1,76 @@ +// 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 jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// ZipkinSpanFormat is an OpenTracing carrier format constant +const ZipkinSpanFormat = "zipkin-span-format" + +// ExtractableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type ExtractableZipkinSpan interface { + TraceID() uint64 + SpanID() uint64 + ParentID() uint64 + Flags() byte +} + +// InjectableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type InjectableZipkinSpan interface { + SetTraceID(traceID uint64) + SetSpanID(spanID uint64) + SetParentID(parentID uint64) + SetFlags(flags byte) +} + +type zipkinPropagator struct { + tracer *Tracer +} + +func (p *zipkinPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(InjectableZipkinSpan) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.SetTraceID(ctx.TraceID().Low) // TODO this cannot work with 128bit IDs + carrier.SetSpanID(uint64(ctx.SpanID())) + carrier.SetParentID(uint64(ctx.ParentID())) + carrier.SetFlags(ctx.flags) + return nil +} + +func (p *zipkinPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(ExtractableZipkinSpan) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if carrier.TraceID() == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + var ctx SpanContext + ctx.traceID.Low = carrier.TraceID() + ctx.spanID = SpanID(carrier.SpanID()) + ctx.parentID = SpanID(carrier.ParentID()) + ctx.flags = carrier.Flags() + return ctx, nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/README.md b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/README.md new file mode 100644 index 000000000..c6a4c027c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/README.md @@ -0,0 +1,34 @@ +# Zipkin compatibility features + +## `NewZipkinB3HTTPHeaderPropagator()` + +Adds support for injecting and extracting Zipkin B3 Propagation HTTP headers, +for use with other Zipkin collectors. + +```go + +// ... +import ( + "github.com/uber/jaeger-client-go/zipkin" +) + +func main() { + // ... + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + injector := jaeger.TracerOptions.Injector(opentracing.HTTPHeaders, zipkinPropagator) + extractor := jaeger.TracerOptions.Extractor(opentracing.HTTPHeaders, zipkinPropagator) + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinSharedRPCSpan := jaeger.TracerOptions.ZipkinSharedRPCSpan(true) + + // create Jaeger tracer + tracer, closer := jaeger.NewTracer( + "myService", + mySampler, // as usual + myReporter // as usual + injector, + extractor, + zipkinSharedRPCSpan, + ) +} +``` diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/doc.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/doc.go new file mode 100644 index 000000000..11357f8a7 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/doc.go @@ -0,0 +1,16 @@ +// 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 zipkin comprises Zipkin functionality for Zipkin compatiblity. +package zipkin diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation.go new file mode 100644 index 000000000..000c52f70 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation.go @@ -0,0 +1,95 @@ +// 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 zipkin + +import ( + "strconv" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" +) + +// Propagator is an Injector and Extractor +type Propagator struct{} + +// NewZipkinB3HTTPHeaderPropagator creates a Propagator for extracting and injecting +// Zipkin HTTP B3 headers into SpanContexts. +func NewZipkinB3HTTPHeaderPropagator() Propagator { + return Propagator{} +} + +// Inject conforms to the Injector interface for decoding Zipkin HTTP B3 headers +func (p Propagator) Inject( + sc jaeger.SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // TODO this needs to change to support 128bit IDs + textMapWriter.Set("x-b3-traceid", strconv.FormatUint(sc.TraceID().Low, 16)) + if sc.ParentID() != 0 { + textMapWriter.Set("x-b3-parentspanid", strconv.FormatUint(uint64(sc.ParentID()), 16)) + } + textMapWriter.Set("x-b3-spanid", strconv.FormatUint(uint64(sc.SpanID()), 16)) + if sc.IsSampled() { + textMapWriter.Set("x-b3-sampled", "1") + } else { + textMapWriter.Set("x-b3-sampled", "0") + } + return nil +} + +// Extract conforms to the Extractor interface for encoding Zipkin HTTP B3 headers +func (p Propagator) Extract(abstractCarrier interface{}) (jaeger.SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return jaeger.SpanContext{}, opentracing.ErrInvalidCarrier + } + var traceID uint64 + var spanID uint64 + var parentID uint64 + sampled := false + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + var err error + if key == "x-b3-traceid" { + traceID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-parentspanid" { + parentID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-spanid" { + spanID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-sampled" && value == "1" { + sampled = true + } + return err + }) + + if err != nil { + return jaeger.SpanContext{}, err + } + if traceID == 0 { + return jaeger.SpanContext{}, opentracing.ErrSpanContextNotFound + } + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, nil), nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation_test.go new file mode 100644 index 000000000..8ef4f3cfc --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin/propagation_test.go @@ -0,0 +1,113 @@ +// 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 zipkin + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-client-go" +) + +var ( + rootSampled = newSpanContext(1, 2, 0, true) + nonRootSampled = newSpanContext(1, 2, 1, true) + nonRootNonSampled = newSpanContext(1, 2, 1, false) +) + +var ( + rootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-sampled": "1", + } + nonRootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "1", + } + nonRootNonSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "0", + } + invalidHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "jdkafhsd", + "x-b3-spanid": "afsdfsdf", + "x-b3-parentspanid": "hiagggdf", + "x-b3-sampled": "sdfgsdfg", + } +) + +var ( + propagator = NewZipkinB3HTTPHeaderPropagator() +) + +func newSpanContext(traceID, spanID, parentID uint64, sampled bool) jaeger.SpanContext { + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, + nil, + ) +} + +func TestExtractorInvalid(t *testing.T) { + _, err := propagator.Extract(invalidHeader) + assert.Error(t, err) +} + +func TestExtractorRootSampled(t *testing.T) { + ctx, err := propagator.Extract(rootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, rootSampled, ctx) +} + +func TestExtractorNonRootSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampled, ctx) +} + +func TestExtractorNonRootNonSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootNonSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampled, ctx) +} + +func TestInjectorRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(rootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, rootSampledHeader, hdr) +} + +func TestInjectorNonRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampledHeader, hdr) +} + +func TestInjectorNonRootNonSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootNonSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampledHeader, hdr) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_test.go new file mode 100644 index 000000000..2d1d464e6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_test.go @@ -0,0 +1,68 @@ +// 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 jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestZipkinPropagator(t *testing.T) { + tracer, tCloser := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + defer tCloser.Close() + + carrier := &TestZipkinSpan{} + sp := tracer.StartSpan("y") + + // Note: we intentionally use string as format, as that's what TChannel would need to do + if err := tracer.Inject(sp.Context(), "zipkin-span-format", carrier); err != nil { + t.Fatalf("Inject failed: %+v", err) + } + sp1 := sp.(*Span) + assert.Equal(t, sp1.context.traceID, TraceID{Low: carrier.traceID}) + assert.Equal(t, sp1.context.spanID, SpanID(carrier.spanID)) + assert.Equal(t, sp1.context.parentID, SpanID(carrier.parentID)) + assert.Equal(t, sp1.context.flags, carrier.flags) + + sp2ctx, err := tracer.Extract("zipkin-span-format", carrier) + if err != nil { + t.Fatalf("Extract failed: %+v", err) + } + sp2 := tracer.StartSpan("x", ext.RPCServerOption(sp2ctx)) + sp3 := sp2.(*Span) + assert.Equal(t, sp1.context.traceID, sp3.context.traceID) + assert.Equal(t, sp1.context.spanID, sp3.context.spanID) + assert.Equal(t, sp1.context.parentID, sp3.context.parentID) + assert.Equal(t, sp1.context.flags, sp3.context.flags) +} + +// TestZipkinSpan is a mock-up of TChannel's internal Span struct +type TestZipkinSpan struct { + traceID uint64 + parentID uint64 + spanID uint64 + flags byte +} + +func (s TestZipkinSpan) TraceID() uint64 { return s.traceID } +func (s TestZipkinSpan) ParentID() uint64 { return s.parentID } +func (s TestZipkinSpan) SpanID() uint64 { return s.spanID } +func (s TestZipkinSpan) Flags() byte { return s.flags } +func (s *TestZipkinSpan) SetTraceID(traceID uint64) { s.traceID = traceID } +func (s *TestZipkinSpan) SetSpanID(spanID uint64) { s.spanID = spanID } +func (s *TestZipkinSpan) SetParentID(parentID uint64) { s.parentID = parentID } +func (s *TestZipkinSpan) SetFlags(flags byte) { s.flags = flags } diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span.go new file mode 100644 index 000000000..b2e9a3e64 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span.go @@ -0,0 +1,325 @@ +// 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 jaeger + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/spanlog" + z "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +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 +) + +var specialTagHandlers = map[string]func(*zipkinSpan, interface{}){ + string(ext.SpanKind): setSpanKind, + string(ext.PeerHostIPv4): setPeerIPv4, + string(ext.PeerPort): setPeerPort, + string(ext.PeerService): setPeerService, + TracerIPTagKey: removeTag, +} + +// BuildZipkinThrift builds thrift span based on internal span. +func BuildZipkinThrift(s *Span) *z.Span { + span := &zipkinSpan{Span: s} + span.handleSpecialTags() + parentID := int64(span.context.parentID) + var ptrParentID *int64 + if parentID != 0 { + ptrParentID = &parentID + } + 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 + ID: int64(span.context.spanID), + ParentID: ptrParentID, + Name: span.operationName, + Timestamp: ×tamp, + Duration: &duration, + Debug: span.context.IsDebug(), + Annotations: buildAnnotations(span, endpoint), + BinaryAnnotations: buildBinaryAnnotations(span, endpoint)} + return thriftSpan +} + +func buildAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.Annotation { + // automatically adding 2 Zipkin CoreAnnotations + annotations := make([]*z.Annotation, 0, 2+len(span.logs)) + var startLabel, endLabel string + if span.spanKind == string(ext.SpanKindRPCClientEnum) { + startLabel, endLabel = z.CLIENT_SEND, z.CLIENT_RECV + } else if span.spanKind == string(ext.SpanKindRPCServerEnum) { + startLabel, endLabel = z.SERVER_RECV, z.SERVER_SEND + } + if !span.startTime.IsZero() && startLabel != "" { + start := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(span.startTime), + Value: startLabel, + Host: endpoint} + annotations = append(annotations, start) + if span.duration != 0 { + endTs := span.startTime.Add(span.duration) + end := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(endTs), + Value: endLabel, + Host: endpoint} + annotations = append(annotations, end) + } + } + for _, log := range span.logs { + anno := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Host: endpoint} + if content, err := spanlog.MaterializeWithJSON(log.Fields); err == nil { + anno.Value = truncateString(string(content)) + } else { + anno.Value = err.Error() + } + annotations = append(annotations, anno) + } + return annotations +} + +func buildBinaryAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.BinaryAnnotation { + // automatically adding local component or server/client address tag, and client version + annotations := make([]*z.BinaryAnnotation, 0, 2+len(span.tags)) + + if span.peerDefined() && span.isRPC() { + peer := z.Endpoint{ + Ipv4: span.peer.Ipv4, + Port: span.peer.Port, + ServiceName: span.peer.ServiceName} + label := z.CLIENT_ADDR + if span.isRPCClient() { + label = z.SERVER_ADDR + } + anno := &z.BinaryAnnotation{ + Key: label, + Value: []byte{1}, + AnnotationType: z.AnnotationType_BOOL, + Host: &peer} + annotations = append(annotations, anno) + } + if !span.isRPC() { + componentName := endpoint.ServiceName + for _, tag := range span.tags { + if tag.key == string(ext.Component) { + componentName = stringify(tag.value) + break + } + } + local := &z.BinaryAnnotation{ + Key: z.LOCAL_COMPONENT, + Value: []byte(componentName), + AnnotationType: z.AnnotationType_STRING, + Host: endpoint} + annotations = append(annotations, local) + } + for _, tag := range span.tags { + // "Special tags" are already handled by this point, we'd be double reporting the + // tags if we don't skip here + if _, ok := specialTagHandlers[tag.key]; ok { + continue + } + if anno := buildBinaryAnnotation(tag.key, tag.value, nil); anno != nil { + annotations = append(annotations, anno) + } + } + return annotations +} + +func buildBinaryAnnotation(key string, val interface{}, endpoint *z.Endpoint) *z.BinaryAnnotation { + bann := &z.BinaryAnnotation{Key: key, Host: endpoint} + if value, ok := val.(string); ok { + bann.Value = []byte(truncateString(value)) + bann.AnnotationType = z.AnnotationType_STRING + } else if value, ok := val.([]byte); ok { + if len(value) > maxAnnotationLength { + value = value[:maxAnnotationLength] + } + bann.Value = value + bann.AnnotationType = z.AnnotationType_BYTES + } else if value, ok := val.(int32); ok && allowPackedNumbers { + bann.Value = int32ToBytes(value) + bann.AnnotationType = z.AnnotationType_I32 + } else if value, ok := val.(int64); ok && allowPackedNumbers { + bann.Value = int64ToBytes(value) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(int); ok && allowPackedNumbers { + bann.Value = int64ToBytes(int64(value)) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(bool); ok { + bann.Value = []byte{boolToByte(value)} + bann.AnnotationType = z.AnnotationType_BOOL + } else { + value := stringify(val) + bann.Value = []byte(truncateString(value)) + bann.AnnotationType = z.AnnotationType_STRING + } + return bann +} + +func stringify(value interface{}) string { + if s, ok := value.(string); ok { + return s + } + return fmt.Sprintf("%+v", value) +} + +func truncateString(value string) 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] + } + return value +} + +func boolToByte(b bool) byte { + if b { + return 1 + } + return 0 +} + +// int32ToBytes converts int32 to bytes. +func int32ToBytes(i int32) []byte { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(i)) + return buf +} + +// int64ToBytes converts int64 to bytes. +func int64ToBytes(i int64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +type zipkinSpan struct { + *Span + + // peer points to the peer service participating in this span, + // e.g. the Client if this span is a server span, + // or Server if this span is a client span + peer struct { + Ipv4 int32 + Port int16 + ServiceName string + } + + // used to distinguish local vs. RPC Server vs. RPC Client spans + spanKind string +} + +func (s *zipkinSpan) handleSpecialTags() { + s.Lock() + defer s.Unlock() + if s.firstInProcess { + // append the process tags + s.tags = append(s.tags, s.tracer.tags...) + } + filteredTags := make([]Tag, 0, len(s.tags)) + for _, tag := range s.tags { + if handler, ok := specialTagHandlers[tag.key]; ok { + handler(s, tag.value) + } else { + filteredTags = append(filteredTags, tag) + } + } + s.tags = filteredTags +} + +func setSpanKind(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.spanKind = val + return + } + if val, ok := value.(ext.SpanKindEnum); ok { + s.spanKind = string(val) + } +} + +func setPeerIPv4(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if ip, err := utils.ParseIPToUint32(val); err == nil { + s.peer.Ipv4 = int32(ip) + return + } + } + if val, ok := value.(uint32); ok { + s.peer.Ipv4 = int32(val) + return + } + if val, ok := value.(int32); ok { + s.peer.Ipv4 = val + } +} + +func setPeerPort(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if port, err := utils.ParsePort(val); err == nil { + s.peer.Port = int16(port) + return + } + } + if val, ok := value.(uint16); ok { + s.peer.Port = int16(val) + return + } + if val, ok := value.(int); ok { + s.peer.Port = int16(val) + } +} + +func setPeerService(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.peer.ServiceName = val + } +} + +func removeTag(s *zipkinSpan, value interface{}) {} + +func (s *zipkinSpan) peerDefined() bool { + return s.peer.ServiceName != "" || s.peer.Ipv4 != 0 || s.peer.Port != 0 +} + +func (s *zipkinSpan) isRPC() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) || s.spanKind == string(ext.SpanKindRPCServerEnum) +} + +func (s *zipkinSpan) isRPCClient() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span_test.go b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span_test.go new file mode 100644 index 000000000..d14b4bee5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-client-go/zipkin_thrift_span_test.go @@ -0,0 +1,329 @@ +// 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 jaeger + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +func TestThriftFirstInProcessSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + sp2.Finish() + sp1.Finish() + + tests := []struct { + span *Span + wantTags bool + }{ + {sp1, true}, + {sp2, false}, + } + + for _, test := range tests { + var check func(assert.TestingT, interface{}, ...interface{}) bool + if test.wantTags { + check = assert.NotNil + } else { + check = assert.Nil + } + thriftSpan := BuildZipkinThrift(test.span) + version := findBinaryAnnotation(thriftSpan, JaegerClientVersionTagKey) + hostname := findBinaryAnnotation(thriftSpan, TracerHostnameTagKey) + check(t, version) + check(t, hostname) + } +} + +func TestThriftForceSampled(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(false), // sample nothing + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SamplingPriority.Set(sp, 1) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + thriftSpan := BuildZipkinThrift(sp) + assert.True(t, thriftSpan.Debug) +} + +func TestThriftSpanLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + fields := func(fields ...log.Field) []log.Field { + return fields + } + tests := []struct { + fields []log.Field + logFunc func(sp opentracing.Span) + expected string + expectedTimestamp int64 + disableSampling bool + }{ + {fields: fields(log.String("event", "happened")), expected: "happened"}, + {fields: fields(log.String("something", "happened")), expected: `{"something":"happened"}`}, + {fields: fields(log.Bool("something", true)), expected: `{"something":"true"}`}, + {fields: fields(log.Int("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Float32("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Float64("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Error(errors.New("drugs are baaad, m-k"))), + expected: `{"error":"drugs are baaad, m-k"}`}, + {fields: fields(log.Object("something", 123)), expected: `{"something":"123"}`}, + { + fields: fields(log.Lazy(func(fv log.Encoder) { + fv.EmitBool("something", true) + })), + expected: `{"something":"true"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: `{"error":"non-even keyValues len: 1","function":"LogKV"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent("something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload("something", "payload") + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something"}) + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something", Payload: "payload"}) + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: fields(log.String("event", "happened")), + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + Payload: "payload", + }, + }, + }) + }, + expected: `{"event":"happened","payload":"payload"}`, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + fields: fields(log.String("event", "happened")), + expected: "", + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "", + }, + } + + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if len(test.fields) > 0 { + sp.LogFields(test.fields...) + } + thriftSpan := BuildZipkinThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(thriftSpan.Annotations), testName) + continue + } + assert.Equal(t, 1, len(thriftSpan.Annotations), testName) + assert.Equal(t, test.expected, thriftSpan.Annotations[0].Value, testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, thriftSpan.Annotations[0].Timestamp, testName) + } + } +} + +func TestThriftLocalComponentSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + tests := []struct { + addComponentTag bool + wantAnnotation string + }{ + {false, "DOOP"}, // Without COMPONENT tag the value is the service name + {true, "c1"}, + } + + for _, test := range tests { + sp := tracer.StartSpan("s1").(*Span) + if test.addComponentTag { + ext.Component.Set(sp, "c1") + } + sp.Finish() + thriftSpan := BuildZipkinThrift(sp) + + anno := findBinaryAnnotation(thriftSpan, "lc") + require.NotNil(t, anno) + assert.EqualValues(t, test.wantAnnotation, anno.Value) + } +} + +func TestSpecialTags(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer") + ext.PeerPort.Set(sp, 80) + ext.PeerHostIPv4.Set(sp, 2130706433) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + // Special tags should not be copied over to binary annotations + assert.Nil(t, findBinaryAnnotation(thriftSpan, "span.kind")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.service")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.port")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.ipv4")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "ip")) + + anno := findBinaryAnnotation(thriftSpan, "ca") + assert.NotNil(t, anno) + assert.NotNil(t, anno.Host) + assert.EqualValues(t, 80, anno.Host.Port) + assert.EqualValues(t, 2130706433, anno.Host.Ipv4) + assert.EqualValues(t, "peer", anno.Host.ServiceName) + + assert.NotNil(t, findAnnotation(thriftSpan, "sr")) + assert.NotNil(t, findAnnotation(thriftSpan, "ss")) +} + +func TestBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + assert.NotNil(t, findAnnotation(thriftSpan, `{"event":"baggage","key":"auth.token","value":"token"}`)) +} + +func findAnnotation(span *zipkincore.Span, name string) *zipkincore.Annotation { + for _, a := range span.Annotations { + if a.Value == name { + return a + } + } + return nil +} + +func findBinaryAnnotation(span *zipkincore.Span, name string) *zipkincore.BinaryAnnotation { + for _, a := range span.BinaryAnnotations { + if a.Key == name { + return a + } + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/counter.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/counter.go new file mode 100644 index 000000000..2a6a43efd --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/counter.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +// Counter tracks the number of times an event has occurred +type Counter interface { + // Inc adds the given value to the counter. + Inc(int64) +} + +// NullCounter counter that does nothing +var NullCounter Counter = nullCounter{} + +type nullCounter struct{} + +func (nullCounter) Inc(int64) {} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/factory.go new file mode 100644 index 000000000..a744a890d --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/factory.go @@ -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 + +// 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 + + // Namespace returns a nested metrics factory. + Namespace(name string, tags map[string]string) Factory +} + +// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge. +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 } diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/gauge.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/gauge.go new file mode 100644 index 000000000..3c606391a --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/gauge.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +// Gauge returns instantaneous measurements of something as an int64 value +type Gauge interface { + // Update the gauge to the value passed in. + Update(int64) +} + +// NullGauge gauge that does nothing +var NullGauge Gauge = nullGauge{} + +type nullGauge struct{} + +func (nullGauge) Update(int64) {} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory.go new file mode 100644 index 000000000..186c87778 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package expvar + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/expvar" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit expvar package. +// buckets is the number of buckets to be used in histograms. +func NewFactory(buckets int) xkit.Factory { + return factory{ + buckets: buckets, + } +} + +type factory struct { + buckets int +} + +func (f factory) Counter(name string) metrics.Counter { + return expvar.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return expvar.NewHistogram(name, f.buckets) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return expvar.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: false} +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory_test.go new file mode 100644 index 000000000..30a0d594c --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/expvar/factory_test.go @@ -0,0 +1,43 @@ +package expvar + +import ( + "expvar" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCounter(t *testing.T) { + f := NewFactory(10) + assert.False(t, f.Capabilities().Tagging) + c := f.Counter("gokit_expvar_counter") + c.Add(42) + kv := findExpvar("gokit_expvar_counter") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestGauge(t *testing.T) { + f := NewFactory(10) + g := f.Gauge("gokit_expvar_gauge") + g.Set(42) + kv := findExpvar("gokit_expvar_gauge") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory(10) + hist := f.Histogram("gokit_expvar_hist") + hist.Observe(10.5) + kv := findExpvar("gokit_expvar_hist.p50") + assert.Equal(t, "10.5", kv.Value.String()) +} + +func findExpvar(key string) *expvar.KeyValue { + var kv *expvar.KeyValue + expvar.Do(func(v expvar.KeyValue) { + if v.Key == key { + kv = &v + } + }) + return kv +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory.go new file mode 100644 index 000000000..d19c23c68 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory.go @@ -0,0 +1,161 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package xkit + +import ( + kit "github.com/go-kit/kit/metrics" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory provides a unified interface for creating named metrics +// from various go-kit metrics implementations. +type Factory interface { + Counter(name string) kit.Counter + Gauge(name string) kit.Gauge + Histogram(name string) kit.Histogram + Capabilities() Capabilities +} + +// Capabilities describes capabilities of a specific metrics factory. +type Capabilities struct { + // Tagging indicates whether the factory has the capability for tagged metrics + Tagging bool +} + +// FactoryOption is a function that adjusts some parameters of the factory. +type FactoryOption func(*factory) + +// Wrap is used to create an adapter from xkit.Factory to metrics.Factory. +func Wrap(namespace string, f Factory, options ...FactoryOption) metrics.Factory { + factory := &factory{ + scope: namespace, + factory: f, + scopeSep: ".", + tagsSep: ".", + tagKVSep: "_", + } + for i := range options { + options[i](factory) + } + return factory +} + +// ScopeSeparator returns an option that overrides default scope separator. +func ScopeSeparator(scopeSep string) FactoryOption { + return func(f *factory) { + f.scopeSep = scopeSep + } +} + +// TagsSeparator returns an option that overrides default tags separator. +func TagsSeparator(tagsSep string) FactoryOption { + return func(f *factory) { + f.tagsSep = tagsSep + } +} + +type factory struct { + scope string + tags map[string]string + factory Factory + scopeSep string + tagsSep string + tagKVSep string +} + +func (f *factory) subScope(name string) string { + if f.scope == "" { + return name + } + if name == "" { + return f.scope + } + return f.scope + f.scopeSep + name +} + +// nameAndTagsList returns a name and tags list for the new metrics. +// The name is a concatenation of nom and the current factory scope. +// The tags list is a flattened list of passed tags merged with factory tags. +// If the underlying factory does not support tags, then the tags are +// transformed into a string and appended to the name. +func (f *factory) nameAndTagsList(nom string, tags map[string]string) (name string, tagsList []string) { + mergedTags := f.mergeTags(tags) + name = f.subScope(nom) + tagsList = f.tagsList(mergedTags) + if len(tagsList) == 0 || f.factory.Capabilities().Tagging { + return + } + name = metrics.GetKey(name, mergedTags, f.tagsSep, f.tagKVSep) + tagsList = nil + return +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + name, tagsList := f.nameAndTagsList(name, tags) + counter := f.factory.Counter(name) + if len(tagsList) > 0 { + counter = counter.With(tagsList...) + } + return NewCounter(counter) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + name, tagsList := f.nameAndTagsList(name, tags) + hist := f.factory.Histogram(name) + if len(tagsList) > 0 { + hist = hist.With(tagsList...) + } + return NewTimer(hist) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + name, tagsList := f.nameAndTagsList(name, tags) + gauge := f.factory.Gauge(name) + if len(tagsList) > 0 { + gauge = gauge.With(tagsList...) + } + return NewGauge(gauge) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + scope: f.subScope(name), + tags: f.mergeTags(tags), + factory: f.factory, + scopeSep: f.scopeSep, + tagsSep: f.tagsSep, + tagKVSep: f.tagKVSep, + } +} + +func (f *factory) tagsList(a map[string]string) []string { + ret := make([]string, 0, 2*len(a)) + for k, v := range a { + ret = append(ret, k, v) + } + return ret +} + +func (f *factory) mergeTags(tags map[string]string) map[string]string { + ret := make(map[string]string, len(f.tags)+len(tags)) + for k, v := range f.tags { + ret[k] = v + } + for k, v := range tags { + ret[k] = v + } + return ret +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory_test.go new file mode 100644 index 000000000..d5e7d0eed --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/factory_test.go @@ -0,0 +1,202 @@ +package xkit + +import ( + "sort" + "testing" + "time" + + kit "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +type genericFactory struct{} + +func (f genericFactory) Counter(name string) kit.Counter { return generic.NewCounter(name) } +func (f genericFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f genericFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f genericFactory) Capabilities() Capabilities { return Capabilities{Tagging: true} } + +// noTagsFactory is similar to genericFactory but overrides With() methods to no-op +type noTagsFactory struct{} + +func (f noTagsFactory) Counter(name string) kit.Counter { + return noTagsCounter{generic.NewCounter(name)} +} +func (f noTagsFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f noTagsFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f noTagsFactory) Capabilities() Capabilities { return Capabilities{Tagging: false} } + +type noTagsCounter struct { + counter *generic.Counter +} + +func (c noTagsCounter) Add(delta float64) { c.counter.Add(delta) } +func (c noTagsCounter) With(labelValues ...string) kit.Counter { return c } + +type noTagsGauge struct { + gauge *generic.Gauge +} + +func (g noTagsGauge) Set(value float64) { g.gauge.Set(value) } +func (g noTagsGauge) Add(delta float64) { g.gauge.Add(delta) } +func (g noTagsGauge) With(labelValues ...string) kit.Gauge { return g } + +type noTagsHistogram struct { + hist *generic.Histogram +} + +func (h noTagsHistogram) Observe(value float64) { h.hist.Observe(value) } +func (h noTagsHistogram) With(labelValues ...string) kit.Histogram { return h } + +type Tags map[string]string +type metricFunc func(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) + +type testCase struct { + f Factory + + prefix string + name string + tags Tags + + useNamespace bool + namespace string + namespaceTags Tags + options []FactoryOption + + expName string + expTags []string +} + +func TestFactoryScoping(t *testing.T) { + genericFactory := genericFactory{} + noTagsFactory := noTagsFactory{} + testSuites := []struct { + metricType string + metricFunc metricFunc + }{ + {"counter", testCounter}, + {"gauge", testGauge}, + {"timer", testTimer}, + } + for _, ts := range testSuites { + testSuite := ts // capture loop var + testCases := []testCase{ + {f: genericFactory, prefix: "x", name: "", expName: "x"}, + {f: genericFactory, prefix: "", name: "y", expName: "y"}, + {f: genericFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: genericFactory, prefix: "x", name: "z", expName: "x.z", tags: Tags{"a": "b"}, expTags: []string{"a", "b"}}, + { + f: genericFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: genericFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y", + expTags: []string{"a", "b"}, + }, + { + f: genericFactory, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w.z", + expTags: []string{"a", "b", "c", "d"}, + }, + {f: noTagsFactory, prefix: "x", name: "", expName: "x"}, + {f: noTagsFactory, prefix: "", name: "y", expName: "y"}, + {f: noTagsFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: noTagsFactory, prefix: "x", name: "z", expName: "x.z.a_b", tags: Tags{"a": "b"}}, + { + f: noTagsFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: noTagsFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y.a_b", + }, + { + f: noTagsFactory, + options: []FactoryOption{ScopeSeparator(":"), TagsSeparator(":")}, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w:z:a_b:c_d", + }, + } + for _, tc := range testCases { + testCase := tc // capture loop var + factoryName := "genericFactory" + if testCase.f == noTagsFactory { + factoryName = "noTagsFactory" + } + t.Run(factoryName+"_"+testSuite.metricType+"_"+testCase.expName, func(t *testing.T) { + f := Wrap(testCase.prefix, testCase.f, testCase.options...) + if testCase.useNamespace { + f = f.Namespace(testCase.namespace, testCase.namespaceTags) + } + name, labels := testSuite.metricFunc(t, testCase, f) + assert.Equal(t, testCase.expName, name()) + actualTags := labels() + sort.Strings(actualTags) + assert.Equal(t, testCase.expTags, actualTags) + }) + } + } +} + +func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + c := f.Counter(testCase.name, testCase.tags) + c.Inc(123) + kc := c.(*Counter).counter + var gc *generic.Counter + if c, ok := kc.(*generic.Counter); ok { + gc = c + } else { + gc = kc.(noTagsCounter).counter + } + assert.EqualValues(t, 123.0, gc.Value()) + name = func() string { return gc.Name } + labels = gc.LabelValues + return +} + +func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + g := f.Gauge(testCase.name, testCase.tags) + g.Update(123) + gg := g.(*Gauge).gauge.(*generic.Gauge) + assert.EqualValues(t, 123.0, gg.Value()) + name = func() string { return gg.Name } + labels = gg.LabelValues + return +} + +func testTimer(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + tm := f.Timer(testCase.name, testCase.tags) + tm.Record(123 * time.Millisecond) + gt := tm.(*Timer).hist.(*generic.Histogram) + assert.InDelta(t, 0.123, gt.Quantile(0.9), 0.00001) + name = func() string { return gt.Name } + labels = gt.LabelValues + return +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory.go new file mode 100644 index 000000000..d92493210 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory.go @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package influx + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/influx" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit influx package. +func NewFactory(client *influx.Influx) xkit.Factory { + return factory{ + client: client, + } +} + +type factory struct { + client *influx.Influx +} + +func (f factory) Counter(name string) metrics.Counter { + return f.client.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return f.client.NewHistogram(name) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return f.client.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory_test.go new file mode 100644 index 000000000..f4dc71ca2 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/influx/factory_test.go @@ -0,0 +1,87 @@ +package influx + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/metrics/influx" + influxdb "github.com/influxdata/influxdb/client/v2" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + c := wf.Counter("gokit.infl-counter", map[string]string{"label": "val1"}) + c.Inc(7) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-counter,label=val1 count=7") +} + +func TestGauge(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + g := wf.Gauge("gokit.infl-gauge", map[string]string{"x": "y"}) + g.Update(42) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-gauge,x=y value=42") +} + +func TestTimer(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + timer := wf.Timer("gokit.infl-timer", map[string]string{"x": "y"}) + timer.Record(time.Second * 1) + timer.Record(time.Second * 1) + timer.Record(time.Second * 10) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-timer,x=y p50=1,p90=10,p95=10,p99=10") +} + +func TestWrapperNamespaces(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + wf = wf.Namespace("bar", map[string]string{"bar_tag": "bar_tag"}) + + c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + + assert.Contains(t, reportToString(in), "namespace.bar.gokit.prom-wrapped-counter,bar_tag=bar_tag,x=y count=42") +} + +func TestCapabilities(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + + assert.True(t, inf.Capabilities().Tagging) +} + +func reportToString(in *influx.Influx) string { + client := &bufWriter{} + in.WriteTo(client) + return client.buf.String() +} + +type bufWriter struct { + buf bytes.Buffer +} + +func (w *bufWriter) Write(bp influxdb.BatchPoints) error { + for _, p := range bp.Points() { + fmt.Fprintf(&w.buf, p.String()+"\n") + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics.go new file mode 100644 index 000000000..3b55b8ba5 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package xkit + +import ( + "time" + + kit "github.com/go-kit/kit/metrics" +) + +// Counter is an adapter from go-kit Counter to jaeger-lib Counter +type Counter struct { + counter kit.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter kit.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Add(float64(delta)) +} + +// Gauge is an adapter from go-kit Gauge to jaeger-lib Gauge +type Gauge struct { + gauge kit.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge kit.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Set(float64(value)) +} + +// Timer is an adapter from go-kit Histogram to jaeger-lib Timer +type Timer struct { + hist kit.Histogram +} + +// NewTimer creates a new Timer +func NewTimer(hist kit.Histogram) *Timer { + return &Timer{hist: hist} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.hist.Observe(delta.Seconds()) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics_test.go new file mode 100644 index 000000000..b62c08af6 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/metrics_test.go @@ -0,0 +1,32 @@ +package xkit + +import ( + "testing" + "time" + + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestCounter(t *testing.T) { + kitCounter := generic.NewCounter("abc") + var counter metrics.Counter = NewCounter(kitCounter) + counter.Inc(123) + assert.EqualValues(t, 123, kitCounter.Value()) +} + +func TestGauge(t *testing.T) { + kitGauge := generic.NewGauge("abc") + var gauge metrics.Gauge = NewGauge(kitGauge) + gauge.Update(123) + assert.EqualValues(t, 123, kitGauge.Value()) +} + +func TestTimer(t *testing.T) { + kitHist := generic.NewHistogram("abc", 10) + var timer metrics.Timer = NewTimer(kitHist) + timer.Record(100*time.Millisecond + 500*time.Microsecond) // 100.5 milliseconds + assert.EqualValues(t, 0.1005, kitHist.Quantile(0.9)) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory.go new file mode 100644 index 000000000..2f133c34e --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory.go @@ -0,0 +1,82 @@ +// 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 prometheus + +import ( + "strings" + + "github.com/go-kit/kit/metrics" + kitprom "github.com/go-kit/kit/metrics/prometheus" + "github.com/prometheus/client_golang/prometheus" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +var normalizer = strings.NewReplacer( + ".", "_", + "-", "_", +) + +// NewFactory creates a new metrics factory using go-kit prometheus package. +// buckets define the buckets into which histogram observations are counted. +// If buckets == nil, the default value prometheus.DefBuckets is used. +func NewFactory(namespace, subsystem string, buckets []float64) xkit.Factory { + return &factory{ + namespace: namespace, + subsystem: subsystem, + buckets: buckets, + } +} + +type factory struct { + namespace string + subsystem string + buckets []float64 +} + +func (f *factory) Counter(name string) metrics.Counter { + opts := prometheus.CounterOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewCounterFrom(opts, nil) +} + +func (f *factory) Histogram(name string) metrics.Histogram { + opts := prometheus.HistogramOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + Buckets: f.buckets, + } + return kitprom.NewHistogramFrom(opts, nil) +} + +func (f *factory) Gauge(name string) metrics.Gauge { + opts := prometheus.GaugeOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewGaugeFrom(opts, nil) +} + +func (f *factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory_test.go new file mode 100644 index 000000000..6aacfd297 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/go-kit/prometheus/factory_test.go @@ -0,0 +1,64 @@ +package prometheus + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + xkit "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + assert.True(t, f.Capabilities().Tagging) + c := f.Counter("gokit.prom-counter") + c.Add(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func TestGauge(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + g := f.Gauge("gokit.prom-gauge") + g.Set(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_gauge") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetGauge().GetValue()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + hist := f.Histogram("gokit.prom-hist") + hist.Observe(10.5) + m := findMetric(t, "namespace_subsystem_gokit_prom_hist") + require.NotNil(t, m) + assert.Equal(t, 10.5, m[0].GetHistogram().GetSampleSum()) +} + +func TestWrapper(t *testing.T) { + f := NewFactory("", "", nil) + wf := xkit.Wrap("foo", f, xkit.ScopeSeparator(":")) + wf = wf.Namespace("bar", nil) + c := wf.Counter("gokit.prom-wrapped-counter", nil) + // TODO tags currently do not work because Prometheus requires to predeclate tag keys. + // c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + m := findMetric(t, "foo:bar:gokit_prom_wrapped_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func findMetric(t *testing.T, key string) []*dto.Metric { + nf, err := prometheus.DefaultGatherer.Gather() + require.NoError(t, err) + for _, nf := range nf { + if nf.GetName() == key { + return nf.GetMetric() + } + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local.go new file mode 100644 index 000000000..8c3624849 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local.go @@ -0,0 +1,332 @@ +// 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 + } + 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, + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local_test.go new file mode 100644 index 000000000..f4dede6b0 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/local_test.go @@ -0,0 +1,116 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLocalMetrics(t *testing.T) { + tags := map[string]string{ + "x": "y", + } + + f := NewLocalFactory(0) + defer f.Stop() + f.Counter("my-counter", tags).Inc(4) + f.Counter("my-counter", tags).Inc(6) + f.Counter("my-counter", nil).Inc(6) + f.Counter("other-counter", nil).Inc(8) + f.Gauge("my-gauge", nil).Update(25) + f.Gauge("my-gauge", nil).Update(43) + f.Gauge("other-gauge", nil).Update(74) + f.Namespace("namespace", tags).Counter("my-counter", nil).Inc(7) + + timings := map[string][]time.Duration{ + "foo-latency": { + time.Second * 35, + time.Second * 6, + time.Millisecond * 576, + time.Second * 12, + }, + "bar-latency": { + time.Minute*4 + time.Second*34, + time.Minute*7 + time.Second*12, + time.Second * 625, + time.Second * 12, + }, + } + + for metric, timing := range timings { + for _, d := range timing { + f.Timer(metric, nil).Record(d) + } + } + + c, g := f.Snapshot() + require.NotNil(t, c) + require.NotNil(t, g) + + assert.Equal(t, map[string]int64{ + "my-counter|x=y": 10, + "my-counter": 6, + "other-counter": 8, + "namespace.my-counter|x=y": 7, + }, c) + + assert.Equal(t, map[string]int64{ + "bar-latency.P50": 278527, + "bar-latency.P75": 278527, + "bar-latency.P90": 442367, + "bar-latency.P95": 442367, + "bar-latency.P99": 442367, + "bar-latency.P999": 442367, + "foo-latency.P50": 6143, + "foo-latency.P75": 12287, + "foo-latency.P90": 36863, + "foo-latency.P95": 36863, + "foo-latency.P99": 36863, + "foo-latency.P999": 36863, + "my-gauge": 43, + "other-gauge": 74, + }, g) + + f.Clear() + c, g = f.Snapshot() + require.Empty(t, c) + require.Empty(t, g) +} + +func TestLocalMetricsInterval(t *testing.T) { + refreshInterval := time.Millisecond + const relativeCheckFrequency = 5 // check 5 times per refreshInterval + const maxChecks = 2 * relativeCheckFrequency + checkInterval := (refreshInterval * relativeCheckFrequency) / maxChecks + + f := NewLocalFactory(refreshInterval) + defer f.Stop() + + f.Timer("timer", nil).Record(1) + + f.tm.Lock() + timer := f.timers["timer"] + f.tm.Unlock() + assert.NotNil(t, timer) + + // timer.hist.Current is modified on every Rotate(), which is called by LocalBackend after every refreshInterval + getCurr := func() interface{} { + timer.Lock() + defer timer.Unlock() + return timer.hist.Current + } + + curr := getCurr() + + // wait for twice as long as the refresh interval + for i := 0; i < maxChecks; i++ { + time.Sleep(checkInterval) + + if getCurr() != curr { + return + } + } + t.Fail() +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics.go new file mode 100644 index 000000000..0b97707b0 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "fmt" + "reflect" + "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 +// 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 { + // Allow user to opt out of reporting metrics by passing in nil. + if factory == nil { + factory = NullFactory + } + + counterPtrType := reflect.TypeOf((*Counter)(nil)).Elem() + gaugePtrType := reflect.TypeOf((*Gauge)(nil)).Elem() + timerPtrType := reflect.TypeOf((*Timer)(nil)).Elem() + + v := reflect.ValueOf(m).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + tags := make(map[string]string) + for k, v := range globalTags { + tags[k] = v + } + field := t.Field(i) + metric := field.Tag.Get("metric") + if metric == "" { + return fmt.Errorf("Field %s is missing a tag 'metric'", field.Name) + } + if tagString := field.Tag.Get("tags"); tagString != "" { + tagPairs := strings.Split(tagString, ",") + for _, tagPair := range tagPairs { + tag := strings.Split(tagPair, "=") + if len(tag) != 2 { + return fmt.Errorf( + "Field [%s]: Tag [%s] is not of the form key=value in 'tags' string [%s]", + field.Name, tagPair, tagString) + } + tags[tag[0]] = tag[1] + } + } + var obj interface{} + if field.Type.AssignableTo(counterPtrType) { + obj = factory.Counter(metric, tags) + } else if field.Type.AssignableTo(gaugePtrType) { + obj = factory.Gauge(metric, tags) + } else if field.Type.AssignableTo(timerPtrType) { + obj = factory.Timer(metric, tags) + } else { + return fmt.Errorf( + "Field %s is not a pointer to timer, gauge, or counter", + field.Name) + } + v.Field(i).Set(reflect.ValueOf(obj)) + } + return nil +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics_test.go new file mode 100644 index 000000000..7d9226c66 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/metrics_test.go @@ -0,0 +1,89 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestInitMetrics(t *testing.T) { + testMetrics := struct { + Gauge Gauge `metric:"gauge" tags:"1=one,2=two"` + Counter Counter `metric:"counter"` + Timer Timer `metric:"timer"` + }{} + + f := NewLocalFactory(0) + defer f.Stop() + + globalTags := map[string]string{"key": "value"} + + err := initMetrics(&testMetrics, f, globalTags) + assert.NoError(t, err) + + testMetrics.Gauge.Update(10) + testMetrics.Counter.Inc(5) + testMetrics.Timer.Record(time.Duration(time.Second * 35)) + + // wait for metrics + for i := 0; i < 1000; i++ { + c, _ := f.Snapshot() + if _, ok := c["counter"]; ok { + break + } + time.Sleep(1 * time.Millisecond) + } + + c, g := f.Snapshot() + + assert.EqualValues(t, 5, c["counter|key=value"]) + assert.EqualValues(t, 10, g["gauge|1=one|2=two|key=value"]) + assert.EqualValues(t, 36863, g["timer|key=value.P50"]) + + stopwatch := StartStopwatch(testMetrics.Timer) + stopwatch.Stop() + assert.True(t, 0 < stopwatch.ElapsedTime()) +} + +var ( + noMetricTag = struct { + NoMetricTag Counter + }{} + + badTags = struct { + BadTags Counter `metric:"counter" tags:"1=one,noValue"` + }{} + + invalidMetricType = struct { + InvalidMetricType int64 `metric:"counter"` + }{} +) + +func TestInitMetricsFailures(t *testing.T) { + assert.EqualError(t, initMetrics(&noMetricTag, nil, nil), "Field NoMetricTag is missing a tag 'metric'") + + assert.EqualError(t, initMetrics(&badTags, nil, nil), + "Field [BadTags]: Tag [noValue] is not of the form key=value in 'tags' string [1=one,noValue]") + + assert.EqualError(t, initMetrics(&invalidMetricType, nil, nil), + "Field InvalidMetricType is not a pointer to timer, gauge, or counter") +} + +func TestInitPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("The code did not panic") + } + }() + + Init(&noMetricTag, NullFactory, nil) +} + +func TestNullMetrics(t *testing.T) { + // This test is just for cover + NullFactory.Timer("name", nil).Record(0) + NullFactory.Counter("name", nil).Inc(0) + NullFactory.Gauge("name", nil).Update(0) + NullFactory.Namespace("name", nil).Gauge("name2", nil).Update(0) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi.go new file mode 100644 index 000000000..c3791b60b --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package multi + +import ( + "time" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory is a metrics factory that dispatches to multiple metrics backends. +type Factory struct { + factories []metrics.Factory +} + +// New creates a new multi.Factory that will dispatch to multiple metrics backends. +func New(factories ...metrics.Factory) *Factory { + return &Factory{ + factories: factories, + } +} + +type counter struct { + counters []metrics.Counter +} + +func (c *counter) Inc(delta int64) { + for _, counter := range c.counters { + counter.Inc(delta) + } +} + +// Counter implements metrics.Factory interface +func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { + counter := &counter{ + counters: make([]metrics.Counter, len(f.factories)), + } + for i, factory := range f.factories { + counter.counters[i] = factory.Counter(name, tags) + } + return counter +} + +type timer struct { + timers []metrics.Timer +} + +func (t *timer) Record(delta time.Duration) { + for _, timer := range t.timers { + timer.Record(delta) + } +} + +// Timer implements metrics.Factory interface +func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { + timer := &timer{ + timers: make([]metrics.Timer, len(f.factories)), + } + for i, factory := range f.factories { + timer.timers[i] = factory.Timer(name, tags) + } + return timer +} + +type gauge struct { + gauges []metrics.Gauge +} + +func (t *gauge) Update(value int64) { + for _, gauge := range t.gauges { + gauge.Update(value) + } +} + +// Gauge implements metrics.Factory interface +func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { + gauge := &gauge{ + gauges: make([]metrics.Gauge, len(f.factories)), + } + for i, factory := range f.factories { + gauge.gauges[i] = factory.Gauge(name, tags) + } + return gauge +} + +// Namespace implements metrics.Factory interface +func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { + newFactory := &Factory{ + factories: make([]metrics.Factory, len(f.factories)), + } + for i, factory := range f.factories { + newFactory.factories[i] = factory.Namespace(name, tags) + } + return newFactory +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi_test.go new file mode 100644 index 000000000..24ecb7335 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/multi/multi_test.go @@ -0,0 +1,32 @@ +package multi + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +var _ metrics.Factory = &Factory{} // API check + +func TestMultiFactory(t *testing.T) { + f1 := metrics.NewLocalFactory(time.Second) + f2 := metrics.NewLocalFactory(time.Second) + multi1 := New(f1, f2) + multi2 := multi1.Namespace("ns2", nil) + tags := map[string]string{"x": "y"} + multi2.Counter("counter", tags).Inc(42) + multi2.Gauge("gauge", tags).Update(42) + multi2.Timer("timer", tags).Record(42 * time.Millisecond) + + for _, f := range []*metrics.LocalFactory{f1, f2} { + testutils.AssertCounterMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.counter", Tags: tags, Value: 42}) + testutils.AssertGaugeMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.gauge", Tags: tags, Value: 42}) + _, g := f.Snapshot() + assert.EqualValues(t, 43, g["ns2.timer|x=y.P99"]) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/stopwatch.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/stopwatch.go new file mode 100644 index 000000000..4a8abdb53 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/stopwatch.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "time" +) + +// StartStopwatch begins recording the executing time of an event, returning +// a Stopwatch that should be used to stop the recording the time for +// that event. Multiple events can be occurring simultaneously each +// represented by different active Stopwatches +func StartStopwatch(timer Timer) Stopwatch { + return Stopwatch{t: timer, start: time.Now()} +} + +// A Stopwatch tracks the execution time of a specific event +type Stopwatch struct { + t Timer + start time.Time +} + +// Stop stops executing of the stopwatch and records the amount of elapsed time +func (s Stopwatch) Stop() { + s.t.Record(s.ElapsedTime()) +} + +// ElapsedTime returns the amount of elapsed time (in time.Duration) +func (s Stopwatch) ElapsedTime() time.Duration { + return time.Since(s.start) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory.go new file mode 100644 index 000000000..792377295 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory.go @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tally + +import ( + "github.com/uber-go/tally" + + "github.com/uber/jaeger-lib/metrics" +) + +// Wrap takes a tally Scope and returns jaeger-lib metrics.Factory. +func Wrap(scope tally.Scope) metrics.Factory { + return &factory{ + tally: scope, + } +} + +// TODO implement support for tags if tally.Scope does not support them +type factory struct { + tally tally.Scope +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewCounter(scope.Counter(name)) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewGauge(scope.Gauge(name)) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewTimer(scope.Timer(name)) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + tally: f.tally.SubScope(name).Tagged(tags), + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory_test.go new file mode 100644 index 000000000..fbbbe20ac --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/factory_test.go @@ -0,0 +1,47 @@ +package tally + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/uber-go/tally" +) + +func TestFactory(t *testing.T) { + testScope := tally.NewTestScope("pre", map[string]string{"a": "b"}) + factory := Wrap(testScope).Namespace("fix", map[string]string{"c": "d"}) + counter := factory.Counter("counter", map[string]string{"x": "y"}) + counter.Inc(42) + gauge := factory.Gauge("gauge", map[string]string{"x": "y"}) + gauge.Update(42) + timer := factory.Timer("timer", map[string]string{"x": "y"}) + timer.Record(42 * time.Millisecond) + snapshot := testScope.Snapshot() + + // tally v3 includes tags in the name, so look + c := snapshot.Counters()["pre.fix.counter"] + if c == nil { + // tally v3 includes tags in the name. + c = snapshot.Counters()["pre.fix.counter+a=b,c=d,x=y"] + } + + g := snapshot.Gauges()["pre.fix.gauge"] + if g == nil { + g = snapshot.Gauges()["pre.fix.gauge+a=b,c=d,x=y"] + } + + h := snapshot.Timers()["pre.fix.timer"] + if h == nil { + h = snapshot.Timers()["pre.fix.timer+a=b,c=d,x=y"] + } + + expectedTags := map[string]string{"a": "b", "c": "d", "x": "y"} + assert.EqualValues(t, 42, c.Value()) + assert.EqualValues(t, expectedTags, c.Tags()) + assert.EqualValues(t, 42, g.Value()) + assert.EqualValues(t, expectedTags, g.Tags()) + assert.Equal(t, []time.Duration{42 * time.Millisecond}, h.Values()) + assert.EqualValues(t, expectedTags, h.Tags()) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/metrics.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/metrics.go new file mode 100644 index 000000000..f8621c66f --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/tally/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tally + +import ( + "time" + + "github.com/uber-go/tally" +) + +// Counter is an adapter from go-tally Counter to jaeger-lib Counter +type Counter struct { + counter tally.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter tally.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Inc(delta) +} + +// Gauge is an adapter from go-tally Gauge to jaeger-lib Gauge +type Gauge struct { + gauge tally.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge tally.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Update(float64(value)) +} + +// Timer is an adapter from go-tally Histogram to jaeger-lib Timer +type Timer struct { + timer tally.Timer +} + +// NewTimer creates a new Timer +func NewTimer(timer tally.Timer) *Timer { + return &Timer{timer: timer} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.timer.Record(delta) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils.go new file mode 100644 index 000000000..66134b6cc --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils.go @@ -0,0 +1,55 @@ +// 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 testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +// ExpectedMetric contains metrics under test. +type ExpectedMetric struct { + Name string + Tags map[string]string + Value int +} + +// TODO do something similar for Timers + +// AssertCounterMetrics checks if counter metrics exist. +func AssertCounterMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + counters, _ := f.Snapshot() + assertMetrics(t, counters, expectedMetrics...) +} + +// AssertGaugeMetrics checks if gauge metrics exist. +func AssertGaugeMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + _, gauges := f.Snapshot() + assertMetrics(t, gauges, expectedMetrics...) +} + +func assertMetrics(t *testing.T, actualMetrics map[string]int64, expectedMetrics ...ExpectedMetric) { + for _, expected := range expectedMetrics { + key := metrics.GetKey(expected.Name, expected.Tags, "|", "=") + assert.EqualValues(t, + expected.Value, + actualMetrics[key], + "expected metric name: %s, tags: %+v", expected.Name, expected.Tags, + ) + } +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils_test.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils_test.go new file mode 100644 index 000000000..9ebd24e89 --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/testutils/testutils_test.go @@ -0,0 +1,17 @@ +package testutils + +import ( + "testing" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestAssertMetrics(t *testing.T) { + f := metrics.NewLocalFactory(0) + tags := map[string]string{"key": "value"} + f.IncCounter("counter", tags, 1) + f.UpdateGauge("gauge", tags, 11) + + AssertCounterMetrics(t, f, ExpectedMetric{Name: "counter", Tags: tags, Value: 1}) + AssertGaugeMetrics(t, f, ExpectedMetric{Name: "gauge", Tags: tags, Value: 11}) +} diff --git a/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/timer.go b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/timer.go new file mode 100644 index 000000000..e18d222ab --- /dev/null +++ b/vendor/src/github.com/jaegertracing/jaeger-lib/metrics/timer.go @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "time" +) + +// Timer accumulates observations about how long some operation took, +// and also maintains a historgam of percentiles. +type Timer interface { + // Records the time passed in. + Record(time.Duration) +} + +// NullTimer timer that does nothing +var NullTimer Timer = nullTimer{} + +type nullTimer struct{} + +func (nullTimer) Record(time.Duration) {} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.lock b/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.lock new file mode 100644 index 000000000..bef2d0092 --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.lock @@ -0,0 +1,9 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "ab4fef131ee828e96ba67d31a7d690bd5f2f42040c6766b1b12fe856f87e0ff7" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.toml b/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.toml new file mode 100644 index 000000000..9425a5429 --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/Gopkg.toml @@ -0,0 +1,22 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/History.md b/vendor/src/gopkg.in/h2non/bimg.v1/History.md index 57cdfc541..427342dfb 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/History.md +++ b/vendor/src/gopkg.in/h2non/bimg.v1/History.md @@ -1,4 +1,96 @@ +## v1.0.18 / 2017-12-22 + + * Merge pull request #216 from Bynder/master + * Merge pull request #208 from mikestead/feature/webp-lossless + * Remove go-debug usage + * refactor(docs): remove codesponsor :( + * fix(options): use float64 type in Options.Threshold + * Merge pull request #206 from tstm/add-trim-options + * Add lossless option for saving webp + * Set the test file to write its own file + * Add the option to use background and threshold options on trim + +## v1.0.17 / 2017-11-14 + + * refactor(resizer): remove fmt statement + * fix(type_test): use string formatting + * Merge pull request #207 from traum-ferienwohnungen/nearest-neighbour + * Add nearest-neighbour interpolation + * Merge pull request #203 from traum-ferienwohnungen/fix_icc_memory_leak + * Fix memory leak on icc_transform + +## v1.0.16 / 2017-10-30 + + * fix(travis): use install directive + * Merge branch 'master' of https://github.com/h2non/bimg + * feat: add Gopkg manifests, move fixtures to testdata, add vendor dependencies + * Merge pull request #202 from openskydoor/openskydoor/fix-build-tag + * fix build tag + * fix(#199): presinstall.sh tarball download URL + +## v1.0.15 / 2017-10-05 + + * Merge pull request #198 from greut/webpload + * Add shrink-on-load for webp. + * Merge pull request #197 from greut/typos + * Small typo. + * feat(docs): add codesponsor + +## v1.0.14 / 2017-09-12 + + * Merge pull request #192 from greut/trim + * Adding trim operation. + * Merge pull request #191 from greut/alpha4 + * Update 8.6 to alpha4. + +## v1.0.13 / 2017-09-11 + + * Merge pull request #190 from greut/typos + * Fix typo and small cleanup. + +## v1.0.12 / 2017-09-10 + + * Merge branch '99designs-vips-reduce' + * fix(reduce): resolve conflicts with master + * Use vips reduce when downscaling + +## v1.0.11 / 2017-09-10 + + * feat(#189): allow strip image metadata via bimg.Options.StripMetadata = bool + * fix(resize): code format issue + * refactor(resize): add Go version comment + * refactor(tests): fix minor code formatting issues + * fix(#162): garbage collection fix. split Resize() implementation for Go runtime specific + * feat(travis): add go 1.9 + * Merge pull request #183 from greut/autorotate + * Proper handling of the EXIF cases. + * Merge pull request #184 from greut/libvips858 + * Merge branch 'master' into libvips858 + * Merge pull request #185 from greut/libvips860 + * Add libvips 8.6 pre-release + * Update to libvips 8.5.8 + * fix(resize): runtime.KeepAlive is only Go + * fix(#159): prevent buf to be freed by the GC before resize function exits + * Merge pull request #171 from greut/fix-170 + * Check the length before jumping into buffer. + * Merge pull request #168 from Traum-Ferienwohnungen/icc_transform + * Add option to convert embedded ICC profiles + * Merge pull request #166 from danjou-a/patch-1 + * Fix Resize verification value + * Merge pull request #165 from greut/libvips846 + * Testing using libvips8.4.6 from Github. + +## v1.0.10 / 2017-06-25 + + * Merge pull request #164 from greut/length + * Add Image.Length() + * Merge pull request #163 from greut/libvips856 + * Run libvips 8.5.6 on Travis. + * Merge pull request #161 from henry-blip/master + * Expose vips cache memory management functions. + * feat(docs): add watermark image note in features + ## v1.0.9 / 2017-05-25 * Merge pull request #156 from Dynom/SmartCropToGravity diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/README.md b/vendor/src/gopkg.in/h2non/bimg.v1/README.md index 022f6540c..45887584d 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/README.md +++ b/vendor/src/gopkg.in/h2non/bimg.v1/README.md @@ -16,8 +16,6 @@ bimg was heavily inspired in [sharp](https://github.com/lovell/sharp), its homol **v1 notice**: `bimg` introduces some minor breaking changes in `v1` release. If you're using `gopkg.in`, you can still rely in the `v0` without worrying about API breaking changes. -`bimg` is currently maintained by [Kirill Danshin](https://github.com/kirillDanshin). - ## Contents - [Supported image operations](#supported-image-operations) @@ -35,18 +33,19 @@ If you're using `gopkg.in`, you can still rely in the `v0` without worrying abou - Resize - Enlarge -- Crop (including smart crop support) +- Crop (including smart crop support, libvips 8.5+) - Rotate (with auto-rotate based on EXIF orientation) - Flip (with auto-flip based on EXIF metadata) - Flop - Zoom - Thumbnail - Extract area -- Watermark (text only) +- Watermark (using text or image) - Gaussian blur effect - Custom output color space (RGB, grayscale...) - Format conversion (with additional quality/compression settings) - EXIF metadata (size, alpha channel, profile, orientation...) +- Trim (libvips 8.6+) ## Prerequisites @@ -132,7 +131,7 @@ if err != nil { } size, err := bimg.NewImage(newImage).Size() -if size.Width == 400 && size.Height == 300 { +if size.Width == 800 && size.Height == 600 { fmt.Println("The image size is valid") } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go index 2144669af..620b4acac 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go @@ -5,7 +5,7 @@ import ( ) func TestRead(t *testing.T) { - buf, err := Read("fixtures/test.jpg") + buf, err := Read("testdata/test.jpg") if err != nil { t.Errorf("Cannot read the image: %#v", err) @@ -21,7 +21,7 @@ func TestRead(t *testing.T) { } func TestWrite(t *testing.T) { - buf, err := Read("fixtures/test.jpg") + buf, err := Read("testdata/test.jpg") if err != nil { t.Errorf("Cannot read the image: %#v", err) @@ -31,7 +31,7 @@ func TestWrite(t *testing.T) { t.Fatal("Empty buffer") } - err = Write("fixtures/test_write_out.jpg", buf) + err = Write("testdata/test_write_out.jpg", buf) if err != nil { t.Fatalf("Cannot write the file: %#v", err) } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/image.go b/vendor/src/gopkg.in/h2non/bimg.v1/image.go index efaffcb73..093d5a5a8 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/image.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/image.go @@ -178,6 +178,13 @@ func (i *Image) Colourspace(c Interpretation) ([]byte, error) { return i.Process(options) } +// Trim removes the background from the picture. It can result in a 0x0 output +// if the image is all background. +func (i *Image) Trim() ([]byte, error) { + options := Options{Trim: true} + return i.Process(options) +} + // Process processes the image based on the given transformation options, // talking with libvips bindings accordingly and returning the resultant // image buffer. @@ -217,7 +224,12 @@ func (i *Image) Size() (ImageSize, error) { return Size(i.buffer) } -// Image returns the current resultant image image buffer. +// Image returns the current resultant image buffer. func (i *Image) Image() []byte { return i.buffer } + +// Length returns the size in bytes of the image buffer. +func (i *Image) Length() int { + return len(i.buffer) +} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go index 96fc4ad9f..5af0431d2 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go @@ -17,7 +17,7 @@ func TestImageResize(t *testing.T) { t.Error(err) } - Write("fixtures/test_resize_out.jpg", buf) + Write("testdata/test_resize_out.jpg", buf) } func TestImageGifResize(t *testing.T) { @@ -52,7 +52,7 @@ func TestImageGifToJpeg(t *testing.T) { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_gif.jpg", buf) + Write("testdata/test_gif.jpg", buf) } } @@ -67,7 +67,7 @@ func TestImagePdfToJpeg(t *testing.T) { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_pdf.jpg", buf) + Write("testdata/test_pdf.jpg", buf) } } @@ -82,7 +82,7 @@ func TestImageSvgToJpeg(t *testing.T) { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_svg.jpg", buf) + Write("testdata/test_svg.jpg", buf) } } @@ -97,7 +97,7 @@ func TestImageResizeAndCrop(t *testing.T) { t.Error(err) } - Write("fixtures/test_resize_crop_out.jpg", buf) + Write("testdata/test_resize_crop_out.jpg", buf) } func TestImageExtract(t *testing.T) { @@ -111,7 +111,7 @@ func TestImageExtract(t *testing.T) { t.Error(err) } - Write("fixtures/test_extract_out.jpg", buf) + Write("testdata/test_extract_out.jpg", buf) } func TestImageExtractZero(t *testing.T) { @@ -125,7 +125,7 @@ func TestImageExtractZero(t *testing.T) { t.Error(err) } - Write("fixtures/test_extract_zero_out.jpg", buf) + Write("testdata/test_extract_zero_out.jpg", buf) } func TestImageEnlarge(t *testing.T) { @@ -139,7 +139,7 @@ func TestImageEnlarge(t *testing.T) { t.Error(err) } - Write("fixtures/test_enlarge_out.jpg", buf) + Write("testdata/test_enlarge_out.jpg", buf) } func TestImageEnlargeAndCrop(t *testing.T) { @@ -153,7 +153,7 @@ func TestImageEnlargeAndCrop(t *testing.T) { t.Error(err) } - Write("fixtures/test_enlarge_crop_out.jpg", buf) + Write("testdata/test_enlarge_crop_out.jpg", buf) } func TestImageCrop(t *testing.T) { @@ -167,7 +167,7 @@ func TestImageCrop(t *testing.T) { t.Error(err) } - Write("fixtures/test_crop_out.jpg", buf) + Write("testdata/test_crop_out.jpg", buf) } func TestImageCropByWidth(t *testing.T) { @@ -181,7 +181,7 @@ func TestImageCropByWidth(t *testing.T) { t.Error(err) } - Write("fixtures/test_crop_width_out.jpg", buf) + Write("testdata/test_crop_width_out.jpg", buf) } func TestImageCropByHeight(t *testing.T) { @@ -195,7 +195,7 @@ func TestImageCropByHeight(t *testing.T) { t.Error(err) } - Write("fixtures/test_crop_height_out.jpg", buf) + Write("testdata/test_crop_height_out.jpg", buf) } func TestImageThumbnail(t *testing.T) { @@ -209,7 +209,7 @@ func TestImageThumbnail(t *testing.T) { t.Error(err) } - Write("fixtures/test_thumbnail_out.jpg", buf) + Write("testdata/test_thumbnail_out.jpg", buf) } func TestImageWatermark(t *testing.T) { @@ -239,7 +239,7 @@ func TestImageWatermark(t *testing.T) { t.Fatal("Image is not jpeg") } - Write("fixtures/test_watermark_text_out.jpg", buf) + Write("testdata/test_watermark_text_out.jpg", buf) } func TestImageWatermarkWithImage(t *testing.T) { @@ -266,7 +266,7 @@ func TestImageWatermarkWithImage(t *testing.T) { t.Fatal("Image is not jpeg") } - Write("fixtures/test_watermark_image_out.jpg", buf) + Write("testdata/test_watermark_image_out.jpg", buf) } func TestImageWatermarkNoReplicate(t *testing.T) { @@ -297,7 +297,7 @@ func TestImageWatermarkNoReplicate(t *testing.T) { t.Fatal("Image is not jpeg") } - Write("fixtures/test_watermark_replicate_out.jpg", buf) + Write("testdata/test_watermark_replicate_out.jpg", buf) } func TestImageZoom(t *testing.T) { @@ -318,7 +318,7 @@ func TestImageZoom(t *testing.T) { t.Error(err) } - Write("fixtures/test_zoom_out.jpg", buf) + Write("testdata/test_zoom_out.jpg", buf) } func TestImageFlip(t *testing.T) { @@ -326,7 +326,7 @@ func TestImageFlip(t *testing.T) { if err != nil { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_flip_out.jpg", buf) + Write("testdata/test_flip_out.jpg", buf) } func TestImageFlop(t *testing.T) { @@ -334,7 +334,7 @@ func TestImageFlop(t *testing.T) { if err != nil { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_flop_out.jpg", buf) + Write("testdata/test_flop_out.jpg", buf) } func TestImageRotate(t *testing.T) { @@ -342,7 +342,7 @@ func TestImageRotate(t *testing.T) { if err != nil { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_image_rotate_out.jpg", buf) + Write("testdata/test_image_rotate_out.jpg", buf) } func TestImageConvert(t *testing.T) { @@ -350,7 +350,7 @@ func TestImageConvert(t *testing.T) { if err != nil { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_image_convert_out.png", buf) + Write("testdata/test_image_convert_out.png", buf) } func TestTransparentImageConvert(t *testing.T) { @@ -363,7 +363,7 @@ func TestTransparentImageConvert(t *testing.T) { if err != nil { t.Errorf("Cannot process the image: %#v", err) } - Write("fixtures/test_transparent_image_convert_out.jpg", buf) + Write("testdata/test_transparent_image_convert_out.jpg", buf) } func TestImageMetadata(t *testing.T) { @@ -452,13 +452,13 @@ func TestFluentInterface(t *testing.T) { t.Fatal("Invalid image type") } - Write("fixtures/test_image_fluent_out.png", image.Image()) + Write("testdata/test_image_fluent_out.png", image.Image()) } func TestImageSmartCrop(t *testing.T) { - if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) { - t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion) + if !(VipsMajorVersion >= 8 && VipsMinorVersion >= 5) { + t.Skipf("Skipping this test, libvips doesn't meet version requirement %s >= 8.5", VipsVersion) } i := initImage("northern_cardinal_bird.jpg") @@ -472,7 +472,63 @@ func TestImageSmartCrop(t *testing.T) { t.Error(err) } - Write("fixtures/test_smart_crop.jpg", buf) + Write("testdata/test_smart_crop.jpg", buf) +} + +func TestImageTrim(t *testing.T) { + + if !(VipsMajorVersion >= 8 && VipsMinorVersion >= 6) { + t.Skipf("Skipping this test, libvips doesn't meet version requirement %s >= 8.6", VipsVersion) + } + + i := initImage("transparent.png") + buf, err := i.Trim() + if err != nil { + t.Errorf("Cannot process the image: %#v", err) + } + + err = assertSize(buf, 250, 208) + if err != nil { + t.Errorf("The image wasn't trimmed.") + } + + Write("testdata/transparent_trim.png", buf) +} + +func TestImageTrimParameters(t *testing.T) { + + if !(VipsMajorVersion >= 8 && VipsMinorVersion >= 6) { + t.Skipf("Skipping this test, libvips doesn't meet version requirement %s >= 8.6", VipsVersion) + } + + i := initImage("test.png") + options := Options{ + Trim: true, + Background: Color{0.0, 0.0, 0.0}, + Threshold: 10.0, + } + buf, err := i.Process(options) + if err != nil { + t.Errorf("Cannot process the image: %#v", err) + } + + err = assertSize(buf, 400, 257) + if err != nil { + t.Errorf("The image wasn't trimmed.") + } + + Write("testdata/parameter_trim.png", buf) +} + +func TestImageLength(t *testing.T) { + i := initImage("test.jpg") + + actual := i.Length() + expected := 53653 + + if expected != actual { + t.Errorf("Size in Bytes of the image doesn't correspond. %d != %d", expected, actual) + } } func initImage(file string) *Image { @@ -481,7 +537,7 @@ func initImage(file string) *Image { } func imageBuf(file string) ([]byte, error) { - return Read(path.Join("fixtures", file)) + return Read(path.Join("testdata", file)) } func assertSize(buf []byte, width, height int) error { diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go index 663ec5641..83be45fd2 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go @@ -118,7 +118,7 @@ func TestColourspaceIsSupported(t *testing.T) { } func readFile(file string) []byte { - data, _ := os.Open(path.Join("fixtures", file)) + data, _ := os.Open(path.Join("testdata", file)) buf, _ := ioutil.ReadAll(data) return buf } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/options.go b/vendor/src/gopkg.in/h2non/bimg.v1/options.go index f6ebec2c0..17a1cb4cf 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/options.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/options.go @@ -41,12 +41,15 @@ const ( Bilinear // Nohalo interpolation value. Nohalo + // Nearest neighbour interpolation value. + Nearest ) var interpolations = map[Interpolator]string{ Bicubic: "bicubic", Bilinear: "bilinear", Nohalo: "nohalo", + Nearest: "nearest", } func (i Interpolator) String() string { @@ -195,7 +198,7 @@ type Options struct { Compression int Zoom int Crop bool - SmartCrop bool // Deprecated + SmartCrop bool // Deprecated, use: bimg.Options.Gravity = bimg.GravitySmart Enlarge bool Embed bool Flip bool @@ -204,6 +207,9 @@ type Options struct { NoAutoRotate bool NoProfile bool Interlace bool + StripMetadata bool + Trim bool + Lossless bool Extend Extend Rotate Angle Background Color @@ -215,4 +221,6 @@ type Options struct { Interpretation Interpretation GaussianBlur GaussianBlur Sharpen Sharpen + Threshold float64 + OutputICC string } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh b/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh index 47fa24c52..33d41fcba 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh +++ b/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh @@ -1,18 +1,24 @@ #!/bin/bash vips_version_minimum=8.4.2 -vips_version_latest_major_minor=8.4 -vips_version_latest_patch=2 +vips_version_latest_major_minor=8.5 +vips_version_latest_patch=1 +vips_version_full="$vips_version_latest_major_minor.$vips_version_latest_patch" openslide_version_minimum=3.4.0 openslide_version_latest_major_minor=3.4 openslide_version_latest_patch=1 +tarbal_url="https://github.com/jcupitt/libvips/releases/download/v$vips_version_full/vips-$vips_version_full.tar.gz" + install_libvips_from_source() { - echo "Compiling libvips $vips_version_latest_major_minor.$vips_version_latest_patch from source" - curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz - tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz - cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch + # Download tarball + echo "Compiling libvips v$vips_version_full from source" + curl -L -o vips-$vips_version_full.tar.gz $tarbal_url + tar zvxf vips-$vips_version_full.tar.gz + cd vips-$vips_version_full + + # Compile CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1 make make install diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resize.go b/vendor/src/gopkg.in/h2non/bimg.v1/resize.go index 93d624805..7e8c2d7dd 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/resize.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/resize.go @@ -1,561 +1,16 @@ +// +build go1.7 + package bimg -/* -#cgo pkg-config: vips -#include "vips/vips.h" -*/ -import "C" - import ( - "errors" - "math" + "runtime" ) // Resize is used to transform a given image as byte buffer // with the passed options. func Resize(buf []byte, o Options) ([]byte, error) { - defer C.vips_thread_shutdown() - - image, imageType, err := loadImage(buf) - - if err != nil { - return nil, err - } - - // Clone and define default options - o = applyDefaults(o, imageType) - - if !IsTypeSupported(o.Type) { - return nil, errors.New("Unsupported image output type") - } - - debug("Options: %#v", o) - - // Auto rotate image based on EXIF orientation header - image, rotated, err := rotateAndFlipImage(image, o) - if err != nil { - return nil, err - } - - // If JPEG image, retrieve the buffer - if rotated && imageType == JPEG && !o.NoAutoRotate { - buf, err = getImageBuffer(image) - if err != nil { - return nil, err - } - } - - inWidth := int(image.Xsize) - inHeight := int(image.Ysize) - - // Infer the required operation based on the in/out image sizes for a coherent transformation - normalizeOperation(&o, inWidth, inHeight) - - // image calculations - factor := imageCalculations(&o, inWidth, inHeight) - shrink := calculateShrink(factor, o.Interpolator) - residual := calculateResidual(factor, shrink) - - // Do not enlarge the output if the input width or height - // are already less than the required dimensions - if !o.Enlarge && !o.Force { - if inWidth < o.Width && inHeight < o.Height { - factor = 1.0 - shrink = 1 - residual = 0 - o.Width = inWidth - o.Height = inHeight - } - } - - // Try to use libjpeg shrink-on-load - if imageType == JPEG && shrink >= 2 { - tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink) - if err != nil { - return nil, err - } - - image = tmpImage - factor = math.Max(factor, 1.0) - shrink = int(math.Floor(factor)) - residual = float64(shrink) / factor - } - - // Zoom image, if necessary - image, err = zoomImage(image, o.Zoom) - if err != nil { - return nil, err - } - - // Transform image, if necessary - if shouldTransformImage(o, inWidth, inHeight) { - image, err = transformImage(image, o, shrink, residual) - if err != nil { - return nil, err - } - } - - // Apply effects, if necessary - if shouldApplyEffects(o) { - image, err = applyEffects(image, o) - if err != nil { - return nil, err - } - } - - // Add watermark, if necessary - image, err = watermarkImageWithText(image, o.Watermark) - if err != nil { - return nil, err - } - - // Add watermark, if necessary - image, err = watermarkImageWithAnotherImage(image, o.WatermarkImage) - if err != nil { - return nil, err - } - - // Flatten image on a background, if necessary - image, err = imageFlatten(image, imageType, o) - if err != nil { - return nil, err - } - - return saveImage(image, o) -} - -func loadImage(buf []byte) (*C.VipsImage, ImageType, error) { - if len(buf) == 0 { - return nil, JPEG, errors.New("Image buffer is empty") - } - - image, imageType, err := vipsRead(buf) - if err != nil { - return nil, JPEG, err - } - - return image, imageType, nil -} - -func applyDefaults(o Options, imageType ImageType) Options { - if o.Quality == 0 { - o.Quality = Quality - } - if o.Compression == 0 { - o.Compression = 6 - } - if o.Type == 0 { - o.Type = imageType - } - if o.Interpretation == 0 { - o.Interpretation = InterpretationSRGB - } - return o -} - -func saveImage(image *C.VipsImage, o Options) ([]byte, error) { - saveOptions := vipsSaveOptions{ - Quality: o.Quality, - Type: o.Type, - Compression: o.Compression, - Interlace: o.Interlace, - NoProfile: o.NoProfile, - Interpretation: o.Interpretation, - } - // Finally get the resultant buffer - return vipsSave(image, saveOptions) -} - -func normalizeOperation(o *Options, inWidth, inHeight int) { - if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) { - o.Force = true - } -} - -func shouldTransformImage(o Options, inWidth, inHeight int) bool { - return o.Force || (o.Width > 0 && o.Width != inWidth) || - (o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0 -} - -func shouldApplyEffects(o Options) bool { - return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 || o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 -} - -func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) { - var err error - // Use vips_shrink with the integral reduction - if shrink > 1 { - image, residual, err = shrinkImage(image, o, residual, shrink) - if err != nil { - return nil, err - } - } - - residualx, residualy := residual, residual - if o.Force { - residualx = float64(o.Width) / float64(image.Xsize) - residualy = float64(o.Height) / float64(image.Ysize) - } - - if o.Force || residual != 0 { - image, err = vipsAffine(image, residualx, residualy, o.Interpolator) - if err != nil { - return nil, err - } - } - - if o.Force { - o.Crop = false - o.Embed = false - } - - image, err = extractOrEmbedImage(image, o) - if err != nil { - return nil, err - } - - debug("Transform: shrink=%v, residual=%v, interpolator=%v", - shrink, residual, o.Interpolator.String()) - - return image, nil -} - -func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) { - var err error - - if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 { - image, err = vipsGaussianBlur(image, o.GaussianBlur) - if err != nil { - return nil, err - } - } - - if o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 { - image, err = vipsSharpen(image, o.Sharpen) - if err != nil { - return nil, err - } - } - - debug("Effects: gaussSigma=%v, gaussMinAmpl=%v, sharpenRadius=%v", - o.GaussianBlur.Sigma, o.GaussianBlur.MinAmpl, o.Sharpen.Radius) - - return image, nil -} - -func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) { - var err error - inWidth := int(image.Xsize) - inHeight := int(image.Ysize) - - switch { - case o.Gravity == GravitySmart, o.SmartCrop: - image, err = vipsSmartCrop(image, o.Width, o.Height) - break - case o.Crop: - width := int(math.Min(float64(inWidth), float64(o.Width))) - height := int(math.Min(float64(inHeight), float64(o.Height))) - left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity) - left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0)) - image, err = vipsExtract(image, left, top, width, height) - break - case o.Embed: - left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2 - image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background) - break - - case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0: - if o.AreaWidth == 0 { - o.AreaHeight = o.Width - } - if o.AreaHeight == 0 { - o.AreaHeight = o.Height - } - if o.AreaWidth == 0 || o.AreaHeight == 0 { - return nil, errors.New("Extract area width/height params are required") - } - image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight) - break - } - - return image, err -} - -func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, bool, error) { - var err error - var rotated bool - var direction Direction = -1 - - if o.NoAutoRotate == false { - rotation, flip := calculateRotationAndFlip(image, o.Rotate) - if flip { - o.Flip = flip - } - if rotation > 0 && o.Rotate == 0 { - o.Rotate = rotation - } - } - - if o.Rotate > 0 { - rotated = true - image, err = vipsRotate(image, getAngle(o.Rotate)) - } - - if o.Flip { - direction = Horizontal - } else if o.Flop { - direction = Vertical - } - - if direction != -1 { - rotated = true - image, err = vipsFlip(image, direction) - } - - return image, rotated, err -} - -func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, error) { - if w.Text == "" { - return image, nil - } - - // Defaults - if w.Font == "" { - w.Font = WatermarkFont - } - if w.Width == 0 { - w.Width = int(math.Floor(float64(image.Xsize / 6))) - } - if w.DPI == 0 { - w.DPI = 150 - } - if w.Margin == 0 { - w.Margin = w.Width - } - if w.Opacity == 0 { - w.Opacity = 0.25 - } else if w.Opacity > 1 { - w.Opacity = 1 - } - - image, err := vipsWatermark(image, w) - if err != nil { - return nil, err - } - - return image, nil -} - -func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) { - - if len(w.Buf) == 0 { - return image, nil - } - - if w.Opacity == 0.0 { - w.Opacity = 1.0 - } - - image, err := vipsDrawWatermark(image, w) - - if err != nil { - return nil, err - } - - return image, nil -} - -func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) { - // Only PNG images are supported for now - if imageType != PNG || o.Background == ColorBlack { - return image, nil - } - return vipsFlattenBackground(image, o.Background) -} - -func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) { - if zoom == 0 { - return image, nil - } - return vipsZoom(image, zoom+1) -} - -func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) { - // Use vips_shrink with the integral reduction - image, err := vipsShrink(image, shrink) - if err != nil { - return nil, 0, err - } - - // Recalculate residual float based on dimensions of required vs shrunk images - residualx := float64(o.Width) / float64(image.Xsize) - residualy := float64(o.Height) / float64(image.Ysize) - - if o.Crop { - residual = math.Max(residualx, residualy) - } else { - residual = math.Min(residualx, residualy) - } - - return image, residual, nil -} - -func shrinkJpegImage(buf []byte, input *C.VipsImage, factor float64, shrink int) (*C.VipsImage, float64, error) { - var image *C.VipsImage - var err error - shrinkOnLoad := 1 - - // Recalculate integral shrink and double residual - switch { - case shrink >= 8: - factor = factor / 8 - shrinkOnLoad = 8 - case shrink >= 4: - factor = factor / 4 - shrinkOnLoad = 4 - case shrink >= 2: - factor = factor / 2 - shrinkOnLoad = 2 - } - - // Reload input using shrink-on-load - if shrinkOnLoad > 1 { - image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad) - } - - return image, factor, err -} - -func imageCalculations(o *Options, inWidth, inHeight int) float64 { - factor := 1.0 - xfactor := float64(inWidth) / float64(o.Width) - yfactor := float64(inHeight) / float64(o.Height) - - switch { - // Fixed width and height - case o.Width > 0 && o.Height > 0: - if o.Crop { - factor = math.Min(xfactor, yfactor) - } else { - factor = math.Max(xfactor, yfactor) - } - // Fixed width, auto height - case o.Width > 0: - if o.Crop { - o.Height = inHeight - } else { - factor = xfactor - o.Height = roundFloat(float64(inHeight) / factor) - } - // Fixed height, auto width - case o.Height > 0: - if o.Crop { - o.Width = inWidth - } else { - factor = yfactor - o.Width = roundFloat(float64(inWidth) / factor) - } - // Identity transform - default: - o.Width = inWidth - o.Height = inHeight - break - } - - return factor -} - -func roundFloat(f float64) int { - if f < 0 { - return int(math.Ceil(f - 0.5)) - } - return int(math.Floor(f + 0.5)) -} - -func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) { - left, top := 0, 0 - - switch gravity { - case GravityNorth: - left = (inWidth - outWidth + 1) / 2 - case GravityEast: - left = inWidth - outWidth - top = (inHeight - outHeight + 1) / 2 - case GravitySouth: - left = (inWidth - outWidth + 1) / 2 - top = inHeight - outHeight - case GravityWest: - top = (inHeight - outHeight + 1) / 2 - default: - left = (inWidth - outWidth + 1) / 2 - top = (inHeight - outHeight + 1) / 2 - } - - return left, top -} - -func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) { - rotate := D0 - flip := false - - if angle > 0 { - return rotate, flip - } - - switch vipsExifOrientation(image) { - case 6: - rotate = D90 - break - case 3: - rotate = D180 - break - case 8: - rotate = D270 - break - case 2: - flip = true - break // flip 1 - case 7: - flip = true - rotate = D90 - break // flip 6 - case 4: - flip = true - rotate = D180 - break // flip 3 - case 5: - flip = true - rotate = D270 - break // flip 8 - } - - return rotate, flip -} - -func calculateShrink(factor float64, i Interpolator) int { - var shrink float64 - - // Calculate integral box shrink - windowSize := vipsWindowSize(i.String()) - if factor >= 2 && windowSize > 3 { - // Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic - shrink = float64(math.Floor(factor * 3.0 / windowSize)) - } else { - shrink = math.Floor(factor) - } - - return int(math.Max(shrink, 1)) -} - -func calculateResidual(factor float64, shrink int) float64 { - return float64(shrink) / factor -} - -func getAngle(angle Angle) Angle { - divisor := angle % 90 - if divisor != 0 { - angle = angle - divisor - } - return Angle(math.Min(float64(angle), 270)) + // Required in order to prevent premature garbage collection. See: + // https://github.com/h2non/bimg/pull/162 + defer runtime.KeepAlive(buf) + return resizer(buf, o) } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resize_legacy.go b/vendor/src/gopkg.in/h2non/bimg.v1/resize_legacy.go new file mode 100644 index 000000000..de777a5b4 --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/resize_legacy.go @@ -0,0 +1,10 @@ +// +build !go1.7 + +package bimg + +// Resize is used to transform a given image as byte buffer +// with the passed options. +// Used as proxy to resizer() only in Go <= 1.6 versions +func Resize(buf []byte, o Options) ([]byte, error) { + return resizer(buf, o) +} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resizer.go b/vendor/src/gopkg.in/h2non/bimg.v1/resizer.go new file mode 100644 index 000000000..ddd41279e --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/resizer.go @@ -0,0 +1,568 @@ +package bimg + +/* +#cgo pkg-config: vips +#include "vips/vips.h" +*/ +import "C" + +import ( + "errors" + "fmt" + "math" +) + +// resizer is used to transform a given image as byte buffer +// with the passed options. +func resizer(buf []byte, o Options) ([]byte, error) { + defer C.vips_thread_shutdown() + + image, imageType, err := loadImage(buf) + if err != nil { + return nil, err + } + + // Clone and define default options + o = applyDefaults(o, imageType) + + if !IsTypeSupported(o.Type) { + return nil, errors.New("Unsupported image output type") + } + + // Auto rotate image based on EXIF orientation header + image, rotated, err := rotateAndFlipImage(image, o) + if err != nil { + return nil, err + } + + // If JPEG image, retrieve the buffer + if rotated && imageType == JPEG && !o.NoAutoRotate { + buf, err = getImageBuffer(image) + if err != nil { + return nil, err + } + } + + inWidth := int(image.Xsize) + inHeight := int(image.Ysize) + + // Infer the required operation based on the in/out image sizes for a coherent transformation + normalizeOperation(&o, inWidth, inHeight) + + // image calculations + factor := imageCalculations(&o, inWidth, inHeight) + shrink := calculateShrink(factor, o.Interpolator) + residual := calculateResidual(factor, shrink) + + // Do not enlarge the output if the input width or height + // are already less than the required dimensions + if !o.Enlarge && !o.Force { + if inWidth < o.Width && inHeight < o.Height { + factor = 1.0 + shrink = 1 + residual = 0 + o.Width = inWidth + o.Height = inHeight + } + } + + // Try to use libjpeg/libwebp shrink-on-load + supportsShrinkOnLoad := imageType == WEBP && VipsMajorVersion >= 8 && VipsMinorVersion >= 3 + supportsShrinkOnLoad = supportsShrinkOnLoad || imageType == JPEG + if supportsShrinkOnLoad && shrink >= 2 { + tmpImage, factor, err := shrinkOnLoad(buf, image, imageType, factor, shrink) + if err != nil { + return nil, err + } + + image = tmpImage + factor = math.Max(factor, 1.0) + shrink = int(math.Floor(factor)) + residual = float64(shrink) / factor + } + + // Zoom image, if necessary + image, err = zoomImage(image, o.Zoom) + if err != nil { + return nil, err + } + + // Transform image, if necessary + if shouldTransformImage(o, inWidth, inHeight) { + image, err = transformImage(image, o, shrink, residual) + if err != nil { + return nil, err + } + } + + // Apply effects, if necessary + if shouldApplyEffects(o) { + image, err = applyEffects(image, o) + if err != nil { + return nil, err + } + } + + // Add watermark, if necessary + image, err = watermarkImageWithText(image, o.Watermark) + if err != nil { + return nil, err + } + + // Add watermark, if necessary + image, err = watermarkImageWithAnotherImage(image, o.WatermarkImage) + if err != nil { + return nil, err + } + + // Flatten image on a background, if necessary + image, err = imageFlatten(image, imageType, o) + if err != nil { + return nil, err + } + + return saveImage(image, o) +} + +func loadImage(buf []byte) (*C.VipsImage, ImageType, error) { + if len(buf) == 0 { + return nil, JPEG, errors.New("Image buffer is empty") + } + + image, imageType, err := vipsRead(buf) + if err != nil { + return nil, JPEG, err + } + + return image, imageType, nil +} + +func applyDefaults(o Options, imageType ImageType) Options { + if o.Quality == 0 { + o.Quality = Quality + } + if o.Compression == 0 { + o.Compression = 6 + } + if o.Type == 0 { + o.Type = imageType + } + if o.Interpretation == 0 { + o.Interpretation = InterpretationSRGB + } + return o +} + +func saveImage(image *C.VipsImage, o Options) ([]byte, error) { + saveOptions := vipsSaveOptions{ + Quality: o.Quality, + Type: o.Type, + Compression: o.Compression, + Interlace: o.Interlace, + NoProfile: o.NoProfile, + Interpretation: o.Interpretation, + OutputICC: o.OutputICC, + StripMetadata: o.StripMetadata, + Lossless: o.Lossless, + } + // Finally get the resultant buffer + return vipsSave(image, saveOptions) +} + +func normalizeOperation(o *Options, inWidth, inHeight int) { + if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) { + o.Force = true + } +} + +func shouldTransformImage(o Options, inWidth, inHeight int) bool { + return o.Force || (o.Width > 0 && o.Width != inWidth) || + (o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0 || + o.Trim +} + +func shouldApplyEffects(o Options) bool { + return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 || o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 +} + +func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) { + var err error + // Use vips_shrink with the integral reduction + if shrink > 1 { + image, residual, err = shrinkImage(image, o, residual, shrink) + if err != nil { + return nil, err + } + } + + residualx, residualy := residual, residual + if o.Force { + residualx = float64(o.Width) / float64(image.Xsize) + residualy = float64(o.Height) / float64(image.Ysize) + } + + if o.Force || residual != 0 { + if residualx < 1 && residualy < 1 { + image, err = vipsReduce(image, 1/residualx, 1/residualy) + } else { + image, err = vipsAffine(image, residualx, residualy, o.Interpolator) + } + if err != nil { + return nil, err + } + } + + if o.Force { + o.Crop = false + o.Embed = false + } + + image, err = extractOrEmbedImage(image, o) + if err != nil { + return nil, err + } + + return image, nil +} + +func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) { + var err error + + if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 { + image, err = vipsGaussianBlur(image, o.GaussianBlur) + if err != nil { + return nil, err + } + } + + if o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 { + image, err = vipsSharpen(image, o.Sharpen) + if err != nil { + return nil, err + } + } + + return image, nil +} + +func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) { + var err error + inWidth := int(image.Xsize) + inHeight := int(image.Ysize) + + switch { + case o.Gravity == GravitySmart, o.SmartCrop: + image, err = vipsSmartCrop(image, o.Width, o.Height) + break + case o.Crop: + width := int(math.Min(float64(inWidth), float64(o.Width))) + height := int(math.Min(float64(inHeight), float64(o.Height))) + left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity) + left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0)) + image, err = vipsExtract(image, left, top, width, height) + break + case o.Embed: + left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2 + image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background) + break + case o.Trim: + left, top, width, height, err := vipsTrim(image, o.Background, o.Threshold) + if err == nil { + image, err = vipsExtract(image, left, top, width, height) + } + break + case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0: + if o.AreaWidth == 0 { + o.AreaHeight = o.Width + } + if o.AreaHeight == 0 { + o.AreaHeight = o.Height + } + if o.AreaWidth == 0 || o.AreaHeight == 0 { + return nil, errors.New("Extract area width/height params are required") + } + image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight) + break + } + + return image, err +} + +func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, bool, error) { + var err error + var rotated bool + var direction Direction = -1 + + if o.NoAutoRotate == false { + rotation, flip := calculateRotationAndFlip(image, o.Rotate) + if flip { + o.Flip = flip + } + if rotation > 0 && o.Rotate == 0 { + o.Rotate = rotation + } + } + + if o.Rotate > 0 { + rotated = true + image, err = vipsRotate(image, getAngle(o.Rotate)) + } + + if o.Flip { + direction = Horizontal + } else if o.Flop { + direction = Vertical + } + + if direction != -1 { + rotated = true + image, err = vipsFlip(image, direction) + } + + return image, rotated, err +} + +func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, error) { + if w.Text == "" { + return image, nil + } + + // Defaults + if w.Font == "" { + w.Font = WatermarkFont + } + if w.Width == 0 { + w.Width = int(math.Floor(float64(image.Xsize / 6))) + } + if w.DPI == 0 { + w.DPI = 150 + } + if w.Margin == 0 { + w.Margin = w.Width + } + if w.Opacity == 0 { + w.Opacity = 0.25 + } else if w.Opacity > 1 { + w.Opacity = 1 + } + + image, err := vipsWatermark(image, w) + if err != nil { + return nil, err + } + + return image, nil +} + +func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) { + + if len(w.Buf) == 0 { + return image, nil + } + + if w.Opacity == 0.0 { + w.Opacity = 1.0 + } + + image, err := vipsDrawWatermark(image, w) + + if err != nil { + return nil, err + } + + return image, nil +} + +func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) { + // Only PNG images are supported for now + if imageType != PNG || o.Background == ColorBlack { + return image, nil + } + return vipsFlattenBackground(image, o.Background) +} + +func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) { + if zoom == 0 { + return image, nil + } + return vipsZoom(image, zoom+1) +} + +func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) { + // Use vips_shrink with the integral reduction + image, err := vipsShrink(image, shrink) + if err != nil { + return nil, 0, err + } + + // Recalculate residual float based on dimensions of required vs shrunk images + residualx := float64(o.Width) / float64(image.Xsize) + residualy := float64(o.Height) / float64(image.Ysize) + + if o.Crop { + residual = math.Max(residualx, residualy) + } else { + residual = math.Min(residualx, residualy) + } + + return image, residual, nil +} + +func shrinkOnLoad(buf []byte, input *C.VipsImage, imageType ImageType, factor float64, shrink int) (*C.VipsImage, float64, error) { + var image *C.VipsImage + var err error + + // Reload input using shrink-on-load + if imageType == JPEG && shrink >= 2 { + shrinkOnLoad := 1 + // Recalculate integral shrink and double residual + switch { + case shrink >= 8: + factor = factor / 8 + shrinkOnLoad = 8 + case shrink >= 4: + factor = factor / 4 + shrinkOnLoad = 4 + case shrink >= 2: + factor = factor / 2 + shrinkOnLoad = 2 + } + + image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad) + } else if imageType == WEBP { + image, err = vipsShrinkWebp(buf, input, shrink) + } else { + return nil, 0, fmt.Errorf("%v doesn't support shrink on load", ImageTypeName(imageType)) + } + + return image, factor, err +} + +func imageCalculations(o *Options, inWidth, inHeight int) float64 { + factor := 1.0 + xfactor := float64(inWidth) / float64(o.Width) + yfactor := float64(inHeight) / float64(o.Height) + + switch { + // Fixed width and height + case o.Width > 0 && o.Height > 0: + factor = math.Min(xfactor, yfactor) + // Fixed width, auto height + case o.Width > 0: + if o.Crop { + o.Height = inHeight + } else { + factor = xfactor + o.Height = roundFloat(float64(inHeight) / factor) + } + // Fixed height, auto width + case o.Height > 0: + if o.Crop { + o.Width = inWidth + } else { + factor = yfactor + o.Width = roundFloat(float64(inWidth) / factor) + } + // Identity transform + default: + o.Width = inWidth + o.Height = inHeight + break + } + + return factor +} + +func roundFloat(f float64) int { + if f < 0 { + return int(math.Ceil(f - 0.5)) + } + return int(math.Floor(f + 0.5)) +} + +func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) { + left, top := 0, 0 + + switch gravity { + case GravityNorth: + left = (inWidth - outWidth + 1) / 2 + case GravityEast: + left = inWidth - outWidth + top = (inHeight - outHeight + 1) / 2 + case GravitySouth: + left = (inWidth - outWidth + 1) / 2 + top = inHeight - outHeight + case GravityWest: + top = (inHeight - outHeight + 1) / 2 + default: + left = (inWidth - outWidth + 1) / 2 + top = (inHeight - outHeight + 1) / 2 + } + + return left, top +} + +func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) { + rotate := D0 + flip := false + + if angle > 0 { + return rotate, flip + } + + switch vipsExifOrientation(image) { + case 6: + rotate = D90 + break + case 3: + rotate = D180 + break + case 8: + rotate = D270 + break + case 2: + flip = true + break // flip 1 + case 7: + flip = true + rotate = D270 + break // flip 6 + case 4: + flip = true + rotate = D180 + break // flip 3 + case 5: + flip = true + rotate = D90 + break // flip 8 + } + + return rotate, flip +} + +func calculateShrink(factor float64, i Interpolator) int { + var shrink float64 + + // Calculate integral box shrink + windowSize := vipsWindowSize(i.String()) + if factor >= 2 && windowSize > 3 { + // Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic + shrink = float64(math.Floor(factor * 3.0 / windowSize)) + } else { + shrink = math.Floor(factor) + } + + return int(math.Max(shrink, 1)) +} + +func calculateResidual(factor float64, shrink int) float64 { + return float64(shrink) / factor +} + +func getAngle(angle Angle) Angle { + divisor := angle % 90 + if divisor != 0 { + angle = angle - divisor + } + return Angle(math.Min(float64(angle), 270)) +} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resizer_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/resizer_test.go new file mode 100644 index 000000000..a4b54b08f --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/resizer_test.go @@ -0,0 +1,748 @@ +package bimg + +import ( + "bytes" + "crypto/md5" + "fmt" + "image" + "image/jpeg" + "io/ioutil" + "os" + "path" + "testing" +) + +func TestResize(t *testing.T) { + options := Options{Width: 800, Height: 600} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != JPEG { + t.Fatal("Image is not jpeg") + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_out.jpg", newImg) +} + +func TestResizeVerticalImage(t *testing.T) { + tests := []Options{ + {Width: 800, Height: 600}, + {Width: 1000, Height: 1000}, + {Width: 1000, Height: 1500}, + {Width: 1000}, + {Height: 1500}, + {Width: 100, Height: 50}, + {Width: 2000, Height: 2000}, + {Width: 500, Height: 1000}, + {Width: 500}, + {Height: 500}, + {Crop: true, Width: 500, Height: 1000}, + {Crop: true, Enlarge: true, Width: 2000, Height: 1400}, + {Enlarge: true, Force: true, Width: 2000, Height: 2000}, + {Force: true, Width: 2000, Height: 2000}, + } + + bufJpeg, err := Read("testdata/vertical.jpg") + if err != nil { + t.Fatal(err) + } + bufWebp, err := Read("testdata/vertical.webp") + if err != nil { + t.Fatal(err) + } + + images := []struct { + format ImageType + buf []byte + }{ + {JPEG, bufJpeg}, + {WEBP, bufWebp}, + } + + for _, source := range images { + for _, options := range tests { + image, err := Resize(source.buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + format := DetermineImageType(image) + if format != source.format { + t.Fatalf("Image format is invalid. Expected: %#v got %v", ImageTypeName(source.format), ImageTypeName(format)) + } + + size, _ := Size(image) + if options.Height > 0 && size.Height != options.Height { + t.Fatalf("Invalid height: %d", size.Height) + } + if options.Width > 0 && size.Width != options.Width { + t.Fatalf("Invalid width: %d", size.Width) + } + + Write( + fmt.Sprintf( + "testdata/test_vertical_%dx%d_out.%s", + options.Width, + options.Height, + ImageTypeName(source.format)), + image) + } + } +} + +func TestResizeCustomSizes(t *testing.T) { + tests := []Options{ + {Width: 800, Height: 600}, + {Width: 1000, Height: 1000}, + {Width: 100, Height: 50}, + {Width: 2000, Height: 2000}, + {Width: 500, Height: 1000}, + {Width: 500}, + {Height: 500}, + {Crop: true, Width: 500, Height: 1000}, + {Crop: true, Enlarge: true, Width: 2000, Height: 1400}, + {Enlarge: true, Force: true, Width: 2000, Height: 2000}, + {Force: true, Width: 2000, Height: 2000}, + } + + bufJpeg, err := Read("testdata/test.jpg") + if err != nil { + t.Fatal(err) + } + bufWebp, err := Read("testdata/test.webp") + if err != nil { + t.Fatal(err) + } + + images := []struct { + format ImageType + buf []byte + }{ + {JPEG, bufJpeg}, + {WEBP, bufWebp}, + } + + for _, source := range images { + for _, options := range tests { + image, err := Resize(source.buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(image) != source.format { + t.Fatalf("Image format is invalid. Expected: %#v", source.format) + } + + size, _ := Size(image) + + invalidHeight := options.Height > 0 && size.Height != options.Height + if !options.Crop && invalidHeight { + t.Fatalf("Invalid height: %d, expected %d", size.Height, options.Height) + } + + invalidWidth := options.Width > 0 && size.Width != options.Width + if !options.Crop && invalidWidth { + t.Fatalf("Invalid width: %d, expected %d", size.Width, options.Width) + } + + if options.Crop && invalidHeight && invalidWidth { + t.Fatalf("Invalid width or height: %dx%d, expected %dx%d (crop)", size.Width, size.Height, options.Width, options.Height) + } + } + } +} + +func TestResizePrecision(t *testing.T) { + // see https://github.com/h2non/bimg/issues/99 + img := image.NewGray16(image.Rect(0, 0, 1920, 1080)) + input := &bytes.Buffer{} + jpeg.Encode(input, img, nil) + + opts := Options{Width: 300} + newImg, err := Resize(input.Bytes(), opts) + if err != nil { + t.Fatalf("Resize(imgData, %#v) error: %#v", opts, err) + } + + size, _ := Size(newImg) + if size.Width != opts.Width { + t.Fatalf("Invalid width: %d", size.Width) + } +} + +func TestRotate(t *testing.T) { + options := Options{Width: 800, Height: 600, Rotate: 270, Crop: true} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != JPEG { + t.Error("Image is not jpeg") + } + + size, _ := Size(newImg) + if size.Width != options.Width || size.Height != options.Height { + t.Errorf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_rotate_out.jpg", newImg) +} + +func TestInvalidRotateDegrees(t *testing.T) { + options := Options{Width: 800, Height: 600, Rotate: 111, Crop: true} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != JPEG { + t.Errorf("Image is not jpeg") + } + + size, _ := Size(newImg) + if size.Width != options.Width || size.Height != options.Height { + t.Errorf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_rotate_invalid_out.jpg", newImg) +} + +func TestCorruptedImage(t *testing.T) { + options := Options{Width: 800, Height: 600} + buf, _ := Read("testdata/corrupt.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != JPEG { + t.Fatal("Image is not jpeg") + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_corrupt_out.jpg", newImg) +} + +func TestNoColorProfile(t *testing.T) { + options := Options{Width: 800, Height: 600, NoProfile: true} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + metadata, err := Metadata(newImg) + if metadata.Profile == true { + t.Fatal("Invalid profile data") + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } +} + +func TestEmbedExtendColor(t *testing.T) { + options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: ExtendWhite, Background: Color{255, 20, 10}} + buf, _ := Read("testdata/test_issue.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_extend_white_out.jpg", newImg) +} + +func TestEmbedExtendWithCustomColor(t *testing.T) { + options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: 5, Background: Color{255, 20, 10}} + buf, _ := Read("testdata/test_issue.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_extend_background_out.jpg", newImg) +} + +func TestGaussianBlur(t *testing.T) { + options := Options{Width: 800, Height: 600, GaussianBlur: GaussianBlur{Sigma: 5}} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_gaussian_out.jpg", newImg) +} + +func TestSharpen(t *testing.T) { + options := Options{Width: 800, Height: 600, Sharpen: Sharpen{Radius: 1, X1: 1.5, Y2: 20, Y3: 50, M1: 1, M2: 2}} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.Height || size.Width != options.Width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_sharpen_out.jpg", newImg) +} + +func TestExtractWithDefaultAxis(t *testing.T) { + options := Options{AreaWidth: 200, AreaHeight: 200} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.AreaHeight || size.Width != options.AreaWidth { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_extract_defaults_out.jpg", newImg) +} + +func TestExtractCustomAxis(t *testing.T) { + options := Options{Top: 100, Left: 100, AreaWidth: 200, AreaHeight: 200} + buf, _ := Read("testdata/test.jpg") + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + size, _ := Size(newImg) + if size.Height != options.AreaHeight || size.Width != options.AreaWidth { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + + Write("testdata/test_extract_custom_axis_out.jpg", newImg) +} + +func TestConvert(t *testing.T) { + width, height := 300, 240 + formats := [3]ImageType{PNG, WEBP, JPEG} + + files := []string{ + "test.jpg", + "test.png", + "test.webp", + } + + for _, file := range files { + img, err := os.Open("testdata/" + file) + if err != nil { + t.Fatal(err) + } + + buf, err := ioutil.ReadAll(img) + if err != nil { + t.Fatal(err) + } + img.Close() + + for _, format := range formats { + options := Options{Width: width, Height: height, Crop: true, Type: format} + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != format { + t.Fatal("Image is not png") + } + + size, _ := Size(newImg) + if size.Height != height || size.Width != width { + t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height) + } + } + } +} + +func TestResizePngWithTransparency(t *testing.T) { + width, height := 300, 240 + + options := Options{Width: width, Height: height, Crop: true} + img, err := os.Open("testdata/transparent.png") + if err != nil { + t.Fatal(err) + } + defer img.Close() + + buf, err := ioutil.ReadAll(img) + if err != nil { + t.Fatal(err) + } + + newImg, err := Resize(buf, options) + if err != nil { + t.Errorf("Resize(imgData, %#v) error: %#v", options, err) + } + + if DetermineImageType(newImg) != PNG { + t.Fatal("Image is not png") + } + + size, _ := Size(newImg) + if size.Height != height || size.Width != width { + t.Fatal("Invalid image size") + } + + Write("testdata/transparent_out.png", newImg) +} + +func TestRotationAndFlip(t *testing.T) { + files := []struct { + Name string + Angle Angle + Flip bool + }{ + {"Landscape_1", 0, false}, + {"Landscape_2", 0, true}, + {"Landscape_3", D180, false}, + {"Landscape_4", D180, true}, + {"Landscape_5", D90, true}, + {"Landscape_6", D90, false}, + {"Landscape_7", D270, true}, + {"Landscape_8", D270, false}, + {"Portrait_1", 0, false}, + {"Portrait_2", 0, true}, + {"Portrait_3", D180, false}, + {"Portrait_4", D180, true}, + {"Portrait_5", D90, true}, + {"Portrait_6", D90, false}, + {"Portrait_7", D270, true}, + {"Portrait_8", D270, false}, + } + + for _, file := range files { + img, err := os.Open(fmt.Sprintf("testdata/exif/%s.jpg", file.Name)) + if err != nil { + t.Fatal(err) + } + + buf, err := ioutil.ReadAll(img) + if err != nil { + t.Fatal(err) + } + img.Close() + + image, _, err := loadImage(buf) + if err != nil { + t.Fatal(err) + } + + angle, flip := calculateRotationAndFlip(image, D0) + if angle != file.Angle { + t.Errorf("Rotation for %v expected to be %v. got %v", file.Name, file.Angle, angle) + } + if flip != file.Flip { + t.Errorf("Flip for %v expected to be %v. got %v", file.Name, file.Flip, flip) + } + + // Visual debugging. + newImg, err := Resize(buf, Options{}) + if err != nil { + t.Fatal(err) + } + + Write(fmt.Sprintf("testdata/exif/%s_out.jpg", file.Name), newImg) + } +} + +func TestIfBothSmartCropOptionsAreIdentical(t *testing.T) { + if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) { + t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion) + } + + benchmarkOptions := Options{Width: 100, Height: 100, Crop: true} + smartCropOptions := Options{Width: 100, Height: 100, Crop: true, SmartCrop: true} + gravityOptions := Options{Width: 100, Height: 100, Crop: true, Gravity: GravitySmart} + + testImg, err := os.Open("testdata/northern_cardinal_bird.jpg") + if err != nil { + t.Fatal(err) + } + defer testImg.Close() + + testImgByte, err := ioutil.ReadAll(testImg) + if err != nil { + t.Fatal(err) + } + + scImg, err := Resize(testImgByte, smartCropOptions) + if err != nil { + t.Fatal(err) + } + + gImg, err := Resize(testImgByte, gravityOptions) + if err != nil { + t.Fatal(err) + } + + benchmarkImg, err := Resize(testImgByte, benchmarkOptions) + if err != nil { + t.Fatal(err) + } + + sch, gh, bh := md5.Sum(scImg), md5.Sum(gImg), md5.Sum(benchmarkImg) + if gh == bh || sch == bh { + t.Error("Expected both options produce a different result from a standard crop.") + } + + if sch != gh { + t.Errorf("Expected both options to result in the same output, %x != %x", sch, gh) + } +} + +func runBenchmarkResize(file string, o Options, b *testing.B) { + buf, _ := Read(path.Join("testdata", file)) + + for n := 0; n < b.N; n++ { + Resize(buf, o) + } +} + +func BenchmarkRotateJpeg(b *testing.B) { + options := Options{Rotate: 180} + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkResizeLargeJpeg(b *testing.B) { + options := Options{ + Width: 800, + Height: 600, + } + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkResizePng(b *testing.B) { + options := Options{ + Width: 200, + Height: 200, + } + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkResizeWebp(b *testing.B) { + options := Options{ + Width: 200, + Height: 200, + } + runBenchmarkResize("test.webp", options, b) +} + +func BenchmarkConvertToJpeg(b *testing.B) { + options := Options{Type: JPEG} + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkConvertToPng(b *testing.B) { + options := Options{Type: PNG} + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkConvertToWebp(b *testing.B) { + options := Options{Type: WEBP} + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkCropJpeg(b *testing.B) { + options := Options{ + Width: 800, + Height: 600, + } + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkCropPng(b *testing.B) { + options := Options{ + Width: 800, + Height: 600, + } + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkCropWebp(b *testing.B) { + options := Options{ + Width: 800, + Height: 600, + } + runBenchmarkResize("test.webp", options, b) +} + +func BenchmarkExtractJpeg(b *testing.B) { + options := Options{ + Top: 100, + Left: 50, + AreaWidth: 600, + AreaHeight: 480, + } + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkExtractPng(b *testing.B) { + options := Options{ + Top: 100, + Left: 50, + AreaWidth: 600, + AreaHeight: 480, + } + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkExtractWebp(b *testing.B) { + options := Options{ + Top: 100, + Left: 50, + AreaWidth: 600, + AreaHeight: 480, + } + runBenchmarkResize("test.webp", options, b) +} + +func BenchmarkZoomJpeg(b *testing.B) { + options := Options{Zoom: 1} + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkZoomPng(b *testing.B) { + options := Options{Zoom: 1} + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkZoomWebp(b *testing.B) { + options := Options{Zoom: 1} + runBenchmarkResize("test.webp", options, b) +} + +func BenchmarkWatermarkJpeg(b *testing.B) { + options := Options{ + Watermark: Watermark{ + Text: "Chuck Norris (c) 2315", + Opacity: 0.25, + Width: 200, + DPI: 100, + Margin: 150, + Font: "sans bold 12", + Background: Color{255, 255, 255}, + }, + } + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkWatermarkPng(b *testing.B) { + options := Options{ + Watermark: Watermark{ + Text: "Chuck Norris (c) 2315", + Opacity: 0.25, + Width: 200, + DPI: 100, + Margin: 150, + Font: "sans bold 12", + Background: Color{255, 255, 255}, + }, + } + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkWatermarkWebp(b *testing.B) { + options := Options{ + Watermark: Watermark{ + Text: "Chuck Norris (c) 2315", + Opacity: 0.25, + Width: 200, + DPI: 100, + Margin: 150, + Font: "sans bold 12", + Background: Color{255, 255, 255}, + }, + } + runBenchmarkResize("test.webp", options, b) +} + +func BenchmarkWatermarkImageJpeg(b *testing.B) { + watermark := readFile("transparent.png") + options := Options{ + WatermarkImage: WatermarkImage{ + Buf: watermark, + Opacity: 0.25, + Left: 100, + Top: 100, + }, + } + runBenchmarkResize("test.jpg", options, b) +} + +func BenchmarkWatermarkImagePng(b *testing.B) { + watermark := readFile("transparent.png") + options := Options{ + WatermarkImage: WatermarkImage{ + Buf: watermark, + Opacity: 0.25, + Left: 100, + Top: 100, + }, + } + runBenchmarkResize("test.png", options, b) +} + +func BenchmarkWatermarkImageWebp(b *testing.B) { + watermark := readFile("transparent.png") + options := Options{ + WatermarkImage: WatermarkImage{ + Buf: watermark, + Opacity: 0.25, + Left: 100, + Top: 100, + }, + } + runBenchmarkResize("test.webp", options, b) +} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/corrupt.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/corrupt.jpg new file mode 100644 index 000000000..e59922201 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/corrupt.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1.jpg new file mode 100644 index 000000000..8d513f46f Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1_out.jpg new file mode 100644 index 000000000..033589e8b Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_1_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2.jpg new file mode 100644 index 000000000..427a551b0 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2_out.jpg new file mode 100644 index 000000000..1b2259f11 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_2_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3.jpg new file mode 100644 index 000000000..d904dc920 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3_out.jpg new file mode 100644 index 000000000..681964a3e Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_3_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4.jpg new file mode 100644 index 000000000..f85f4eb41 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4_out.jpg new file mode 100644 index 000000000..fee30ead2 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_4_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5.jpg new file mode 100644 index 000000000..1abf17e7d Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5_out.jpg new file mode 100644 index 000000000..fbd8f5b10 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_5_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6.jpg new file mode 100644 index 000000000..74d68ddd7 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6_out.jpg new file mode 100644 index 000000000..1ec5d1259 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_6_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7.jpg new file mode 100644 index 000000000..ba17a9e57 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7_out.jpg new file mode 100644 index 000000000..8a4f94c57 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_7_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8.jpg new file mode 100644 index 000000000..4c43c368e Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8_out.jpg new file mode 100644 index 000000000..3e50c6fdc Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Landscape_8_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1.jpg new file mode 100644 index 000000000..ab2d16860 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1_out.jpg new file mode 100644 index 000000000..e3af98cef Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_1_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2.jpg new file mode 100644 index 000000000..b2ccfd152 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2_out.jpg new file mode 100644 index 000000000..5c5366f4c Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_2_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3.jpg new file mode 100644 index 000000000..87b0da45d Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3_out.jpg new file mode 100644 index 000000000..8a8da006b Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_3_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4.jpg new file mode 100644 index 000000000..81be6ad46 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4_out.jpg new file mode 100644 index 000000000..2528749a9 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_4_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5.jpg new file mode 100644 index 000000000..3384800fa Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5_out.jpg new file mode 100644 index 000000000..c2b2ff3c5 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_5_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6.jpg new file mode 100644 index 000000000..40f5199a8 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6_out.jpg new file mode 100644 index 000000000..a13b3f1ad Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_6_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7.jpg new file mode 100644 index 000000000..b590bfc23 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7_out.jpg new file mode 100644 index 000000000..e64cc8d31 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_7_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8.jpg new file mode 100644 index 000000000..e15ebe429 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8_out.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8_out.jpg new file mode 100644 index 000000000..ffe52aa26 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/exif/Portrait_8_out.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/northern_cardinal_bird.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/northern_cardinal_bird.jpg new file mode 100644 index 000000000..1bf53853d Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/northern_cardinal_bird.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.gif b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.gif new file mode 100644 index 000000000..7bf290acc Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.gif differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jp2 b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jp2 new file mode 100644 index 000000000..940778fff Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jp2 differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jpg new file mode 100644 index 000000000..f17d2f189 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.pdf b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.pdf new file mode 100644 index 000000000..c14cc561f Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.pdf differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.png b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.png new file mode 100644 index 000000000..d2f059120 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.png differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.svg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.svg new file mode 100644 index 000000000..679edec2e --- /dev/null +++ b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.svg @@ -0,0 +1,725 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.webp b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.webp new file mode 100644 index 000000000..122741b60 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test.webp differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_gif.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_gif.jpg new file mode 100644 index 000000000..7f28bcef7 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_gif.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_icc_prophoto.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_icc_prophoto.jpg new file mode 100644 index 000000000..ebf7f02b3 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_icc_prophoto.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_issue.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_issue.jpg new file mode 100644 index 000000000..8348e3878 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_issue.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_pdf.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_pdf.jpg new file mode 100644 index 000000000..bbb26c525 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_pdf.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_smart_crop.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_smart_crop.jpg new file mode 100644 index 000000000..ff4a34916 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_smart_crop.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_square.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_square.jpg new file mode 100644 index 000000000..c69aab42b Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_square.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_svg.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_svg.jpg new file mode 100644 index 000000000..77b4714e2 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/test_svg.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent.png b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent.png new file mode 100644 index 000000000..c82d01517 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent.png differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent_trim.png b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent_trim.png new file mode 100644 index 000000000..808781068 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/transparent_trim.png differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.jpg new file mode 100644 index 000000000..3e12e65e5 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.jpg differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.webp b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.webp new file mode 100644 index 000000000..117890e50 Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/testdata/vertical.webp differ diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/type_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/type_test.go index 9c83b0f52..92ac4e56a 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/type_test.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/type_test.go @@ -22,12 +22,14 @@ func TestDeterminateImageType(t *testing.T) { } for _, file := range files { - img, _ := os.Open(path.Join("fixtures", file.name)) + img, _ := os.Open(path.Join("testdata", file.name)) buf, _ := ioutil.ReadAll(img) defer img.Close() - if DetermineImageType(buf) != file.expected { - t.Fatal("Image type is not valid") + if VipsIsTypeSupported(file.expected) { + if DetermineImageType(buf) != file.expected { + t.Fatalf("Image type is not valid: %s != %s", file.name, ImageTypes[file.expected]) + } } } } @@ -47,12 +49,12 @@ func TestDeterminateImageTypeName(t *testing.T) { } for _, file := range files { - img, _ := os.Open(path.Join("fixtures", file.name)) + img, _ := os.Open(path.Join("testdata", file.name)) buf, _ := ioutil.ReadAll(img) defer img.Close() if DetermineImageTypeName(buf) != file.expected { - t.Fatal("Image type is not valid") + t.Fatalf("Image type is not valid: %s != %s", file.name, file.expected) } } } @@ -66,7 +68,7 @@ func TestIsTypeSupported(t *testing.T) { for _, n := range types { if IsTypeSupported(n.name) == false { - t.Fatalf("Image type %#v is not valid", ImageTypes[n.name]) + t.Fatalf("Image type %s is not valid", ImageTypes[n.name]) } } } @@ -85,7 +87,7 @@ func TestIsTypeNameSupported(t *testing.T) { for _, n := range types { if IsTypeNameSupported(n.name) != n.expected { - t.Fatalf("Image type %#v is not valid", n.name) + t.Fatalf("Image type %s is not valid", n.name) } } } @@ -102,7 +104,7 @@ func TestIsTypeSupportedSave(t *testing.T) { for _, n := range types { if IsTypeSupportedSave(n.name) == false { - t.Fatalf("Image type %#v is not valid", ImageTypes[n.name]) + t.Fatalf("Image type %s is not valid", ImageTypes[n.name]) } } } @@ -122,7 +124,7 @@ func TestIsTypeNameSupportedSave(t *testing.T) { for _, n := range types { if IsTypeNameSupportedSave(n.name) != n.expected { - t.Fatalf("Image type %#v is not valid", n.name) + t.Fatalf("Image type %s is not valid", n.name) } } } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/version.go b/vendor/src/gopkg.in/h2non/bimg.v1/version.go index 682eaaaf3..69e7efb9f 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/version.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/version.go @@ -1,4 +1,4 @@ package bimg // Version represents the current package semantic version. -const Version = "1.0.9" +const Version = "1.0.18" diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/vips.go b/vendor/src/gopkg.in/h2non/bimg.v1/vips.go index ae654c5cf..fb1790138 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/vips.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/vips.go @@ -15,13 +15,8 @@ import ( "strings" "sync" "unsafe" - - d "github.com/tj/go-debug" ) -// debug is internally used to -var debug = d.Debug("bimg") - // VipsVersion exposes the current libvips semantic version const VipsVersion = string(C.VIPS_VERSION) @@ -55,6 +50,9 @@ type vipsSaveOptions struct { Type ImageType Interlace bool NoProfile bool + StripMetadata bool + Lossless bool + OutputICC string // Absolute path to the output ICC profile Interpretation Interpretation } @@ -130,6 +128,22 @@ func Shutdown() { } } +// VipsCacheSetMaxMem Sets the maximum amount of tracked memory allowed before the vips operation cache +// begins to drop entries. +func VipsCacheSetMaxMem(maxCacheMem int) { + C.vips_cache_set_max_mem(C.size_t(maxCacheMem)) +} + +// VipsCacheSetMax sets the maximum number of operations to keep in the vips operation cache. +func VipsCacheSetMax(maxCacheSize int) { + C.vips_cache_set_max(C.int(maxCacheSize)) +} + +// VipsCacheDropAll drops the vips operation cache, freeing the allocated memory. +func VipsCacheDropAll() { + C.vips_cache_drop_all() +} + // VipsDebugInfo outputs to stdout libvips collected data. Useful for debugging. func VipsDebugInfo() { C.im__print_all() @@ -346,6 +360,7 @@ func vipsFlattenBackground(image *C.VipsImage, background Color) (*C.VipsImage, } func vipsPreSave(image *C.VipsImage, o *vipsSaveOptions) (*C.VipsImage, error) { + var outImage *C.VipsImage // Remove ICC profile metadata if o.NoProfile { C.remove_profile(image) @@ -358,7 +373,6 @@ func vipsPreSave(image *C.VipsImage, o *vipsSaveOptions) (*C.VipsImage, error) { interpretation := C.VipsInterpretation(o.Interpretation) // Apply the proper colour space - var outImage *C.VipsImage if vipsColourspaceIsSupported(image) { err := C.vips_colourspace_bridge(image, &outImage, interpretation) if int(err) != 0 { @@ -367,6 +381,18 @@ func vipsPreSave(image *C.VipsImage, o *vipsSaveOptions) (*C.VipsImage, error) { image = outImage } + if o.OutputICC != "" && vipsHasProfile(image) { + outputIccPath := C.CString(o.OutputICC) + defer C.free(unsafe.Pointer(outputIccPath)) + + err := C.vips_icc_transform_bridge(image, &outImage, outputIccPath) + if int(err) != 0 { + return nil, catchVipsError() + } + C.g_object_unref(C.gpointer(image)) + image = outImage + } + return image, nil } @@ -391,6 +417,8 @@ func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) { saveErr := C.int(0) interlace := C.int(boolToInt(o.Interlace)) quality := C.int(o.Quality) + strip := C.int(boolToInt(o.StripMetadata)) + lossless := C.int(boolToInt(o.Lossless)) if o.Type != 0 && !IsTypeSupportedSave(o.Type) { return nil, fmt.Errorf("VIPS cannot save to %#v", ImageTypes[o.Type]) @@ -398,13 +426,13 @@ func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) { var ptr unsafe.Pointer switch o.Type { case WEBP: - saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, 1, quality) + saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, strip, quality, lossless) case PNG: - saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, 1, C.int(o.Compression), quality, interlace) + saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, strip, C.int(o.Compression), quality, interlace) case TIFF: saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length) default: - saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, 1, quality, interlace) + saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, strip, quality, interlace) } if int(saveErr) != 0 { @@ -472,6 +500,25 @@ func vipsSmartCrop(image *C.VipsImage, width, height int) (*C.VipsImage, error) return buf, nil } +func vipsTrim(image *C.VipsImage, background Color, threshold float64) (int, int, int, int, error) { + defer C.g_object_unref(C.gpointer(image)) + + top := C.int(0) + left := C.int(0) + width := C.int(0) + height := C.int(0) + + err := C.vips_find_trim_bridge(image, + &top, &left, &width, &height, + C.double(background.R), C.double(background.G), C.double(background.B), + C.double(threshold)) + if err != 0 { + return 0, 0, 0, 0, catchVipsError() + } + + return int(top), int(left), int(width), int(height), nil +} + func vipsShrinkJpeg(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) { var image *C.VipsImage var ptr = unsafe.Pointer(&buf[0]) @@ -485,6 +532,19 @@ func vipsShrinkJpeg(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, e return image, nil } +func vipsShrinkWebp(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) { + var image *C.VipsImage + var ptr = unsafe.Pointer(&buf[0]) + defer C.g_object_unref(C.gpointer(input)) + + err := C.vips_webpload_buffer_shrink(ptr, C.size_t(len(buf)), &image, C.int(shrink)) + if err != 0 { + return nil, catchVipsError() + } + + return image, nil +} + func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) { var image *C.VipsImage defer C.g_object_unref(C.gpointer(input)) @@ -497,6 +557,18 @@ func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) { return image, nil } +func vipsReduce(input *C.VipsImage, xshrink float64, yshrink float64) (*C.VipsImage, error) { + var image *C.VipsImage + defer C.g_object_unref(C.gpointer(input)) + + err := C.vips_reduce_bridge(input, &image, C.double(xshrink), C.double(yshrink)) + if err != 0 { + return nil, catchVipsError() + } + + return image, nil +} + func vipsEmbed(input *C.VipsImage, left, top, width, height int, extend Extend, background Color) (*C.VipsImage, error) { var image *C.VipsImage @@ -533,35 +605,36 @@ func vipsAffine(input *C.VipsImage, residualx, residualy float64, i Interpolator } func vipsImageType(buf []byte) ImageType { - if len(buf) == 0 { + if len(buf) < 12 { return UNKNOWN } - if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 { - return PNG - } if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF { return JPEG } - if IsTypeSupported(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 { - return WEBP + if IsTypeSupported(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 { + return GIF + } + if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 { + return PNG } if IsTypeSupported(TIFF) && ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) { return TIFF } - if IsTypeSupported(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 { - return GIF - } if IsTypeSupported(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 { return PDF } + if IsTypeSupported(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 { + return WEBP + } if IsTypeSupported(SVG) && IsSVGImage(buf) { return SVG } if IsTypeSupported(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") { return MAGICK } + return UNKNOWN } diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/vips.h b/vendor/src/gopkg.in/h2non/bimg.v1/vips.h index 6d7655024..29a2e638a 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/vips.h +++ b/vendor/src/gopkg.in/h2non/bimg.v1/vips.h @@ -20,6 +20,9 @@ #define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation" +#define INT_TO_GBOOLEAN(bool) (bool > 0 ? TRUE : FALSE) + + enum types { UNKNOWN = 0, JPEG, @@ -62,11 +65,6 @@ remove_profile(VipsImage *image) { vips_image_remove(image, VIPS_META_ICC_NAME); } -static gboolean -with_interlace(int interlace) { - return interlace > 0 ? TRUE : FALSE; -} - static int has_alpha_channel(VipsImage *image) { return ( @@ -112,6 +110,11 @@ vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink) return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL); } +int +vips_webpload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink) { + return vips_webpload_buffer(buf, len, out, "shrink", shrink, NULL); +} + int vips_flip_bridge(VipsImage *in, VipsImage **out, int direction) { return vips_flip(in, out, direction, NULL); @@ -122,6 +125,11 @@ vips_shrink_bridge(VipsImage *in, VipsImage **out, double xshrink, double yshrin return vips_shrink(in, out, xshrink, yshrink, NULL); } +int +vips_reduce_bridge(VipsImage *in, VipsImage **out, double xshrink, double yshrink) { + return vips_reduce(in, out, xshrink, yshrink, NULL); +} + int vips_type_find_bridge(int t) { if (t == GIF) { @@ -260,13 +268,19 @@ vips_colourspace_bridge(VipsImage *in, VipsImage **out, VipsInterpretation space return vips_colourspace(in, out, space, NULL); } +int +vips_icc_transform_bridge (VipsImage *in, VipsImage **out, const char *output_icc_profile) { + // `output_icc_profile` represents the absolute path to the output ICC profile file + return vips_icc_transform(in, out, output_icc_profile, "embedded", TRUE, NULL); +} + int vips_jpegsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) { return vips_jpegsave_buffer(in, buf, len, - "strip", strip, + "strip", INT_TO_GBOOLEAN(strip), "Q", quality, "optimize_coding", TRUE, - "interlace", with_interlace(interlace), + "interlace", INT_TO_GBOOLEAN(interlace), NULL ); } @@ -275,27 +289,28 @@ int vips_pngsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace) { #if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)) return vips_pngsave_buffer(in, buf, len, - "strip", FALSE, + "strip", INT_TO_GBOOLEAN(strip), "compression", compression, - "interlace", with_interlace(interlace), + "interlace", INT_TO_GBOOLEAN(interlace), "filter", VIPS_FOREIGN_PNG_FILTER_NONE, NULL ); #else return vips_pngsave_buffer(in, buf, len, - "strip", FALSE, + "strip", INT_TO_GBOOLEAN(strip), "compression", compression, - "interlace", with_interlace(interlace), + "interlace", INT_TO_GBOOLEAN(interlace), NULL ); #endif } int -vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality) { +vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int lossless) { return vips_webpsave_buffer(in, buf, len, - "strip", strip, + "strip", INT_TO_GBOOLEAN(strip), "Q", quality, + "lossless", INT_TO_GBOOLEAN(lossless), NULL ); } @@ -530,3 +545,19 @@ vips_smartcrop_bridge(VipsImage *in, VipsImage **out, int width, int height) { return 0; #endif } + +int vips_find_trim_bridge(VipsImage *in, int *top, int *left, int *width, int *height, double r, double g, double b, double threshold) { +#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 6) + if (vips_is_16bit(in->Type)) { + r = 65535 * r / 255; + g = 65535 * g / 255; + b = 65535 * b / 255; + } + + double background[3] = {r, g, b}; + VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3); + return vips_find_trim(in, top, left, width, height, "background", vipsBackground, "threshold", threshold, NULL); +#else + return 0; +#endif +} diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/vips_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/vips_test.go index 47b3f4140..d8bd2b230 100644 --- a/vendor/src/gopkg.in/h2non/bimg.v1/vips_test.go +++ b/vendor/src/gopkg.in/h2non/bimg.v1/vips_test.go @@ -33,7 +33,7 @@ func TestVipsSave(t *testing.T) { for _, typ := range types { image, _, _ := vipsRead(readImage("test.jpg")) - options := vipsSaveOptions{Quality: 95, Type: typ} + options := vipsSaveOptions{Quality: 95, Type: typ, StripMetadata: true} buf, err := vipsSave(image, options) if err != nil { @@ -144,6 +144,13 @@ func TestVipsImageType(t *testing.T) { } } +func TestVipsImageTypeInvalid(t *testing.T) { + imgType := vipsImageType([]byte("vip")) + if imgType != UNKNOWN { + t.Fatal("Invalid image type") + } +} + func TestVipsMemory(t *testing.T) { mem := VipsMemory() @@ -156,7 +163,7 @@ func TestVipsMemory(t *testing.T) { } func readImage(file string) []byte { - img, _ := os.Open(path.Join("fixtures", file)) + img, _ := os.Open(path.Join("testdata", file)) buf, _ := ioutil.ReadAll(img) defer img.Close() return buf