Remove dependency errors (#507)
parent
06338b06d7
commit
a76e31f1b3
|
@ -298,12 +298,6 @@
|
||||||
"revision": "6a22caf2fd45d5e2119bfc3717e984f15a7eb7ee",
|
"revision": "6a22caf2fd45d5e2119bfc3717e984f15a7eb7ee",
|
||||||
"branch": "master"
|
"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",
|
"importpath": "github.com/uber-go/atomic",
|
||||||
"repository": "https://github.com/uber-go/atomic",
|
"repository": "https://github.com/uber-go/atomic",
|
||||||
|
@ -311,14 +305,14 @@
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"importpath": "github.com/uber/jaeger-client-go",
|
"importpath": "github.com/jaegertracing/jaeger-client-go",
|
||||||
"repository": "https://github.com/uber/jaeger-client-go",
|
"repository": "https://github.com/jaegertracing/jaeger-client-go",
|
||||||
"revision": "3ad49a1d839b517923a6fdac36d81cbf7b744f37",
|
"revision": "3ad49a1d839b517923a6fdac36d81cbf7b744f37",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"importpath": "github.com/uber/jaeger-lib/metrics",
|
"importpath": "github.com/jaegertracing/jaeger-lib/metrics",
|
||||||
"repository": "https://github.com/uber/jaeger-lib",
|
"repository": "https://github.com/jaegertracing/jaeger-lib",
|
||||||
"revision": "21a3da6d66fe0e278072676fdc84cd4c9ccb9b67",
|
"revision": "21a3da6d66fe0e278072676fdc84cd4c9ccb9b67",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"path": "/metrics"
|
"path": "/metrics"
|
||||||
|
@ -458,7 +452,7 @@
|
||||||
{
|
{
|
||||||
"importpath": "gopkg.in/h2non/bimg.v1",
|
"importpath": "gopkg.in/h2non/bimg.v1",
|
||||||
"repository": "https://gopkg.in/h2non/bimg.v1",
|
"repository": "https://gopkg.in/h2non/bimg.v1",
|
||||||
"revision": "45f8993550e71ee7b8001d40c681c6c9fa822357",
|
"revision": "02e621739c77c791d8c153f240b7a1f75b07816f",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -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"
|
|
@ -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.
|
||||||
|
```
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Release Process
|
||||||
|
|
||||||
|
1. Create a PR "Preparing for release X.Y.Z" against master branch
|
||||||
|
* Alter CHANGELOG.md from `<placeholder_version> (unreleased)` to `<X.Y.Z> (YYYY-MM-DD)`
|
||||||
|
* Update `JaegerClientVersion` in constants.go to `Go-X.Y.Z`
|
||||||
|
2. Create a release "Release X.Y.Z" on Github
|
||||||
|
* Create Tag `vX.Y.Z`
|
||||||
|
* Copy CHANGELOG.md into the release notes
|
||||||
|
3. Create a PR "Back to development" against master branch
|
||||||
|
* Add `<next_version> (unreleased)` to CHANGELOG.md
|
||||||
|
* Update `JaegerClientVersion` in constants.go to `Go-<next_version>dev`
|
|
@ -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...)
|
||||||
|
}
|
126
vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter_test.go
vendored
Normal file
126
vendor/src/github.com/jaegertracing/jaeger-client-go/baggage_setter_test.go
vendored
Normal file
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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"])
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM scratch
|
||||||
|
ADD crossdock /
|
||||||
|
CMD ["/crossdock"]
|
||||||
|
EXPOSE 8080-8082
|
|
@ -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.
|
||||||
|
|
109
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client.go
vendored
Normal file
109
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
109
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client_test.go
vendored
Normal file
109
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/client_test.go
vendored
Normal file
|
@ -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"))
|
||||||
|
}
|
43
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/constants.go
vendored
Normal file
43
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/constants.go
vendored
Normal file
|
@ -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"
|
||||||
|
)
|
167
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/trace.go
vendored
Normal file
167
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/client/trace.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
32
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/constants.go
vendored
Normal file
32
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/constants.go
vendored
Normal file
|
@ -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"
|
||||||
|
)
|
73
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/json.go
vendored
Normal file
73
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/common/json.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
71
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/docker-compose.yml
vendored
Normal file
71
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/docker-compose.yml
vendored
Normal file
|
@ -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
|
137
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler.go
vendored
Normal file
137
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler.go
vendored
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
154
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler_test.go
vendored
Normal file
154
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/endtoend/handler_test.go
vendored
Normal file
|
@ -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())
|
||||||
|
}
|
29
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/log/logger.go
vendored
Normal file
29
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/log/logger.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
26
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/constants.go
vendored
Normal file
26
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/constants.go
vendored
Normal file
|
@ -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")
|
||||||
|
)
|
164
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server.go
vendored
Normal file
164
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
87
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server_test.go
vendored
Normal file
87
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/server_test.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
88
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/tchannel.go
vendored
Normal file
88
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/tchannel.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
101
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/trace.go
vendored
Normal file
101
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/server/trace.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
18
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/constants.go
vendored
Normal file
18
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/constants.go
vendored
Normal file
|
@ -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() {
|
||||||
|
}
|
137
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go
vendored
Normal file
137
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
747
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go
vendored
Normal file
747
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go
vendored
Normal file
|
@ -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 "<nil>"
|
||||||
|
}
|
||||||
|
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 "<nil>"
|
||||||
|
}
|
||||||
|
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 "<nil>"
|
||||||
|
}
|
||||||
|
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 "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("TracedServiceJoinTraceResult(%+v)", *p)
|
||||||
|
}
|
1103
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go
vendored
Normal file
1103
vendor/src/github.com/jaegertracing/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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: []
|
|
@ -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
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
101
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/options.go
vendored
Normal file
101
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/options.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
157
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager.go
vendored
Normal file
157
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/remote/restriction_manager.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
71
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager.go
vendored
Normal file
71
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/baggage/restriction_manager.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
81
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/spanlog/json.go
vendored
Normal file
81
vendor/src/github.com/jaegertracing/jaeger-client-go/internal/spanlog/json.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
388
vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span_test.go
vendored
Normal file
388
vendor/src/github.com/jaegertracing/jaeger-client-go/jaeger_thrift_span_test.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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{}) {}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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...)
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -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{}) {}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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):]
|
||||||
|
}
|
|
@ -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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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).
|
|
@ -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
|
63
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints.go
vendored
Normal file
63
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
43
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints_test.go
vendored
Normal file
43
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/endpoints_test.go
vendored
Normal file
|
@ -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")
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
61
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics_test.go
vendored
Normal file
61
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/metrics_test.go
vendored
Normal file
|
@ -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},
|
||||||
|
)
|
||||||
|
}
|
101
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer.go
vendored
Normal file
101
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
34
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer_test.go
vendored
Normal file
34
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/normalizer_test.go
vendored
Normal file
|
@ -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")
|
||||||
|
}
|
171
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer.go
vendored
Normal file
171
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer.go
vendored
Normal file
|
@ -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()
|
||||||
|
}
|
177
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer_test.go
vendored
Normal file
177
vendor/src/github.com/jaegertracing/jaeger-client-go/rpcmetrics/observer_test.go
vendored
Normal file
|
@ -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...)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
120
vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicense.py
vendored
Normal file
120
vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicense.py
vendored
Normal file
|
@ -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()
|
5
vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicenses.sh
vendored
Normal file
5
vendor/src/github.com/jaegertracing/jaeger-client-go/scripts/updateLicenses.sh
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
python scripts/updateLicense.py $(git ls-files "*\.go" | grep -v thrift-gen | grep -v tracetest)
|
|
@ -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
|
||||||
|
}
|
|
@ -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())
|
||||||
|
}
|
189
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent.go
vendored
Normal file
189
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
93
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent_test.go
vendored
Normal file
93
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/mock_agent_test.go
vendored
Normal file
|
@ -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)
|
||||||
|
}
|
53
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/sampling_manager.go
vendored
Normal file
53
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/sampling_manager.go
vendored
Normal file
|
@ -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
|
||||||
|
}
|
106
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport.go
vendored
Normal file
106
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport.go
vendored
Normal file
|
@ -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")
|
||||||
|
}
|
67
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport_test.go
vendored
Normal file
67
vendor/src/github.com/jaegertracing/jaeger-client-go/testutils/udp_transport_test.go
vendored
Normal file
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
410
vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/agent.go
vendored
Normal file
410
vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/agent.go
vendored
Normal file
|
@ -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 "<nil>"
|
||||||
|
}
|
||||||
|
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 "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p)
|
||||||
|
}
|
21
vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/constants.go
vendored
Normal file
21
vendor/src/github.com/jaegertracing/jaeger-client-go/thrift-gen/agent/constants.go
vendored
Normal file
|
@ -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() {
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue