From 02975256d1225b4976387a0a3f0bf7858077e48f Mon Sep 17 00:00:00 2001 From: Jonathan Rockway Date: Wed, 5 Jun 2019 07:12:05 -0400 Subject: [PATCH] Tracing: allow propagation with Zipkin headers (#17009) Closes #17006 --- conf/defaults.ini | 5 + conf/sample.ini | 6 +- pkg/infra/tracing/tracing.go | 37 ++++-- .../uber/jaeger-client-go/zipkin/README.md | 91 +++++++++++++ .../uber/jaeger-client-go/zipkin/doc.go | 16 +++ .../jaeger-client-go/zipkin/propagation.go | 121 ++++++++++++++++++ vendor/modules.txt | 1 + 7 files changed, 267 insertions(+), 10 deletions(-) create mode 100644 vendor/github.com/uber/jaeger-client-go/zipkin/README.md create mode 100644 vendor/github.com/uber/jaeger-client-go/zipkin/doc.go create mode 100644 vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go diff --git a/conf/defaults.ini b/conf/defaults.ini index ec83a5ea1a5..3cabe6c1aff 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -553,6 +553,11 @@ sampler_type = const # and indicates the initial sampling rate before the actual one # is received from the mothership sampler_param = 1 +# Whether or not to use Zipkin span propagation (x-b3- HTTP headers). +zipkin_propagation = false +# Setting this to true disables shared RPC spans. +# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure. +disable_shared_zipkin_spans = false #################################### External Image Storage ############## [external_image_storage] diff --git a/conf/sample.ini b/conf/sample.ini index dc7d5bc5467..8e71ed9c96d 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -476,6 +476,11 @@ log_queries = # and indicates the initial sampling rate before the actual one # is received from the mothership ;sampler_param = 1 +# Whether or not to use Zipkin propagation (x-b3- HTTP headers). +;zipkin_propagation = false +# Setting this to true disables shared RPC spans. +# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure. +;disable_shared_zipkin_spans = false #################################### Grafana.com integration ########################## # Url used to import dashboards directly from Grafana.com @@ -530,4 +535,3 @@ log_queries = [plugins] ;enable_alpha = false ;app_tls_skip_verify_insecure = false - diff --git a/pkg/infra/tracing/tracing.go b/pkg/infra/tracing/tracing.go index 2e1b75e81d1..d1511a53341 100644 --- a/pkg/infra/tracing/tracing.go +++ b/pkg/infra/tracing/tracing.go @@ -2,6 +2,7 @@ package tracing import ( "context" + "fmt" "io" "strings" @@ -11,6 +12,7 @@ import ( opentracing "github.com/opentracing/opentracing-go" jaegercfg "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/zipkin" ) func init() { @@ -18,13 +20,15 @@ func init() { } type TracingService struct { - enabled bool - address string - customTags map[string]string - samplerType string - samplerParam float64 - log log.Logger - closer io.Closer + enabled bool + address string + customTags map[string]string + samplerType string + samplerParam float64 + log log.Logger + closer io.Closer + zipkinPropagation bool + disableSharedZipkinSpans bool Cfg *setting.Cfg `inject:""` } @@ -54,6 +58,8 @@ func (ts *TracingService) parseSettings() { ts.customTags = splitTagSettings(section.Key("always_included_tag").MustString("")) ts.samplerType = section.Key("sampler_type").MustString("") ts.samplerParam = section.Key("sampler_param").MustFloat64(1) + ts.zipkinPropagation = section.Key("zipkin_propagation").MustBool(false) + ts.disableSharedZipkinSpans = section.Key("disable_shared_zipkin_spans").MustBool(false) } func (ts *TracingService) initGlobalTracer() error { @@ -79,6 +85,18 @@ func (ts *TracingService) initGlobalTracer() error { options = append(options, jaegercfg.Tag(tag, value)) } + if ts.zipkinPropagation { + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + options = append(options, + jaegercfg.Injector(opentracing.HTTPHeaders, zipkinPropagator), + jaegercfg.Extractor(opentracing.HTTPHeaders, zipkinPropagator), + ) + + if !ts.disableSharedZipkinSpans { + options = append(options, jaegercfg.ZipkinSharedRPCSpan(true)) + } + } + tracer, closer, err := cfg.NewTracer(options...) if err != nil { return err @@ -124,6 +142,7 @@ func (jlw *jaegerLogWrapper) Error(msg string) { jlw.logger.Error(msg) } -func (jlw *jaegerLogWrapper) Infof(msg string, args ...interface{}) { - jlw.logger.Info(msg, args) +func (jlw *jaegerLogWrapper) Infof(format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + jlw.logger.Info(msg) } diff --git a/vendor/github.com/uber/jaeger-client-go/zipkin/README.md b/vendor/github.com/uber/jaeger-client-go/zipkin/README.md new file mode 100644 index 00000000000..6e51f10dd79 --- /dev/null +++ b/vendor/github.com/uber/jaeger-client-go/zipkin/README.md @@ -0,0 +1,91 @@ +# Zipkin compatibility features + +## `NewZipkinB3HTTPHeaderPropagator()` + +Adds support for injecting and extracting Zipkin B3 Propagation HTTP headers, +for use with other Zipkin collectors. + +```go + +// ... +import ( + opentracing "github.com/opentracing/opentracing-go" + jaeger "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/zipkin" +) + +func main() { + // ... + + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + injector := jaeger.TracerOptions.Injector(opentracing.HTTPHeaders, zipkinPropagator) + extractor := jaeger.TracerOptions.Extractor(opentracing.HTTPHeaders, zipkinPropagator) + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinSharedRPCSpan := jaeger.TracerOptions.ZipkinSharedRPCSpan(true) + + // create Jaeger tracer + tracer, closer := jaeger.NewTracer( + "myService", + mySampler, // as usual + myReporter // as usual + injector, + extractor, + zipkinSharedRPCSpan, + ) + + opentracing.SetGlobalTracer(tracer) + + // continue main() +} +``` + +If you'd like to follow the official guides from https://godoc.org/github.com/uber/jaeger-client-go/config#example-Configuration-InitGlobalTracer-Production, here is an example. + +```go +import ( + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + jaegerClientConfig "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/zipkin" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-lib/metrics" +) + +func main(){ + //... + + // Recommended configuration for production. + cfg := jaegercfg.Configuration{} + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + + // Create tracer and then initialize global tracer + closer, err := cfg.InitGlobalTracer( + serviceName, + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + jaegercfg.Injector(opentracing.HTTPHeaders, zipkinPropagator), + jaegercfg.Extractor(opentracing.HTTPHeaders, zipkinPropagator), + jaegercfg.ZipkinSharedRPCSpan(true), + ) + + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +``` diff --git a/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go b/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go new file mode 100644 index 00000000000..736e5cb309b --- /dev/null +++ b/vendor/github.com/uber/jaeger-client-go/zipkin/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package zipkin comprises Zipkin functionality for Zipkin compatibility. +package zipkin diff --git a/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go b/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go new file mode 100644 index 00000000000..722b0875de2 --- /dev/null +++ b/vendor/github.com/uber/jaeger-client-go/zipkin/propagation.go @@ -0,0 +1,121 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zipkin + +import ( + "strconv" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" +) + +// Option is a function that sets an option on Propagator +type Option func(propagator *Propagator) + +// BaggagePrefix is a function that sets baggage prefix on Propagator +func BaggagePrefix(prefix string) Option { + return func(propagator *Propagator) { + propagator.baggagePrefix = prefix + } +} + +// Propagator is an Injector and Extractor +type Propagator struct { + baggagePrefix string +} + +// NewZipkinB3HTTPHeaderPropagator creates a Propagator for extracting and injecting +// Zipkin HTTP B3 headers into SpanContexts. Baggage is by default enabled and uses prefix +// 'baggage-'. +func NewZipkinB3HTTPHeaderPropagator(opts ...Option) Propagator { + p := Propagator{baggagePrefix: "baggage-"} + for _, opt := range opts { + opt(&p) + } + return p +} + +// Inject conforms to the Injector interface for decoding Zipkin HTTP B3 headers +func (p Propagator) Inject( + sc jaeger.SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + textMapWriter.Set("x-b3-traceid", sc.TraceID().String()) + if sc.ParentID() != 0 { + textMapWriter.Set("x-b3-parentspanid", strconv.FormatUint(uint64(sc.ParentID()), 16)) + } + textMapWriter.Set("x-b3-spanid", strconv.FormatUint(uint64(sc.SpanID()), 16)) + if sc.IsSampled() { + textMapWriter.Set("x-b3-sampled", "1") + } else { + textMapWriter.Set("x-b3-sampled", "0") + } + sc.ForeachBaggageItem(func(k, v string) bool { + textMapWriter.Set(p.baggagePrefix+k, v) + return true + }) + return nil +} + +// Extract conforms to the Extractor interface for encoding Zipkin HTTP B3 headers +func (p Propagator) Extract(abstractCarrier interface{}) (jaeger.SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return jaeger.SpanContext{}, opentracing.ErrInvalidCarrier + } + var traceID jaeger.TraceID + var spanID uint64 + var parentID uint64 + sampled := false + var baggage map[string]string + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + var err error + if key == "x-b3-traceid" { + traceID, err = jaeger.TraceIDFromString(value) + } else if key == "x-b3-parentspanid" { + parentID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-spanid" { + spanID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-sampled" && (value == "1" || value == "true") { + sampled = true + } else if strings.HasPrefix(key, p.baggagePrefix) { + if baggage == nil { + baggage = make(map[string]string) + } + baggage[key[len(p.baggagePrefix):]] = value + } + return err + }) + + if err != nil { + return jaeger.SpanContext{}, err + } + if !traceID.IsValid() { + return jaeger.SpanContext{}, opentracing.ErrSpanContextNotFound + } + return jaeger.NewSpanContext( + traceID, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, baggage), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 19e66874848..ee48091e274 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -194,6 +194,7 @@ github.com/stretchr/testify/assert github.com/teris-io/shortid # github.com/uber/jaeger-client-go v2.16.0+incompatible github.com/uber/jaeger-client-go/config +github.com/uber/jaeger-client-go/zipkin github.com/uber/jaeger-client-go github.com/uber/jaeger-client-go/internal/baggage/remote github.com/uber/jaeger-client-go/internal/throttler/remote