Commit 02975256 by Jonathan Rockway Committed by Carl Bergquist

Tracing: allow propagation with Zipkin headers (#17009)

Closes #17006
parent 1c3ad786
...@@ -553,6 +553,11 @@ sampler_type = const ...@@ -553,6 +553,11 @@ sampler_type = const
# and indicates the initial sampling rate before the actual one # and indicates the initial sampling rate before the actual one
# is received from the mothership # is received from the mothership
sampler_param = 1 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 ##############
[external_image_storage] [external_image_storage]
......
...@@ -476,6 +476,11 @@ log_queries = ...@@ -476,6 +476,11 @@ log_queries =
# and indicates the initial sampling rate before the actual one # and indicates the initial sampling rate before the actual one
# is received from the mothership # is received from the mothership
;sampler_param = 1 ;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 ########################## #################################### Grafana.com integration ##########################
# Url used to import dashboards directly from Grafana.com # Url used to import dashboards directly from Grafana.com
...@@ -530,4 +535,3 @@ log_queries = ...@@ -530,4 +535,3 @@ log_queries =
[plugins] [plugins]
;enable_alpha = false ;enable_alpha = false
;app_tls_skip_verify_insecure = false ;app_tls_skip_verify_insecure = false
...@@ -2,6 +2,7 @@ package tracing ...@@ -2,6 +2,7 @@ package tracing
import ( import (
"context" "context"
"fmt"
"io" "io"
"strings" "strings"
...@@ -11,6 +12,7 @@ import ( ...@@ -11,6 +12,7 @@ import (
opentracing "github.com/opentracing/opentracing-go" opentracing "github.com/opentracing/opentracing-go"
jaegercfg "github.com/uber/jaeger-client-go/config" jaegercfg "github.com/uber/jaeger-client-go/config"
"github.com/uber/jaeger-client-go/zipkin"
) )
func init() { func init() {
...@@ -18,13 +20,15 @@ func init() { ...@@ -18,13 +20,15 @@ func init() {
} }
type TracingService struct { type TracingService struct {
enabled bool enabled bool
address string address string
customTags map[string]string customTags map[string]string
samplerType string samplerType string
samplerParam float64 samplerParam float64
log log.Logger log log.Logger
closer io.Closer closer io.Closer
zipkinPropagation bool
disableSharedZipkinSpans bool
Cfg *setting.Cfg `inject:""` Cfg *setting.Cfg `inject:""`
} }
...@@ -54,6 +58,8 @@ func (ts *TracingService) parseSettings() { ...@@ -54,6 +58,8 @@ func (ts *TracingService) parseSettings() {
ts.customTags = splitTagSettings(section.Key("always_included_tag").MustString("")) ts.customTags = splitTagSettings(section.Key("always_included_tag").MustString(""))
ts.samplerType = section.Key("sampler_type").MustString("") ts.samplerType = section.Key("sampler_type").MustString("")
ts.samplerParam = section.Key("sampler_param").MustFloat64(1) 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 { func (ts *TracingService) initGlobalTracer() error {
...@@ -79,6 +85,18 @@ func (ts *TracingService) initGlobalTracer() error { ...@@ -79,6 +85,18 @@ func (ts *TracingService) initGlobalTracer() error {
options = append(options, jaegercfg.Tag(tag, value)) 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...) tracer, closer, err := cfg.NewTracer(options...)
if err != nil { if err != nil {
return err return err
...@@ -124,6 +142,7 @@ func (jlw *jaegerLogWrapper) Error(msg string) { ...@@ -124,6 +142,7 @@ func (jlw *jaegerLogWrapper) Error(msg string) {
jlw.logger.Error(msg) jlw.logger.Error(msg)
} }
func (jlw *jaegerLogWrapper) Infof(msg string, args ...interface{}) { func (jlw *jaegerLogWrapper) Infof(format string, args ...interface{}) {
jlw.logger.Info(msg, args) msg := fmt.Sprintf(format, args...)
jlw.logger.Info(msg)
} }
# 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()
}
```
// 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
// 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
}
...@@ -194,6 +194,7 @@ github.com/stretchr/testify/assert ...@@ -194,6 +194,7 @@ github.com/stretchr/testify/assert
github.com/teris-io/shortid github.com/teris-io/shortid
# github.com/uber/jaeger-client-go v2.16.0+incompatible # github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-client-go/config 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
github.com/uber/jaeger-client-go/internal/baggage/remote github.com/uber/jaeger-client-go/internal/baggage/remote
github.com/uber/jaeger-client-go/internal/throttler/remote github.com/uber/jaeger-client-go/internal/throttler/remote
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment