mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
opentracing -> opencensus (#802)
* update vendor directory, add go.opencensus.io * update imports * oops * s/opentracing/opencensus/ & remove prometheus / zipkin stuff & remove old stats * the dep train rides again * fix gin build * deps from last guy * start in on the agent metrics * she builds * remove tags for now, cardinality error is fussing. subscribe instead of register * update to patched version of opencensus to proceed for now TODO switch to a release * meh fix imports * println debug the bad boys * lace it with the tags * update deps again * fix all inconsistent cardinality errors * add our own logger * fix init * fix oom measure * remove bugged removal code * fix s3 measures * fix prom handler nil
This commit is contained in:
24
vendor/github.com/openzipkin/zipkin-go/.gitignore
generated
vendored
Normal file
24
vendor/github.com/openzipkin/zipkin-go/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
19
vendor/github.com/openzipkin/zipkin-go/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/openzipkin/zipkin-go/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
install:
|
||||
- go get -d -t ./...
|
||||
- go get -u github.com/golang/lint/...
|
||||
|
||||
script:
|
||||
- make test vet lint bench
|
||||
- $GOPATH/bin/goveralls -service=travis-ci
|
||||
201
vendor/github.com/openzipkin/zipkin-go/LICENSE
generated
vendored
Normal file
201
vendor/github.com/openzipkin/zipkin-go/LICENSE
generated
vendored
Normal file
@@ -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 2017 The OpenZipkin Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
26
vendor/github.com/openzipkin/zipkin-go/Makefile
generated
vendored
Normal file
26
vendor/github.com/openzipkin/zipkin-go/Makefile
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
.DEFAULT_GOAL := test
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v -race -cover ./...
|
||||
|
||||
.PHONY: bench
|
||||
bench:
|
||||
go test -v -run - -bench . -benchmem ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
# Ignore grep's exit code since no match returns 1.
|
||||
-golint ./... | grep --invert-match -E '^.*\.pb\.go|^thrift'
|
||||
@
|
||||
@! (golint ./... | grep --invert-match -E '^.*\.pb\.go|^thrift' | read dummy)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
.PHONY: all
|
||||
all: vet lint test bench
|
||||
|
||||
.PHONY: example
|
||||
79
vendor/github.com/openzipkin/zipkin-go/README.md
generated
vendored
Normal file
79
vendor/github.com/openzipkin/zipkin-go/README.md
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# Zipkin Library for Go
|
||||
|
||||
[](https://travis-ci.org/openzipkin/zipkin-go)
|
||||
[](https://circleci.com/gh/openzipkin/zipkin-go)
|
||||
[](https://ci.appveyor.com/project/basvanbeek/zipkin-go)
|
||||
[](https://coveralls.io/github/openzipkin/zipkin-go?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/openzipkin/zipkin-go)
|
||||
[](https://godoc.org/github.com/openzipkin/zipkin-go)
|
||||
[](https://gitter.im/openzipkin/zipkin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://sourcegraph.com/github.com/openzipkin/zipkin-go?badge)
|
||||
|
||||
Zipkin Go is the official Go Tracer implementation for Zipkin, supported by the
|
||||
OpenZipkin community.
|
||||
|
||||
## package organization
|
||||
`zipkin-go` is built with interoperability in mind within the OpenZipkin
|
||||
community and even 3rd parties, the library consists of several packages.
|
||||
|
||||
The main tracing implementation can be found in the root folder of this
|
||||
repository. Reusable parts not considered core implementation or deemed
|
||||
beneficiary for usage by others are placed in their own packages within this
|
||||
repository.
|
||||
|
||||
### model
|
||||
This library implements the Zipkin V2 Span Model which is available in the model
|
||||
package. It contains a Go data model compatible with the Zipkin V2 API and can
|
||||
automatically sanitize, parse and (de)serialize to and from the required JSON
|
||||
representation as used by the official Zipkin V2 Collectors.
|
||||
|
||||
### propagation
|
||||
The propagation package and B3 subpackage hold the logic for propagating
|
||||
SpanContext (span identifiers and sampling flags) between services participating
|
||||
in traces. Currently Zipkin B3 Propagation is supported for HTTP and GRPC.
|
||||
|
||||
### middleware
|
||||
The middleware subpackages contain officially supported middleware handlers and
|
||||
tracing wrappers.
|
||||
|
||||
#### http
|
||||
An easy to use http.Handler middleware for tracing server side requests is
|
||||
provided. This allows one to use this middleware in applications using
|
||||
standard library servers as well as most available higher level frameworks. Some
|
||||
frameworks will have their own instrumentation and middleware that maps better
|
||||
for their ecosystem.
|
||||
|
||||
For HTTP client operations `NewTransport` can return a `http.RoundTripper`
|
||||
implementation that can either wrap the standard http.Client's Transport or a
|
||||
custom provided one and add per request tracing. Since HTTP Requests can have
|
||||
one or multiple redirects it is advisable to always enclose HTTP Client calls
|
||||
with a `Span` either around the `*http.Client` call level or parent function
|
||||
level.
|
||||
|
||||
For convenience `NewClient` is provided which returns a HTTP Client which embeds
|
||||
`*http.Client` and provides an `application span` around the HTTP calls when
|
||||
calling the `DoWithAppSpan()` method.
|
||||
|
||||
#### grpc
|
||||
gRPC middleware / interceptors are planned for the near future.
|
||||
|
||||
### reporter
|
||||
The reporter package holds the interface which the various Reporter
|
||||
implementations use. It is exported into its own package as it can be used by
|
||||
3rd parties to use these Reporter packages in their own libraries for exporting
|
||||
to the Zipkin ecosystem. The `zipkin-go` tracer also uses the interface to
|
||||
accept 3rd party Reporter implementations.
|
||||
|
||||
#### HTTP Reporter
|
||||
Most common Reporter type used by Zipkin users transporting Spans to the Zipkin
|
||||
server using JSON over HTTP. The reporter holds a buffer and reports to the
|
||||
backend asynchronously.
|
||||
|
||||
#### Kafka Reporter
|
||||
High performance Reporter transporting Spans to the Zipkin server using a Kafka
|
||||
Producer digesting JSON V2 Spans. The reporter uses the
|
||||
[Sarama async producer](https://godoc.org/github.com/Shopify/sarama#AsyncProducer)
|
||||
underneath.
|
||||
|
||||
## usage and examples
|
||||
[HTTP Server Example](example_httpserver_test.go)
|
||||
21
vendor/github.com/openzipkin/zipkin-go/appveyor.yml
generated
vendored
Normal file
21
vendor/github.com/openzipkin/zipkin-go/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
version: v1.0.0.{build}
|
||||
|
||||
platform: x64
|
||||
|
||||
clone_folder: c:\gopath\src\github.com\openzipkin\zipkin-go
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
|
||||
install:
|
||||
- echo %PATH%
|
||||
- echo %GOPATH%
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- go version
|
||||
- go env
|
||||
|
||||
build_script:
|
||||
- go get -t -v ./...
|
||||
- go vet ./...
|
||||
- go test -v -race -cover ./...
|
||||
- go test -v -run - -bench . -benchmem ./...
|
||||
182
vendor/github.com/openzipkin/zipkin-go/bench_test.go
generated
vendored
Normal file
182
vendor/github.com/openzipkin/zipkin-go/bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
package zipkin_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
b3HTTP = "b3-http"
|
||||
b3GRPC = "b3-grpc"
|
||||
)
|
||||
|
||||
var tags []string
|
||||
|
||||
func init() {
|
||||
var (
|
||||
traceID model.TraceID
|
||||
gen = idgenerator.NewRandom64()
|
||||
)
|
||||
|
||||
tags = make([]string, 1000)
|
||||
for j := 0; j < len(tags); j++ {
|
||||
tags[j] = fmt.Sprintf("%d", gen.SpanID(traceID))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addAnnotationsAndTags(sp zipkin.Span, numAnnotation, numTag int) {
|
||||
for j := 0; j < numAnnotation; j++ {
|
||||
sp.Annotate(time.Now(), "event")
|
||||
}
|
||||
|
||||
for j := 0; j < numTag; j++ {
|
||||
sp.Tag(tags[j], "")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkWithOps(b *testing.B, numAnnotation, numTag int) {
|
||||
var (
|
||||
r countingRecorder
|
||||
t, _ = zipkin.NewTracer(&r)
|
||||
)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
sp := t.StartSpan("test")
|
||||
addAnnotationsAndTags(sp, numAnnotation, numTag)
|
||||
sp.Finish()
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
|
||||
if int(r) != b.N {
|
||||
b.Fatalf("missing traces: want %d, have %d", b.N, r)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSpan_Empty(b *testing.B) {
|
||||
benchmarkWithOps(b, 0, 0)
|
||||
}
|
||||
|
||||
func BenchmarkSpan_100Annotations(b *testing.B) {
|
||||
benchmarkWithOps(b, 100, 0)
|
||||
}
|
||||
|
||||
func BenchmarkSpan_1000Annotations(b *testing.B) {
|
||||
benchmarkWithOps(b, 1000, 0)
|
||||
}
|
||||
|
||||
func BenchmarkSpan_100Tags(b *testing.B) {
|
||||
benchmarkWithOps(b, 0, 100)
|
||||
}
|
||||
|
||||
func BenchmarkSpan_1000Tags(b *testing.B) {
|
||||
benchmarkWithOps(b, 0, 1000)
|
||||
}
|
||||
|
||||
func benchmarkInject(b *testing.B, propagationType string) {
|
||||
var (
|
||||
r countingRecorder
|
||||
injector propagation.Injector
|
||||
tracer, _ = zipkin.NewTracer(&r)
|
||||
)
|
||||
|
||||
switch propagationType {
|
||||
case b3HTTP:
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
injector = b3.InjectHTTP(req)
|
||||
case b3GRPC:
|
||||
md := metadata.MD{}
|
||||
injector = b3.InjectGRPC(&md)
|
||||
default:
|
||||
b.Fatalf("unknown injector: %s", propagationType)
|
||||
}
|
||||
|
||||
sp := tracer.StartSpan("testing")
|
||||
addAnnotationsAndTags(sp, 0, 0)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := injector(sp.Context()); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkExtract(b *testing.B, propagationType string) {
|
||||
var (
|
||||
r countingRecorder
|
||||
tracer, _ = zipkin.NewTracer(&r)
|
||||
)
|
||||
|
||||
sp := tracer.StartSpan("testing")
|
||||
|
||||
switch propagationType {
|
||||
case b3HTTP:
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
b3.InjectHTTP(req)(sp.Context())
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = b3.ExtractHTTP(copyRequest(req))
|
||||
}
|
||||
|
||||
case b3GRPC:
|
||||
md := metadata.MD{}
|
||||
b3.InjectGRPC(&md)(sp.Context())
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
md2 := md.Copy()
|
||||
if _, err := b3.ExtractGRPC(&md2)(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
b.Fatalf("unknown propagation type: %s", propagationType)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInject_B3_HTTP_Empty(b *testing.B) {
|
||||
benchmarkInject(b, b3HTTP)
|
||||
}
|
||||
|
||||
func BenchmarkInject_B3_GRPC_Empty(b *testing.B) {
|
||||
benchmarkInject(b, b3GRPC)
|
||||
}
|
||||
|
||||
func BenchmarkExtract_B3_HTTP_Empty(b *testing.B) {
|
||||
benchmarkExtract(b, b3HTTP)
|
||||
}
|
||||
|
||||
func BenchmarkExtract_B3_GRPC_Empty(b *testing.B) {
|
||||
benchmarkExtract(b, b3GRPC)
|
||||
}
|
||||
|
||||
type countingRecorder int32
|
||||
|
||||
func (c *countingRecorder) Send(_ model.SpanModel) {
|
||||
atomic.AddInt32((*int32)(c), 1)
|
||||
}
|
||||
|
||||
func (c *countingRecorder) Close() error { return nil }
|
||||
|
||||
func copyRequest(req *http.Request) *http.Request {
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
for k, v := range req.Header {
|
||||
r.Header[k] = v
|
||||
}
|
||||
return r
|
||||
}
|
||||
10
vendor/github.com/openzipkin/zipkin-go/circle.yml
generated
vendored
Normal file
10
vendor/github.com/openzipkin/zipkin-go/circle.yml
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
dependencies:
|
||||
override:
|
||||
- sudo rm -rf /home/ubuntu/.go_workspace/src/github.com/openzipkin
|
||||
- mkdir -p /home/ubuntu/.go_workspace/src/github.com/openzipkin
|
||||
- mv /home/ubuntu/zipkin-go /home/ubuntu/.go_workspace/src/github.com/openzipkin
|
||||
- ln -s /home/ubuntu/.go_workspace/src/github.com/openzipkin/zipkin-go /home/ubuntu/zipkin-go
|
||||
- go get -u -t -v github.com/openzipkin/zipkin-go/...
|
||||
test:
|
||||
override:
|
||||
- make test bench
|
||||
23
vendor/github.com/openzipkin/zipkin-go/context.go
generated
vendored
Normal file
23
vendor/github.com/openzipkin/zipkin-go/context.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// SpanFromContext retrieves a Zipkin Span from Go's context propagation
|
||||
// mechanism if found. If not found, returns nil.
|
||||
func SpanFromContext(ctx context.Context) Span {
|
||||
if s, ok := ctx.Value(spanKey).(Span); ok {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewContext stores a Zipkin Span into Go's context propagation mechanism.
|
||||
func NewContext(ctx context.Context, s Span) context.Context {
|
||||
return context.WithValue(ctx, spanKey, s)
|
||||
}
|
||||
|
||||
type ctxKey struct{}
|
||||
|
||||
var spanKey = ctxKey{}
|
||||
6
vendor/github.com/openzipkin/zipkin-go/doc.go
generated
vendored
Normal file
6
vendor/github.com/openzipkin/zipkin-go/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
Package zipkin implements a native Zipkin instrumentation library for Go.
|
||||
|
||||
See https://zipkin.io for more information about Zipkin.
|
||||
*/
|
||||
package zipkin
|
||||
71
vendor/github.com/openzipkin/zipkin-go/endpoint.go
generated
vendored
Normal file
71
vendor/github.com/openzipkin/zipkin-go/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// NewEndpoint creates a new endpoint given the provided serviceName and
|
||||
// hostPort.
|
||||
func NewEndpoint(serviceName string, hostPort string) (*model.Endpoint, error) {
|
||||
e := &model.Endpoint{
|
||||
ServiceName: serviceName,
|
||||
}
|
||||
|
||||
if hostPort == "" || hostPort == ":0" {
|
||||
if serviceName == "" {
|
||||
// if all properties are empty we should not have an Endpoint object.
|
||||
return nil, nil
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
if strings.IndexByte(hostPort, ':') < 0 {
|
||||
hostPort += ":0"
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Port = uint16(p)
|
||||
|
||||
addrs, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range addrs {
|
||||
addr := addrs[i].To4()
|
||||
if addr == nil {
|
||||
// IPv6 - 16 bytes
|
||||
if e.IPv6 == nil {
|
||||
e.IPv6 = addrs[i].To16()
|
||||
}
|
||||
} else {
|
||||
// IPv4 - 4 bytes
|
||||
if e.IPv4 == nil {
|
||||
e.IPv4 = addr
|
||||
}
|
||||
}
|
||||
if e.IPv4 != nil && e.IPv6 != nil {
|
||||
// Both IPv4 & IPv6 have been set, done...
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// default to 0 filled 4 byte array for IPv4 if IPv6 only host was found
|
||||
if e.IPv4 == nil {
|
||||
e.IPv4 = make([]byte, 4)
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
151
vendor/github.com/openzipkin/zipkin-go/endpoint_test.go
generated
vendored
Normal file
151
vendor/github.com/openzipkin/zipkin-go/endpoint_test.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package zipkin_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceName = "my_service"
|
||||
onlyHost = "localhost"
|
||||
defaultPort = 0
|
||||
port = 8081
|
||||
invalidNegativePort = "localhost:-8081"
|
||||
invalidOutOfRangePort = "localhost:65536"
|
||||
invalidHostPort = "::1:8081"
|
||||
unreachableHostPort = "nosuchhost:8081"
|
||||
)
|
||||
|
||||
var (
|
||||
ipv4HostPort = "localhost:" + fmt.Sprintf("%d", port)
|
||||
ipv6HostPort = "[2001:db8::68]:" + fmt.Sprintf("%d", port)
|
||||
ipv4ForHostPort = net.IPv4(127, 0, 0, 1)
|
||||
ipv6ForHostPort = net.ParseIP("2001:db8::68")
|
||||
)
|
||||
|
||||
func TestEmptyEndpoint(t *testing.T) {
|
||||
ep, err := zipkin.NewEndpoint("", "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
if ep != nil {
|
||||
t.Errorf("endpoint want nil, have: %+v", ep)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceNameOnlyEndpoint(t *testing.T) {
|
||||
have, err := zipkin.NewEndpoint(serviceName, "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
want := &model.Endpoint{ServiceName: serviceName}
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("endpoint want %+v, have: %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidHostPort(t *testing.T) {
|
||||
_, err := zipkin.NewEndpoint(serviceName, invalidHostPort)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "too many colons in address") {
|
||||
t.Fatalf("expected too many colons in address error, got: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointFailsDueToOutOfRangePort(t *testing.T) {
|
||||
_, err := zipkin.NewEndpoint(serviceName, invalidOutOfRangePort)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "value out of range") {
|
||||
t.Fatalf("expected out of range error, got: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointFailsDueToNegativePort(t *testing.T) {
|
||||
_, err := zipkin.NewEndpoint(serviceName, invalidNegativePort)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "invalid syntax") {
|
||||
t.Fatalf("expected invalid syntax error, got: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointFailsDueToLookupIP(t *testing.T) {
|
||||
_, err := zipkin.NewEndpoint(serviceName, unreachableHostPort)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "no such host") {
|
||||
t.Fatalf("expected no such host error, got: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointDefaultsPortToZeroWhenMissing(t *testing.T) {
|
||||
endpoint, err := zipkin.NewEndpoint(serviceName, onlyHost)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
if endpoint.Port != defaultPort {
|
||||
t.Fatalf("expected port %d, got %d", defaultPort, endpoint.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointIpv4Success(t *testing.T) {
|
||||
endpoint, err := zipkin.NewEndpoint(serviceName, ipv4HostPort)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
if serviceName != endpoint.ServiceName {
|
||||
t.Fatalf("expected service name %s, got %s", serviceName, endpoint.ServiceName)
|
||||
}
|
||||
|
||||
if !ipv4ForHostPort.Equal(endpoint.IPv4) {
|
||||
t.Fatalf("expected IPv4 %s, got %s", ipv4ForHostPort.String(), endpoint.IPv4.String())
|
||||
}
|
||||
|
||||
if port != endpoint.Port {
|
||||
t.Fatalf("expected port %d, got %d", port, endpoint.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEndpointIpv6Success(t *testing.T) {
|
||||
endpoint, err := zipkin.NewEndpoint(serviceName, ipv6HostPort)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
if serviceName != endpoint.ServiceName {
|
||||
t.Fatalf("expected service name %s, got %s", serviceName, endpoint.ServiceName)
|
||||
}
|
||||
|
||||
if !ipv6ForHostPort.Equal(endpoint.IPv6) {
|
||||
t.Fatalf("expected IPv6 %s, got %s", ipv6ForHostPort.String(), endpoint.IPv6.String())
|
||||
}
|
||||
|
||||
if port != endpoint.Port {
|
||||
t.Fatalf("expected port %d, got %d", port, endpoint.Port)
|
||||
}
|
||||
}
|
||||
109
vendor/github.com/openzipkin/zipkin-go/example_httpserver_test.go
generated
vendored
Normal file
109
vendor/github.com/openzipkin/zipkin-go/example_httpserver_test.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package zipkin_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
zipkinhttp "github.com/openzipkin/zipkin-go/middleware/http"
|
||||
logreporter "github.com/openzipkin/zipkin-go/reporter/log"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
// set up a span reporter
|
||||
reporter := logreporter.NewReporter(log.New(os.Stderr, "", log.LstdFlags))
|
||||
defer reporter.Close()
|
||||
|
||||
// create our local service endpoint
|
||||
endpoint, err := zipkin.NewEndpoint("myService", "localhost:0")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create local endpoint: %+v\n", err)
|
||||
}
|
||||
|
||||
// initialize our tracer
|
||||
tracer, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(endpoint))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create tracer: %+v\n", err)
|
||||
}
|
||||
|
||||
// create global zipkin http server middleware
|
||||
serverMiddleware := zipkinhttp.NewServerMiddleware(
|
||||
tracer, zipkinhttp.TagResponseSize(true),
|
||||
)
|
||||
|
||||
// create global zipkin traced http client
|
||||
client, err := zipkinhttp.NewClient(tracer, zipkinhttp.ClientTrace(true))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create client: %+v\n", err)
|
||||
}
|
||||
|
||||
// initialize router
|
||||
router := mux.NewRouter()
|
||||
|
||||
// start web service with zipkin http server middleware
|
||||
ts := httptest.NewServer(serverMiddleware(router))
|
||||
defer ts.Close()
|
||||
|
||||
// set-up handlers
|
||||
router.Methods("GET").Path("/some_function").HandlerFunc(someFunc(client, ts.URL))
|
||||
router.Methods("POST").Path("/other_function").HandlerFunc(otherFunc(client))
|
||||
|
||||
// initiate a call to some_func
|
||||
req, err := http.NewRequest("GET", ts.URL+"/some_function", nil)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create http request: %+v\n", err)
|
||||
}
|
||||
|
||||
res, err := client.DoWithAppSpan(req, "some_function")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to do http request: %+v\n", err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
// Output:
|
||||
}
|
||||
|
||||
func someFunc(client *zipkinhttp.Client, url string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("some_function called with method: %s\n", r.Method)
|
||||
|
||||
// retrieve span from context (created by server middleware)
|
||||
span := zipkin.SpanFromContext(r.Context())
|
||||
span.Tag("custom_key", "some value")
|
||||
|
||||
// doing some expensive calculations....
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
span.Annotate(time.Now(), "expensive_calc_done")
|
||||
|
||||
newRequest, err := http.NewRequest("POST", url+"/other_function", nil)
|
||||
if err != nil {
|
||||
log.Printf("unable to create client: %+v\n", err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := zipkin.NewContext(newRequest.Context(), span)
|
||||
|
||||
newRequest = newRequest.WithContext(ctx)
|
||||
|
||||
res, err := client.DoWithAppSpan(newRequest, "other_function")
|
||||
if err != nil {
|
||||
log.Printf("call to other_function returned error: %+v\n", err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
res.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func otherFunc(client *zipkinhttp.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("other_function called with method: %s\n", r.Method)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
104
vendor/github.com/openzipkin/zipkin-go/example_test.go
generated
vendored
Normal file
104
vendor/github.com/openzipkin/zipkin-go/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package zipkin_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
httpreporter "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
)
|
||||
|
||||
func doSomeWork(context.Context) {}
|
||||
|
||||
func ExampleNewTracer() {
|
||||
// create a reporter to be used by the tracer
|
||||
reporter := httpreporter.NewReporter("http://localhost:9411/api/v2/spans")
|
||||
defer reporter.Close()
|
||||
|
||||
// set-up the local endpoint for our service
|
||||
endpoint, err := zipkin.NewEndpoint("demoService", "172.20.23.100:80")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create local endpoint: %+v\n", err)
|
||||
}
|
||||
|
||||
// set-up our sampling strategy
|
||||
sampler, err := zipkin.NewBoundarySampler(0.01, time.Now().UnixNano())
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create sampler: %+v\n", err)
|
||||
}
|
||||
|
||||
// initialize the tracer
|
||||
tracer, err := zipkin.NewTracer(
|
||||
reporter,
|
||||
zipkin.WithLocalEndpoint(endpoint),
|
||||
zipkin.WithSampler(sampler),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create tracer: %+v\n", err)
|
||||
}
|
||||
|
||||
// tracer can now be used to create spans.
|
||||
span := tracer.StartSpan("some_operation")
|
||||
// ... do some work ...
|
||||
span.Finish()
|
||||
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleTracerOption() {
|
||||
// initialize the tracer and use the WithNoopSpan TracerOption
|
||||
tracer, _ := zipkin.NewTracer(
|
||||
reporter.NewNoopReporter(),
|
||||
zipkin.WithNoopSpan(true),
|
||||
)
|
||||
|
||||
// tracer can now be used to create spans
|
||||
span := tracer.StartSpan("some_operation")
|
||||
// ... do some work ...
|
||||
span.Finish()
|
||||
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleNewContext() {
|
||||
var (
|
||||
tracer, _ = zipkin.NewTracer(reporter.NewNoopReporter())
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
// span for this function
|
||||
span := tracer.StartSpan("ExampleNewContext")
|
||||
defer span.Finish()
|
||||
|
||||
// add span to Context
|
||||
ctx = zipkin.NewContext(ctx, span)
|
||||
|
||||
// pass along Context which holds the span to another function
|
||||
doSomeWork(ctx)
|
||||
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleSpanOption() {
|
||||
tracer, _ := zipkin.NewTracer(reporter.NewNoopReporter())
|
||||
|
||||
// set-up the remote endpoint for the service we're about to call
|
||||
endpoint, err := zipkin.NewEndpoint("otherService", "172.20.23.101:80")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create remote endpoint: %+v\n", err)
|
||||
}
|
||||
|
||||
// start a client side RPC span and use RemoteEndpoint SpanOption
|
||||
span := tracer.StartSpan(
|
||||
"some-operation",
|
||||
zipkin.RemoteEndpoint(endpoint),
|
||||
zipkin.Kind(model.Client),
|
||||
)
|
||||
// ... call other service ...
|
||||
span.Finish()
|
||||
|
||||
// Output:
|
||||
}
|
||||
116
vendor/github.com/openzipkin/zipkin-go/idgenerator/idgenerator.go
generated
vendored
Normal file
116
vendor/github.com/openzipkin/zipkin-go/idgenerator/idgenerator.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Package idgenerator contains several Span and Trace ID generators which can be
|
||||
used by the Zipkin tracer. Additional third party generators can be plugged in
|
||||
if they adhere to the IDGenerator interface.
|
||||
*/
|
||||
package idgenerator
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
var (
|
||||
seededIDGen = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// NewSource returns a new pseudo-random Source seeded with the given value.
|
||||
// Unlike the default Source used by top-level functions, this source is not
|
||||
// safe for concurrent use by multiple goroutines. Hence the need for a mutex.
|
||||
seededIDLock sync.Mutex
|
||||
)
|
||||
|
||||
// IDGenerator interface can be used to provide the Zipkin Tracer with custom
|
||||
// implementations to generate Span and Trace IDs.
|
||||
type IDGenerator interface {
|
||||
SpanID(traceID model.TraceID) model.ID // Generates a new Span ID
|
||||
TraceID() model.TraceID // Generates a new Trace ID
|
||||
}
|
||||
|
||||
// NewRandom64 returns an ID Generator which can generate 64 bit trace and span
|
||||
// id's
|
||||
func NewRandom64() IDGenerator {
|
||||
return &randomID64{}
|
||||
}
|
||||
|
||||
// NewRandom128 returns an ID Generator which can generate 128 bit trace and 64
|
||||
// bit span id's
|
||||
func NewRandom128() IDGenerator {
|
||||
return &randomID128{}
|
||||
}
|
||||
|
||||
// NewRandomTimestamped generates 128 bit time sortable traceid's and 64 bit
|
||||
// spanid's.
|
||||
func NewRandomTimestamped() IDGenerator {
|
||||
return &randomTimestamped{}
|
||||
}
|
||||
|
||||
// randomID64 can generate 64 bit traceid's and 64 bit spanid's.
|
||||
type randomID64 struct{}
|
||||
|
||||
func (r *randomID64) TraceID() (id model.TraceID) {
|
||||
seededIDLock.Lock()
|
||||
id = model.TraceID{
|
||||
Low: uint64(seededIDGen.Int63()),
|
||||
}
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *randomID64) SpanID(traceID model.TraceID) (id model.ID) {
|
||||
if !traceID.Empty() {
|
||||
return model.ID(traceID.Low)
|
||||
}
|
||||
seededIDLock.Lock()
|
||||
id = model.ID(seededIDGen.Int63())
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// randomID128 can generate 128 bit traceid's and 64 bit spanid's.
|
||||
type randomID128 struct{}
|
||||
|
||||
func (r *randomID128) TraceID() (id model.TraceID) {
|
||||
seededIDLock.Lock()
|
||||
id = model.TraceID{
|
||||
High: uint64(seededIDGen.Int63()),
|
||||
Low: uint64(seededIDGen.Int63()),
|
||||
}
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *randomID128) SpanID(traceID model.TraceID) (id model.ID) {
|
||||
if !traceID.Empty() {
|
||||
return model.ID(traceID.Low)
|
||||
}
|
||||
seededIDLock.Lock()
|
||||
id = model.ID(seededIDGen.Int63())
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// randomTimestamped can generate 128 bit time sortable traceid's compatible
|
||||
// with AWS X-Ray and 64 bit spanid's.
|
||||
type randomTimestamped struct{}
|
||||
|
||||
func (t *randomTimestamped) TraceID() (id model.TraceID) {
|
||||
seededIDLock.Lock()
|
||||
id = model.TraceID{
|
||||
High: uint64(time.Now().Unix()<<32) + uint64(seededIDGen.Int31()),
|
||||
Low: uint64(seededIDGen.Int63()),
|
||||
}
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (t *randomTimestamped) SpanID(traceID model.TraceID) (id model.ID) {
|
||||
if !traceID.Empty() {
|
||||
return model.ID(traceID.Low)
|
||||
}
|
||||
seededIDLock.Lock()
|
||||
id = model.ID(seededIDGen.Int63())
|
||||
seededIDLock.Unlock()
|
||||
return
|
||||
}
|
||||
116
vendor/github.com/openzipkin/zipkin-go/idgenerator/idgenerator_test.go
generated
vendored
Normal file
116
vendor/github.com/openzipkin/zipkin-go/idgenerator/idgenerator_test.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package idgenerator_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
func TestRandom64(t *testing.T) {
|
||||
var (
|
||||
spanID model.ID
|
||||
gen = idgenerator.NewRandom64()
|
||||
traceID = gen.TraceID()
|
||||
)
|
||||
|
||||
if traceID.Empty() {
|
||||
t.Errorf("Expected valid TraceID, got: %+v", traceID)
|
||||
}
|
||||
|
||||
if want, have := uint64(0), traceID.High; want != have {
|
||||
t.Errorf("Expected TraceID.High to be 0, got %d", have)
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(traceID)
|
||||
|
||||
if want, have := model.ID(traceID.Low), spanID; want != have {
|
||||
t.Errorf("Expected root span to have span ID %d, got %d", want, have)
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(model.TraceID{})
|
||||
|
||||
if spanID == 0 {
|
||||
t.Errorf("Expected child span to have a valid span ID, got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandom128(t *testing.T) {
|
||||
var (
|
||||
spanID model.ID
|
||||
gen = idgenerator.NewRandom128()
|
||||
traceID = gen.TraceID()
|
||||
)
|
||||
|
||||
if traceID.Empty() {
|
||||
t.Errorf("Expected valid TraceID, got: %+v", traceID)
|
||||
}
|
||||
|
||||
if traceID.Low == 0 {
|
||||
t.Error("Expected TraceID.Low to have value, got 0")
|
||||
}
|
||||
|
||||
if traceID.High == 0 {
|
||||
t.Error("Expected TraceID.High to have value, got 0")
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(traceID)
|
||||
|
||||
if want, have := model.ID(traceID.Low), spanID; want != have {
|
||||
t.Errorf("Expected root span to have span ID %d, got %d", want, have)
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(model.TraceID{})
|
||||
|
||||
if spanID == 0 {
|
||||
t.Errorf("Expected child span to have a valid span ID, got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomTimeStamped(t *testing.T) {
|
||||
var (
|
||||
spanID model.ID
|
||||
gen = idgenerator.NewRandomTimestamped()
|
||||
traceID = gen.TraceID()
|
||||
)
|
||||
|
||||
if traceID.Empty() {
|
||||
t.Errorf("Expected valid TraceID, got: %+v", traceID)
|
||||
}
|
||||
|
||||
if traceID.Low == 0 {
|
||||
t.Error("Expected TraceID.Low to have value, got 0")
|
||||
}
|
||||
|
||||
if traceID.High == 0 {
|
||||
t.Error("Expected TraceID.High to have value, got 0")
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(traceID)
|
||||
|
||||
if want, have := model.ID(traceID.Low), spanID; want != have {
|
||||
t.Errorf("Expected root span to have span ID %d, got %d", want, have)
|
||||
}
|
||||
|
||||
spanID = gen.SpanID(model.TraceID{})
|
||||
|
||||
if spanID == 0 {
|
||||
t.Errorf("Expected child span to have a valid span ID, got 0")
|
||||
}
|
||||
|
||||
// test chronological order
|
||||
var ids []model.TraceID
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
ids = append(ids, gen.TraceID())
|
||||
}
|
||||
|
||||
var latestTS uint64
|
||||
for idx, traceID := range ids {
|
||||
if new, old := traceID.High>>32, latestTS; new < old {
|
||||
t.Errorf("[%d] expected a higher timestamp part in traceid but got: old: %d new: %d", idx, old, new)
|
||||
}
|
||||
latestTS = traceID.High >> 32
|
||||
}
|
||||
|
||||
}
|
||||
131
vendor/github.com/openzipkin/zipkin-go/middleware/http/client.go
generated
vendored
Normal file
131
vendor/github.com/openzipkin/zipkin-go/middleware/http/client.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// ErrValidTracerRequired error
|
||||
var ErrValidTracerRequired = errors.New("valid tracer required")
|
||||
|
||||
// Client holds a Zipkin instrumented HTTP Client.
|
||||
type Client struct {
|
||||
*http.Client
|
||||
tracer *zipkin.Tracer
|
||||
httpTrace bool
|
||||
defaultTags map[string]string
|
||||
transportOptions []TransportOption
|
||||
}
|
||||
|
||||
// ClientOption allows optional configuration of Client.
|
||||
type ClientOption func(*Client)
|
||||
|
||||
// WithClient allows one to add a custom configured http.Client to use.
|
||||
func WithClient(client *http.Client) ClientOption {
|
||||
return func(c *Client) {
|
||||
if client == nil {
|
||||
client = &http.Client{}
|
||||
}
|
||||
c.Client = client
|
||||
}
|
||||
}
|
||||
|
||||
// ClientTrace allows one to enable Go's net/http/httptrace.
|
||||
func ClientTrace(enabled bool) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.httpTrace = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// ClientTags adds default Tags to inject into client application spans.
|
||||
func ClientTags(tags map[string]string) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.defaultTags = tags
|
||||
}
|
||||
}
|
||||
|
||||
// TransportOptions passes optional Transport configuration to the internal
|
||||
// transport used by Client.
|
||||
func TransportOptions(options ...TransportOption) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.transportOptions = options
|
||||
}
|
||||
}
|
||||
|
||||
// NewClient returns an HTTP Client adding Zipkin instrumentation around an
|
||||
// embedded standard Go http.Client.
|
||||
func NewClient(tracer *zipkin.Tracer, options ...ClientOption) (*Client, error) {
|
||||
if tracer == nil {
|
||||
return nil, ErrValidTracerRequired
|
||||
}
|
||||
|
||||
c := &Client{tracer: tracer, Client: &http.Client{}}
|
||||
for _, option := range options {
|
||||
option(c)
|
||||
}
|
||||
|
||||
c.transportOptions = append(
|
||||
c.transportOptions,
|
||||
// the following Client settings override provided transport settings.
|
||||
RoundTripper(c.Client.Transport),
|
||||
TransportTrace(c.httpTrace),
|
||||
)
|
||||
transport, err := NewTransport(tracer, c.transportOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Client.Transport = transport
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DoWithAppSpan wraps http.Client's Do with tracing using an application span.
|
||||
func (c *Client) DoWithAppSpan(req *http.Request, name string) (res *http.Response, err error) {
|
||||
var parentContext model.SpanContext
|
||||
|
||||
if span := zipkin.SpanFromContext(req.Context()); span != nil {
|
||||
parentContext = span.Context()
|
||||
}
|
||||
|
||||
appSpan := c.tracer.StartSpan(name, zipkin.Parent(parentContext))
|
||||
|
||||
zipkin.TagHTTPMethod.Set(appSpan, req.Method)
|
||||
zipkin.TagHTTPUrl.Set(appSpan, req.URL.String())
|
||||
zipkin.TagHTTPPath.Set(appSpan, req.URL.Path)
|
||||
|
||||
res, err = c.Client.Do(
|
||||
req.WithContext(zipkin.NewContext(req.Context(), appSpan)),
|
||||
)
|
||||
if err != nil {
|
||||
zipkin.TagError.Set(appSpan, err.Error())
|
||||
appSpan.Finish()
|
||||
return
|
||||
}
|
||||
|
||||
if c.httpTrace {
|
||||
appSpan.Annotate(time.Now(), "wr")
|
||||
}
|
||||
|
||||
if res.ContentLength > 0 {
|
||||
zipkin.TagHTTPResponseSize.Set(appSpan, strconv.FormatInt(res.ContentLength, 10))
|
||||
}
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
statusCode := strconv.FormatInt(int64(res.StatusCode), 10)
|
||||
zipkin.TagHTTPStatusCode.Set(appSpan, statusCode)
|
||||
if res.StatusCode > 399 {
|
||||
zipkin.TagError.Set(appSpan, statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
res.Body = &spanCloser{
|
||||
ReadCloser: res.Body,
|
||||
sp: appSpan,
|
||||
traceEnabled: c.httpTrace,
|
||||
}
|
||||
return
|
||||
}
|
||||
81
vendor/github.com/openzipkin/zipkin-go/middleware/http/client_test.go
generated
vendored
Normal file
81
vendor/github.com/openzipkin/zipkin-go/middleware/http/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package http_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
httpclient "github.com/openzipkin/zipkin-go/middleware/http"
|
||||
"github.com/openzipkin/zipkin-go/reporter/recorder"
|
||||
)
|
||||
|
||||
func TestHTTPClient(t *testing.T) {
|
||||
reporter := recorder.NewReporter()
|
||||
defer reporter.Close()
|
||||
|
||||
ep, _ := zipkin.NewEndpoint("httpClient", "")
|
||||
tracer, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(ep))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer: %+v", err)
|
||||
}
|
||||
|
||||
clientTags := map[string]string{
|
||||
"client": "testClient",
|
||||
}
|
||||
|
||||
transportTags := map[string]string{
|
||||
"conf.timeout": "default",
|
||||
}
|
||||
|
||||
client, err := httpclient.NewClient(
|
||||
tracer,
|
||||
httpclient.WithClient(&http.Client{}),
|
||||
httpclient.ClientTrace(true),
|
||||
httpclient.ClientTags(clientTags),
|
||||
httpclient.TransportOptions(httpclient.TransportTags(transportTags)),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create http client: %+v", err)
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://www.google.com", nil)
|
||||
|
||||
res, err := client.DoWithAppSpan(req, "Get Google")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to execute client request: %+v", err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
spans := reporter.Flush()
|
||||
if len(spans) < 2 {
|
||||
t.Errorf("Span Count want 2+, have %d", len(spans))
|
||||
}
|
||||
|
||||
req, _ = http.NewRequest("GET", "https://www.google.com", nil)
|
||||
|
||||
res, err = client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to execute client request: %+v", err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
spans = reporter.Flush()
|
||||
if len(spans) == 0 {
|
||||
t.Errorf("Span Count want 1+, have 0")
|
||||
}
|
||||
|
||||
span := tracer.StartSpan("ParentSpan")
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://www.google.com", nil)
|
||||
|
||||
ctx := zipkin.NewContext(req.Context(), span)
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
res, err = client.DoWithAppSpan(req, "ChildSpan")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to execute client request: %+v", err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
}
|
||||
5
vendor/github.com/openzipkin/zipkin-go/middleware/http/doc.go
generated
vendored
Normal file
5
vendor/github.com/openzipkin/zipkin-go/middleware/http/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
Package http contains several http middlewares which can be used for
|
||||
instrumenting calls with Zipkin.
|
||||
*/
|
||||
package http
|
||||
149
vendor/github.com/openzipkin/zipkin-go/middleware/http/server.go
generated
vendored
Normal file
149
vendor/github.com/openzipkin/zipkin-go/middleware/http/server.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
tracer *zipkin.Tracer
|
||||
name string
|
||||
next http.Handler
|
||||
tagResponseSize bool
|
||||
defaultTags map[string]string
|
||||
}
|
||||
|
||||
// ServerOption allows Middleware to be optionally configured.
|
||||
type ServerOption func(*handler)
|
||||
|
||||
// ServerTags adds default Tags to inject into server spans.
|
||||
func ServerTags(tags map[string]string) ServerOption {
|
||||
return func(h *handler) {
|
||||
h.defaultTags = tags
|
||||
}
|
||||
}
|
||||
|
||||
// TagResponseSize will instruct the middleware to Tag the http response size
|
||||
// in the server side span.
|
||||
func TagResponseSize(enabled bool) ServerOption {
|
||||
return func(h *handler) {
|
||||
h.tagResponseSize = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// SpanName sets the name of the spans the middleware creates. Use this if
|
||||
// wrapping each endpoint with its own Middleware.
|
||||
// If omitting the SpanName option, the middleware will use the http request
|
||||
// method as span name.
|
||||
func SpanName(name string) ServerOption {
|
||||
return func(h *handler) {
|
||||
h.name = name
|
||||
}
|
||||
}
|
||||
|
||||
// NewServerMiddleware returns a http.Handler middleware with Zipkin tracing.
|
||||
func NewServerMiddleware(t *zipkin.Tracer, options ...ServerOption) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
h := &handler{
|
||||
tracer: t,
|
||||
next: next,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var spanName string
|
||||
|
||||
// try to extract B3 Headers from upstream
|
||||
sc := h.tracer.Extract(b3.ExtractHTTP(r))
|
||||
|
||||
remoteEndpoint, _ := zipkin.NewEndpoint("", r.RemoteAddr)
|
||||
|
||||
if len(h.name) == 0 {
|
||||
spanName = r.Method
|
||||
} else {
|
||||
spanName = h.name
|
||||
}
|
||||
|
||||
// create Span using SpanContext if found
|
||||
sp := h.tracer.StartSpan(
|
||||
spanName,
|
||||
zipkin.Kind(model.Server),
|
||||
zipkin.Parent(sc),
|
||||
zipkin.RemoteEndpoint(remoteEndpoint),
|
||||
)
|
||||
|
||||
for k, v := range h.defaultTags {
|
||||
sp.Tag(k, v)
|
||||
}
|
||||
|
||||
// add our span to context
|
||||
ctx := zipkin.NewContext(r.Context(), sp)
|
||||
|
||||
// tag typical HTTP request items
|
||||
zipkin.TagHTTPMethod.Set(sp, r.Method)
|
||||
zipkin.TagHTTPUrl.Set(sp, r.URL.String())
|
||||
zipkin.TagHTTPRequestSize.Set(sp, strconv.FormatInt(r.ContentLength, 10))
|
||||
|
||||
// create http.ResponseWriter interceptor for tracking response size and
|
||||
// status code.
|
||||
ri := &rwInterceptor{w: w, statusCode: 200}
|
||||
|
||||
// tag found response size and status code on exit
|
||||
defer func() {
|
||||
code := ri.getStatusCode()
|
||||
sCode := strconv.Itoa(code)
|
||||
if code > 399 {
|
||||
zipkin.TagError.Set(sp, sCode)
|
||||
}
|
||||
zipkin.TagHTTPStatusCode.Set(sp, sCode)
|
||||
if h.tagResponseSize {
|
||||
zipkin.TagHTTPResponseSize.Set(sp, ri.getResponseSize())
|
||||
}
|
||||
sp.Finish()
|
||||
}()
|
||||
|
||||
// call next http Handler func using our updated context.
|
||||
h.next.ServeHTTP(ri, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// rwInterceptor intercepts the ResponseWriter so it can track response size
|
||||
// and returned status code.
|
||||
type rwInterceptor struct {
|
||||
w http.ResponseWriter
|
||||
size uint64
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (r *rwInterceptor) Header() http.Header {
|
||||
return r.w.Header()
|
||||
}
|
||||
|
||||
func (r *rwInterceptor) Write(b []byte) (n int, err error) {
|
||||
n, err = r.w.Write(b)
|
||||
atomic.AddUint64(&r.size, uint64(n))
|
||||
return
|
||||
}
|
||||
|
||||
func (r *rwInterceptor) WriteHeader(i int) {
|
||||
r.statusCode = i
|
||||
r.w.WriteHeader(i)
|
||||
}
|
||||
|
||||
func (r *rwInterceptor) getStatusCode() int {
|
||||
return r.statusCode
|
||||
}
|
||||
|
||||
func (r *rwInterceptor) getResponseSize() string {
|
||||
return strconv.FormatUint(atomic.LoadUint64(&r.size), 10)
|
||||
}
|
||||
141
vendor/github.com/openzipkin/zipkin-go/middleware/http/server_test.go
generated
vendored
Normal file
141
vendor/github.com/openzipkin/zipkin-go/middleware/http/server_test.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package http_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
mw "github.com/openzipkin/zipkin-go/middleware/http"
|
||||
"github.com/openzipkin/zipkin-go/reporter/recorder"
|
||||
)
|
||||
|
||||
var (
|
||||
lep, _ = zipkin.NewEndpoint("testSvc", "127.0.0.1:0")
|
||||
)
|
||||
|
||||
func httpHandler(code int, headers http.Header, body *bytes.Buffer) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(code)
|
||||
for key, value := range headers {
|
||||
w.Header().Add(key, value[0])
|
||||
}
|
||||
w.Write(body.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPHandlerWrapping(t *testing.T) {
|
||||
var (
|
||||
spanRecorder = &recorder.ReporterRecorder{}
|
||||
tr, _ = zipkin.NewTracer(spanRecorder, zipkin.WithLocalEndpoint(lep))
|
||||
httpRecorder = httptest.NewRecorder()
|
||||
requestBuf = bytes.NewBufferString("incoming data")
|
||||
responseBuf = bytes.NewBufferString("oh oh we have a 404")
|
||||
headers = make(http.Header)
|
||||
spanName = "wrapper_test"
|
||||
code = 404
|
||||
)
|
||||
headers.Add("some-key", "some-value")
|
||||
headers.Add("other-key", "other-value")
|
||||
|
||||
request, err := http.NewRequest("POST", "/test", requestBuf)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create request")
|
||||
}
|
||||
|
||||
httpHandlerFunc := http.HandlerFunc(httpHandler(code, headers, responseBuf))
|
||||
|
||||
tags := map[string]string{
|
||||
"component": "testServer",
|
||||
}
|
||||
handler := mw.NewServerMiddleware(
|
||||
tr,
|
||||
mw.SpanName(spanName),
|
||||
mw.TagResponseSize(true),
|
||||
mw.ServerTags(tags),
|
||||
)(httpHandlerFunc)
|
||||
|
||||
handler.ServeHTTP(httpRecorder, request)
|
||||
|
||||
spans := spanRecorder.Flush()
|
||||
|
||||
if want, have := 1, len(spans); want != have {
|
||||
t.Errorf("Expected %d spans, got %d", want, have)
|
||||
}
|
||||
|
||||
span := spans[0]
|
||||
|
||||
if want, have := spanName, span.Name; want != have {
|
||||
t.Errorf("Expected span name %s, got %s", want, have)
|
||||
}
|
||||
|
||||
if want, have := strconv.Itoa(requestBuf.Len()), span.Tags["http.request.size"]; want != have {
|
||||
t.Errorf("Expected span request size %s, got %s", want, have)
|
||||
}
|
||||
|
||||
if want, have := strconv.Itoa(responseBuf.Len()), span.Tags["http.response.size"]; want != have {
|
||||
t.Errorf("Expected span response size %s, got %s", want, have)
|
||||
|
||||
}
|
||||
|
||||
if want, have := strconv.Itoa(code), span.Tags["http.status_code"]; want != have {
|
||||
t.Errorf("Expected span status code %s, got %s", want, have)
|
||||
}
|
||||
|
||||
if want, have := strconv.Itoa(code), span.Tags["error"]; want != have {
|
||||
t.Errorf("Expected span error %q, got %q", want, have)
|
||||
}
|
||||
|
||||
if want, have := len(headers), len(httpRecorder.HeaderMap); want != have {
|
||||
t.Errorf("Expected http header count %d, got %d", want, have)
|
||||
}
|
||||
|
||||
if want, have := code, httpRecorder.Code; want != have {
|
||||
t.Errorf("Expected http status code %d, got %d", want, have)
|
||||
}
|
||||
|
||||
for key, value := range headers {
|
||||
if want, have := value, httpRecorder.HeaderMap.Get(key); want[0] != have {
|
||||
t.Errorf("Expected header %s value %s, got %s", key, want, have)
|
||||
}
|
||||
}
|
||||
|
||||
if want, have := responseBuf.String(), httpRecorder.Body.String(); want != have {
|
||||
t.Errorf("Expected body value %q, got %q", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPDefaultSpanName(t *testing.T) {
|
||||
var (
|
||||
spanRecorder = &recorder.ReporterRecorder{}
|
||||
tr, _ = zipkin.NewTracer(spanRecorder, zipkin.WithLocalEndpoint(lep))
|
||||
httpRecorder = httptest.NewRecorder()
|
||||
requestBuf = bytes.NewBufferString("incoming data")
|
||||
methodType = "POST"
|
||||
)
|
||||
|
||||
request, err := http.NewRequest(methodType, "/test", requestBuf)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create request")
|
||||
}
|
||||
|
||||
httpHandlerFunc := http.HandlerFunc(httpHandler(200, nil, bytes.NewBufferString("")))
|
||||
|
||||
handler := mw.NewServerMiddleware(tr)(httpHandlerFunc)
|
||||
|
||||
handler.ServeHTTP(httpRecorder, request)
|
||||
|
||||
spans := spanRecorder.Flush()
|
||||
|
||||
if want, have := 1, len(spans); want != have {
|
||||
t.Errorf("Expected %d spans, got %d", want, have)
|
||||
}
|
||||
|
||||
span := spans[0]
|
||||
|
||||
if want, have := methodType, span.Name; want != have {
|
||||
t.Errorf("Expected span name %s, got %s", want, have)
|
||||
}
|
||||
}
|
||||
23
vendor/github.com/openzipkin/zipkin-go/middleware/http/spancloser.go
generated
vendored
Normal file
23
vendor/github.com/openzipkin/zipkin-go/middleware/http/spancloser.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
)
|
||||
|
||||
type spanCloser struct {
|
||||
io.ReadCloser
|
||||
sp zipkin.Span
|
||||
traceEnabled bool
|
||||
}
|
||||
|
||||
func (s *spanCloser) Close() (err error) {
|
||||
if s.traceEnabled {
|
||||
s.sp.Annotate(time.Now(), "Body Close")
|
||||
}
|
||||
err = s.ReadCloser.Close()
|
||||
s.sp.Finish()
|
||||
return
|
||||
}
|
||||
103
vendor/github.com/openzipkin/zipkin-go/middleware/http/spantrace.go
generated
vendored
Normal file
103
vendor/github.com/openzipkin/zipkin-go/middleware/http/spantrace.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http/httptrace"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
)
|
||||
|
||||
type spanTrace struct {
|
||||
zipkin.Span
|
||||
c *httptrace.ClientTrace
|
||||
}
|
||||
|
||||
func (s *spanTrace) getConn(hostPort string) {
|
||||
s.Annotate(time.Now(), "Connecting")
|
||||
s.Tag("httptrace.get_connection.host_port", hostPort)
|
||||
}
|
||||
|
||||
func (s *spanTrace) gotConn(info httptrace.GotConnInfo) {
|
||||
s.Annotate(time.Now(), "Connected")
|
||||
s.Tag("httptrace.got_connection.reused", fmt.Sprintf("%t", info.Reused))
|
||||
s.Tag("httptrace.got_connection.was_idle", fmt.Sprintf("%t", info.WasIdle))
|
||||
if info.WasIdle {
|
||||
s.Tag("httptrace.got_connection.idle_time", info.IdleTime.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanTrace) putIdleConn(err error) {
|
||||
s.Annotate(time.Now(), "Put Idle Connection")
|
||||
if err != nil {
|
||||
s.Tag("httptrace.put_idle_connection.error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanTrace) gotFirstResponseByte() {
|
||||
s.Annotate(time.Now(), "First Response Byte")
|
||||
}
|
||||
|
||||
func (s *spanTrace) got100Continue() {
|
||||
s.Annotate(time.Now(), "Got 100 Continue")
|
||||
}
|
||||
|
||||
func (s *spanTrace) dnsStart(info httptrace.DNSStartInfo) {
|
||||
s.Annotate(time.Now(), "DNS Start")
|
||||
s.Tag("httptrace.dns_start.host", info.Host)
|
||||
}
|
||||
|
||||
func (s *spanTrace) dnsDone(info httptrace.DNSDoneInfo) {
|
||||
s.Annotate(time.Now(), "DNS Done")
|
||||
var addrs []string
|
||||
for _, addr := range info.Addrs {
|
||||
addrs = append(addrs, addr.String())
|
||||
}
|
||||
s.Tag("httptrace.dns_done.addrs", strings.Join(addrs, " , "))
|
||||
if info.Err != nil {
|
||||
s.Tag("httptrace.dns_done.error", info.Err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanTrace) connectStart(network, addr string) {
|
||||
s.Annotate(time.Now(), "Connect Start")
|
||||
s.Tag("httptrace.connect_start.network", network)
|
||||
s.Tag("httptrace.connect_start.addr", addr)
|
||||
}
|
||||
|
||||
func (s *spanTrace) connectDone(network, addr string, err error) {
|
||||
s.Annotate(time.Now(), "Connect Done")
|
||||
s.Tag("httptrace.connect_done.network", network)
|
||||
s.Tag("httptrace.connect_done.addr", addr)
|
||||
if err != nil {
|
||||
s.Tag("httptrace.connect_done.error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanTrace) tlsHandshakeStart() {
|
||||
s.Annotate(time.Now(), "TLS Handshake Start")
|
||||
}
|
||||
|
||||
func (s *spanTrace) tlsHandshakeDone(_ tls.ConnectionState, err error) {
|
||||
s.Annotate(time.Now(), "TLS Handshake Done")
|
||||
if err != nil {
|
||||
s.Tag("httptrace.tls_handshake_done.error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanTrace) wroteHeaders() {
|
||||
s.Annotate(time.Now(), "Wrote Headers")
|
||||
}
|
||||
|
||||
func (s *spanTrace) wait100Continue() {
|
||||
s.Annotate(time.Now(), "Wait 100 Continue")
|
||||
}
|
||||
|
||||
func (s *spanTrace) wroteRequest(info httptrace.WroteRequestInfo) {
|
||||
s.Annotate(time.Now(), "Wrote Request")
|
||||
if info.Err != nil {
|
||||
s.Tag("httptrace.wrote_request.error", info.Err.Error())
|
||||
}
|
||||
}
|
||||
128
vendor/github.com/openzipkin/zipkin-go/middleware/http/transport.go
generated
vendored
Normal file
128
vendor/github.com/openzipkin/zipkin-go/middleware/http/transport.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"strconv"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
)
|
||||
|
||||
type transport struct {
|
||||
tracer *zipkin.Tracer
|
||||
rt http.RoundTripper
|
||||
httpTrace bool
|
||||
defaultTags map[string]string
|
||||
}
|
||||
|
||||
// TransportOption allows one to configure optional transport configuration.
|
||||
type TransportOption func(*transport)
|
||||
|
||||
// RoundTripper adds the Transport RoundTripper to wrap.
|
||||
func RoundTripper(rt http.RoundTripper) TransportOption {
|
||||
return func(t *transport) {
|
||||
if rt != nil {
|
||||
t.rt = rt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TransportTags adds default Tags to inject into transport spans.
|
||||
func TransportTags(tags map[string]string) TransportOption {
|
||||
return func(t *transport) {
|
||||
t.defaultTags = tags
|
||||
}
|
||||
}
|
||||
|
||||
// TransportTrace allows one to enable Go's net/http/httptrace.
|
||||
func TransportTrace(enable bool) TransportOption {
|
||||
return func(t *transport) {
|
||||
t.httpTrace = enable
|
||||
}
|
||||
}
|
||||
|
||||
// NewTransport returns a new Zipkin instrumented http RoundTripper which can be
|
||||
// used with a standard library http Client.
|
||||
func NewTransport(tracer *zipkin.Tracer, options ...TransportOption) (http.RoundTripper, error) {
|
||||
if tracer == nil {
|
||||
return nil, ErrValidTracerRequired
|
||||
}
|
||||
|
||||
t := &transport{
|
||||
tracer: tracer,
|
||||
rt: http.DefaultTransport,
|
||||
httpTrace: false,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(t)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// RoundTrip satisfies the RoundTripper interface.
|
||||
func (t *transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
||||
sp, _ := t.tracer.StartSpanFromContext(
|
||||
req.Context(), req.URL.Scheme+"/"+req.Method, zipkin.Kind(model.Client),
|
||||
)
|
||||
|
||||
for k, v := range t.defaultTags {
|
||||
sp.Tag(k, v)
|
||||
}
|
||||
|
||||
if t.httpTrace {
|
||||
sptr := spanTrace{
|
||||
Span: sp,
|
||||
}
|
||||
sptr.c = &httptrace.ClientTrace{
|
||||
GetConn: sptr.getConn,
|
||||
GotConn: sptr.gotConn,
|
||||
PutIdleConn: sptr.putIdleConn,
|
||||
GotFirstResponseByte: sptr.gotFirstResponseByte,
|
||||
Got100Continue: sptr.got100Continue,
|
||||
DNSStart: sptr.dnsStart,
|
||||
DNSDone: sptr.dnsDone,
|
||||
ConnectStart: sptr.connectStart,
|
||||
ConnectDone: sptr.connectDone,
|
||||
TLSHandshakeStart: sptr.tlsHandshakeStart,
|
||||
TLSHandshakeDone: sptr.tlsHandshakeDone,
|
||||
WroteHeaders: sptr.wroteHeaders,
|
||||
Wait100Continue: sptr.wait100Continue,
|
||||
WroteRequest: sptr.wroteRequest,
|
||||
}
|
||||
|
||||
req = req.WithContext(
|
||||
httptrace.WithClientTrace(req.Context(), sptr.c),
|
||||
)
|
||||
}
|
||||
|
||||
zipkin.TagHTTPMethod.Set(sp, req.Method)
|
||||
zipkin.TagHTTPUrl.Set(sp, req.URL.String())
|
||||
zipkin.TagHTTPPath.Set(sp, req.URL.Path)
|
||||
|
||||
_ = b3.InjectHTTP(req)(sp.Context())
|
||||
|
||||
res, err = t.rt.RoundTrip(req)
|
||||
|
||||
if err != nil {
|
||||
zipkin.TagError.Set(sp, err.Error())
|
||||
sp.Finish()
|
||||
return
|
||||
}
|
||||
|
||||
if res.ContentLength > 0 {
|
||||
zipkin.TagHTTPResponseSize.Set(sp, strconv.FormatInt(res.ContentLength, 10))
|
||||
}
|
||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
||||
statusCode := strconv.FormatInt(int64(res.StatusCode), 10)
|
||||
zipkin.TagHTTPStatusCode.Set(sp, statusCode)
|
||||
if res.StatusCode > 399 {
|
||||
zipkin.TagError.Set(sp, statusCode)
|
||||
}
|
||||
}
|
||||
sp.Finish()
|
||||
return
|
||||
}
|
||||
46
vendor/github.com/openzipkin/zipkin-go/model/annotation.go
generated
vendored
Normal file
46
vendor/github.com/openzipkin/zipkin-go/model/annotation.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrValidTimestampRequired error
|
||||
var ErrValidTimestampRequired = errors.New("valid annotation timestamp required")
|
||||
|
||||
// Annotation associates an event that explains latency with a timestamp.
|
||||
type Annotation struct {
|
||||
Timestamp time.Time
|
||||
Value string
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON encoding
|
||||
func (a *Annotation) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Value string `json:"value"`
|
||||
}{
|
||||
Timestamp: a.Timestamp.Round(time.Microsecond).UnixNano() / 1e3,
|
||||
Value: a.Value,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON decoding
|
||||
func (a *Annotation) UnmarshalJSON(b []byte) error {
|
||||
type Alias Annotation
|
||||
annotation := &struct {
|
||||
TimeStamp uint64 `json:"timestamp"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(a),
|
||||
}
|
||||
if err := json.Unmarshal(b, &annotation); err != nil {
|
||||
return err
|
||||
}
|
||||
if annotation.TimeStamp < 1 {
|
||||
return ErrValidTimestampRequired
|
||||
}
|
||||
a.Timestamp = time.Unix(0, int64(annotation.TimeStamp)*1e3)
|
||||
return nil
|
||||
}
|
||||
22
vendor/github.com/openzipkin/zipkin-go/model/annotation_test.go
generated
vendored
Normal file
22
vendor/github.com/openzipkin/zipkin-go/model/annotation_test.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAnnotationNegativeTimestamp(t *testing.T) {
|
||||
var (
|
||||
span SpanModel
|
||||
b1 = []byte(`{"annotations":[{"timestamp":-1}]}`)
|
||||
b2 = []byte(`{"annotations":[{"timestamp":0}]}`)
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(b1, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b2, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/openzipkin/zipkin-go/model/doc.go
generated
vendored
Normal file
9
vendor/github.com/openzipkin/zipkin-go/model/doc.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
Package model contains the Zipkin V2 model which is used by the Zipkin Go
|
||||
tracer implementation.
|
||||
|
||||
Third party instrumentation libraries can use the model and transport packages
|
||||
found in this Zipkin Go library to directly interface with the Zipkin Server or
|
||||
Zipkin Collectors without the need to use the tracer implementation itself.
|
||||
*/
|
||||
package model
|
||||
17
vendor/github.com/openzipkin/zipkin-go/model/endpoint.go
generated
vendored
Normal file
17
vendor/github.com/openzipkin/zipkin-go/model/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
import "net"
|
||||
|
||||
// Endpoint holds the network context of a node in the service graph.
|
||||
type Endpoint struct {
|
||||
ServiceName string `json:"serviceName,omitempty"`
|
||||
IPv4 net.IP `json:"ipv4,omitempty"`
|
||||
IPv6 net.IP `json:"ipv6,omitempty"`
|
||||
Port uint16 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// Empty returns if all Endpoint properties are empty / unspecified.
|
||||
func (e *Endpoint) Empty() bool {
|
||||
return e == nil ||
|
||||
(e.ServiceName == "" && e.Port == 0 && len(e.IPv4) == 0 && len(e.IPv6) == 0)
|
||||
}
|
||||
38
vendor/github.com/openzipkin/zipkin-go/model/endpoint_test.go
generated
vendored
Normal file
38
vendor/github.com/openzipkin/zipkin-go/model/endpoint_test.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package model_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
func TestEmptyEndpoint(t *testing.T) {
|
||||
var e *model.Endpoint
|
||||
|
||||
if want, have := true, e.Empty(); want != have {
|
||||
t.Errorf("Endpoint want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
e = &model.Endpoint{}
|
||||
|
||||
if want, have := true, e.Empty(); want != have {
|
||||
t.Errorf("Endpoint want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
e = &model.Endpoint{
|
||||
IPv4: net.IPv4zero,
|
||||
}
|
||||
|
||||
if want, have := false, e.Empty(); want != have {
|
||||
t.Errorf("Endpoint want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
e = &model.Endpoint{
|
||||
IPv6: net.IPv6zero,
|
||||
}
|
||||
|
||||
if want, have := false, e.Empty(); want != have {
|
||||
t.Errorf("Endpoint want %t, have %t", want, have)
|
||||
}
|
||||
}
|
||||
13
vendor/github.com/openzipkin/zipkin-go/model/kind.go
generated
vendored
Normal file
13
vendor/github.com/openzipkin/zipkin-go/model/kind.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
// Kind clarifies context of timestamp, duration and remoteEndpoint in a span.
|
||||
type Kind string
|
||||
|
||||
// Available Kind values
|
||||
const (
|
||||
Undetermined Kind = ""
|
||||
Client Kind = "CLIENT"
|
||||
Server Kind = "SERVER"
|
||||
Producer Kind = "PRODUCER"
|
||||
Consumer Kind = "CONSUMER"
|
||||
)
|
||||
124
vendor/github.com/openzipkin/zipkin-go/model/span.go
generated
vendored
Normal file
124
vendor/github.com/openzipkin/zipkin-go/model/span.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// unmarshal errors
|
||||
var (
|
||||
ErrValidTraceIDRequired = errors.New("valid traceId required")
|
||||
ErrValidIDRequired = errors.New("valid span id required")
|
||||
ErrValidDurationRequired = errors.New("valid duration required")
|
||||
)
|
||||
|
||||
// SpanContext holds the context of a Span.
|
||||
type SpanContext struct {
|
||||
TraceID TraceID `json:"traceId"`
|
||||
ID ID `json:"id"`
|
||||
ParentID *ID `json:"parentId,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
Sampled *bool `json:"-"`
|
||||
Err error `json:"-"`
|
||||
}
|
||||
|
||||
// SpanModel structure.
|
||||
//
|
||||
// If using this library to instrument your application you will not need to
|
||||
// directly access or modify this representation. The SpanModel is exported for
|
||||
// use cases involving 3rd party Go instrumentation libraries desiring to
|
||||
// export data to a Zipkin server using the Zipkin V2 Span model.
|
||||
type SpanModel struct {
|
||||
SpanContext
|
||||
Name string `json:"name,omitempty"`
|
||||
Kind Kind `json:"kind,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp,omitempty"`
|
||||
Duration time.Duration `json:"duration,omitempty"`
|
||||
Shared bool `json:"shared,omitempty"`
|
||||
LocalEndpoint *Endpoint `json:"localEndpoint,omitempty"`
|
||||
RemoteEndpoint *Endpoint `json:"remoteEndpoint,omitempty"`
|
||||
Annotations []Annotation `json:"annotations,omitempty"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON exports our Model into the correct format for the Zipkin V2 API.
|
||||
func (s SpanModel) MarshalJSON() ([]byte, error) {
|
||||
type Alias SpanModel
|
||||
|
||||
var timestamp int64
|
||||
if !s.Timestamp.IsZero() {
|
||||
if s.Timestamp.Unix() < 1 {
|
||||
// Zipkin does not allow Timestamps before Unix epoch
|
||||
return nil, ErrValidTimestampRequired
|
||||
}
|
||||
timestamp = s.Timestamp.Round(time.Microsecond).UnixNano() / 1e3
|
||||
}
|
||||
|
||||
if s.Duration < time.Microsecond {
|
||||
if s.Duration < 0 {
|
||||
// negative duration is not allowed and signals a timing logic error
|
||||
return nil, ErrValidDurationRequired
|
||||
} else if s.Duration > 0 {
|
||||
// sub microsecond durations are reported as 1 microsecond
|
||||
s.Duration = 1 * time.Microsecond
|
||||
}
|
||||
} else {
|
||||
// Duration will be rounded to nearest microsecond representation.
|
||||
//
|
||||
// NOTE: Duration.Round() is not available in Go 1.8 which we still support.
|
||||
// To handle microsecond resolution rounding we'll add 500 nanoseconds to
|
||||
// the duration. When truncated to microseconds in the call to marshal, it
|
||||
// will be naturally rounded. See TestSpanDurationRounding in span_test.go
|
||||
s.Duration += 500 * time.Nanosecond
|
||||
}
|
||||
|
||||
if s.LocalEndpoint.Empty() {
|
||||
s.LocalEndpoint = nil
|
||||
}
|
||||
|
||||
if s.RemoteEndpoint.Empty() {
|
||||
s.RemoteEndpoint = nil
|
||||
}
|
||||
|
||||
return json.Marshal(&struct {
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
Duration int64 `json:"duration,omitempty"`
|
||||
Alias
|
||||
}{
|
||||
Timestamp: timestamp,
|
||||
Duration: s.Duration.Nanoseconds() / 1e3,
|
||||
Alias: (Alias)(s),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON imports our Model from a Zipkin V2 API compatible span
|
||||
// representation.
|
||||
func (s *SpanModel) UnmarshalJSON(b []byte) error {
|
||||
type Alias SpanModel
|
||||
span := &struct {
|
||||
TimeStamp uint64 `json:"timestamp,omitempty"`
|
||||
Duration uint64 `json:"duration,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(s),
|
||||
}
|
||||
if err := json.Unmarshal(b, &span); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.ID < 1 {
|
||||
return ErrValidIDRequired
|
||||
}
|
||||
if span.TimeStamp > 0 {
|
||||
s.Timestamp = time.Unix(0, int64(span.TimeStamp)*1e3)
|
||||
}
|
||||
s.Duration = time.Duration(span.Duration*1e3) * time.Nanosecond
|
||||
if s.LocalEndpoint.Empty() {
|
||||
s.LocalEndpoint = nil
|
||||
}
|
||||
|
||||
if s.RemoteEndpoint.Empty() {
|
||||
s.RemoteEndpoint = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
30
vendor/github.com/openzipkin/zipkin-go/model/span_id.go
generated
vendored
Normal file
30
vendor/github.com/openzipkin/zipkin-go/model/span_id.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ID type
|
||||
type ID uint64
|
||||
|
||||
// String outputs the 64-bit ID as hex string.
|
||||
func (i ID) String() string {
|
||||
return fmt.Sprintf("%016x", uint64(i))
|
||||
}
|
||||
|
||||
// MarshalJSON serializes an ID type (SpanID, ParentSpanID) to HEX.
|
||||
func (i ID) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%q", i.String())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserializes an ID type (SpanID, ParentSpanID) from HEX.
|
||||
func (i *ID) UnmarshalJSON(b []byte) (err error) {
|
||||
var id uint64
|
||||
if len(b) < 3 {
|
||||
return nil
|
||||
}
|
||||
id, err = strconv.ParseUint(string(b[1:len(b)-1]), 16, 64)
|
||||
*i = ID(id)
|
||||
return err
|
||||
}
|
||||
242
vendor/github.com/openzipkin/zipkin-go/model/span_test.go
generated
vendored
Normal file
242
vendor/github.com/openzipkin/zipkin-go/model/span_test.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSpanJSON(t *testing.T) {
|
||||
var (
|
||||
span1 SpanModel
|
||||
span2 SpanModel
|
||||
parentID = ID(1003)
|
||||
sampled = true
|
||||
tags = make(map[string]string)
|
||||
)
|
||||
tags["myKey"] = "myValue"
|
||||
tags["another"] = "tag"
|
||||
|
||||
span1 = SpanModel{
|
||||
SpanContext: SpanContext{
|
||||
TraceID: TraceID{
|
||||
High: 1001,
|
||||
Low: 1002,
|
||||
},
|
||||
ID: ID(1004),
|
||||
ParentID: &parentID,
|
||||
Debug: true,
|
||||
Sampled: &sampled,
|
||||
Err: errors.New("dummy"),
|
||||
},
|
||||
Name: "myMethod",
|
||||
Kind: Server,
|
||||
Timestamp: time.Now().Add(-100 * time.Millisecond),
|
||||
Duration: 50 * time.Millisecond,
|
||||
Shared: true,
|
||||
LocalEndpoint: &Endpoint{
|
||||
ServiceName: "myService",
|
||||
IPv4: net.IPv4(127, 0, 0, 1),
|
||||
IPv6: net.IPv6loopback,
|
||||
},
|
||||
RemoteEndpoint: nil,
|
||||
Annotations: []Annotation{
|
||||
{time.Now().Add(-90 * time.Millisecond), "myAnnotation"},
|
||||
},
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(&span1)
|
||||
if err != nil {
|
||||
t.Errorf("expected successful serialization to JSON, got error: %+v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &span2)
|
||||
if err != nil {
|
||||
t.Errorf("expected successful deserialization from JSON, got error: %+v", err)
|
||||
}
|
||||
|
||||
/* remove items from span1 which should not have exported */
|
||||
span1.Sampled = nil
|
||||
span1.Err = nil
|
||||
|
||||
// trim resolution back to microseconds (Zipkin's smallest time unit)
|
||||
span1.Timestamp = span1.Timestamp.Round(time.Microsecond)
|
||||
for idx := range span1.Annotations {
|
||||
span1.Annotations[idx].Timestamp = span1.Annotations[idx].Timestamp.Round(time.Microsecond)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(span1, span2) {
|
||||
t.Errorf("want SpanModel: %+v, have: %+v", span1, span2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyTraceID(t *testing.T) {
|
||||
var (
|
||||
span SpanModel
|
||||
b = []byte(`{"traceId":"","id":"1"}`)
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(b, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptySpanID(t *testing.T) {
|
||||
var (
|
||||
span SpanModel
|
||||
b = []byte(`{"traceId":"1","id":""}`)
|
||||
)
|
||||
|
||||
if err := json.Unmarshal(b, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanEmptyTimeStamp(t *testing.T) {
|
||||
var (
|
||||
span1 SpanModel
|
||||
span2 SpanModel
|
||||
ts time.Time
|
||||
)
|
||||
|
||||
span1 = SpanModel{
|
||||
SpanContext: SpanContext{
|
||||
TraceID: TraceID{
|
||||
Low: 1,
|
||||
},
|
||||
ID: 1,
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(span1)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to marshal span: %+v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &span2); err != nil {
|
||||
t.Fatalf("unable to unmarshal span: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := ts, span2.Timestamp; want != have {
|
||||
t.Errorf("Timestamp want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanDurationRounding(t *testing.T) {
|
||||
durations := []struct {
|
||||
nano time.Duration
|
||||
micro time.Duration
|
||||
}{
|
||||
{0, 0},
|
||||
{1, 1000},
|
||||
{999, 1000},
|
||||
{1000, 1000},
|
||||
{1001, 1000},
|
||||
{1499, 1000},
|
||||
{1500, 2000},
|
||||
{2000, 2000},
|
||||
{2001, 2000},
|
||||
{2499, 2000},
|
||||
{2500, 3000},
|
||||
{2999, 3000},
|
||||
{3000, 3000},
|
||||
}
|
||||
|
||||
for i, duration := range durations {
|
||||
span := SpanModel{
|
||||
SpanContext: SpanContext{
|
||||
TraceID: TraceID{Low: 1},
|
||||
ID: ID(1),
|
||||
},
|
||||
Timestamp: time.Now(),
|
||||
Duration: duration.nano,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(span)
|
||||
if err != nil {
|
||||
t.Fatalf("span marshal failed: %+v", err)
|
||||
}
|
||||
|
||||
span2 := SpanModel{}
|
||||
|
||||
if err := json.Unmarshal(b, &span2); err != nil {
|
||||
t.Fatalf("span unmarshal failed: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := duration.micro, span2.Duration; want != have {
|
||||
t.Errorf("[%d] Duration want %d, have %d", i, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanNegativeDuration(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
span SpanModel
|
||||
b = []byte(`{"duration":-1}`)
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(b, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
|
||||
span = SpanModel{
|
||||
SpanContext: SpanContext{
|
||||
TraceID: TraceID{Low: 1},
|
||||
ID: ID(1),
|
||||
},
|
||||
Timestamp: time.Now(),
|
||||
Duration: -1 * time.Nanosecond,
|
||||
}
|
||||
|
||||
if _, err = json.Marshal(span); err == nil {
|
||||
t.Fatalf("Span Marshal Error expected, have nil")
|
||||
}
|
||||
|
||||
want := fmt.Sprintf(
|
||||
"json: error calling MarshalJSON for type model.SpanModel: %s",
|
||||
ErrValidDurationRequired.Error(),
|
||||
)
|
||||
|
||||
if have := err.Error(); want != have {
|
||||
t.Errorf("Span Marshal Error want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanNegativeTimestamp(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
span SpanModel
|
||||
b = []byte(`{"timestamp":-1}`)
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(b, &span); err == nil {
|
||||
t.Errorf("Unmarshal should have failed with error, have: %+v", span)
|
||||
}
|
||||
|
||||
span = SpanModel{
|
||||
SpanContext: SpanContext{
|
||||
TraceID: TraceID{Low: 1},
|
||||
ID: ID(1),
|
||||
},
|
||||
Timestamp: time.Unix(-1, 0),
|
||||
}
|
||||
|
||||
if _, err = json.Marshal(span); err == nil {
|
||||
t.Fatalf("Span Marshal Error expected, have nil")
|
||||
}
|
||||
|
||||
want := fmt.Sprintf(
|
||||
"json: error calling MarshalJSON for type model.SpanModel: %s",
|
||||
ErrValidTimestampRequired.Error(),
|
||||
)
|
||||
if have := err.Error(); want != have {
|
||||
t.Errorf("Span Marshal Error want %s, have %s", want, have)
|
||||
}
|
||||
|
||||
}
|
||||
59
vendor/github.com/openzipkin/zipkin-go/model/traceid.go
generated
vendored
Normal file
59
vendor/github.com/openzipkin/zipkin-go/model/traceid.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TraceID is a 128 bit number internally stored as 2x uint64 (high & low).
|
||||
// In case of 64 bit traceIDs, the value can be found in Low.
|
||||
type TraceID struct {
|
||||
High uint64
|
||||
Low uint64
|
||||
}
|
||||
|
||||
// Empty returns if TraceID has zero value.
|
||||
func (t TraceID) Empty() bool {
|
||||
return t.Low == 0 && t.High == 0
|
||||
}
|
||||
|
||||
// String outputs the 128-bit traceID as hex string.
|
||||
func (t TraceID) String() string {
|
||||
if t.High == 0 {
|
||||
return fmt.Sprintf("%016x", t.Low)
|
||||
}
|
||||
return fmt.Sprintf("%016x%016x", t.High, t.Low)
|
||||
}
|
||||
|
||||
// TraceIDFromHex returns the TraceID from a hex string.
|
||||
func TraceIDFromHex(h string) (t TraceID, err error) {
|
||||
if len(h) > 16 {
|
||||
if t.High, err = strconv.ParseUint(h[0:len(h)-16], 16, 64); err != nil {
|
||||
return
|
||||
}
|
||||
t.Low, err = strconv.ParseUint(h[len(h)-16:], 16, 64)
|
||||
return
|
||||
}
|
||||
t.Low, err = strconv.ParseUint(h, 16, 64)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON custom JSON serializer to export the TraceID in the required
|
||||
// zero padded hex representation.
|
||||
func (t TraceID) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%q", t.String())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON custom JSON deserializer to retrieve the traceID from the hex
|
||||
// encoded representation.
|
||||
func (t *TraceID) UnmarshalJSON(traceID []byte) error {
|
||||
if len(traceID) < 3 {
|
||||
return ErrValidTraceIDRequired
|
||||
}
|
||||
tID, err := TraceIDFromHex(string(traceID[1 : len(traceID)-1]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = tID
|
||||
return nil
|
||||
}
|
||||
60
vendor/github.com/openzipkin/zipkin-go/model/traceid_test.go
generated
vendored
Normal file
60
vendor/github.com/openzipkin/zipkin-go/model/traceid_test.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTraceID(t *testing.T) {
|
||||
traceID := TraceID{High: 1, Low: 2}
|
||||
if len(traceID.String()) != 32 {
|
||||
t.Errorf("Expected zero-padded TraceID to have 32 characters")
|
||||
}
|
||||
|
||||
b, err := json.Marshal(traceID)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected successful json serialization, got error: %+v", err)
|
||||
}
|
||||
|
||||
var traceID2 TraceID
|
||||
if err = json.Unmarshal(b, &traceID2); err != nil {
|
||||
t.Fatalf("Expected successful json deserialization, got error: %+v", err)
|
||||
}
|
||||
|
||||
have, err := TraceIDFromHex(traceID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected traceID got error: %+v", err)
|
||||
}
|
||||
if traceID.High != have.High || traceID.Low != have.Low {
|
||||
t.Errorf("Expected %+v, got %+v", traceID, have)
|
||||
}
|
||||
|
||||
traceID = TraceID{High: 0, Low: 2}
|
||||
|
||||
if len(traceID.String()) != 16 {
|
||||
t.Errorf("Expected zero-padded TraceID to have 16 characters, got %d", len(traceID.String()))
|
||||
}
|
||||
|
||||
have, err = TraceIDFromHex(traceID.String())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected traceID got error: %+v", err)
|
||||
}
|
||||
if traceID.High != have.High || traceID.Low != have.Low {
|
||||
t.Errorf("Expected %+v, got %+v", traceID, have)
|
||||
}
|
||||
|
||||
traceID = TraceID{High: 0, Low: 0}
|
||||
|
||||
if !traceID.Empty() {
|
||||
t.Errorf("Expected TraceID to be empty")
|
||||
}
|
||||
|
||||
if _, err = TraceIDFromHex("12345678901234zz12345678901234zz"); err == nil {
|
||||
t.Errorf("Expected error got nil")
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(`"12345678901234zz12345678901234zz"`), &traceID); err == nil {
|
||||
t.Errorf("Expected error got nil")
|
||||
}
|
||||
|
||||
}
|
||||
25
vendor/github.com/openzipkin/zipkin-go/noop.go
generated
vendored
Normal file
25
vendor/github.com/openzipkin/zipkin-go/noop.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
type noopSpan struct {
|
||||
model.SpanContext
|
||||
}
|
||||
|
||||
func (n *noopSpan) Context() model.SpanContext { return n.SpanContext }
|
||||
|
||||
func (n *noopSpan) SetName(string) {}
|
||||
|
||||
func (*noopSpan) SetRemoteEndpoint(*model.Endpoint) {}
|
||||
|
||||
func (*noopSpan) Annotate(time.Time, string) {}
|
||||
|
||||
func (*noopSpan) Tag(string, string) {}
|
||||
|
||||
func (*noopSpan) Finish() {}
|
||||
|
||||
func (*noopSpan) Flush() {}
|
||||
49
vendor/github.com/openzipkin/zipkin-go/noop_test.go
generated
vendored
Normal file
49
vendor/github.com/openzipkin/zipkin-go/noop_test.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
func TestNoopContext(t *testing.T) {
|
||||
var (
|
||||
span Span
|
||||
sc model.SpanContext
|
||||
parentID = model.ID(3)
|
||||
tr, _ = NewTracer(
|
||||
reporter.NewNoopReporter(),
|
||||
WithNoopSpan(true),
|
||||
WithSampler(neverSample),
|
||||
WithSharedSpans(true),
|
||||
)
|
||||
)
|
||||
|
||||
sc = model.SpanContext{
|
||||
TraceID: model.TraceID{High: 1, Low: 2},
|
||||
ID: model.ID(4),
|
||||
ParentID: &parentID,
|
||||
Debug: false, // debug must be false
|
||||
Sampled: new(bool), // bool must be pointer to false
|
||||
}
|
||||
|
||||
span = tr.StartSpan("testNoop", Parent(sc), Kind(model.Server))
|
||||
|
||||
noop, ok := span.(*noopSpan)
|
||||
if !ok {
|
||||
t.Fatalf("Span type want %s, have %s", reflect.TypeOf(&spanImpl{}), reflect.TypeOf(span))
|
||||
}
|
||||
|
||||
if have := noop.Context(); !reflect.DeepEqual(sc, have) {
|
||||
t.Errorf("Context want %+v, have %+v", sc, have)
|
||||
}
|
||||
|
||||
span.Tag("dummy", "dummy")
|
||||
span.Annotate(time.Now(), "dummy")
|
||||
span.SetName("dummy")
|
||||
span.SetRemoteEndpoint(nil)
|
||||
span.Flush()
|
||||
}
|
||||
5
vendor/github.com/openzipkin/zipkin-go/propagation/b3/doc.go
generated
vendored
Normal file
5
vendor/github.com/openzipkin/zipkin-go/propagation/b3/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
Package b3 implements serialization and deserialization logic for Zipkin
|
||||
B3 Headers.
|
||||
*/
|
||||
package b3
|
||||
73
vendor/github.com/openzipkin/zipkin-go/propagation/b3/grpc.go
generated
vendored
Normal file
73
vendor/github.com/openzipkin/zipkin-go/propagation/b3/grpc.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package b3
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation"
|
||||
)
|
||||
|
||||
// ExtractGRPC will extract a span.Context from the gRPC Request metadata if
|
||||
// found in B3 header format.
|
||||
func ExtractGRPC(md *metadata.MD) propagation.Extractor {
|
||||
return func() (*model.SpanContext, error) {
|
||||
var (
|
||||
traceIDHeader = GetGRPCHeader(md, TraceID)
|
||||
spanIDHeader = GetGRPCHeader(md, SpanID)
|
||||
parentSpanIDHeader = GetGRPCHeader(md, ParentSpanID)
|
||||
sampledHeader = GetGRPCHeader(md, Sampled)
|
||||
flagsHeader = GetGRPCHeader(md, Flags)
|
||||
)
|
||||
|
||||
return ParseHeaders(
|
||||
traceIDHeader, spanIDHeader, parentSpanIDHeader, sampledHeader,
|
||||
flagsHeader,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// InjectGRPC will inject a span.Context into gRPC metadata.
|
||||
func InjectGRPC(md *metadata.MD) propagation.Injector {
|
||||
return func(sc model.SpanContext) error {
|
||||
if (model.SpanContext{}) == sc {
|
||||
return ErrEmptyContext
|
||||
}
|
||||
|
||||
if sc.Debug {
|
||||
setGRPCHeader(md, Flags, "1")
|
||||
} else if sc.Sampled != nil {
|
||||
// Debug is encoded as X-B3-Flags: 1. Since Debug implies Sampled,
|
||||
// we don't send "X-B3-Sampled" if Debug is set.
|
||||
if *sc.Sampled {
|
||||
setGRPCHeader(md, Sampled, "1")
|
||||
} else {
|
||||
setGRPCHeader(md, Sampled, "0")
|
||||
}
|
||||
}
|
||||
|
||||
if !sc.TraceID.Empty() && sc.ID > 0 {
|
||||
// set identifiers
|
||||
setGRPCHeader(md, TraceID, sc.TraceID.String())
|
||||
setGRPCHeader(md, SpanID, sc.ID.String())
|
||||
if sc.ParentID != nil {
|
||||
setGRPCHeader(md, ParentSpanID, sc.ParentID.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetGRPCHeader retrieves the last value found for a particular key. If key is
|
||||
// not found it returns an empty string.
|
||||
func GetGRPCHeader(md *metadata.MD, key string) string {
|
||||
v := (*md)[key]
|
||||
if len(v) < 1 {
|
||||
return ""
|
||||
}
|
||||
return v[len(v)-1]
|
||||
}
|
||||
|
||||
func setGRPCHeader(md *metadata.MD, key, value string) {
|
||||
(*md)[key] = append((*md)[key], value)
|
||||
}
|
||||
290
vendor/github.com/openzipkin/zipkin-go/propagation/b3/grpc_test.go
generated
vendored
Normal file
290
vendor/github.com/openzipkin/zipkin-go/propagation/b3/grpc_test.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
package b3_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
"github.com/openzipkin/zipkin-go/reporter/recorder"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestGRPCExtractFlagsOnly(t *testing.T) {
|
||||
md := metadata.Pairs(b3.Flags, "1")
|
||||
|
||||
sc, err := b3.ExtractGRPC(&md)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractGRPC Failed: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := true, sc.Debug; want != have {
|
||||
t.Errorf("sc.Debug want %+v, have: %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractSampledOnly(t *testing.T) {
|
||||
md := metadata.Pairs(b3.Sampled, "0")
|
||||
|
||||
sc, err := b3.ExtractGRPC(&md)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractGRPC failed: %+v", err)
|
||||
}
|
||||
|
||||
if sc.Sampled == nil {
|
||||
t.Fatalf("Sampled want %t, have nil", false)
|
||||
}
|
||||
|
||||
if want, have := false, *sc.Sampled; want != have {
|
||||
t.Errorf("Sampled want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
md = metadata.Pairs(b3.Sampled, "1")
|
||||
|
||||
sc, err = b3.ExtractGRPC(&md)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractGRPC failed: %+v", err)
|
||||
}
|
||||
|
||||
if sc.Sampled == nil {
|
||||
t.Fatalf("Sampled want %t, have nil", true)
|
||||
}
|
||||
|
||||
if want, have := true, *sc.Sampled; want != have {
|
||||
t.Errorf("Sampled want %t, have %t", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractFlagsAndSampledOnly(t *testing.T) {
|
||||
md := metadata.Pairs(
|
||||
b3.Flags, "1",
|
||||
b3.Sampled, "1",
|
||||
)
|
||||
|
||||
sc, err := b3.ExtractGRPC(&md)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractGRPC failed: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := true, sc.Debug; want != have {
|
||||
t.Errorf("Debug want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
if sc.Sampled != nil {
|
||||
t.Fatalf("Sampled want nil, have %+v", *sc.Sampled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractSampledErrors(t *testing.T) {
|
||||
md := metadata.Pairs(b3.Sampled, "2")
|
||||
|
||||
sc, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidSampledHeader, err; want != have {
|
||||
t.Errorf("SpanContext Error want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if sc != nil {
|
||||
t.Errorf("SpanContext want nil, have: %+v", sc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractFlagsErrors(t *testing.T) {
|
||||
md := metadata.Pairs(b3.Flags, "2")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidFlagsHeader, err; want != have {
|
||||
t.Errorf("SpanContext Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractScope(t *testing.T) {
|
||||
recorder := &recorder.ReporterRecorder{}
|
||||
defer recorder.Close()
|
||||
|
||||
tracer, err := zipkin.NewTracer(recorder, zipkin.WithTraceID128Bit(true))
|
||||
if err != nil {
|
||||
t.Fatalf("Tracer failed: %+v", err)
|
||||
}
|
||||
|
||||
iterations := 1000
|
||||
for i := 0; i < iterations; i++ {
|
||||
var (
|
||||
parent = tracer.StartSpan("parent")
|
||||
child = tracer.StartSpan("child", zipkin.Parent(parent.Context()))
|
||||
wantContext = child.Context()
|
||||
)
|
||||
|
||||
md := metadata.MD{}
|
||||
b3.InjectGRPC(&md)(wantContext)
|
||||
|
||||
haveContext, err := b3.ExtractGRPC(&md)()
|
||||
if err != nil {
|
||||
t.Errorf("ExtractGRPC failed: %+v", err)
|
||||
}
|
||||
|
||||
if haveContext == nil {
|
||||
t.Fatalf("SpanContext want valid value, have nil")
|
||||
}
|
||||
|
||||
if want, have := wantContext.TraceID, haveContext.TraceID; want != have {
|
||||
t.Errorf("Traceid want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := wantContext.ID, haveContext.ID; want != have {
|
||||
t.Errorf("ID want %+v, have %+v", want, have)
|
||||
}
|
||||
if want, have := *wantContext.ParentID, *haveContext.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
child.Finish()
|
||||
parent.Finish()
|
||||
}
|
||||
|
||||
// check if we have all spans (2x the iterations: parent+child span)
|
||||
if want, have := 2*iterations, len(recorder.Flush()); want != have {
|
||||
t.Errorf("Recorded Span Count want %d, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractTraceIDError(t *testing.T) {
|
||||
md := metadata.Pairs(b3.TraceID, "invalid_data")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidTraceIDHeader, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractSpanIDError(t *testing.T) {
|
||||
md := metadata.Pairs(b3.SpanID, "invalid_data")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidSpanIDHeader, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractTraceIDOnlyError(t *testing.T) {
|
||||
md := metadata.Pairs(b3.TraceID, "1")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidScope, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, got %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractSpanIDOnlyError(t *testing.T) {
|
||||
md := metadata.Pairs(b3.SpanID, "1")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidScope, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractParentIDOnlyError(t *testing.T) {
|
||||
md := metadata.Pairs(b3.ParentSpanID, "1")
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidScopeParent, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCExtractInvalidParentIDError(t *testing.T) {
|
||||
md := metadata.Pairs(
|
||||
b3.TraceID, "1",
|
||||
b3.SpanID, "2",
|
||||
b3.ParentSpanID, "invalid_data",
|
||||
)
|
||||
|
||||
_, err := b3.ExtractGRPC(&md)()
|
||||
|
||||
if want, have := b3.ErrInvalidParentSpanIDHeader, err; want != have {
|
||||
t.Errorf("ExtractGRPC Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCInjectEmptyContextError(t *testing.T) {
|
||||
err := b3.InjectGRPC(nil)(model.SpanContext{})
|
||||
|
||||
if want, have := b3.ErrEmptyContext, err; want != have {
|
||||
t.Errorf("GRPCInject Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCInjectDebugOnly(t *testing.T) {
|
||||
md := &metadata.MD{}
|
||||
|
||||
sc := model.SpanContext{
|
||||
Debug: true,
|
||||
}
|
||||
|
||||
b3.InjectGRPC(md)(sc)
|
||||
|
||||
if want, have := "1", b3.GetGRPCHeader(md, b3.Flags); want != have {
|
||||
t.Errorf("Flags want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCInjectSampledOnly(t *testing.T) {
|
||||
md := &metadata.MD{}
|
||||
|
||||
sampled := false
|
||||
sc := model.SpanContext{
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectGRPC(md)(sc)
|
||||
|
||||
if want, have := "0", b3.GetGRPCHeader(md, b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCInjectUnsampledTrace(t *testing.T) {
|
||||
md := &metadata.MD{}
|
||||
|
||||
sampled := false
|
||||
sc := model.SpanContext{
|
||||
TraceID: model.TraceID{Low: 1},
|
||||
ID: model.ID(2),
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectGRPC(md)(sc)
|
||||
|
||||
if want, have := "0", b3.GetGRPCHeader(md, b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPCInjectSampledAndDebugTrace(t *testing.T) {
|
||||
md := &metadata.MD{}
|
||||
|
||||
sampled := true
|
||||
sc := model.SpanContext{
|
||||
TraceID: model.TraceID{Low: 1},
|
||||
ID: model.ID(2),
|
||||
Debug: true,
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectGRPC(md)(sc)
|
||||
|
||||
if want, have := "", b3.GetGRPCHeader(md, b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want empty, have %s", have)
|
||||
}
|
||||
|
||||
if want, have := "1", b3.GetGRPCHeader(md, b3.Flags); want != have {
|
||||
t.Errorf("Debug want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
58
vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go
generated
vendored
Normal file
58
vendor/github.com/openzipkin/zipkin-go/propagation/b3/http.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package b3
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation"
|
||||
)
|
||||
|
||||
// ExtractHTTP will extract a span.Context from the HTTP Request if found in
|
||||
// B3 header format.
|
||||
func ExtractHTTP(r *http.Request) propagation.Extractor {
|
||||
return func() (*model.SpanContext, error) {
|
||||
var (
|
||||
traceIDHeader = r.Header.Get(TraceID)
|
||||
spanIDHeader = r.Header.Get(SpanID)
|
||||
parentSpanIDHeader = r.Header.Get(ParentSpanID)
|
||||
sampledHeader = r.Header.Get(Sampled)
|
||||
flagsHeader = r.Header.Get(Flags)
|
||||
)
|
||||
|
||||
return ParseHeaders(
|
||||
traceIDHeader, spanIDHeader, parentSpanIDHeader, sampledHeader,
|
||||
flagsHeader,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// InjectHTTP will inject a span.Context into a HTTP Request
|
||||
func InjectHTTP(r *http.Request) propagation.Injector {
|
||||
return func(sc model.SpanContext) error {
|
||||
if (model.SpanContext{}) == sc {
|
||||
return ErrEmptyContext
|
||||
}
|
||||
|
||||
if sc.Debug {
|
||||
r.Header.Set(Flags, "1")
|
||||
} else if sc.Sampled != nil {
|
||||
// Debug is encoded as X-B3-Flags: 1. Since Debug implies Sampled,
|
||||
// so don't also send "X-B3-Sampled: 1".
|
||||
if *sc.Sampled {
|
||||
r.Header.Set(Sampled, "1")
|
||||
} else {
|
||||
r.Header.Set(Sampled, "0")
|
||||
}
|
||||
}
|
||||
|
||||
if !sc.TraceID.Empty() && sc.ID > 0 {
|
||||
r.Header.Set(TraceID, sc.TraceID.String())
|
||||
r.Header.Set(SpanID, sc.ID.String())
|
||||
if sc.ParentID != nil {
|
||||
r.Header.Set(ParentSpanID, sc.ParentID.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
321
vendor/github.com/openzipkin/zipkin-go/propagation/b3/http_test.go
generated
vendored
Normal file
321
vendor/github.com/openzipkin/zipkin-go/propagation/b3/http_test.go
generated
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
package b3_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation/b3"
|
||||
"github.com/openzipkin/zipkin-go/reporter/recorder"
|
||||
)
|
||||
|
||||
func TestHTTPExtractFlagsOnly(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Flags, "1")
|
||||
|
||||
sc, err := b3.ExtractHTTP(r)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractHTTP failed: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := true, sc.Debug; want != have {
|
||||
t.Errorf("sc.Debug want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractSampledOnly(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Sampled, "0")
|
||||
|
||||
sc, err := b3.ExtractHTTP(r)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractHTTP failed: %+v", err)
|
||||
}
|
||||
|
||||
if sc.Sampled == nil {
|
||||
t.Fatalf("Sampled want %t, have nil", false)
|
||||
}
|
||||
|
||||
if want, have := false, *sc.Sampled; want != have {
|
||||
t.Errorf("Sampled want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
r = newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Sampled, "1")
|
||||
|
||||
sc, err = b3.ExtractHTTP(r)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractHTTP failed: %+v", err)
|
||||
}
|
||||
|
||||
if sc.Sampled == nil {
|
||||
t.Fatalf("Sampled want %t, have nil", true)
|
||||
}
|
||||
|
||||
if want, have := true, *sc.Sampled; want != have {
|
||||
t.Errorf("Sampled want %t, have %t", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractFlagsAndSampledOnly(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Flags, "1")
|
||||
r.Header.Set(b3.Sampled, "1")
|
||||
|
||||
sc, err := b3.ExtractHTTP(r)()
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractHTTP failed: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := true, sc.Debug; want != have {
|
||||
t.Errorf("Debug want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
// Sampled should not be set when sc.Debug is set.
|
||||
if sc.Sampled != nil {
|
||||
t.Errorf("Sampled want nil, have %+v", *sc.Sampled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractSampledErrors(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Sampled, "2")
|
||||
|
||||
sc, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidSampledHeader, err; want != have {
|
||||
t.Errorf("SpanContext Error want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if sc != nil {
|
||||
t.Errorf("SpanContext want nil, have: %+v", sc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractFlagsErrors(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.Flags, "2")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidFlagsHeader, err; want != have {
|
||||
t.Errorf("SpanContext Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractScope(t *testing.T) {
|
||||
recorder := &recorder.ReporterRecorder{}
|
||||
defer recorder.Close()
|
||||
|
||||
tracer, err := zipkin.NewTracer(recorder, zipkin.WithTraceID128Bit(true))
|
||||
if err != nil {
|
||||
t.Fatalf("Tracer failed: %+v", err)
|
||||
}
|
||||
|
||||
iterations := 1000
|
||||
for i := 0; i < iterations; i++ {
|
||||
var (
|
||||
parent = tracer.StartSpan("parent")
|
||||
child = tracer.StartSpan("child", zipkin.Parent(parent.Context()))
|
||||
wantContext = child.Context()
|
||||
)
|
||||
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
b3.InjectHTTP(r)(wantContext)
|
||||
|
||||
haveContext, err := b3.ExtractHTTP(r)()
|
||||
if err != nil {
|
||||
t.Errorf("ExtractHTTP failed: %+v", err)
|
||||
}
|
||||
|
||||
if haveContext == nil {
|
||||
t.Fatal("SpanContext want valid value, have nil")
|
||||
}
|
||||
|
||||
if want, have := wantContext.TraceID, haveContext.TraceID; want != have {
|
||||
t.Errorf("TraceID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := wantContext.ID, haveContext.ID; want != have {
|
||||
t.Errorf("ID want %+v, have %+v", want, have)
|
||||
}
|
||||
if want, have := *wantContext.ParentID, *haveContext.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
child.Finish()
|
||||
parent.Finish()
|
||||
}
|
||||
|
||||
// check if we have all spans (2x the iterations: parent+child span)
|
||||
if want, have := 2*iterations, len(recorder.Flush()); want != have {
|
||||
t.Errorf("Recorded Span Count want %d, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractTraceIDError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.TraceID, "invalid_data")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidTraceIDHeader, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractSpanIDError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.SpanID, "invalid_data")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidSpanIDHeader, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractTraceIDOnlyError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.TraceID, "1")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidScope, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractSpanIDOnlyError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.SpanID, "1")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidScope, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractParentIDOnlyError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.ParentSpanID, "1")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidScopeParent, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPExtractInvalidParentIDError(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
r.Header.Set(b3.TraceID, "1")
|
||||
r.Header.Set(b3.SpanID, "2")
|
||||
r.Header.Set(b3.ParentSpanID, "invalid_data")
|
||||
|
||||
_, err := b3.ExtractHTTP(r)()
|
||||
|
||||
if want, have := b3.ErrInvalidParentSpanIDHeader, err; want != have {
|
||||
t.Errorf("ExtractHTTP Error want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHTTPInjectEmptyContextError(t *testing.T) {
|
||||
err := b3.InjectHTTP(nil)(model.SpanContext{})
|
||||
|
||||
if want, have := b3.ErrEmptyContext, err; want != have {
|
||||
t.Errorf("HTTPInject Error want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPInjectDebugOnly(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
sc := model.SpanContext{
|
||||
Debug: true,
|
||||
}
|
||||
|
||||
b3.InjectHTTP(r)(sc)
|
||||
|
||||
if want, have := "1", r.Header.Get(b3.Flags); want != have {
|
||||
t.Errorf("Flags want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPInjectSampledOnly(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
sampled := false
|
||||
sc := model.SpanContext{
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectHTTP(r)(sc)
|
||||
|
||||
if want, have := "0", r.Header.Get(b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPInjectUnsampledTrace(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
sampled := false
|
||||
sc := model.SpanContext{
|
||||
TraceID: model.TraceID{Low: 1},
|
||||
ID: model.ID(2),
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectHTTP(r)(sc)
|
||||
|
||||
if want, have := "0", r.Header.Get(b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPInjectSampledAndDebugTrace(t *testing.T) {
|
||||
r := newHTTPRequest(t)
|
||||
|
||||
sampled := true
|
||||
sc := model.SpanContext{
|
||||
TraceID: model.TraceID{Low: 1},
|
||||
ID: model.ID(2),
|
||||
Debug: true,
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
b3.InjectHTTP(r)(sc)
|
||||
|
||||
if want, have := "", r.Header.Get(b3.Sampled); want != have {
|
||||
t.Errorf("Sampled want empty, have %s", have)
|
||||
}
|
||||
|
||||
if want, have := "1", r.Header.Get(b3.Flags); want != have {
|
||||
t.Errorf("Debug want %s, have %s", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func newHTTPRequest(t *testing.T) *http.Request {
|
||||
r, err := http.NewRequest("test", "", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("HTTP Request failed: %+v", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
24
vendor/github.com/openzipkin/zipkin-go/propagation/b3/shared.go
generated
vendored
Normal file
24
vendor/github.com/openzipkin/zipkin-go/propagation/b3/shared.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package b3
|
||||
|
||||
import "errors"
|
||||
|
||||
// Common Header Extraction / Injection errors
|
||||
var (
|
||||
ErrInvalidSampledHeader = errors.New("invalid B3 Sampled header found")
|
||||
ErrInvalidFlagsHeader = errors.New("invalid B3 Flags header found")
|
||||
ErrInvalidTraceIDHeader = errors.New("invalid B3 TraceID header found")
|
||||
ErrInvalidSpanIDHeader = errors.New("invalid B3 SpanID header found")
|
||||
ErrInvalidParentSpanIDHeader = errors.New("invalid B3 ParentSpanID header found")
|
||||
ErrInvalidScope = errors.New("require either both TraceID and SpanID or none")
|
||||
ErrInvalidScopeParent = errors.New("ParentSpanID requires both TraceID and SpanID to be available")
|
||||
ErrEmptyContext = errors.New("empty request context")
|
||||
)
|
||||
|
||||
// Default B3 Header keys
|
||||
const (
|
||||
TraceID = "x-b3-traceid"
|
||||
SpanID = "x-b3-spanid"
|
||||
ParentSpanID = "x-b3-parentspanid"
|
||||
Sampled = "x-b3-sampled"
|
||||
Flags = "x-b3-flags"
|
||||
)
|
||||
81
vendor/github.com/openzipkin/zipkin-go/propagation/b3/spancontext.go
generated
vendored
Normal file
81
vendor/github.com/openzipkin/zipkin-go/propagation/b3/spancontext.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package b3
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// ParseHeaders takes values found from B3 Headers and tries to reconstruct a
|
||||
// SpanContext.
|
||||
func ParseHeaders(
|
||||
hdrTraceID, hdrSpanID, hdrParentSpanID, hdrSampled, hdrFlags string,
|
||||
) (*model.SpanContext, error) {
|
||||
var (
|
||||
err error
|
||||
spanID uint64
|
||||
requiredCount int
|
||||
sc = &model.SpanContext{}
|
||||
)
|
||||
|
||||
// correct values for an existing sampled header are "0" and "1".
|
||||
// For legacy support and being lenient to other tracing implementations we
|
||||
// allow "true" and "false" as inputs for interop purposes.
|
||||
switch strings.ToLower(hdrSampled) {
|
||||
case "0", "false":
|
||||
sampled := false
|
||||
sc.Sampled = &sampled
|
||||
case "1", "true":
|
||||
sampled := true
|
||||
sc.Sampled = &sampled
|
||||
case "":
|
||||
// sc.Sampled = nil
|
||||
default:
|
||||
return nil, ErrInvalidSampledHeader
|
||||
}
|
||||
|
||||
switch hdrFlags {
|
||||
case "", "0":
|
||||
// sc.Debug = false
|
||||
case "1":
|
||||
sc.Debug = true
|
||||
if sc.Sampled != nil {
|
||||
sc.Sampled = nil
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidFlagsHeader
|
||||
}
|
||||
|
||||
if hdrTraceID != "" {
|
||||
requiredCount++
|
||||
if sc.TraceID, err = model.TraceIDFromHex(hdrTraceID); err != nil {
|
||||
return nil, ErrInvalidTraceIDHeader
|
||||
}
|
||||
}
|
||||
|
||||
if hdrSpanID != "" {
|
||||
requiredCount++
|
||||
if spanID, err = strconv.ParseUint(hdrSpanID, 16, 64); err != nil {
|
||||
return nil, ErrInvalidSpanIDHeader
|
||||
}
|
||||
sc.ID = model.ID(spanID)
|
||||
}
|
||||
|
||||
if requiredCount != 0 && requiredCount != 2 {
|
||||
return nil, ErrInvalidScope
|
||||
}
|
||||
|
||||
if hdrParentSpanID != "" {
|
||||
if requiredCount == 0 {
|
||||
return nil, ErrInvalidScopeParent
|
||||
}
|
||||
if spanID, err = strconv.ParseUint(hdrParentSpanID, 16, 64); err != nil {
|
||||
return nil, ErrInvalidParentSpanIDHeader
|
||||
}
|
||||
parentSpanID := model.ID(spanID)
|
||||
sc.ParentID = &parentSpanID
|
||||
}
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
16
vendor/github.com/openzipkin/zipkin-go/propagation/propagation.go
generated
vendored
Normal file
16
vendor/github.com/openzipkin/zipkin-go/propagation/propagation.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Package propagation holds the required function signatures for Injection and
|
||||
Extraction as used by the Zipkin Tracer.
|
||||
|
||||
Subpackages of this package contain officially supported standard propagation
|
||||
implementations.
|
||||
*/
|
||||
package propagation
|
||||
|
||||
import "github.com/openzipkin/zipkin-go/model"
|
||||
|
||||
// Extractor function signature
|
||||
type Extractor func() (*model.SpanContext, error)
|
||||
|
||||
// Injector function signature
|
||||
type Injector func(model.SpanContext) error
|
||||
218
vendor/github.com/openzipkin/zipkin-go/reporter/http/http.go
generated
vendored
Normal file
218
vendor/github.com/openzipkin/zipkin-go/reporter/http/http.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Package http implements a HTTP reporter to send spans to Zipkin V2 collectors.
|
||||
*/
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
// defaults
|
||||
const (
|
||||
defaultTimeout = time.Second * 5 // timeout for http request in seconds
|
||||
defaultBatchInterval = time.Second * 1 // BatchInterval in seconds
|
||||
defaultBatchSize = 100
|
||||
defaultMaxBacklog = 1000
|
||||
)
|
||||
|
||||
// httpReporter will send spans to a Zipkin HTTP Collector using Zipkin V2 API.
|
||||
type httpReporter struct {
|
||||
url string
|
||||
client *http.Client
|
||||
logger *log.Logger
|
||||
batchInterval time.Duration
|
||||
batchSize int
|
||||
maxBacklog int
|
||||
sendMtx *sync.Mutex
|
||||
batchMtx *sync.Mutex
|
||||
batch []*model.SpanModel
|
||||
spanC chan *model.SpanModel
|
||||
quit chan struct{}
|
||||
shutdown chan error
|
||||
reqCallback RequestCallbackFn
|
||||
}
|
||||
|
||||
// Send implements reporter
|
||||
func (r *httpReporter) Send(s model.SpanModel) {
|
||||
r.spanC <- &s
|
||||
}
|
||||
|
||||
// Close implements reporter
|
||||
func (r *httpReporter) Close() error {
|
||||
close(r.quit)
|
||||
return <-r.shutdown
|
||||
}
|
||||
|
||||
func (r *httpReporter) loop() {
|
||||
var (
|
||||
nextSend = time.Now().Add(r.batchInterval)
|
||||
ticker = time.NewTicker(r.batchInterval / 10)
|
||||
tickerChan = ticker.C
|
||||
)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case span := <-r.spanC:
|
||||
currentBatchSize := r.append(span)
|
||||
if currentBatchSize >= r.batchSize {
|
||||
nextSend = time.Now().Add(r.batchInterval)
|
||||
go func() {
|
||||
_ = r.sendBatch()
|
||||
}()
|
||||
}
|
||||
case <-tickerChan:
|
||||
if time.Now().After(nextSend) {
|
||||
nextSend = time.Now().Add(r.batchInterval)
|
||||
go func() {
|
||||
_ = r.sendBatch()
|
||||
}()
|
||||
}
|
||||
case <-r.quit:
|
||||
r.shutdown <- r.sendBatch()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *httpReporter) append(span *model.SpanModel) (newBatchSize int) {
|
||||
r.batchMtx.Lock()
|
||||
|
||||
r.batch = append(r.batch, span)
|
||||
if len(r.batch) > r.maxBacklog {
|
||||
dispose := len(r.batch) - r.maxBacklog
|
||||
r.logger.Printf("backlog too long, disposing %d spans", dispose)
|
||||
r.batch = r.batch[dispose:]
|
||||
}
|
||||
newBatchSize = len(r.batch)
|
||||
|
||||
r.batchMtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *httpReporter) sendBatch() error {
|
||||
// in order to prevent sending the same batch twice
|
||||
r.sendMtx.Lock()
|
||||
defer r.sendMtx.Unlock()
|
||||
|
||||
// Select all current spans in the batch to be sent
|
||||
r.batchMtx.Lock()
|
||||
sendBatch := r.batch[:]
|
||||
r.batchMtx.Unlock()
|
||||
|
||||
if len(sendBatch) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := json.Marshal(sendBatch)
|
||||
if err != nil {
|
||||
r.logger.Printf("failed when marshalling the spans batch: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", r.url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
r.logger.Printf("failed when creating the request: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if r.reqCallback != nil {
|
||||
r.reqCallback(req)
|
||||
}
|
||||
|
||||
resp, err := r.client.Do(req)
|
||||
if err != nil {
|
||||
r.logger.Printf("failed to send the request: %s\n", err.Error())
|
||||
return err
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
r.logger.Printf("failed the request with status code %d\n", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Remove sent spans from the batch even if they were not saved
|
||||
r.batchMtx.Lock()
|
||||
r.batch = r.batch[len(sendBatch):]
|
||||
r.batchMtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestCallbackFn receives the initialized request from the Collector before
|
||||
// sending it over the wire. This allows one to plug in additional headers or
|
||||
// do other customization.
|
||||
type RequestCallbackFn func(*http.Request)
|
||||
|
||||
// ReporterOption sets a parameter for the HTTP Reporter
|
||||
type ReporterOption func(r *httpReporter)
|
||||
|
||||
// Timeout sets maximum timeout for http request.
|
||||
func Timeout(duration time.Duration) ReporterOption {
|
||||
return func(r *httpReporter) { r.client.Timeout = duration }
|
||||
}
|
||||
|
||||
// BatchSize sets the maximum batch size, after which a collect will be
|
||||
// triggered. The default batch size is 100 traces.
|
||||
func BatchSize(n int) ReporterOption {
|
||||
return func(r *httpReporter) { r.batchSize = n }
|
||||
}
|
||||
|
||||
// MaxBacklog sets the maximum backlog size. When batch size reaches this
|
||||
// threshold, spans from the beginning of the batch will be disposed.
|
||||
func MaxBacklog(n int) ReporterOption {
|
||||
return func(r *httpReporter) { r.maxBacklog = n }
|
||||
}
|
||||
|
||||
// BatchInterval sets the maximum duration we will buffer traces before
|
||||
// emitting them to the collector. The default batch interval is 1 second.
|
||||
func BatchInterval(d time.Duration) ReporterOption {
|
||||
return func(r *httpReporter) { r.batchInterval = d }
|
||||
}
|
||||
|
||||
// Client sets a custom http client to use.
|
||||
func Client(client *http.Client) ReporterOption {
|
||||
return func(r *httpReporter) { r.client = client }
|
||||
}
|
||||
|
||||
// RequestCallback registers a callback function to adjust the reporter
|
||||
// *http.Request before it sends the request to Zipkin.
|
||||
func RequestCallback(rc RequestCallbackFn) ReporterOption {
|
||||
return func(r *httpReporter) { r.reqCallback = rc }
|
||||
}
|
||||
|
||||
// NewReporter returns a new HTTP Reporter.
|
||||
// url should be the endpoint to send the spans to, e.g.
|
||||
// http://localhost:9411/api/v2/spans
|
||||
func NewReporter(url string, opts ...ReporterOption) reporter.Reporter {
|
||||
r := httpReporter{
|
||||
url: url,
|
||||
logger: log.New(os.Stderr, "", log.LstdFlags),
|
||||
client: &http.Client{Timeout: defaultTimeout},
|
||||
batchInterval: defaultBatchInterval,
|
||||
batchSize: defaultBatchSize,
|
||||
maxBacklog: defaultMaxBacklog,
|
||||
batch: []*model.SpanModel{},
|
||||
spanC: make(chan *model.SpanModel),
|
||||
quit: make(chan struct{}, 1),
|
||||
shutdown: make(chan error, 1),
|
||||
sendMtx: &sync.Mutex{},
|
||||
batchMtx: &sync.Mutex{},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&r)
|
||||
}
|
||||
|
||||
go r.loop()
|
||||
|
||||
return &r
|
||||
}
|
||||
77
vendor/github.com/openzipkin/zipkin-go/reporter/http/http_test.go
generated
vendored
Normal file
77
vendor/github.com/openzipkin/zipkin-go/reporter/http/http_test.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package http_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
)
|
||||
|
||||
func TestSpanIsBeingReported(t *testing.T) {
|
||||
idGen := idgenerator.NewRandom64()
|
||||
traceID := idGen.TraceID()
|
||||
|
||||
nSpans := 2
|
||||
var aSpans []model.SpanModel
|
||||
var eSpans []string
|
||||
|
||||
for i := 0; i < nSpans; i++ {
|
||||
span := model.SpanModel{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: traceID,
|
||||
ID: idGen.SpanID(traceID),
|
||||
},
|
||||
Name: "name",
|
||||
Kind: model.Client,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
aSpans = append(aSpans, span)
|
||||
eSpans = append(
|
||||
eSpans,
|
||||
fmt.Sprintf(
|
||||
`{"timestamp":%d,"traceId":"%s","id":"%s","name":"%s","kind":"%s"}`,
|
||||
span.Timestamp.Round(time.Microsecond).UnixNano()/1e3,
|
||||
span.SpanContext.TraceID,
|
||||
span.SpanContext.ID,
|
||||
span.Name,
|
||||
span.Kind,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
eSpansPayload := fmt.Sprintf("[%s]", strings.Join(eSpans, ","))
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Errorf("expected 'POST' request, got '%s'", r.Method)
|
||||
}
|
||||
|
||||
aSpanPayload, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
if eSpansPayload != string(aSpanPayload) {
|
||||
t.Errorf("unexpected span payload \nwant %s, \nhave %s\n", eSpansPayload, string(aSpanPayload))
|
||||
}
|
||||
}))
|
||||
|
||||
defer ts.Close()
|
||||
|
||||
rep := zipkinhttp.NewReporter(ts.URL)
|
||||
defer rep.Close()
|
||||
|
||||
for _, span := range aSpans {
|
||||
rep.Send(span)
|
||||
}
|
||||
}
|
||||
100
vendor/github.com/openzipkin/zipkin-go/reporter/kafka/kafka.go
generated
vendored
Normal file
100
vendor/github.com/openzipkin/zipkin-go/reporter/kafka/kafka.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Package kafka implements a Kafka reporter to send spans to a Kafka server/cluster.
|
||||
*/
|
||||
package kafka
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/Shopify/sarama"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
// defaultKafkaTopic sets the standard Kafka topic our Reporter will publish
|
||||
// on. The default topic for zipkin-receiver-kafka is "zipkin", see:
|
||||
// https://github.com/openzipkin/zipkin/tree/master/zipkin-receiver-kafka
|
||||
const defaultKafkaTopic = "zipkin"
|
||||
|
||||
// kafkaReporter implements Reporter by publishing spans to a Kafka
|
||||
// broker.
|
||||
type kafkaReporter struct {
|
||||
producer sarama.AsyncProducer
|
||||
logger *log.Logger
|
||||
topic string
|
||||
}
|
||||
|
||||
// ReporterOption sets a parameter for the kafkaReporter
|
||||
type ReporterOption func(c *kafkaReporter)
|
||||
|
||||
// Logger sets the logger used to report errors in the collection
|
||||
// process.
|
||||
func Logger(logger *log.Logger) ReporterOption {
|
||||
return func(c *kafkaReporter) {
|
||||
c.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
// Producer sets the producer used to produce to Kafka.
|
||||
func Producer(p sarama.AsyncProducer) ReporterOption {
|
||||
return func(c *kafkaReporter) {
|
||||
c.producer = p
|
||||
}
|
||||
}
|
||||
|
||||
// Topic sets the kafka topic to attach the reporter producer on.
|
||||
func Topic(t string) ReporterOption {
|
||||
return func(c *kafkaReporter) {
|
||||
c.topic = t
|
||||
}
|
||||
}
|
||||
|
||||
// NewReporter returns a new Kafka-backed Reporter. address should be a slice of
|
||||
// TCP endpoints of the form "host:port".
|
||||
func NewReporter(address []string, options ...ReporterOption) (reporter.Reporter, error) {
|
||||
c := &kafkaReporter{
|
||||
logger: log.New(os.Stderr, "", log.LstdFlags),
|
||||
topic: defaultKafkaTopic,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(c)
|
||||
}
|
||||
if c.producer == nil {
|
||||
p, err := sarama.NewAsyncProducer(address, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.producer = p
|
||||
}
|
||||
|
||||
go c.logErrors()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *kafkaReporter) logErrors() {
|
||||
for pe := range c.producer.Errors() {
|
||||
c.logger.Print("msg", pe.Msg, "err", pe.Err, "result", "failed to produce msg")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *kafkaReporter) Send(s model.SpanModel) {
|
||||
m, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
c.logger.Printf("failed when marshalling the span: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.producer.Input() <- &sarama.ProducerMessage{
|
||||
Topic: c.topic,
|
||||
Key: nil,
|
||||
Value: sarama.ByteEncoder(m),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *kafkaReporter) Close() error {
|
||||
return c.producer.Close()
|
||||
}
|
||||
218
vendor/github.com/openzipkin/zipkin-go/reporter/kafka/kafka_test.go
generated
vendored
Normal file
218
vendor/github.com/openzipkin/zipkin-go/reporter/kafka/kafka_test.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package kafka_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/Shopify/sarama"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
"github.com/openzipkin/zipkin-go/reporter/kafka"
|
||||
)
|
||||
|
||||
type stubProducer struct {
|
||||
in chan *sarama.ProducerMessage
|
||||
err chan *sarama.ProducerError
|
||||
kafkaDown bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (p *stubProducer) AsyncClose() {}
|
||||
func (p *stubProducer) Close() error {
|
||||
if p.kafkaDown {
|
||||
return errors.New("kafka is down")
|
||||
}
|
||||
p.closed = true
|
||||
return nil
|
||||
}
|
||||
func (p *stubProducer) Input() chan<- *sarama.ProducerMessage { return p.in }
|
||||
func (p *stubProducer) Successes() <-chan *sarama.ProducerMessage { return nil }
|
||||
func (p *stubProducer) Errors() <-chan *sarama.ProducerError { return p.err }
|
||||
|
||||
func newStubProducer(kafkaDown bool) *stubProducer {
|
||||
return &stubProducer{
|
||||
make(chan *sarama.ProducerMessage),
|
||||
make(chan *sarama.ProducerError),
|
||||
kafkaDown,
|
||||
false,
|
||||
}
|
||||
}
|
||||
|
||||
var spans = []*model.SpanModel{
|
||||
makeNewSpan("avg", 123, 456, 0, true),
|
||||
makeNewSpan("sum", 123, 789, 456, true),
|
||||
makeNewSpan("div", 123, 101112, 456, true),
|
||||
}
|
||||
|
||||
func TestKafkaProduce(t *testing.T) {
|
||||
p := newStubProducer(false)
|
||||
c, err := kafka.NewReporter(
|
||||
[]string{"192.0.2.10:9092"}, kafka.Producer(p),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, want := range spans {
|
||||
m := sendSpan(t, c, p, *want)
|
||||
testMetadata(t, m)
|
||||
got := deserializeSpan(t, m.Value)
|
||||
testEqual(t, want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKafkaClose(t *testing.T) {
|
||||
p := newStubProducer(false)
|
||||
c, err := kafka.NewReporter(
|
||||
[]string{"192.0.2.10:9092"}, kafka.Producer(p),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = c.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !p.closed {
|
||||
t.Fatal("producer not closed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestKafkaCloseError(t *testing.T) {
|
||||
p := newStubProducer(true)
|
||||
c, err := kafka.NewReporter(
|
||||
[]string{"192.0.2.10:9092"}, kafka.Producer(p),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = c.Close(); err == nil {
|
||||
t.Error("no error on close")
|
||||
}
|
||||
}
|
||||
|
||||
type chanWriter struct {
|
||||
errs chan []interface{}
|
||||
}
|
||||
|
||||
func (cw *chanWriter) Write(p []byte) (n int, err error) {
|
||||
cw.errs <- []interface{}{p}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func TestKafkaErrors(t *testing.T) {
|
||||
p := newStubProducer(true)
|
||||
errs := make(chan []interface{}, len(spans))
|
||||
|
||||
c, err := kafka.NewReporter(
|
||||
[]string{"192.0.2.10:9092"},
|
||||
kafka.Producer(p),
|
||||
kafka.Logger(log.New(&chanWriter{errs}, "", log.LstdFlags)),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range spans {
|
||||
_ = sendSpan(t, c, p, *want)
|
||||
}
|
||||
|
||||
for i := 0; i < len(spans); i++ {
|
||||
select {
|
||||
case <-errs:
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("errors not logged. got %d, wanted %d", i, len(spans))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendSpan(t *testing.T, c reporter.Reporter, p *stubProducer, s model.SpanModel) *sarama.ProducerMessage {
|
||||
var m *sarama.ProducerMessage
|
||||
rcvd := make(chan bool, 1)
|
||||
go func() {
|
||||
select {
|
||||
case m = <-p.in:
|
||||
rcvd <- true
|
||||
if p.kafkaDown {
|
||||
p.err <- &sarama.ProducerError{
|
||||
Msg: m,
|
||||
Err: errors.New("kafka is down"),
|
||||
}
|
||||
}
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
rcvd <- false
|
||||
}
|
||||
}()
|
||||
|
||||
c.Send(s)
|
||||
|
||||
if !<-rcvd {
|
||||
t.Fatal("span message was not produced")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func testMetadata(t *testing.T, m *sarama.ProducerMessage) {
|
||||
if m.Topic != "zipkin" {
|
||||
t.Errorf("produced to topic %q, want %q", m.Topic, "zipkin")
|
||||
}
|
||||
if m.Key != nil {
|
||||
t.Errorf("produced with key %q, want nil", m.Key)
|
||||
}
|
||||
}
|
||||
|
||||
func deserializeSpan(t *testing.T, e sarama.Encoder) *model.SpanModel {
|
||||
bytes, err := e.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("error in encoding: %v", err)
|
||||
}
|
||||
|
||||
var s model.SpanModel
|
||||
|
||||
err = json.Unmarshal(bytes, &s)
|
||||
if err != nil {
|
||||
t.Errorf("error in decoding: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func testEqual(t *testing.T, want *model.SpanModel, got *model.SpanModel) {
|
||||
if got.TraceID != want.TraceID {
|
||||
t.Errorf("trace_id %d, want %d", got.TraceID, want.TraceID)
|
||||
}
|
||||
if got.ID != want.ID {
|
||||
t.Errorf("id %d, want %d", got.ID, want.ID)
|
||||
}
|
||||
if got.ParentID == nil {
|
||||
if want.ParentID != nil {
|
||||
t.Errorf("parent_id %d, want %d", got.ParentID, want.ParentID)
|
||||
}
|
||||
} else if *got.ParentID != *want.ParentID {
|
||||
t.Errorf("parent_id %d, want %d", got.ParentID, want.ParentID)
|
||||
}
|
||||
}
|
||||
|
||||
func makeNewSpan(methodName string, traceID, spanID, parentSpanID uint64, debug bool) *model.SpanModel {
|
||||
timestamp := time.Now()
|
||||
|
||||
var parentID = new(model.ID)
|
||||
if parentSpanID != 0 {
|
||||
*parentID = model.ID(parentSpanID)
|
||||
}
|
||||
|
||||
return &model.SpanModel{
|
||||
SpanContext: model.SpanContext{
|
||||
TraceID: model.TraceID{Low: traceID},
|
||||
ID: model.ID(spanID),
|
||||
ParentID: parentID,
|
||||
Debug: debug,
|
||||
},
|
||||
Name: methodName,
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
}
|
||||
41
vendor/github.com/openzipkin/zipkin-go/reporter/log/log.go
generated
vendored
Normal file
41
vendor/github.com/openzipkin/zipkin-go/reporter/log/log.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Package log implements a reporter to send spans in V2 JSON format to the Go
|
||||
standard Logger.
|
||||
*/
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
// logReporter will send spans to the default Go Logger.
|
||||
type logReporter struct {
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewReporter returns a new log reporter.
|
||||
func NewReporter(l *log.Logger) reporter.Reporter {
|
||||
if l == nil {
|
||||
// use standard type of log setup
|
||||
l = log.New(os.Stderr, "", log.LstdFlags)
|
||||
}
|
||||
return &logReporter{
|
||||
logger: l,
|
||||
}
|
||||
}
|
||||
|
||||
// Send outputs a span to the Go logger.
|
||||
func (r *logReporter) Send(s model.SpanModel) {
|
||||
if b, err := json.MarshalIndent(s, "", " "); err == nil {
|
||||
r.logger.Printf("%s:\n%s\n\n", time.Now(), string(b))
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the reporter
|
||||
func (*logReporter) Close() error { return nil }
|
||||
43
vendor/github.com/openzipkin/zipkin-go/reporter/recorder/recorder.go
generated
vendored
Normal file
43
vendor/github.com/openzipkin/zipkin-go/reporter/recorder/recorder.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Package recorder implements a reporter to record spans in v2 format.
|
||||
*/
|
||||
package recorder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// ReporterRecorder records Zipkin spans.
|
||||
type ReporterRecorder struct {
|
||||
mtx sync.Mutex
|
||||
spans []model.SpanModel
|
||||
}
|
||||
|
||||
// NewReporter returns a new recording reporter.
|
||||
func NewReporter() *ReporterRecorder {
|
||||
return &ReporterRecorder{}
|
||||
}
|
||||
|
||||
// Send adds the provided span to the span list held by the recorder.
|
||||
func (r *ReporterRecorder) Send(span model.SpanModel) {
|
||||
r.mtx.Lock()
|
||||
r.spans = append(r.spans, span)
|
||||
r.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Flush returns all recorded spans and clears its internal span storage
|
||||
func (r *ReporterRecorder) Flush() []model.SpanModel {
|
||||
r.mtx.Lock()
|
||||
spans := r.spans
|
||||
r.spans = nil
|
||||
r.mtx.Unlock()
|
||||
return spans
|
||||
}
|
||||
|
||||
// Close flushes the reporter
|
||||
func (r *ReporterRecorder) Close() error {
|
||||
r.Flush()
|
||||
return nil
|
||||
}
|
||||
41
vendor/github.com/openzipkin/zipkin-go/reporter/recorder/recorder_test.go
generated
vendored
Normal file
41
vendor/github.com/openzipkin/zipkin-go/reporter/recorder/recorder_test.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package recorder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
func TestFlushInRecorderSuccess(t *testing.T) {
|
||||
rec := NewReporter()
|
||||
|
||||
span := model.SpanModel{}
|
||||
rec.Send(span)
|
||||
|
||||
if len(rec.spans) != 1 {
|
||||
t.Fatalf("Span Count want 1, have %d", len(rec.spans))
|
||||
}
|
||||
|
||||
rec.Flush()
|
||||
|
||||
if len(rec.spans) != 0 {
|
||||
t.Fatalf("Span Count want 0, have %d", len(rec.spans))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseInRecorderSuccess(t *testing.T) {
|
||||
rec := NewReporter()
|
||||
|
||||
span := model.SpanModel{}
|
||||
rec.Send(span)
|
||||
|
||||
if len(rec.spans) != 1 {
|
||||
t.Fatalf("Span Count want 1, have %d", len(rec.spans))
|
||||
}
|
||||
|
||||
rec.Close()
|
||||
|
||||
if len(rec.spans) != 0 {
|
||||
t.Fatalf("Span Count want 0, have %d", len(rec.spans))
|
||||
}
|
||||
}
|
||||
27
vendor/github.com/openzipkin/zipkin-go/reporter/reporter.go
generated
vendored
Normal file
27
vendor/github.com/openzipkin/zipkin-go/reporter/reporter.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Package reporter holds the Reporter interface which is used by the Zipkin
|
||||
Tracer to send finished spans.
|
||||
|
||||
Subpackages of package reporter contain officially supported standard
|
||||
reporter implementations.
|
||||
*/
|
||||
package reporter
|
||||
|
||||
import "github.com/openzipkin/zipkin-go/model"
|
||||
|
||||
// Reporter interface can be used to provide the Zipkin Tracer with custom
|
||||
// implementations to publish Zipkin Span data.
|
||||
type Reporter interface {
|
||||
Send(model.SpanModel) // Send Span data to the reporter
|
||||
Close() error // Close the reporter
|
||||
}
|
||||
|
||||
type noopReporter struct {}
|
||||
|
||||
func (r *noopReporter) Send(model.SpanModel) {}
|
||||
func (r *noopReporter) Close() error { return nil }
|
||||
|
||||
// NewNoopReporter returns a no-op Reporter implementation.
|
||||
func NewNoopReporter() Reporter {
|
||||
return &noopReporter{}
|
||||
}
|
||||
107
vendor/github.com/openzipkin/zipkin-go/sample.go
generated
vendored
Normal file
107
vendor/github.com/openzipkin/zipkin-go/sample.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sampler functions return if a Zipkin span should be sampled, based on its
|
||||
// traceID.
|
||||
type Sampler func(id uint64) bool
|
||||
|
||||
func neverSample(_ uint64) bool { return false }
|
||||
|
||||
func alwaysSample(_ uint64) bool { return true }
|
||||
|
||||
// NewModuloSampler provides a generic type Sampler.
|
||||
func NewModuloSampler(mod uint64) Sampler {
|
||||
if mod < 2 {
|
||||
return alwaysSample
|
||||
}
|
||||
return func(id uint64) bool {
|
||||
return (id % mod) == 0
|
||||
}
|
||||
}
|
||||
|
||||
// NewBoundarySampler is appropriate for high-traffic instrumentation who
|
||||
// provision random trace ids, and make the sampling decision only once.
|
||||
// It defends against nodes in the cluster selecting exactly the same ids.
|
||||
func NewBoundarySampler(rate float64, salt int64) (Sampler, error) {
|
||||
if rate == 0.0 {
|
||||
return neverSample, nil
|
||||
}
|
||||
if rate == 1.0 {
|
||||
return alwaysSample, nil
|
||||
}
|
||||
if rate < 0.0001 || rate > 1 {
|
||||
return nil, fmt.Errorf("rate should be 0.0 or between 0.0001 and 1: was %f", rate)
|
||||
}
|
||||
|
||||
var (
|
||||
boundary = int64(rate * 10000)
|
||||
usalt = uint64(salt)
|
||||
)
|
||||
return func(id uint64) bool {
|
||||
return int64(math.Abs(float64(id^usalt)))%10000 < boundary
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewCountingSampler is appropriate for low-traffic instrumentation or
|
||||
// those who do not provision random trace ids. It is not appropriate for
|
||||
// collectors as the sampling decision isn't idempotent (consistent based
|
||||
// on trace id).
|
||||
func NewCountingSampler(rate float64) (Sampler, error) {
|
||||
if rate == 0.0 {
|
||||
return neverSample, nil
|
||||
}
|
||||
if rate == 1.0 {
|
||||
return alwaysSample, nil
|
||||
}
|
||||
if rate < 0.01 || rate > 1 {
|
||||
return nil, fmt.Errorf("rate should be 0.0 or between 0.01 and 1: was %f", rate)
|
||||
}
|
||||
var (
|
||||
i = 0
|
||||
outOf100 = int(rate*100 + math.Copysign(0.5, rate*100)) // for rounding float to int conversion instead of truncation
|
||||
decisions = randomBitSet(100, outOf100, rand.New(rand.NewSource(time.Now().UnixNano())))
|
||||
mtx = &sync.Mutex{}
|
||||
)
|
||||
|
||||
return func(_ uint64) bool {
|
||||
mtx.Lock()
|
||||
result := decisions[i]
|
||||
i++
|
||||
if i == 100 {
|
||||
i = 0
|
||||
}
|
||||
mtx.Unlock()
|
||||
return result
|
||||
}, nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Reservoir sampling algorithm borrowed from Stack Overflow.
|
||||
*
|
||||
* http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s
|
||||
*/
|
||||
func randomBitSet(size int, cardinality int, rnd *rand.Rand) []bool {
|
||||
result := make([]bool, size)
|
||||
chosen := make([]int, cardinality)
|
||||
var i int
|
||||
for i = 0; i < cardinality; i++ {
|
||||
chosen[i] = i
|
||||
result[i] = true
|
||||
}
|
||||
for ; i < size; i++ {
|
||||
j := rnd.Intn(i + 1)
|
||||
if j < cardinality {
|
||||
result[chosen[j]] = false
|
||||
result[i] = true
|
||||
chosen[j] = i
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
107
vendor/github.com/openzipkin/zipkin-go/sample_test.go
generated
vendored
Normal file
107
vendor/github.com/openzipkin/zipkin-go/sample_test.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package zipkin_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go"
|
||||
)
|
||||
|
||||
func TestBoundarySampler(t *testing.T) {
|
||||
type triple struct {
|
||||
id uint64
|
||||
salt int64
|
||||
rate float64
|
||||
hasError bool
|
||||
}
|
||||
for input, sampled := range map[triple]bool{
|
||||
{123, 456, 1.0, false}: true,
|
||||
{123, 456, 999, true}: true,
|
||||
{123, 456, 0.0, false}: false,
|
||||
{123, 456, -42, true}: false,
|
||||
{1229998, 0, 0.01, false}: false,
|
||||
{1229999, 0, 0.01, false}: false,
|
||||
{1230000, 0, 0.01, false}: true,
|
||||
{1230001, 0, 0.01, false}: true,
|
||||
{1230098, 0, 0.01, false}: true,
|
||||
{1230099, 0, 0.01, false}: true,
|
||||
{1230100, 0, 0.01, false}: false,
|
||||
{1230101, 0, 0.01, false}: false,
|
||||
{1, 9999999, 0.01, false}: false,
|
||||
{999, 0, 0.99, false}: true,
|
||||
{9999, 0, 0.99, false}: false,
|
||||
} {
|
||||
sampler, err := zipkin.NewBoundarySampler(input.rate, input.salt)
|
||||
if want, have := input.hasError, (err != nil); want != have {
|
||||
t.Fatalf("%#+v: want error %t, have error %t", input, want, have)
|
||||
}
|
||||
if input.hasError {
|
||||
want := fmt.Errorf("rate should be 0.0 or between 0.0001 and 1: was %f", input.rate)
|
||||
if have := err; have == nil || want.Error() != have.Error() {
|
||||
t.Fatalf("%#+v: want error %+v, have error %+v", input, want, have)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if want, have := sampled, sampler(input.id); want != have {
|
||||
t.Errorf("%#+v: want %v, have %v", input, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountingSampler(t *testing.T) {
|
||||
{
|
||||
_, have := zipkin.NewCountingSampler(0.009)
|
||||
want := fmt.Errorf("rate should be 0.0 or between 0.01 and 1: was %f", 0.009)
|
||||
if have == nil || want.Error() != have.Error() {
|
||||
t.Errorf("rate 0.009, want error %+v, got %+v", want, have)
|
||||
}
|
||||
}
|
||||
{
|
||||
_, have := zipkin.NewCountingSampler(1.001)
|
||||
want := fmt.Errorf("rate should be 0.0 or between 0.01 and 1: was %f", 1.001)
|
||||
if have == nil || want.Error() != have.Error() {
|
||||
t.Errorf("rate 1.001, want error %+v, got %+v", want, have)
|
||||
}
|
||||
}
|
||||
for n := 0; n <= 100; n++ {
|
||||
var (
|
||||
rate = float64(n) / 100
|
||||
sampler, _ = zipkin.NewCountingSampler(rate)
|
||||
found = 0
|
||||
)
|
||||
for i := 0; i < 1000; i++ {
|
||||
if sampler(1) {
|
||||
found++
|
||||
}
|
||||
}
|
||||
if found != n*10 {
|
||||
t.Errorf("rate %f, want %d, have %d", rate, n, found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleSampler(t *testing.T) {
|
||||
rand.Seed(time.Now().Unix())
|
||||
|
||||
for mod := uint64(1); mod <= 100; mod++ {
|
||||
var (
|
||||
sampler = zipkin.NewModuloSampler(mod)
|
||||
want = uint64(rand.Intn(1000))
|
||||
max = mod * want
|
||||
found = uint64(0)
|
||||
)
|
||||
|
||||
for i := uint64(0); i < max; i++ {
|
||||
if sampler(i) {
|
||||
found++
|
||||
}
|
||||
}
|
||||
|
||||
if want, have := max/mod, found; want != have {
|
||||
t.Errorf("expected %d samples, got %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
vendor/github.com/openzipkin/zipkin-go/span.go
generated
vendored
Normal file
38
vendor/github.com/openzipkin/zipkin-go/span.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// Span interface as returned by Tracer.StartSpan()
|
||||
type Span interface {
|
||||
// Context returns the Span's SpanContext.
|
||||
Context() model.SpanContext
|
||||
|
||||
// SetName updates the Span's name.
|
||||
SetName(string)
|
||||
|
||||
// SetRemoteEndpoint updates the Span's Remote Endpoint.
|
||||
SetRemoteEndpoint(*model.Endpoint)
|
||||
|
||||
// Annotate adds a timed event to the Span.
|
||||
Annotate(time.Time, string)
|
||||
|
||||
// Tag sets Tag with given key and value to the Span. If key already exists in
|
||||
// the Span the value will be overridden except for error tags where the first
|
||||
// value is persisted.
|
||||
Tag(string, string)
|
||||
|
||||
// Finish the Span and send to Reporter. If DelaySend option was used at
|
||||
// Span creation time, Finish will not send the Span to the Reporter. It then
|
||||
// becomes the user's responsibility to get the Span reported (by using
|
||||
// span.Flush).
|
||||
Finish()
|
||||
|
||||
// Flush the Span to the Reporter (regardless of being finished or not).
|
||||
// This can be used if the DelaySend SpanOption was set or when dealing with
|
||||
// one-way RPC tracing where duration might not be measured.
|
||||
Flush()
|
||||
}
|
||||
78
vendor/github.com/openzipkin/zipkin-go/span_implementation.go
generated
vendored
Normal file
78
vendor/github.com/openzipkin/zipkin-go/span_implementation.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
type spanImpl struct {
|
||||
mtx sync.RWMutex
|
||||
model.SpanModel
|
||||
tracer *Tracer
|
||||
mustCollect int32 // used as atomic bool (1 = true, 0 = false)
|
||||
flushOnFinish bool
|
||||
}
|
||||
|
||||
func (s *spanImpl) Context() model.SpanContext {
|
||||
return s.SpanContext
|
||||
}
|
||||
|
||||
func (s *spanImpl) SetName(name string) {
|
||||
s.mtx.Lock()
|
||||
s.Name = name
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (s *spanImpl) SetRemoteEndpoint(e *model.Endpoint) {
|
||||
s.mtx.Lock()
|
||||
if e == nil {
|
||||
s.RemoteEndpoint = nil
|
||||
} else {
|
||||
s.RemoteEndpoint = &model.Endpoint{}
|
||||
*s.RemoteEndpoint = *e
|
||||
}
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (s *spanImpl) Annotate(t time.Time, value string) {
|
||||
a := model.Annotation{
|
||||
Timestamp: t,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
s.mtx.Lock()
|
||||
s.Annotations = append(s.Annotations, a)
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (s *spanImpl) Tag(key, value string) {
|
||||
s.mtx.Lock()
|
||||
|
||||
if key == string(TagError) {
|
||||
if _, found := s.Tags[key]; found {
|
||||
s.mtx.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.Tags[key] = value
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (s *spanImpl) Finish() {
|
||||
if atomic.CompareAndSwapInt32(&s.mustCollect, 1, 0) {
|
||||
s.Duration = time.Since(s.Timestamp)
|
||||
if s.flushOnFinish {
|
||||
s.tracer.reporter.Send(s.SpanModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanImpl) Flush() {
|
||||
if s.SpanModel.Debug || (s.SpanModel.Sampled != nil && *s.SpanModel.Sampled) {
|
||||
s.tracer.reporter.Send(s.SpanModel)
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/openzipkin/zipkin-go/span_options.go
generated
vendored
Normal file
74
vendor/github.com/openzipkin/zipkin-go/span_options.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// SpanOption allows for functional options to adjust behavior and payload of
|
||||
// the Span to be created with tracer.StartSpan().
|
||||
type SpanOption func(t *Tracer, s *spanImpl)
|
||||
|
||||
// Kind sets the kind of the span being created..
|
||||
func Kind(kind model.Kind) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
s.Kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
// Parent will use provided SpanContext as parent to the span being created.
|
||||
func Parent(sc model.SpanContext) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
if sc.Err != nil {
|
||||
// encountered an extraction error
|
||||
switch t.extractFailurePolicy {
|
||||
case ExtractFailurePolicyRestart:
|
||||
case ExtractFailurePolicyError:
|
||||
panic(s.SpanContext.Err)
|
||||
case ExtractFailurePolicyTagAndRestart:
|
||||
s.Tags["error.extract"] = sc.Err.Error()
|
||||
default:
|
||||
panic(ErrInvalidExtractFailurePolicy)
|
||||
}
|
||||
/* don't use provided SpanContext, but restart trace */
|
||||
return
|
||||
}
|
||||
s.SpanContext = sc
|
||||
}
|
||||
}
|
||||
|
||||
// StartTime uses a given start time for the span being created.
|
||||
func StartTime(start time.Time) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
s.Timestamp = start
|
||||
}
|
||||
}
|
||||
|
||||
// RemoteEndpoint sets the remote endpoint of the span being created.
|
||||
func RemoteEndpoint(e *model.Endpoint) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
s.RemoteEndpoint = e
|
||||
}
|
||||
}
|
||||
|
||||
// Tags sets initial tags for the span being created. If default tracer tags
|
||||
// are present they will be overwritten on key collisions.
|
||||
func Tags(tags map[string]string) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
for k, v := range tags {
|
||||
s.Tags[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FlushOnFinish when set to false will disable span.Finish() to send the Span
|
||||
// to the Reporter automatically (which is the default behavior). If set to
|
||||
// false, having the Span be reported becomes the responsibility of the user.
|
||||
// This is available if late tag data is expected to be only available after the
|
||||
// required finish time of the Span.
|
||||
func FlushOnFinish(b bool) SpanOption {
|
||||
return func(t *Tracer, s *spanImpl) {
|
||||
s.flushOnFinish = b
|
||||
}
|
||||
}
|
||||
137
vendor/github.com/openzipkin/zipkin-go/span_test.go
generated
vendored
Normal file
137
vendor/github.com/openzipkin/zipkin-go/span_test.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
"github.com/openzipkin/zipkin-go/reporter/recorder"
|
||||
)
|
||||
|
||||
func TestSpanNameUpdate(t *testing.T) {
|
||||
var (
|
||||
oldName = "oldName"
|
||||
newName = "newName"
|
||||
)
|
||||
|
||||
tracer, _ := NewTracer(reporter.NewNoopReporter())
|
||||
|
||||
span := tracer.StartSpan(oldName)
|
||||
|
||||
if want, have := oldName, span.(*spanImpl).Name; want != have {
|
||||
t.Errorf("Name want %q, have %q", want, have)
|
||||
}
|
||||
|
||||
span.SetName(newName)
|
||||
|
||||
if want, have := newName, span.(*spanImpl).Name; want != have {
|
||||
t.Errorf("Name want %q, have %q", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteEndpoint(t *testing.T) {
|
||||
tracer, err := NewTracer(reporter.NewNoopReporter())
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
ep1, err := NewEndpoint("myService", "www.google.com:80")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid endpoint, got error: %+v", err)
|
||||
}
|
||||
|
||||
span := tracer.StartSpan("test", RemoteEndpoint(ep1))
|
||||
|
||||
if !reflect.DeepEqual(span.(*spanImpl).RemoteEndpoint, ep1) {
|
||||
t.Errorf("RemoteEndpoint want %+v, have %+v", ep1, span.(*spanImpl).RemoteEndpoint)
|
||||
}
|
||||
|
||||
ep2, err := NewEndpoint("otherService", "www.microsoft.com:443")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid endpoint, got error: %+v", err)
|
||||
}
|
||||
|
||||
span.SetRemoteEndpoint(ep2)
|
||||
|
||||
if !reflect.DeepEqual(span.(*spanImpl).RemoteEndpoint, ep2) {
|
||||
t.Errorf("RemoteEndpoint want %+v, have %+v", ep1, span.(*spanImpl).RemoteEndpoint)
|
||||
}
|
||||
|
||||
span.SetRemoteEndpoint(nil)
|
||||
|
||||
if have := span.(*spanImpl).RemoteEndpoint; have != nil {
|
||||
t.Errorf("RemoteEndpoint want nil, have %+v", have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagsSpanOption(t *testing.T) {
|
||||
tracerTags := map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "will_be_overwritten",
|
||||
}
|
||||
tracer, err := NewTracer(reporter.NewNoopReporter(), WithTags(tracerTags))
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
spanTags := map[string]string{
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
span := tracer.StartSpan("test", Tags(spanTags))
|
||||
defer span.Finish()
|
||||
|
||||
allTags := map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
|
||||
if want, have := allTags, span.(*spanImpl).Tags; !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("Tags want: %+v, have: %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlushOnFinishSpanOption(t *testing.T) {
|
||||
rec := recorder.NewReporter()
|
||||
defer rec.Close()
|
||||
|
||||
tracer, _ := NewTracer(rec)
|
||||
|
||||
span := tracer.StartSpan("test")
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
span.Finish()
|
||||
|
||||
spans := rec.Flush()
|
||||
|
||||
if want, have := 1, len(spans); want != have {
|
||||
t.Errorf("Spans want: %d, have %d", want, have)
|
||||
}
|
||||
|
||||
span = tracer.StartSpan("test", FlushOnFinish(false))
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
span.Finish()
|
||||
|
||||
spans = rec.Flush()
|
||||
|
||||
if want, have := 0, len(spans); want != have {
|
||||
t.Errorf("Spans want: %d, have %d", want, have)
|
||||
}
|
||||
|
||||
span.Tag("post", "finish")
|
||||
span.Flush()
|
||||
|
||||
spans = rec.Flush()
|
||||
|
||||
if want, have := 1, len(spans); want != have {
|
||||
t.Errorf("Spans want: %d, have %d", want, have)
|
||||
}
|
||||
|
||||
if want, have := map[string]string{"post": "finish"}, spans[0].Tags; !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("Tags want: %+v, have: %+v", want, have)
|
||||
}
|
||||
|
||||
}
|
||||
21
vendor/github.com/openzipkin/zipkin-go/tags.go
generated
vendored
Normal file
21
vendor/github.com/openzipkin/zipkin-go/tags.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package zipkin
|
||||
|
||||
// Tag holds available types
|
||||
type Tag string
|
||||
|
||||
// Common Tag values
|
||||
const (
|
||||
TagHTTPMethod Tag = "http.method"
|
||||
TagHTTPPath Tag = "http.path"
|
||||
TagHTTPUrl Tag = "http.url"
|
||||
TagHTTPStatusCode Tag = "http.status_code"
|
||||
TagHTTPRequestSize Tag = "http.request.size"
|
||||
TagHTTPResponseSize Tag = "http.response.size"
|
||||
TagGRPCStatusCode Tag = "grpc.status_code"
|
||||
TagError Tag = "error"
|
||||
)
|
||||
|
||||
// Set a standard Tag with a payload on provided Span.
|
||||
func (t Tag) Set(s Span, value string) {
|
||||
s.Tag(string(t), value)
|
||||
}
|
||||
173
vendor/github.com/openzipkin/zipkin-go/tracer.go
generated
vendored
Normal file
173
vendor/github.com/openzipkin/zipkin-go/tracer.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/propagation"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
// Tracer is our Zipkin tracer implementation. It should be initialized using
|
||||
// the NewTracer method.
|
||||
type Tracer struct {
|
||||
defaultTags map[string]string
|
||||
extractFailurePolicy ExtractFailurePolicy
|
||||
sampler Sampler
|
||||
generate idgenerator.IDGenerator
|
||||
reporter reporter.Reporter
|
||||
localEndpoint *model.Endpoint
|
||||
noop int32 // used as atomic bool (1 = true, 0 = false)
|
||||
sharedSpans bool
|
||||
unsampledNoop bool
|
||||
}
|
||||
|
||||
// NewTracer returns a new Zipkin Tracer.
|
||||
func NewTracer(rep reporter.Reporter, opts ...TracerOption) (*Tracer, error) {
|
||||
// set default tracer options
|
||||
t := &Tracer{
|
||||
defaultTags: make(map[string]string),
|
||||
extractFailurePolicy: ExtractFailurePolicyRestart,
|
||||
sampler: alwaysSample,
|
||||
generate: idgenerator.NewRandom64(),
|
||||
reporter: rep,
|
||||
localEndpoint: nil,
|
||||
noop: 0,
|
||||
sharedSpans: true,
|
||||
unsampledNoop: false,
|
||||
}
|
||||
|
||||
// if no reporter was provided we default to noop implementation.
|
||||
if t.reporter == nil {
|
||||
t.reporter = reporter.NewNoopReporter()
|
||||
t.noop = 1
|
||||
}
|
||||
|
||||
// process functional options
|
||||
for _, opt := range opts {
|
||||
if err := opt(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// StartSpanFromContext creates and starts a span using the span found in
|
||||
// context as parent. If no parent span is found a root span is created.
|
||||
func (t *Tracer) StartSpanFromContext(ctx context.Context, name string, options ...SpanOption) (Span, context.Context) {
|
||||
if parentSpan := SpanFromContext(ctx); parentSpan != nil {
|
||||
options = append(options, Parent(parentSpan.Context()))
|
||||
}
|
||||
span := t.StartSpan(name, options...)
|
||||
return span, NewContext(ctx, span)
|
||||
}
|
||||
|
||||
// StartSpan creates and starts a span.
|
||||
func (t *Tracer) StartSpan(name string, options ...SpanOption) Span {
|
||||
if atomic.LoadInt32(&t.noop) == 1 {
|
||||
return &noopSpan{}
|
||||
}
|
||||
s := &spanImpl{
|
||||
SpanModel: model.SpanModel{
|
||||
Kind: model.Undetermined,
|
||||
Name: name,
|
||||
LocalEndpoint: t.localEndpoint,
|
||||
Annotations: make([]model.Annotation, 0),
|
||||
Tags: make(map[string]string),
|
||||
},
|
||||
flushOnFinish: true,
|
||||
tracer: t,
|
||||
}
|
||||
|
||||
// add default tracer tags to span
|
||||
for k, v := range t.defaultTags {
|
||||
s.Tag(k, v)
|
||||
}
|
||||
|
||||
// handle provided functional options
|
||||
for _, option := range options {
|
||||
option(t, s)
|
||||
}
|
||||
|
||||
if s.TraceID.Empty() {
|
||||
// create root span
|
||||
s.SpanContext.TraceID = t.generate.TraceID()
|
||||
s.SpanContext.ID = t.generate.SpanID(s.SpanContext.TraceID)
|
||||
} else {
|
||||
// valid parent context found
|
||||
if t.sharedSpans && s.Kind == model.Server {
|
||||
// join span
|
||||
s.Shared = true
|
||||
} else {
|
||||
// regular child span
|
||||
parentID := s.SpanContext.ID
|
||||
s.SpanContext.ParentID = &parentID
|
||||
s.SpanContext.ID = t.generate.SpanID(model.TraceID{})
|
||||
}
|
||||
}
|
||||
|
||||
if !s.SpanContext.Debug && s.Sampled == nil {
|
||||
// deferred sampled context found, invoke sampler
|
||||
sampled := t.sampler(s.SpanContext.TraceID.Low)
|
||||
s.SpanContext.Sampled = &sampled
|
||||
if sampled {
|
||||
s.mustCollect = 1
|
||||
}
|
||||
} else {
|
||||
if s.SpanContext.Debug || *s.Sampled {
|
||||
s.mustCollect = 1
|
||||
}
|
||||
}
|
||||
|
||||
if t.unsampledNoop && s.mustCollect == 0 {
|
||||
// trace not being sampled and noop requested
|
||||
return &noopSpan{
|
||||
SpanContext: s.SpanContext,
|
||||
}
|
||||
}
|
||||
|
||||
// add start time
|
||||
if s.Timestamp.IsZero() {
|
||||
s.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Extract extracts a SpanContext using the provided Extractor function.
|
||||
func (t *Tracer) Extract(extractor propagation.Extractor) (sc model.SpanContext) {
|
||||
if atomic.LoadInt32(&t.noop) == 1 {
|
||||
return
|
||||
}
|
||||
psc, err := extractor()
|
||||
if psc != nil {
|
||||
sc = *psc
|
||||
}
|
||||
sc.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
// SetNoop allows for killswitch behavior. If set to true the tracer will return
|
||||
// noopSpans and all data is dropped. This allows operators to stop tracing in
|
||||
// risk scenarios. Set back to false to resume tracing.
|
||||
func (t *Tracer) SetNoop(noop bool) {
|
||||
if noop {
|
||||
atomic.CompareAndSwapInt32(&t.noop, 0, 1)
|
||||
} else {
|
||||
atomic.CompareAndSwapInt32(&t.noop, 1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// LocalEndpoint returns a copy of the currently set local endpoint of the
|
||||
// tracer instance.
|
||||
func (t *Tracer) LocalEndpoint() *model.Endpoint {
|
||||
if t.localEndpoint == nil {
|
||||
return nil
|
||||
}
|
||||
ep := *t.localEndpoint
|
||||
return &ep
|
||||
}
|
||||
124
vendor/github.com/openzipkin/zipkin-go/tracer_options.go
generated
vendored
Normal file
124
vendor/github.com/openzipkin/zipkin-go/tracer_options.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
)
|
||||
|
||||
// Tracer Option Errors
|
||||
var (
|
||||
ErrInvalidEndpoint = errors.New("requires valid local endpoint")
|
||||
ErrInvalidExtractFailurePolicy = errors.New("invalid extract failure policy provided")
|
||||
)
|
||||
|
||||
// ExtractFailurePolicy deals with Extraction errors
|
||||
type ExtractFailurePolicy int
|
||||
|
||||
// ExtractFailurePolicyOptions
|
||||
const (
|
||||
ExtractFailurePolicyRestart ExtractFailurePolicy = iota
|
||||
ExtractFailurePolicyError
|
||||
ExtractFailurePolicyTagAndRestart
|
||||
)
|
||||
|
||||
// TracerOption allows for functional options to adjust behavior of the Tracer
|
||||
// to be created with NewTracer().
|
||||
type TracerOption func(o *Tracer) error
|
||||
|
||||
// WithLocalEndpoint sets the local endpoint of the tracer.
|
||||
func WithLocalEndpoint(e *model.Endpoint) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
if e == nil {
|
||||
o.localEndpoint = nil
|
||||
return nil
|
||||
}
|
||||
ep := *e
|
||||
o.localEndpoint = &ep
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithExtractFailurePolicy allows one to set the ExtractFailurePolicy.
|
||||
func WithExtractFailurePolicy(p ExtractFailurePolicy) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
if p < 0 || p > ExtractFailurePolicyTagAndRestart {
|
||||
return ErrInvalidExtractFailurePolicy
|
||||
}
|
||||
o.extractFailurePolicy = p
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoopSpan if set to true will switch to a NoopSpan implementation
|
||||
// if the trace is not sampled.
|
||||
func WithNoopSpan(unsampledNoop bool) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
o.unsampledNoop = unsampledNoop
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSharedSpans allows to place client-side and server-side annotations
|
||||
// for a RPC call in the same span (Zipkin V1 behavior) or different spans
|
||||
// (more in line with other tracing solutions). By default this Tracer
|
||||
// uses shared host spans (so client-side and server-side in the same span).
|
||||
func WithSharedSpans(val bool) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
o.sharedSpans = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSampler allows one to set a Sampler function
|
||||
func WithSampler(sampler Sampler) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
o.sampler = sampler
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTraceID128Bit if set to true will instruct the Tracer to start traces
|
||||
// with 128 bit TraceID's. If set to false the Tracer will start traces with
|
||||
// 64 bits.
|
||||
func WithTraceID128Bit(val bool) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
if val {
|
||||
o.generate = idgenerator.NewRandom128()
|
||||
} else {
|
||||
o.generate = idgenerator.NewRandom64()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithIDGenerator allows one to set a custom ID Generator
|
||||
func WithIDGenerator(generator idgenerator.IDGenerator) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
o.generate = generator
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTags allows one to set default tags to be added to each created span
|
||||
func WithTags(tags map[string]string) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
for k, v := range tags {
|
||||
o.defaultTags[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoopTracer allows one to start the Tracer as Noop implementation.
|
||||
func WithNoopTracer(tracerNoop bool) TracerOption {
|
||||
return func(o *Tracer) error {
|
||||
if tracerNoop {
|
||||
o.noop = 1
|
||||
} else {
|
||||
o.noop = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
782
vendor/github.com/openzipkin/zipkin-go/tracer_test.go
generated
vendored
Normal file
782
vendor/github.com/openzipkin/zipkin-go/tracer_test.go
generated
vendored
Normal file
@@ -0,0 +1,782 @@
|
||||
package zipkin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/openzipkin/zipkin-go/idgenerator"
|
||||
"github.com/openzipkin/zipkin-go/model"
|
||||
"github.com/openzipkin/zipkin-go/reporter"
|
||||
)
|
||||
|
||||
func TestTracerOptionLocalEndpoint(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
wantEP *model.Endpoint
|
||||
)
|
||||
|
||||
tr, err := NewTracer(nil, WithLocalEndpoint(nil))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected tracer creation failure: %+v", err.Error())
|
||||
}
|
||||
|
||||
if tr == nil {
|
||||
t.Error("expected valid tracer, got: nil")
|
||||
}
|
||||
|
||||
if want, have := wantEP, tr.LocalEndpoint(); !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("local Endpoint want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
wantEP, err = NewEndpoint("testService", "localhost:80")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid endpoint, got error: %+v", err)
|
||||
}
|
||||
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err = NewTracer(rep, WithLocalEndpoint(wantEP))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
if tr == nil {
|
||||
t.Error("expected valid tracer, got nil")
|
||||
}
|
||||
|
||||
haveEP := tr.LocalEndpoint()
|
||||
|
||||
if want, have := wantEP.ServiceName, haveEP.ServiceName; want != have {
|
||||
t.Errorf("ServiceName want %s, have %s", want, have)
|
||||
}
|
||||
|
||||
if !wantEP.IPv4.Equal(haveEP.IPv4) {
|
||||
t.Errorf(" IPv4 want %+v, have %+v", wantEP.IPv4, haveEP.IPv4)
|
||||
}
|
||||
|
||||
if !wantEP.IPv6.Equal(haveEP.IPv6) {
|
||||
t.Errorf("IPv6 want %+v, have %+v", wantEP.IPv6, haveEP.IPv6)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTracerOptionExtractFailurePolicy(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
policies := []struct {
|
||||
policy ExtractFailurePolicy
|
||||
err error
|
||||
}{
|
||||
{-1, ErrInvalidExtractFailurePolicy},
|
||||
{ExtractFailurePolicyRestart, nil},
|
||||
{ExtractFailurePolicyError, nil},
|
||||
{ExtractFailurePolicyTagAndRestart, nil},
|
||||
{3, ErrInvalidExtractFailurePolicy},
|
||||
}
|
||||
|
||||
for idx, item := range policies {
|
||||
tr, err := NewTracer(rep, WithExtractFailurePolicy(item.policy))
|
||||
|
||||
if want, have := item.err, err; want != have {
|
||||
t.Fatalf("[%d] expected tracer creation failure: want %+v, have %+v", idx, item.err, err)
|
||||
}
|
||||
|
||||
if err != nil && tr != nil {
|
||||
t.Fatalf("[%d] expected tracer to be nil, have: %+v", idx, tr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
tr, _ = NewTracer(rep)
|
||||
tr.extractFailurePolicy = item.policy
|
||||
}
|
||||
|
||||
errStr := failSpan(t, tr, idx, item.err)
|
||||
if item.policy == ExtractFailurePolicyTagAndRestart {
|
||||
if want, have := "dummy", errStr; want != have {
|
||||
t.Errorf("[%d] tag[error.extract tag] want %s, have %s", idx, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func failSpan(t *testing.T, tr *Tracer, idx int, want error) string {
|
||||
sc := model.SpanContext{
|
||||
Err: errors.New("dummy"),
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if err != want {
|
||||
t.Errorf("[%d] Context Error want %+v, have %+v", idx, want, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
sp := tr.StartSpan("test", Parent(sc))
|
||||
sp.Finish()
|
||||
return sp.(*spanImpl).Tags["error.extract"]
|
||||
}
|
||||
|
||||
func TestTracerIDGeneratorOption(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
gen := idgenerator.NewRandomTimestamped()
|
||||
|
||||
tr, err := NewTracer(rep, WithIDGenerator(gen))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := gen, tr.generate; want != have {
|
||||
t.Errorf("id generator want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTracerWithTraceID128BitOption(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithTraceID128Bit(false))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := reflect.TypeOf(idgenerator.NewRandom64()), reflect.TypeOf(tr.generate); want != have {
|
||||
t.Errorf("id generator want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
tr, err = NewTracer(rep, WithTraceID128Bit(true))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
if want, have := reflect.TypeOf(idgenerator.NewRandom128()), reflect.TypeOf(tr.generate); want != have {
|
||||
t.Errorf("id generator want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTracerExtractor(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
testErr1 := errors.New("extractor error")
|
||||
extractorErr := func() (*model.SpanContext, error) {
|
||||
return nil, testErr1
|
||||
}
|
||||
|
||||
sc := tr.Extract(extractorErr)
|
||||
|
||||
if want, have := testErr1, sc.Err; want != have {
|
||||
t.Errorf("Err want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
spanContext := model.SpanContext{}
|
||||
extractor := func() (*model.SpanContext, error) {
|
||||
return &spanContext, nil
|
||||
}
|
||||
|
||||
sc = tr.Extract(extractor)
|
||||
|
||||
if want, have := spanContext, sc; want != have {
|
||||
t.Errorf("SpanContext want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := &spanContext, ≻ want == have {
|
||||
t.Error("expected different span context objects")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoopTracer(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(1),
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&spanImpl{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
span.Finish()
|
||||
|
||||
tr.SetNoop(true)
|
||||
|
||||
testErr1 := errors.New("extractor error")
|
||||
extractor := func() (*model.SpanContext, error) {
|
||||
return nil, testErr1
|
||||
}
|
||||
|
||||
sc := tr.Extract(extractor)
|
||||
|
||||
if sc.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", sc.Err)
|
||||
}
|
||||
|
||||
span = tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&noopSpan{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
span.Finish()
|
||||
|
||||
tr.SetNoop(false)
|
||||
|
||||
span = tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&spanImpl{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
span.Finish()
|
||||
|
||||
tr, err = NewTracer(rep, WithNoopTracer(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
testErr1 = errors.New("extractor error")
|
||||
extractor = func() (*model.SpanContext, error) {
|
||||
return nil, testErr1
|
||||
}
|
||||
|
||||
sc = tr.Extract(extractor)
|
||||
|
||||
if sc.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", sc.Err)
|
||||
}
|
||||
|
||||
span = tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&noopSpan{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
tr, err = NewTracer(rep, WithNoopTracer(false))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
span = tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&spanImpl{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoopSpan(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithNoopSpan(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
sampled := false
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(1),
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&noopSpan{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
func TestUnsampledSpan(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithTraceID128Bit(false))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
sampled := false
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(1),
|
||||
Sampled: &sampled,
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
if want, have := reflect.TypeOf(&spanImpl{}), reflect.TypeOf(span); want != have {
|
||||
t.Errorf("span implementation type want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
cSC := span.Context()
|
||||
|
||||
if cSC.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", cSC.Err)
|
||||
}
|
||||
|
||||
if want, have := pSC.Debug, cSC.Debug; want != have {
|
||||
t.Errorf("Debug want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
if want, have := pSC.TraceID, cSC.TraceID; want != have {
|
||||
t.Errorf("TraceID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.ID == 0 {
|
||||
t.Error("ID want valid value, have 0")
|
||||
}
|
||||
|
||||
if cSC.ParentID == nil {
|
||||
t.Errorf("ParentID want %+v, have nil", pSC.ID)
|
||||
} else if want, have := pSC.ID, *cSC.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.Sampled == nil {
|
||||
t.Error("Sampled want false, have nil")
|
||||
} else if *cSC.Sampled {
|
||||
t.Errorf("Sampled want false, have %+v", *cSC.Sampled)
|
||||
}
|
||||
|
||||
if want, have := int32(0), span.(*spanImpl).mustCollect; want != have {
|
||||
t.Errorf("expected mustCollect %d, got %d", want, have)
|
||||
}
|
||||
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
func TestDefaultTags(t *testing.T) {
|
||||
var (
|
||||
scTagKey = "spanScopedTag"
|
||||
scTagValue = "spanPayload"
|
||||
tags = make(map[string]string)
|
||||
)
|
||||
tags["platform"] = "zipkin_test"
|
||||
tags["version"] = "1.0"
|
||||
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithTags(tags), WithTraceID128Bit(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(1),
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Kind(model.Server), Parent(pSC))
|
||||
span.Tag(scTagKey, scTagValue)
|
||||
|
||||
foundTags := span.(*spanImpl).Tags
|
||||
|
||||
for key, value := range tags {
|
||||
foundValue, foundKey := foundTags[key]
|
||||
if !foundKey {
|
||||
t.Errorf("Tag want %s=%s, have key not found", key, value)
|
||||
} else if value != foundValue {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", key, value, key, foundValue)
|
||||
}
|
||||
}
|
||||
|
||||
foundValue, foundKey := foundTags[scTagKey]
|
||||
if !foundKey {
|
||||
t.Errorf("Tag want %s=%s, have key not found", scTagKey, scTagValue)
|
||||
} else if want, have := scTagValue, foundValue; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", scTagKey, scTagValue, scTagKey, foundValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagOverwriteRules(t *testing.T) {
|
||||
var (
|
||||
k1 = "key1"
|
||||
v1First = "value to overwrite"
|
||||
v1Last = "value to keep"
|
||||
k2 = string(TagError)
|
||||
)
|
||||
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithIDGenerator(idgenerator.NewRandomTimestamped()))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
s := tr.StartSpan("test_tags")
|
||||
defer s.Finish()
|
||||
|
||||
s.Tag(k1, v1First)
|
||||
|
||||
if want, have := v1First, s.(*spanImpl).Tags[k1]; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", k1, want, k1, have)
|
||||
}
|
||||
|
||||
s.Tag(k1, v1Last)
|
||||
|
||||
if want, have := v1Last, s.(*spanImpl).Tags[k1]; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", k1, want, k1, have)
|
||||
}
|
||||
|
||||
s.Tag(k2, v1First)
|
||||
|
||||
if want, have := v1First, s.(*spanImpl).Tags[k2]; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", k1, want, k1, have)
|
||||
}
|
||||
|
||||
s.Tag(k2, v1Last)
|
||||
|
||||
if want, have := v1First, s.(*spanImpl).Tags[k2]; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", k1, want, k1, have)
|
||||
}
|
||||
|
||||
TagError.Set(s, v1Last)
|
||||
|
||||
if want, have := v1First, s.(*spanImpl).Tags[k2]; want != have {
|
||||
t.Errorf("Tag want %s=%s, have %s=%s", k1, want, k1, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotations(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
s := tr.StartSpan("test_tags")
|
||||
defer s.Finish()
|
||||
|
||||
annotations := []model.Annotation{
|
||||
{
|
||||
Timestamp: time.Now().Add(10 * time.Millisecond),
|
||||
Value: "annotation 1",
|
||||
},
|
||||
{
|
||||
Timestamp: time.Now().Add(20 * time.Millisecond),
|
||||
Value: "annotation 2",
|
||||
},
|
||||
{
|
||||
Timestamp: time.Now().Add(30 * time.Millisecond),
|
||||
Value: "annotation 3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, annotation := range annotations {
|
||||
s.Annotate(annotation.Timestamp, annotation.Value)
|
||||
}
|
||||
|
||||
time.Sleep(40 * time.Millisecond)
|
||||
|
||||
if want, have := len(annotations), len(s.(*spanImpl).Annotations); want != have {
|
||||
t.Fatalf("Annotation count want %d, have %d", want, have)
|
||||
}
|
||||
|
||||
for idx, annotation := range annotations {
|
||||
if want, have := annotation, s.(*spanImpl).Annotations[idx]; want != have {
|
||||
t.Errorf("Annotation #%d want %+v, have %+v", idx, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExplicitStartTime(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithSampler(NewModuloSampler(2)))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
st := time.Now()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
s := tr.StartSpan("test_tags", StartTime(st))
|
||||
defer s.Finish()
|
||||
|
||||
if want, have := st, s.(*spanImpl).Timestamp; want != have {
|
||||
t.Errorf("Timestamp want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebugFlagWithoutParentTrace(t *testing.T) {
|
||||
/*
|
||||
Test handling of a single Debug flag without an existing trace
|
||||
*/
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithSharedSpans(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
pSC := model.SpanContext{
|
||||
Debug: true,
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Parent(pSC))
|
||||
|
||||
cSC := span.Context()
|
||||
|
||||
if cSC.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", cSC.Err)
|
||||
}
|
||||
|
||||
if want, have := pSC.Debug, cSC.Debug; want != have {
|
||||
t.Errorf("Debug want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
if want, have := false, cSC.TraceID.Empty(); want != have {
|
||||
t.Error("expected valid TraceID")
|
||||
}
|
||||
|
||||
if cSC.ID == 0 {
|
||||
t.Error("expected valid ID")
|
||||
}
|
||||
|
||||
if cSC.ParentID != nil {
|
||||
t.Errorf("ParentID want nil, have %+v", cSC.ParentID)
|
||||
}
|
||||
|
||||
if cSC.Sampled != nil {
|
||||
t.Errorf("Sampled want nil, have %+v", cSC.Sampled)
|
||||
}
|
||||
|
||||
if want, have := int32(1), span.(*spanImpl).mustCollect; want != have {
|
||||
t.Errorf("mustCollect want %d, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParentSpanInSharedMode(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithSharedSpans(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
parentID := model.ID(1)
|
||||
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(2),
|
||||
ParentID: &parentID,
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Kind(model.Server), Parent(pSC))
|
||||
|
||||
cSC := span.Context()
|
||||
|
||||
if cSC.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", cSC.Err)
|
||||
}
|
||||
|
||||
if want, have := pSC.Debug, cSC.Debug; want != have {
|
||||
t.Errorf("Debug want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
if want, have := pSC.TraceID, cSC.TraceID; want != have {
|
||||
t.Errorf("TraceID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := pSC.ID, cSC.ID; want != have {
|
||||
t.Errorf("ID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.ParentID == nil {
|
||||
t.Error("ParentID want valid value, have nil")
|
||||
} else if want, have := parentID, *cSC.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.Sampled == nil {
|
||||
t.Error("Sampled want explicit value, have nil")
|
||||
} else if !*cSC.Sampled {
|
||||
t.Errorf("Sampled want true, have %+v", *cSC.Sampled)
|
||||
}
|
||||
|
||||
if want, have := int32(1), span.(*spanImpl).mustCollect; want != have {
|
||||
t.Errorf("mustCollect want %d, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParentSpanInSpanPerNodeMode(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithSharedSpans(false))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
pSC := model.SpanContext{
|
||||
TraceID: model.TraceID{
|
||||
High: 0,
|
||||
Low: 1,
|
||||
},
|
||||
ID: model.ID(1),
|
||||
}
|
||||
|
||||
span := tr.StartSpan("test", Kind(model.Server), Parent(pSC))
|
||||
|
||||
cSC := span.Context()
|
||||
|
||||
if cSC.Err != nil {
|
||||
t.Errorf("Err want nil, have %+v", cSC.Err)
|
||||
}
|
||||
|
||||
if want, have := pSC.Debug, cSC.Debug; want != have {
|
||||
t.Errorf("Debug want %t, have %t", want, have)
|
||||
}
|
||||
|
||||
if want, have := pSC.TraceID, cSC.TraceID; want != have {
|
||||
t.Errorf("TraceID want %+v, have: %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.ID == 0 {
|
||||
t.Error("expected valid ID")
|
||||
}
|
||||
|
||||
if cSC.ParentID == nil {
|
||||
t.Error("ParentID want valid value, have nil")
|
||||
} else if want, have := pSC.ID, *cSC.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if cSC.Sampled == nil {
|
||||
t.Error("Sampled want explicit value, have nil")
|
||||
} else if !*cSC.Sampled {
|
||||
t.Errorf("Sampled want true, have %+v", *cSC.Sampled)
|
||||
}
|
||||
|
||||
if want, have := int32(1), span.(*spanImpl).mustCollect; want != have {
|
||||
t.Errorf("mustCollect want %d, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartSpanFromContext(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
tr, err := NewTracer(rep, WithSharedSpans(true))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create tracer instance: %+v", err)
|
||||
}
|
||||
|
||||
if ctxSpan := SpanFromContext(ctx); ctxSpan != nil {
|
||||
t.Errorf("SpanFromContext want nil, have %+v", ctxSpan)
|
||||
}
|
||||
|
||||
cSpan := tr.StartSpan("test", Kind(model.Client))
|
||||
|
||||
ctx = NewContext(ctx, cSpan)
|
||||
|
||||
sSpan, _ := tr.StartSpanFromContext(ctx, "testChild", Kind(model.Server))
|
||||
|
||||
cS, sS := cSpan.(*spanImpl), sSpan.(*spanImpl)
|
||||
|
||||
if want, have := model.Client, cS.Kind; want != have {
|
||||
t.Errorf("Kind want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := model.Server, sS.Kind; want != have {
|
||||
t.Errorf("Kind want %+v, have: %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := cS.TraceID, sS.TraceID; want != have {
|
||||
t.Errorf("TraceID want %+v, have: %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := cS.ID, sS.ID; want != have {
|
||||
t.Errorf("ID want %+v, have %+v", want, have)
|
||||
}
|
||||
|
||||
if want, have := cS.ParentID, sS.ParentID; want != have {
|
||||
t.Errorf("ParentID want %+v, have %+v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalEndpoint(t *testing.T) {
|
||||
rep := reporter.NewNoopReporter()
|
||||
defer rep.Close()
|
||||
|
||||
ep, err := NewEndpoint("my service", "localhost:80")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid endpoint, got error: %+v", err)
|
||||
}
|
||||
|
||||
tracer, err := NewTracer(rep, WithLocalEndpoint(ep))
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid tracer, got error: %+v", err)
|
||||
}
|
||||
|
||||
want, have := ep, tracer.LocalEndpoint()
|
||||
|
||||
if have == nil {
|
||||
t.Fatalf("endpoint want %+v, have nil", want)
|
||||
}
|
||||
|
||||
if want.ServiceName != have.ServiceName {
|
||||
t.Errorf("serviceName want %s, have %s", want.ServiceName, have.ServiceName)
|
||||
}
|
||||
|
||||
if !want.IPv4.Equal(have.IPv4) {
|
||||
t.Errorf("IPv4 endpoint want %+v, have %+v", want.IPv4, have.IPv4)
|
||||
}
|
||||
|
||||
if !want.IPv6.Equal(have.IPv6) {
|
||||
t.Errorf("IPv6 endpoint want %+v, have %+v", want.IPv6, have.IPv6)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user