1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package sdk
5
6import (
7 "context"
8 "time"
9
10 "go.opentelemetry.io/otel/trace"
11 "go.opentelemetry.io/otel/trace/noop"
12
13 "go.opentelemetry.io/auto/sdk/internal/telemetry"
14)
15
16type tracer struct {
17 noop.Tracer
18
19 name, schemaURL, version string
20}
21
22var _ trace.Tracer = tracer{}
23
24func (t tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
25 var psc trace.SpanContext
26 sampled := true
27 span := new(span)
28
29 // Ask eBPF for sampling decision and span context info.
30 t.start(ctx, span, &psc, &sampled, &span.spanContext)
31
32 span.sampled.Store(sampled)
33
34 ctx = trace.ContextWithSpan(ctx, span)
35
36 if sampled {
37 // Only build traces if sampled.
38 cfg := trace.NewSpanStartConfig(opts...)
39 span.traces, span.span = t.traces(name, cfg, span.spanContext, psc)
40 }
41
42 return ctx, span
43}
44
45// Expected to be implemented in eBPF.
46//
47//go:noinline
48func (t *tracer) start(
49 ctx context.Context,
50 spanPtr *span,
51 psc *trace.SpanContext,
52 sampled *bool,
53 sc *trace.SpanContext,
54) {
55 start(ctx, spanPtr, psc, sampled, sc)
56}
57
58// start is used for testing.
59var start = func(context.Context, *span, *trace.SpanContext, *bool, *trace.SpanContext) {}
60
61func (t tracer) traces(name string, cfg trace.SpanConfig, sc, psc trace.SpanContext) (*telemetry.Traces, *telemetry.Span) {
62 span := &telemetry.Span{
63 TraceID: telemetry.TraceID(sc.TraceID()),
64 SpanID: telemetry.SpanID(sc.SpanID()),
65 Flags: uint32(sc.TraceFlags()),
66 TraceState: sc.TraceState().String(),
67 ParentSpanID: telemetry.SpanID(psc.SpanID()),
68 Name: name,
69 Kind: spanKind(cfg.SpanKind()),
70 }
71
72 span.Attrs, span.DroppedAttrs = convCappedAttrs(maxSpan.Attrs, cfg.Attributes())
73
74 links := cfg.Links()
75 if limit := maxSpan.Links; limit == 0 {
76 span.DroppedLinks = uint32(len(links))
77 } else {
78 if limit > 0 {
79 n := max(len(links)-limit, 0)
80 span.DroppedLinks = uint32(n)
81 links = links[n:]
82 }
83 span.Links = convLinks(links)
84 }
85
86 if t := cfg.Timestamp(); !t.IsZero() {
87 span.StartTime = cfg.Timestamp()
88 } else {
89 span.StartTime = time.Now()
90 }
91
92 return &telemetry.Traces{
93 ResourceSpans: []*telemetry.ResourceSpans{
94 {
95 ScopeSpans: []*telemetry.ScopeSpans{
96 {
97 Scope: &telemetry.Scope{
98 Name: t.name,
99 Version: t.version,
100 },
101 Spans: []*telemetry.Span{span},
102 SchemaURL: t.schemaURL,
103 },
104 },
105 },
106 },
107 }, span
108}
109
110func spanKind(kind trace.SpanKind) telemetry.SpanKind {
111 switch kind {
112 case trace.SpanKindInternal:
113 return telemetry.SpanKindInternal
114 case trace.SpanKindServer:
115 return telemetry.SpanKindServer
116 case trace.SpanKindClient:
117 return telemetry.SpanKindClient
118 case trace.SpanKindProducer:
119 return telemetry.SpanKindProducer
120 case trace.SpanKindConsumer:
121 return telemetry.SpanKindConsumer
122 }
123 return telemetry.SpanKind(0) // undefined.
124}