Skip to content

Commit 5b808c6

Browse files
MrAliasCopilot
andauthored
Encapsulate SDK Tracer observability (#7331)
Split from #7316 [Follow guidelines](https://github.com/open-telemetry/opentelemetry-go/blob/a5dcd68ebb2f3669f7685ac7b0f3f1624251a381/CONTRIBUTING.md#encapsulation) and move instrumentation into its own type. ### Benchmarks #### Added `sdk/trace/internal/observ` benchmarks ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/trace/internal/observ cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz │ enc-trace-sdk-tracer-obs.out │ │ sec/op │ Tracer/SpanStarted-8 7.436n ± 6% Tracer/SpanLive-8 9.987n ± 8% Tracer/SpanEnded-8 11.32n ± 7% NewTracer-8 87.64n ± 6% geomean 16.48n │ enc-trace-sdk-tracer-obs.out │ │ B/op │ Tracer/SpanStarted-8 0.000 ± 0% Tracer/SpanLive-8 0.000 ± 0% Tracer/SpanEnded-8 0.000 ± 0% NewTracer-8 0.000 ± 0% geomean ¹ ¹ summaries must be >0 to compute geomean │ enc-trace-sdk-tracer-obs.out │ │ allocs/op │ Tracer/SpanStarted-8 0.000 ± 0% Tracer/SpanLive-8 0.000 ± 0% Tracer/SpanEnded-8 0.000 ± 0% NewTracer-8 0.000 ± 0% geomean ¹ ¹ summaries must be >0 to compute geomean ``` #### Existing `sdk/trace` benchmarks ```console > benchstat main.out enc-trace-sdk-tracer-obs.out goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/trace cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz │ main.out │ enc-trace-sdk-tracer-obs.out │ │ sec/op │ sec/op vs base │ SpanEnd/ObservabilityEnabled-8 188.5n ± 4% 131.5n ± 32% -30.24% (p=0.000 n=10) TraceStart/ObservabilityEnabled-8 886.9n ± 8% 663.9n ± 2% -25.14% (p=0.000 n=10) geomean 408.9n 295.5n -27.73% │ main.out │ enc-trace-sdk-tracer-obs.out │ │ B/op │ B/op vs base │ SpanEnd/ObservabilityEnabled-8 16.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) TraceStart/ObservabilityEnabled-8 608.0 ± 0% 576.0 ± 0% -5.26% (p=0.000 n=10) geomean 98.63 ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean │ main.out │ enc-trace-sdk-tracer-obs.out │ │ allocs/op │ allocs/op vs base │ SpanEnd/ObservabilityEnabled-8 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) TraceStart/ObservabilityEnabled-8 5.000 ± 0% 3.000 ± 0% -40.00% (p=0.000 n=10) geomean 2.236 ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean ``` --------- Co-authored-by: Copilot <[email protected]>
1 parent e4ab314 commit 5b808c6

File tree

7 files changed

+550
-176
lines changed

7 files changed

+550
-176
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ"
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
11+
"go.opentelemetry.io/otel"
12+
"go.opentelemetry.io/otel/attribute"
13+
"go.opentelemetry.io/otel/metric"
14+
"go.opentelemetry.io/otel/sdk"
15+
"go.opentelemetry.io/otel/sdk/trace/internal/x"
16+
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
17+
"go.opentelemetry.io/otel/trace"
18+
)
19+
20+
var meterOpts = []metric.MeterOption{
21+
metric.WithInstrumentationVersion(sdk.Version()),
22+
metric.WithSchemaURL(SchemaURL),
23+
}
24+
25+
// Tracer is instrumentation for an OTel SDK Tracer.
26+
type Tracer struct {
27+
enabled bool
28+
29+
live metric.Int64UpDownCounter
30+
started metric.Int64Counter
31+
}
32+
33+
func NewTracer() (Tracer, error) {
34+
if !x.Observability.Enabled() {
35+
return Tracer{}, nil
36+
}
37+
meter := otel.GetMeterProvider().Meter(ScopeName, meterOpts...)
38+
39+
var err error
40+
l, e := otelconv.NewSDKSpanLive(meter)
41+
if e != nil {
42+
e = fmt.Errorf("failed to create span live metric: %w", e)
43+
err = errors.Join(err, e)
44+
}
45+
46+
s, e := otelconv.NewSDKSpanStarted(meter)
47+
if e != nil {
48+
e = fmt.Errorf("failed to create span started metric: %w", e)
49+
err = errors.Join(err, e)
50+
}
51+
52+
return Tracer{enabled: true, live: l.Inst(), started: s.Inst()}, err
53+
}
54+
55+
func (t Tracer) Enabled() bool { return t.enabled }
56+
57+
func (t Tracer) SpanStarted(ctx context.Context, psc trace.SpanContext, span trace.Span) {
58+
key := spanStartedKey{
59+
parent: parentStateNoParent,
60+
sampling: samplingStateDrop,
61+
}
62+
63+
if psc.IsValid() {
64+
if psc.IsRemote() {
65+
key.parent = parentStateRemoteParent
66+
} else {
67+
key.parent = parentStateLocalParent
68+
}
69+
}
70+
71+
if span.IsRecording() {
72+
if span.SpanContext().IsSampled() {
73+
key.sampling = samplingStateRecordAndSample
74+
} else {
75+
key.sampling = samplingStateRecordOnly
76+
}
77+
}
78+
79+
opts := spanStartedOpts[key]
80+
t.started.Add(ctx, 1, opts...)
81+
}
82+
83+
func (t Tracer) SpanLive(ctx context.Context, span trace.Span) {
84+
t.spanLive(ctx, 1, span)
85+
}
86+
87+
func (t Tracer) SpanEnded(ctx context.Context, span trace.Span) {
88+
t.spanLive(ctx, -1, span)
89+
}
90+
91+
func (t Tracer) spanLive(ctx context.Context, value int64, span trace.Span) {
92+
key := spanLiveKey{sampled: span.SpanContext().IsSampled()}
93+
opts := spanLiveOpts[key]
94+
t.live.Add(ctx, value, opts...)
95+
}
96+
97+
type parentState int
98+
99+
const (
100+
parentStateNoParent parentState = iota
101+
parentStateLocalParent
102+
parentStateRemoteParent
103+
)
104+
105+
type samplingState int
106+
107+
const (
108+
samplingStateDrop samplingState = iota
109+
samplingStateRecordOnly
110+
samplingStateRecordAndSample
111+
)
112+
113+
type spanStartedKey struct {
114+
parent parentState
115+
sampling samplingState
116+
}
117+
118+
var spanStartedOpts = map[spanStartedKey][]metric.AddOption{
119+
{
120+
parentStateNoParent,
121+
samplingStateDrop,
122+
}: {
123+
metric.WithAttributeSet(attribute.NewSet(
124+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
125+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
126+
)),
127+
},
128+
{
129+
parentStateLocalParent,
130+
samplingStateDrop,
131+
}: {
132+
metric.WithAttributeSet(attribute.NewSet(
133+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
134+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
135+
)),
136+
},
137+
{
138+
parentStateRemoteParent,
139+
samplingStateDrop,
140+
}: {
141+
metric.WithAttributeSet(attribute.NewSet(
142+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
143+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
144+
)),
145+
},
146+
147+
{
148+
parentStateNoParent,
149+
samplingStateRecordOnly,
150+
}: {
151+
metric.WithAttributeSet(attribute.NewSet(
152+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
153+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
154+
)),
155+
},
156+
{
157+
parentStateLocalParent,
158+
samplingStateRecordOnly,
159+
}: {
160+
metric.WithAttributeSet(attribute.NewSet(
161+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
162+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
163+
)),
164+
},
165+
{
166+
parentStateRemoteParent,
167+
samplingStateRecordOnly,
168+
}: {
169+
metric.WithAttributeSet(attribute.NewSet(
170+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
171+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
172+
)),
173+
},
174+
175+
{
176+
parentStateNoParent,
177+
samplingStateRecordAndSample,
178+
}: {
179+
metric.WithAttributeSet(attribute.NewSet(
180+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
181+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
182+
)),
183+
},
184+
{
185+
parentStateLocalParent,
186+
samplingStateRecordAndSample,
187+
}: {
188+
metric.WithAttributeSet(attribute.NewSet(
189+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
190+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
191+
)),
192+
},
193+
{
194+
parentStateRemoteParent,
195+
samplingStateRecordAndSample,
196+
}: {
197+
metric.WithAttributeSet(attribute.NewSet(
198+
otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
199+
otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
200+
)),
201+
},
202+
}
203+
204+
type spanLiveKey struct {
205+
sampled bool
206+
}
207+
208+
var spanLiveOpts = map[spanLiveKey][]metric.AddOption{
209+
{true}: {
210+
metric.WithAttributeSet(attribute.NewSet(
211+
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
212+
otelconv.SpanSamplingResultRecordAndSample,
213+
),
214+
)),
215+
},
216+
{false}: {
217+
metric.WithAttributeSet(attribute.NewSet(
218+
otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
219+
otelconv.SpanSamplingResultRecordOnly,
220+
),
221+
)),
222+
},
223+
}

0 commit comments

Comments
 (0)