mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-02-01 13:07:51 +02:00
547d584da8
* Add global propagators The default global propagators are set to the chained W3C trace and correlation context propagators. * Use global propagators in plugins The httptrace and grpcplugins should also get some API for setting a propagator to use (the othttp plugin already has such an API), but that can come in some other PR. * Decrease indentation in trace propagators * Drop obsolete TODOs Now we do "something" with correlation context - it ends up in the context, and we put the context into the request, so the chained HTTP handler can access it too. The other TODO was about tag.Upsert which is long gone. * Do not unnecessarily update the request context The request context already contains the span (and we add some attribute there), so inserting it into context again is pointless. Co-authored-by: Joshua MacDonald <jmacd@users.noreply.github.com>
208 lines
4.9 KiB
Go
208 lines
4.9 KiB
Go
// Copyright 2019, OpenTelemetry 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.
|
|
|
|
package trace
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"go.opentelemetry.io/otel/api/core"
|
|
"go.opentelemetry.io/otel/api/propagation"
|
|
)
|
|
|
|
const (
|
|
B3SingleHeader = "X-B3"
|
|
B3DebugFlagHeader = "X-B3-Flags"
|
|
B3TraceIDHeader = "X-B3-TraceId"
|
|
B3SpanIDHeader = "X-B3-SpanId"
|
|
B3SampledHeader = "X-B3-Sampled"
|
|
B3ParentSpanIDHeader = "X-B3-ParentSpanId"
|
|
)
|
|
|
|
// B3 propagator serializes core.SpanContext to/from B3 Headers.
|
|
// This propagator supports both version of B3 headers,
|
|
// 1. Single Header :
|
|
// X-B3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
|
|
// 2. Multiple Headers:
|
|
// X-B3-TraceId: {TraceId}
|
|
// X-B3-ParentSpanId: {ParentSpanId}
|
|
// X-B3-SpanId: {SpanId}
|
|
// X-B3-Sampled: {SamplingState}
|
|
// X-B3-Flags: {DebugFlag}
|
|
//
|
|
// If SingleHeader is set to true then X-B3 header is used to inject and extract. Otherwise,
|
|
// separate headers are used to inject and extract.
|
|
type B3 struct {
|
|
SingleHeader bool
|
|
}
|
|
|
|
var _ propagation.HTTPPropagator = B3{}
|
|
|
|
func (b3 B3) Inject(ctx context.Context, supplier propagation.HTTPSupplier) {
|
|
sc := SpanFromContext(ctx).SpanContext()
|
|
if !sc.IsValid() {
|
|
return
|
|
}
|
|
if b3.SingleHeader {
|
|
sampled := sc.TraceFlags & core.TraceFlagsSampled
|
|
supplier.Set(B3SingleHeader,
|
|
fmt.Sprintf("%s-%.16x-%.1d", sc.TraceIDString(), sc.SpanID, sampled))
|
|
} else {
|
|
supplier.Set(B3TraceIDHeader, sc.TraceIDString())
|
|
supplier.Set(B3SpanIDHeader,
|
|
fmt.Sprintf("%.16x", sc.SpanID))
|
|
|
|
var sampled string
|
|
if sc.IsSampled() {
|
|
sampled = "1"
|
|
} else {
|
|
sampled = "0"
|
|
}
|
|
supplier.Set(B3SampledHeader, sampled)
|
|
}
|
|
}
|
|
|
|
// Extract retrieves B3 Headers from the supplier
|
|
func (b3 B3) Extract(ctx context.Context, supplier propagation.HTTPSupplier) context.Context {
|
|
var sc core.SpanContext
|
|
if b3.SingleHeader {
|
|
sc = b3.extractSingleHeader(supplier)
|
|
} else {
|
|
sc = b3.extract(supplier)
|
|
}
|
|
return ContextWithRemoteSpanContext(ctx, sc)
|
|
}
|
|
|
|
func (b3 B3) extract(supplier propagation.HTTPSupplier) core.SpanContext {
|
|
tid, err := core.TraceIDFromHex(supplier.Get(B3TraceIDHeader))
|
|
if err != nil {
|
|
return core.EmptySpanContext()
|
|
}
|
|
sid, err := core.SpanIDFromHex(supplier.Get(B3SpanIDHeader))
|
|
if err != nil {
|
|
return core.EmptySpanContext()
|
|
}
|
|
sampled, ok := b3.extractSampledState(supplier.Get(B3SampledHeader))
|
|
if !ok {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
debug, ok := b3.extracDebugFlag(supplier.Get(B3DebugFlagHeader))
|
|
if !ok {
|
|
return core.EmptySpanContext()
|
|
}
|
|
if debug == core.TraceFlagsSampled {
|
|
sampled = core.TraceFlagsSampled
|
|
}
|
|
|
|
sc := core.SpanContext{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
TraceFlags: sampled,
|
|
}
|
|
|
|
if !sc.IsValid() {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
return sc
|
|
}
|
|
|
|
func (b3 B3) extractSingleHeader(supplier propagation.HTTPSupplier) core.SpanContext {
|
|
h := supplier.Get(B3SingleHeader)
|
|
if h == "" || h == "0" {
|
|
return core.EmptySpanContext()
|
|
}
|
|
sc := core.SpanContext{}
|
|
parts := strings.Split(h, "-")
|
|
l := len(parts)
|
|
if l > 4 {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
if l < 2 {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
var err error
|
|
sc.TraceID, err = core.TraceIDFromHex(parts[0])
|
|
if err != nil {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
sc.SpanID, err = core.SpanIDFromHex(parts[1])
|
|
if err != nil {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
if l > 2 {
|
|
var ok bool
|
|
sc.TraceFlags, ok = b3.extractSampledState(parts[2])
|
|
if !ok {
|
|
return core.EmptySpanContext()
|
|
}
|
|
}
|
|
if l == 4 {
|
|
_, err = core.SpanIDFromHex(parts[3])
|
|
if err != nil {
|
|
return core.EmptySpanContext()
|
|
}
|
|
}
|
|
|
|
if !sc.IsValid() {
|
|
return core.EmptySpanContext()
|
|
}
|
|
|
|
return sc
|
|
}
|
|
|
|
// extractSampledState parses the value of the X-B3-Sampled b3Header.
|
|
func (b3 B3) extractSampledState(sampled string) (flag byte, ok bool) {
|
|
switch sampled {
|
|
case "", "0":
|
|
return 0, true
|
|
case "1":
|
|
return core.TraceFlagsSampled, true
|
|
case "true":
|
|
if !b3.SingleHeader {
|
|
return core.TraceFlagsSampled, true
|
|
}
|
|
case "d":
|
|
if b3.SingleHeader {
|
|
return core.TraceFlagsSampled, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// extracDebugFlag parses the value of the X-B3-Sampled b3Header.
|
|
func (b3 B3) extracDebugFlag(debug string) (flag byte, ok bool) {
|
|
switch debug {
|
|
case "", "0":
|
|
return 0, true
|
|
case "1":
|
|
return core.TraceFlagsSampled, true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (b3 B3) GetAllKeys() []string {
|
|
if b3.SingleHeader {
|
|
return []string{B3SingleHeader}
|
|
}
|
|
return []string{B3TraceIDHeader, B3SpanIDHeader, B3SampledHeader}
|
|
}
|