You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-13 01:00:22 +02:00
Add Baggage API and move Baggage propagator (#1217)
* Move api/baggage to the propagators package * Create Baggage API to match specification * Update CHANGELOG.md * Baggage API unit tests * Rename and add unit test * Update unit test value checking * Update TODO with issue tracking work.
This commit is contained in:
@ -11,12 +11,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
### Added
|
||||
|
||||
- OTLP Metric exporter supports Histogram aggregation. (#1209)
|
||||
- A Baggage API to implement the OpenTelemetry specification. (#1217)
|
||||
|
||||
### Changed
|
||||
|
||||
- Set default propagator to no-op propagator. (#1184)
|
||||
- The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212)
|
||||
- The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212)
|
||||
- Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/propagators`. (#1217)
|
||||
|
||||
### Removed
|
||||
|
||||
@ -24,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
- The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification.
|
||||
The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212)
|
||||
- The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216)
|
||||
- The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217)
|
||||
- Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219)
|
||||
|
||||
## [0.12.0] - 2020-09-24
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright The 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 baggage provides types and utilities for baggage features.
|
||||
package baggage // import "go.opentelemetry.io/otel/api/baggage"
|
@ -1,176 +0,0 @@
|
||||
// Copyright The 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 baggage
|
||||
|
||||
import "go.opentelemetry.io/otel/label"
|
||||
|
||||
type rawMap map[label.Key]label.Value
|
||||
type keySet map[label.Key]struct{}
|
||||
|
||||
// Map is an immutable storage for correlations.
|
||||
type Map struct {
|
||||
m rawMap
|
||||
}
|
||||
|
||||
// MapUpdate contains information about correlation changes to be
|
||||
// made.
|
||||
type MapUpdate struct {
|
||||
// DropSingleK contains a single key to be dropped from
|
||||
// correlations. Use this to avoid an overhead of a slice
|
||||
// allocation if there is only one key to drop.
|
||||
DropSingleK label.Key
|
||||
// DropMultiK contains all the keys to be dropped from
|
||||
// correlations.
|
||||
DropMultiK []label.Key
|
||||
|
||||
// SingleKV contains a single key-value pair to be added to
|
||||
// correlations. Use this to avoid an overhead of a slice
|
||||
// allocation if there is only one key-value pair to add.
|
||||
SingleKV label.KeyValue
|
||||
// MultiKV contains all the key-value pairs to be added to
|
||||
// correlations.
|
||||
MultiKV []label.KeyValue
|
||||
}
|
||||
|
||||
func newMap(raw rawMap) Map {
|
||||
return Map{
|
||||
m: raw,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEmptyMap creates an empty correlations map.
|
||||
func NewEmptyMap() Map {
|
||||
return newMap(nil)
|
||||
}
|
||||
|
||||
// NewMap creates a map with the contents of the update applied. In
|
||||
// this function, having an update with DropSingleK or DropMultiK
|
||||
// makes no sense - those fields are effectively ignored.
|
||||
func NewMap(update MapUpdate) Map {
|
||||
return NewEmptyMap().Apply(update)
|
||||
}
|
||||
|
||||
// Apply creates a copy of the map with the contents of the update
|
||||
// applied. Apply will first drop the keys from DropSingleK and
|
||||
// DropMultiK, then add key-value pairs from SingleKV and MultiKV.
|
||||
func (m Map) Apply(update MapUpdate) Map {
|
||||
delSet, addSet := getModificationSets(update)
|
||||
mapSize := getNewMapSize(m.m, delSet, addSet)
|
||||
|
||||
r := make(rawMap, mapSize)
|
||||
for k, v := range m.m {
|
||||
// do not copy items we want to drop
|
||||
if _, ok := delSet[k]; ok {
|
||||
continue
|
||||
}
|
||||
// do not copy items we would overwrite
|
||||
if _, ok := addSet[k]; ok {
|
||||
continue
|
||||
}
|
||||
r[k] = v
|
||||
}
|
||||
if update.SingleKV.Key.Defined() {
|
||||
r[update.SingleKV.Key] = update.SingleKV.Value
|
||||
}
|
||||
for _, kv := range update.MultiKV {
|
||||
r[kv.Key] = kv.Value
|
||||
}
|
||||
if len(r) == 0 {
|
||||
r = nil
|
||||
}
|
||||
return newMap(r)
|
||||
}
|
||||
|
||||
func getModificationSets(update MapUpdate) (delSet, addSet keySet) {
|
||||
deletionsCount := len(update.DropMultiK)
|
||||
if update.DropSingleK.Defined() {
|
||||
deletionsCount++
|
||||
}
|
||||
if deletionsCount > 0 {
|
||||
delSet = make(map[label.Key]struct{}, deletionsCount)
|
||||
for _, k := range update.DropMultiK {
|
||||
delSet[k] = struct{}{}
|
||||
}
|
||||
if update.DropSingleK.Defined() {
|
||||
delSet[update.DropSingleK] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
additionsCount := len(update.MultiKV)
|
||||
if update.SingleKV.Key.Defined() {
|
||||
additionsCount++
|
||||
}
|
||||
if additionsCount > 0 {
|
||||
addSet = make(map[label.Key]struct{}, additionsCount)
|
||||
for _, k := range update.MultiKV {
|
||||
addSet[k.Key] = struct{}{}
|
||||
}
|
||||
if update.SingleKV.Key.Defined() {
|
||||
addSet[update.SingleKV.Key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getNewMapSize(m rawMap, delSet, addSet keySet) int {
|
||||
mapSizeDiff := 0
|
||||
for k := range addSet {
|
||||
if _, ok := m[k]; !ok {
|
||||
mapSizeDiff++
|
||||
}
|
||||
}
|
||||
for k := range delSet {
|
||||
if _, ok := m[k]; ok {
|
||||
if _, inAddSet := addSet[k]; !inAddSet {
|
||||
mapSizeDiff--
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(m) + mapSizeDiff
|
||||
}
|
||||
|
||||
// Value gets a value from correlations map and returns a boolean
|
||||
// value indicating whether the key exist in the map.
|
||||
func (m Map) Value(k label.Key) (label.Value, bool) {
|
||||
value, ok := m.m[k]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
// HasValue returns a boolean value indicating whether the key exist
|
||||
// in the map.
|
||||
func (m Map) HasValue(k label.Key) bool {
|
||||
_, has := m.Value(k)
|
||||
return has
|
||||
}
|
||||
|
||||
// Len returns a length of the map.
|
||||
func (m Map) Len() int {
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
// Foreach calls a passed callback once on each key-value pair until
|
||||
// all the key-value pairs of the map were iterated or the callback
|
||||
// returns false, whichever happens first.
|
||||
func (m Map) Foreach(f func(label.KeyValue) bool) {
|
||||
for k, v := range m.m {
|
||||
if !f(label.KeyValue{
|
||||
Key: k,
|
||||
Value: v,
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
67
baggage.go
Normal file
67
baggage.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright The 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 otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
// Baggage returns a copy of the baggage in ctx.
|
||||
func Baggage(ctx context.Context) label.Set {
|
||||
// TODO (MrAlias, #1222): The underlying storage, the Map, shares many of
|
||||
// the functional elements of the label.Set. These should be unified so
|
||||
// this conversion is unnecessary and there is no performance hit calling
|
||||
// this.
|
||||
m := baggage.MapFromContext(ctx)
|
||||
values := make([]label.KeyValue, 0, m.Len())
|
||||
m.Foreach(func(kv label.KeyValue) bool {
|
||||
values = append(values, kv)
|
||||
return true
|
||||
})
|
||||
return label.NewSet(values...)
|
||||
}
|
||||
|
||||
// BaggageValue returns the value related to key in the baggage of ctx. If no
|
||||
// value is set, the returned label.Value will be an uninitialized zero-value
|
||||
// with type INVALID.
|
||||
func BaggageValue(ctx context.Context, key label.Key) label.Value {
|
||||
v, _ := baggage.MapFromContext(ctx).Value(key)
|
||||
return v
|
||||
}
|
||||
|
||||
// ContextWithBaggageValues returns a copy of parent with pairs updated in the baggage.
|
||||
func ContextWithBaggageValues(parent context.Context, pairs ...label.KeyValue) context.Context {
|
||||
m := baggage.MapFromContext(parent).Apply(baggage.MapUpdate{
|
||||
MultiKV: pairs,
|
||||
})
|
||||
return baggage.ContextWithMap(parent, m)
|
||||
}
|
||||
|
||||
// ContextWithoutBaggageValues returns a copy of parent in which the values related
|
||||
// to keys have been removed from the baggage.
|
||||
func ContextWithoutBaggageValues(parent context.Context, keys ...label.Key) context.Context {
|
||||
m := baggage.MapFromContext(parent).Apply(baggage.MapUpdate{
|
||||
DropMultiK: keys,
|
||||
})
|
||||
return baggage.ContextWithMap(parent, m)
|
||||
}
|
||||
|
||||
// ContextWithoutBaggage returns a copy of parent without baggage.
|
||||
func ContextWithoutBaggage(parent context.Context) context.Context {
|
||||
return baggage.ContextWithNoCorrelationData(parent)
|
||||
}
|
86
baggage_test.go
Normal file
86
baggage_test.go
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright The 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 otel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
func TestBaggage(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = baggage.ContextWithMap(ctx, baggage.NewEmptyMap())
|
||||
|
||||
b := Baggage(ctx)
|
||||
if b.Len() != 0 {
|
||||
t.Fatalf("empty baggage returned a set with %d elements", b.Len())
|
||||
}
|
||||
|
||||
first, second, third := label.Key("first"), label.Key("second"), label.Key("third")
|
||||
ctx = ContextWithBaggageValues(ctx, first.Bool(true), second.String("2"))
|
||||
m := baggage.MapFromContext(ctx)
|
||||
v, ok := m.Value(first)
|
||||
if !ok {
|
||||
t.Fatal("WithBaggageValues failed to set first value")
|
||||
}
|
||||
if !v.AsBool() {
|
||||
t.Fatal("WithBaggageValues failed to set first correct value")
|
||||
}
|
||||
v, ok = m.Value(second)
|
||||
if !ok {
|
||||
t.Fatal("WithBaggageValues failed to set second value")
|
||||
}
|
||||
if v.AsString() != "2" {
|
||||
t.Fatal("WithBaggageValues failed to set second correct value")
|
||||
}
|
||||
_, ok = m.Value(third)
|
||||
if ok {
|
||||
t.Fatal("WithBaggageValues set an unexpected third value")
|
||||
}
|
||||
|
||||
b = Baggage(ctx)
|
||||
if b.Len() != 2 {
|
||||
t.Fatalf("Baggage returned a set with %d elements, want 2", b.Len())
|
||||
}
|
||||
|
||||
v = BaggageValue(ctx, first)
|
||||
if v.Type() != label.BOOL || !v.AsBool() {
|
||||
t.Fatal("BaggageValue failed to get correct first value")
|
||||
}
|
||||
v = BaggageValue(ctx, second)
|
||||
if v.Type() != label.STRING || v.AsString() != "2" {
|
||||
t.Fatal("BaggageValue failed to get correct second value")
|
||||
}
|
||||
|
||||
ctx = ContextWithoutBaggageValues(ctx, first)
|
||||
m = baggage.MapFromContext(ctx)
|
||||
_, ok = m.Value(first)
|
||||
if ok {
|
||||
t.Fatal("WithoutBaggageValues failed to remove a baggage value")
|
||||
}
|
||||
_, ok = m.Value(second)
|
||||
if !ok {
|
||||
t.Fatal("WithoutBaggageValues removed incorrect value")
|
||||
}
|
||||
|
||||
ctx = ContextWithoutBaggage(ctx)
|
||||
m = baggage.MapFromContext(ctx)
|
||||
if m.Len() != 0 {
|
||||
t.Fatal("WithoutBaggage failed to clear baggage")
|
||||
}
|
||||
}
|
@ -26,10 +26,10 @@ import (
|
||||
otlog "github.com/opentracing/opentracing-go/log"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
otelbaggage "go.opentelemetry.io/otel/api/baggage"
|
||||
otelglobal "go.opentelemetry.io/otel/api/global"
|
||||
oteltrace "go.opentelemetry.io/otel/api/trace"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/internal/trace/noop"
|
||||
otelparent "go.opentelemetry.io/otel/internal/trace/parent"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
@ -38,7 +38,7 @@ import (
|
||||
)
|
||||
|
||||
type bridgeSpanContext struct {
|
||||
baggageItems otelbaggage.Map
|
||||
baggageItems baggage.Map
|
||||
otelSpanContext oteltrace.SpanContext
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ var _ ot.SpanContext = &bridgeSpanContext{}
|
||||
|
||||
func newBridgeSpanContext(otelSpanContext oteltrace.SpanContext, parentOtSpanContext ot.SpanContext) *bridgeSpanContext {
|
||||
bCtx := &bridgeSpanContext{
|
||||
baggageItems: otelbaggage.NewEmptyMap(),
|
||||
baggageItems: baggage.NewEmptyMap(),
|
||||
otelSpanContext: otelSpanContext,
|
||||
}
|
||||
if parentOtSpanContext != nil {
|
||||
@ -66,7 +66,7 @@ func (c *bridgeSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
|
||||
|
||||
func (c *bridgeSpanContext) setBaggageItem(restrictedKey, value string) {
|
||||
crk := http.CanonicalHeaderKey(restrictedKey)
|
||||
c.baggageItems = c.baggageItems.Apply(otelbaggage.MapUpdate{SingleKV: label.String(crk, value)})
|
||||
c.baggageItems = c.baggageItems.Apply(baggage.MapUpdate{SingleKV: label.String(crk, value)})
|
||||
}
|
||||
|
||||
func (c *bridgeSpanContext) baggageItem(restrictedKey string) string {
|
||||
@ -327,8 +327,8 @@ func (t *BridgeTracer) SetTextMapPropagator(propagator otel.TextMapPropagator) {
|
||||
}
|
||||
|
||||
func (t *BridgeTracer) NewHookedContext(ctx context.Context) context.Context {
|
||||
ctx = otelbaggage.ContextWithSetHook(ctx, t.baggageSetHook)
|
||||
ctx = otelbaggage.ContextWithGetHook(ctx, t.baggageGetHook)
|
||||
ctx = baggage.ContextWithSetHook(ctx, t.baggageSetHook)
|
||||
ctx = baggage.ContextWithGetHook(ctx, t.baggageGetHook)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@ -346,8 +346,8 @@ func (t *BridgeTracer) baggageSetHook(ctx context.Context) context.Context {
|
||||
// we clear the context only to avoid calling a get hook
|
||||
// during MapFromContext, but otherwise we don't change the
|
||||
// context, so we don't care about the old hooks.
|
||||
clearCtx, _, _ := otelbaggage.ContextWithNoHooks(ctx)
|
||||
m := otelbaggage.MapFromContext(clearCtx)
|
||||
clearCtx, _, _ := baggage.ContextWithNoHooks(ctx)
|
||||
m := baggage.MapFromContext(clearCtx)
|
||||
m.Foreach(func(kv label.KeyValue) bool {
|
||||
bSpan.setBaggageItemOnly(string(kv.Key), kv.Value.Emit())
|
||||
return true
|
||||
@ -355,7 +355,7 @@ func (t *BridgeTracer) baggageSetHook(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (t *BridgeTracer) baggageGetHook(ctx context.Context, m otelbaggage.Map) otelbaggage.Map {
|
||||
func (t *BridgeTracer) baggageGetHook(ctx context.Context, m baggage.Map) baggage.Map {
|
||||
span := ot.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
t.warningHandler("No active OpenTracing span, can not propagate the baggage items from OpenTracing span context\n")
|
||||
@ -374,7 +374,7 @@ func (t *BridgeTracer) baggageGetHook(ctx context.Context, m otelbaggage.Map) ot
|
||||
for k, v := range items {
|
||||
kv = append(kv, label.String(k, v))
|
||||
}
|
||||
return m.Apply(otelbaggage.MapUpdate{MultiKV: kv})
|
||||
return m.Apply(baggage.MapUpdate{MultiKV: kv})
|
||||
}
|
||||
|
||||
// StartSpan is a part of the implementation of the OpenTracing Tracer
|
||||
@ -613,7 +613,7 @@ func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier int
|
||||
sc: bridgeSC.otelSpanContext,
|
||||
}
|
||||
ctx := oteltrace.ContextWithSpan(context.Background(), fs)
|
||||
ctx = otelbaggage.ContextWithMap(ctx, bridgeSC.baggageItems)
|
||||
ctx = baggage.ContextWithMap(ctx, bridgeSC.baggageItems)
|
||||
t.getPropagator().Inject(ctx, header)
|
||||
return nil
|
||||
}
|
||||
@ -632,7 +632,7 @@ func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.Span
|
||||
}
|
||||
header := http.Header(hhcarrier)
|
||||
ctx := t.getPropagator().Extract(context.Background(), header)
|
||||
baggage := otelbaggage.MapFromContext(ctx)
|
||||
baggage := baggage.MapFromContext(ctx)
|
||||
otelSC, _, _ := otelparent.GetSpanContextAndLinks(ctx, false)
|
||||
bridgeSC := &bridgeSpanContext{
|
||||
baggageItems: baggage,
|
||||
|
@ -21,9 +21,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
otelbaggage "go.opentelemetry.io/otel/api/baggage"
|
||||
oteltrace "go.opentelemetry.io/otel/api/trace"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
otelparent "go.opentelemetry.io/otel/internal/trace/parent"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
|
||||
@ -45,7 +45,7 @@ type MockContextKeyValue struct {
|
||||
}
|
||||
|
||||
type MockTracer struct {
|
||||
Resources otelbaggage.Map
|
||||
Resources baggage.Map
|
||||
FinishedSpans []*MockSpan
|
||||
SpareTraceIDs []oteltrace.ID
|
||||
SpareSpanIDs []oteltrace.SpanID
|
||||
@ -60,7 +60,7 @@ var _ migration.DeferredContextSetupTracerExtension = &MockTracer{}
|
||||
|
||||
func NewMockTracer() *MockTracer {
|
||||
return &MockTracer{
|
||||
Resources: otelbaggage.NewEmptyMap(),
|
||||
Resources: baggage.NewEmptyMap(),
|
||||
FinishedSpans: nil,
|
||||
SpareTraceIDs: nil,
|
||||
SpareSpanIDs: nil,
|
||||
@ -86,7 +86,7 @@ func (t *MockTracer) Start(ctx context.Context, name string, opts ...oteltrace.S
|
||||
officialTracer: t,
|
||||
spanContext: spanContext,
|
||||
recording: config.Record,
|
||||
Attributes: otelbaggage.NewMap(otelbaggage.MapUpdate{
|
||||
Attributes: baggage.NewMap(baggage.MapUpdate{
|
||||
MultiKV: config.Attributes,
|
||||
}),
|
||||
StartTime: startTime,
|
||||
@ -179,10 +179,10 @@ func (t *MockTracer) DeferredContextSetupHook(ctx context.Context, span oteltrac
|
||||
}
|
||||
|
||||
type MockEvent struct {
|
||||
CtxAttributes otelbaggage.Map
|
||||
CtxAttributes baggage.Map
|
||||
Timestamp time.Time
|
||||
Name string
|
||||
Attributes otelbaggage.Map
|
||||
Attributes baggage.Map
|
||||
}
|
||||
|
||||
type MockSpan struct {
|
||||
@ -192,7 +192,7 @@ type MockSpan struct {
|
||||
SpanKind oteltrace.SpanKind
|
||||
recording bool
|
||||
|
||||
Attributes otelbaggage.Map
|
||||
Attributes baggage.Map
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
ParentSpanID oteltrace.SpanID
|
||||
@ -223,12 +223,12 @@ func (s *MockSpan) SetError(v bool) {
|
||||
}
|
||||
|
||||
func (s *MockSpan) SetAttributes(attributes ...label.KeyValue) {
|
||||
s.applyUpdate(otelbaggage.MapUpdate{
|
||||
s.applyUpdate(baggage.MapUpdate{
|
||||
MultiKV: attributes,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *MockSpan) applyUpdate(update otelbaggage.MapUpdate) {
|
||||
func (s *MockSpan) applyUpdate(update baggage.MapUpdate) {
|
||||
s.Attributes = s.Attributes.Apply(update)
|
||||
}
|
||||
|
||||
@ -283,10 +283,10 @@ func (s *MockSpan) AddEvent(ctx context.Context, name string, attrs ...label.Key
|
||||
|
||||
func (s *MockSpan) AddEventWithTimestamp(ctx context.Context, timestamp time.Time, name string, attrs ...label.KeyValue) {
|
||||
s.Events = append(s.Events, MockEvent{
|
||||
CtxAttributes: otelbaggage.MapFromContext(ctx),
|
||||
CtxAttributes: baggage.MapFromContext(ctx),
|
||||
Timestamp: timestamp,
|
||||
Name: name,
|
||||
Attributes: otelbaggage.NewMap(otelbaggage.MapUpdate{
|
||||
Attributes: baggage.NewMap(baggage.MapUpdate{
|
||||
MultiKV: attrs,
|
||||
}),
|
||||
})
|
||||
|
@ -21,9 +21,9 @@ import (
|
||||
|
||||
ot "github.com/opentracing/opentracing-go"
|
||||
|
||||
otelbaggage "go.opentelemetry.io/otel/api/baggage"
|
||||
otelglobal "go.opentelemetry.io/otel/api/global"
|
||||
oteltrace "go.opentelemetry.io/otel/api/trace"
|
||||
otelbaggage "go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
|
||||
"go.opentelemetry.io/otel/bridge/opentracing/internal"
|
||||
|
@ -18,12 +18,13 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"go.opentelemetry.io/otel/api/baggage"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
"go.opentelemetry.io/otel/api/metric"
|
||||
"go.opentelemetry.io/otel/api/trace"
|
||||
"go.opentelemetry.io/otel/exporters/stdout"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagators"
|
||||
"go.opentelemetry.io/otel/sdk/metric/controller/push"
|
||||
"go.opentelemetry.io/otel/sdk/metric/processor/basic"
|
||||
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
|
||||
@ -62,7 +63,7 @@ func main() {
|
||||
global.SetMeterProvider(pusher.MeterProvider())
|
||||
|
||||
// set global propagator to baggage (the default is no-op).
|
||||
global.SetTextMapPropagator(baggage.Baggage{})
|
||||
global.SetTextMapPropagator(propagators.Baggage{})
|
||||
tracer := global.Tracer("ex.com/basic")
|
||||
meter := global.Meter("ex.com/basic")
|
||||
|
||||
@ -78,11 +79,7 @@ func main() {
|
||||
valuerecorderTwo := metric.Must(meter).NewFloat64ValueRecorder("ex.com.two")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
ctx = baggage.NewContext(ctx,
|
||||
fooKey.String("foo1"),
|
||||
barKey.String("bar1"),
|
||||
)
|
||||
ctx = otel.ContextWithBaggageValues(ctx, fooKey.String("foo1"), barKey.String("bar1"))
|
||||
|
||||
valuerecorder := valuerecorderTwo.Bind(commonLabels...)
|
||||
defer valuerecorder.Unbind()
|
||||
@ -97,7 +94,7 @@ func main() {
|
||||
|
||||
meter.RecordBatch(
|
||||
// Note: call-site variables added as context Entries:
|
||||
baggage.NewContext(ctx, anotherKey.String("xyz")),
|
||||
otel.ContextWithBaggageValues(ctx, anotherKey.String("xyz")),
|
||||
commonLabels,
|
||||
|
||||
valuerecorderTwo.Measurement(2.0),
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"go.opentelemetry.io/otel/api/baggage"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/api/global"
|
||||
"go.opentelemetry.io/otel/api/trace"
|
||||
"go.opentelemetry.io/otel/example/namedtracer/foo"
|
||||
@ -64,11 +64,7 @@ func main() {
|
||||
// Create a named tracer with package path as its name.
|
||||
tracer := tp.Tracer("example/namedtracer/main")
|
||||
ctx := context.Background()
|
||||
|
||||
ctx = baggage.NewContext(ctx,
|
||||
fooKey.String("foo1"),
|
||||
barKey.String("bar1"),
|
||||
)
|
||||
ctx = otel.ContextWithBaggageValues(ctx, fooKey.String("foo1"), barKey.String("bar1"))
|
||||
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(ctx, "operation")
|
||||
|
@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package baggage provides types and functions to manage W3C Baggage.
|
||||
package baggage
|
||||
|
||||
import (
|
||||
@ -20,6 +21,165 @@ import (
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
type rawMap map[label.Key]label.Value
|
||||
type keySet map[label.Key]struct{}
|
||||
|
||||
// Map is an immutable storage for correlations.
|
||||
type Map struct {
|
||||
m rawMap
|
||||
}
|
||||
|
||||
// MapUpdate contains information about correlation changes to be
|
||||
// made.
|
||||
type MapUpdate struct {
|
||||
// DropSingleK contains a single key to be dropped from
|
||||
// correlations. Use this to avoid an overhead of a slice
|
||||
// allocation if there is only one key to drop.
|
||||
DropSingleK label.Key
|
||||
// DropMultiK contains all the keys to be dropped from
|
||||
// correlations.
|
||||
DropMultiK []label.Key
|
||||
|
||||
// SingleKV contains a single key-value pair to be added to
|
||||
// correlations. Use this to avoid an overhead of a slice
|
||||
// allocation if there is only one key-value pair to add.
|
||||
SingleKV label.KeyValue
|
||||
// MultiKV contains all the key-value pairs to be added to
|
||||
// correlations.
|
||||
MultiKV []label.KeyValue
|
||||
}
|
||||
|
||||
func newMap(raw rawMap) Map {
|
||||
return Map{
|
||||
m: raw,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEmptyMap creates an empty correlations map.
|
||||
func NewEmptyMap() Map {
|
||||
return newMap(nil)
|
||||
}
|
||||
|
||||
// NewMap creates a map with the contents of the update applied. In
|
||||
// this function, having an update with DropSingleK or DropMultiK
|
||||
// makes no sense - those fields are effectively ignored.
|
||||
func NewMap(update MapUpdate) Map {
|
||||
return NewEmptyMap().Apply(update)
|
||||
}
|
||||
|
||||
// Apply creates a copy of the map with the contents of the update
|
||||
// applied. Apply will first drop the keys from DropSingleK and
|
||||
// DropMultiK, then add key-value pairs from SingleKV and MultiKV.
|
||||
func (m Map) Apply(update MapUpdate) Map {
|
||||
delSet, addSet := getModificationSets(update)
|
||||
mapSize := getNewMapSize(m.m, delSet, addSet)
|
||||
|
||||
r := make(rawMap, mapSize)
|
||||
for k, v := range m.m {
|
||||
// do not copy items we want to drop
|
||||
if _, ok := delSet[k]; ok {
|
||||
continue
|
||||
}
|
||||
// do not copy items we would overwrite
|
||||
if _, ok := addSet[k]; ok {
|
||||
continue
|
||||
}
|
||||
r[k] = v
|
||||
}
|
||||
if update.SingleKV.Key.Defined() {
|
||||
r[update.SingleKV.Key] = update.SingleKV.Value
|
||||
}
|
||||
for _, kv := range update.MultiKV {
|
||||
r[kv.Key] = kv.Value
|
||||
}
|
||||
if len(r) == 0 {
|
||||
r = nil
|
||||
}
|
||||
return newMap(r)
|
||||
}
|
||||
|
||||
func getModificationSets(update MapUpdate) (delSet, addSet keySet) {
|
||||
deletionsCount := len(update.DropMultiK)
|
||||
if update.DropSingleK.Defined() {
|
||||
deletionsCount++
|
||||
}
|
||||
if deletionsCount > 0 {
|
||||
delSet = make(map[label.Key]struct{}, deletionsCount)
|
||||
for _, k := range update.DropMultiK {
|
||||
delSet[k] = struct{}{}
|
||||
}
|
||||
if update.DropSingleK.Defined() {
|
||||
delSet[update.DropSingleK] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
additionsCount := len(update.MultiKV)
|
||||
if update.SingleKV.Key.Defined() {
|
||||
additionsCount++
|
||||
}
|
||||
if additionsCount > 0 {
|
||||
addSet = make(map[label.Key]struct{}, additionsCount)
|
||||
for _, k := range update.MultiKV {
|
||||
addSet[k.Key] = struct{}{}
|
||||
}
|
||||
if update.SingleKV.Key.Defined() {
|
||||
addSet[update.SingleKV.Key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getNewMapSize(m rawMap, delSet, addSet keySet) int {
|
||||
mapSizeDiff := 0
|
||||
for k := range addSet {
|
||||
if _, ok := m[k]; !ok {
|
||||
mapSizeDiff++
|
||||
}
|
||||
}
|
||||
for k := range delSet {
|
||||
if _, ok := m[k]; ok {
|
||||
if _, inAddSet := addSet[k]; !inAddSet {
|
||||
mapSizeDiff--
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(m) + mapSizeDiff
|
||||
}
|
||||
|
||||
// Value gets a value from correlations map and returns a boolean
|
||||
// value indicating whether the key exist in the map.
|
||||
func (m Map) Value(k label.Key) (label.Value, bool) {
|
||||
value, ok := m.m[k]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
// HasValue returns a boolean value indicating whether the key exist
|
||||
// in the map.
|
||||
func (m Map) HasValue(k label.Key) bool {
|
||||
_, has := m.Value(k)
|
||||
return has
|
||||
}
|
||||
|
||||
// Len returns a length of the map.
|
||||
func (m Map) Len() int {
|
||||
return len(m.m)
|
||||
}
|
||||
|
||||
// Foreach calls a passed callback once on each key-value pair until
|
||||
// all the key-value pairs of the map were iterated or the callback
|
||||
// returns false, whichever happens first.
|
||||
func (m Map) Foreach(f func(label.KeyValue) bool) {
|
||||
for k, v := range m.m {
|
||||
if !f(label.KeyValue{
|
||||
Key: k,
|
||||
Value: v,
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type correlationsType struct{}
|
||||
|
||||
// SetHookFunc describes a type of a callback that is called when
|
||||
@ -148,6 +308,12 @@ func ContextWithMap(ctx context.Context, m Map) context.Context {
|
||||
}
|
||||
}
|
||||
|
||||
// ContextWithNoCorrelationData returns a context stripped of correlation
|
||||
// data.
|
||||
func ContextWithNoCorrelationData(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, correlationsKey, nil)
|
||||
}
|
||||
|
||||
// NewContext returns a context with the map from passed context
|
||||
// updated with the passed key-value pairs.
|
||||
func NewContext(ctx context.Context, keyvalues ...label.KeyValue) context.Context {
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package baggage
|
||||
package propagators
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
@ -27,15 +28,17 @@ import (
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/18b2752ebe6c7f0cdd8c7b2bcbdceb0ae3f5ad95/specification/correlationcontext/api.md#header-name
|
||||
const baggageHeader = "otcorrelations"
|
||||
|
||||
// Baggage propagates Key:Values in W3C CorrelationContext format.
|
||||
// nolint:golint
|
||||
// Baggage is a propagator that supports the W3C Baggage format.
|
||||
//
|
||||
// This propagates user-defined baggage associated with a trace. The complete
|
||||
// specification is defined at https://w3c.github.io/baggage/.
|
||||
type Baggage struct{}
|
||||
|
||||
var _ otel.TextMapPropagator = Baggage{}
|
||||
|
||||
// Inject set baggage key-values from the Context into the carrier.
|
||||
// Inject sets baggage key-values from ctx into the carrier.
|
||||
func (b Baggage) Inject(ctx context.Context, carrier otel.TextMapCarrier) {
|
||||
baggageMap := MapFromContext(ctx)
|
||||
baggageMap := baggage.MapFromContext(ctx)
|
||||
firstIter := true
|
||||
var headerValueBuilder strings.Builder
|
||||
baggageMap.Foreach(func(kv label.KeyValue) bool {
|
||||
@ -54,14 +57,14 @@ func (b Baggage) Inject(ctx context.Context, carrier otel.TextMapCarrier) {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract reads baggage key-values from the carrier into a returned Context.
|
||||
func (b Baggage) Extract(ctx context.Context, carrier otel.TextMapCarrier) context.Context {
|
||||
baggage := carrier.Get(baggageHeader)
|
||||
if baggage == "" {
|
||||
return ctx
|
||||
// Extract returns a copy of parent with the baggage from the carrier added.
|
||||
func (b Baggage) Extract(parent context.Context, carrier otel.TextMapCarrier) context.Context {
|
||||
bVal := carrier.Get(baggageHeader)
|
||||
if bVal == "" {
|
||||
return parent
|
||||
}
|
||||
|
||||
baggageValues := strings.Split(baggage, ",")
|
||||
baggageValues := strings.Split(bVal, ",")
|
||||
keyValues := make([]label.KeyValue, 0, len(baggageValues))
|
||||
for _, baggageValue := range baggageValues {
|
||||
valueAndProps := strings.Split(baggageValue, ";")
|
||||
@ -97,12 +100,12 @@ func (b Baggage) Extract(ctx context.Context, carrier otel.TextMapCarrier) conte
|
||||
|
||||
if len(keyValues) > 0 {
|
||||
// Only update the context if valid values were found
|
||||
return ContextWithMap(ctx, NewMap(MapUpdate{
|
||||
return baggage.ContextWithMap(parent, baggage.NewMap(baggage.MapUpdate{
|
||||
MultiKV: keyValues,
|
||||
}))
|
||||
}
|
||||
|
||||
return ctx
|
||||
return parent
|
||||
}
|
||||
|
||||
// Fields returns the keys who's values are set with Inject.
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package baggage_test
|
||||
package propagators_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -23,12 +23,13 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/api/baggage"
|
||||
"go.opentelemetry.io/otel/internal/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagators"
|
||||
)
|
||||
|
||||
func TestExtractValidBaggageFromHTTPReq(t *testing.T) {
|
||||
prop := otel.TextMapPropagator(baggage.Baggage{})
|
||||
prop := otel.TextMapPropagator(propagators.Baggage{})
|
||||
tests := []struct {
|
||||
name string
|
||||
header string
|
||||
@ -117,7 +118,7 @@ func TestExtractValidBaggageFromHTTPReq(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractInvalidDistributedContextFromHTTPReq(t *testing.T) {
|
||||
prop := otel.TextMapPropagator(baggage.Baggage{})
|
||||
prop := otel.TextMapPropagator(propagators.Baggage{})
|
||||
tests := []struct {
|
||||
name string
|
||||
header string
|
||||
@ -175,7 +176,7 @@ func TestExtractInvalidDistributedContextFromHTTPReq(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInjectBaggageToHTTPReq(t *testing.T) {
|
||||
propagator := baggage.Baggage{}
|
||||
propagator := propagators.Baggage{}
|
||||
tests := []struct {
|
||||
name string
|
||||
kvs []label.KeyValue
|
||||
@ -248,8 +249,8 @@ func TestInjectBaggageToHTTPReq(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceContextPropagator_GetAllKeys(t *testing.T) {
|
||||
var propagator baggage.Baggage
|
||||
func TestBaggagePropagatorGetAllKeys(t *testing.T) {
|
||||
var propagator propagators.Baggage
|
||||
want := []string{"otcorrelations"}
|
||||
got := propagator.Fields()
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
@ -17,6 +17,8 @@ Package propagators contains OpenTelemetry context propagators.
|
||||
|
||||
OpenTelemetry propagators are used to extract and inject context data from and
|
||||
into messages exchanged by applications. The propagator supported by this
|
||||
package is the W3C Trace Context encoding (https://www.w3.org/TR/trace-context/).
|
||||
package is the W3C Trace Context encoding
|
||||
(https://www.w3.org/TR/trace-context/), and W3C Baggage
|
||||
(https://w3c.github.io/baggage/).
|
||||
*/
|
||||
package propagators // import "go.opentelemetry.io/otel/propagators"
|
||||
|
Reference in New Issue
Block a user