forked from DataDog/dd-trace-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
span.go
146 lines (129 loc) · 4.4 KB
/
span.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2023 Datadog, Inc.
package opentelemetry
import (
"encoding/binary"
"strconv"
"strings"
"go.opentelemetry.io/otel/attribute"
otelcodes "go.opentelemetry.io/otel/codes"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/lannguyen-c0x12c/dd-trace-go/ddtrace"
"github.com/lannguyen-c0x12c/dd-trace-go/ddtrace/ext"
"github.com/lannguyen-c0x12c/dd-trace-go/ddtrace/tracer"
"github.com/lannguyen-c0x12c/dd-trace-go/internal/log"
)
var _ oteltrace.Span = (*span)(nil)
type span struct {
tracer.Span
finished bool
finishOpts []tracer.FinishOption
statusInfo
*oteltracer
}
func (s *span) TracerProvider() oteltrace.TracerProvider { return s.oteltracer.provider }
func (s *span) AddEvent(_ string, _ ...oteltrace.EventOption) { /* no-op */ }
func (s *span) RecordError(_ error, _ ...oteltrace.EventOption) { /* no-op */ }
func (s *span) SetName(name string) { s.SetOperationName(name) }
func (s *span) End(options ...oteltrace.SpanEndOption) {
if s.finished {
return
}
s.finished = true
var finishCfg = oteltrace.NewSpanEndConfig(options...)
var opts []tracer.FinishOption
if s.statusInfo.code == otelcodes.Error {
s.SetTag(ext.ErrorMsg, s.statusInfo.description)
}
if t := finishCfg.Timestamp(); !t.IsZero() {
opts = append(opts, tracer.FinishTime(t))
}
if len(s.finishOpts) != 0 {
opts = append(opts, s.finishOpts...)
}
s.Finish(opts...)
}
// EndOptions sets tracer.FinishOption on a given span to be executed when span is finished.
func EndOptions(sp oteltrace.Span, options ...tracer.FinishOption) {
s, ok := sp.(*span)
if !ok || !s.IsRecording() {
return
}
s.finishOpts = options
}
// SpanContext returns implementation of the oteltrace.SpanContext.
func (s *span) SpanContext() oteltrace.SpanContext {
ctx := s.Span.Context()
var traceID oteltrace.TraceID
var spanID oteltrace.SpanID
if w3cCtx, ok := ctx.(ddtrace.SpanContextW3C); ok {
traceID = w3cCtx.TraceID128Bytes()
} else {
log.Debug("Non-W3C context found in span, unable to get full 128 bit trace id")
uint64ToByte(ctx.TraceID(), traceID[:])
}
uint64ToByte(ctx.SpanID(), spanID[:])
config := oteltrace.SpanContextConfig{
TraceID: traceID,
SpanID: spanID,
}
s.extractTraceData(&config)
return oteltrace.NewSpanContext(config)
}
func (s *span) extractTraceData(c *oteltrace.SpanContextConfig) {
headers := tracer.TextMapCarrier{}
if err := tracer.Inject(s.Context(), headers); err != nil {
return
}
state, err := oteltrace.ParseTraceState(headers["tracestate"])
if err != nil {
log.Debug("Couldn't parse tracestate: %v", err)
return
}
c.TraceState = state
parent := strings.Trim(headers["traceparent"], " \t-")
if len(parent) > 3 {
// checking the length to avoid panic when parsing
// The format of the traceparent is `-` separated string,
// where flags represents the propagated flags in the format of 2 hex-encoded digits at the end of the traceparent.
otelFlagLen := 2
if f, err := strconv.ParseUint(parent[len(parent)-otelFlagLen:], 16, 8); err != nil {
log.Debug("Couldn't parse traceparent: %v", err)
} else {
c.TraceFlags = oteltrace.TraceFlags(f)
}
}
// Remote indicates a remotely-created Span
c.Remote = true
}
func uint64ToByte(n uint64, b []byte) {
binary.BigEndian.PutUint64(b, n)
}
// IsRecording returns the recording state of the Span. It will return
// true if the Span is active and events can be recorded.
func (s *span) IsRecording() bool {
return !s.finished
}
type statusInfo struct {
code otelcodes.Code
description string
}
// SetStatus saves state of code and description indicating
// whether the span has recorded errors. This will be done by setting
// `error.message` tag on the span. If the code has been set to a higher
// value before (OK > Error > Unset), the code will not be changed.
// The code and description are set once when the span is finished.
func (s *span) SetStatus(code otelcodes.Code, description string) {
if code >= s.statusInfo.code {
s.statusInfo = statusInfo{code, description}
}
}
// SetAttributes sets the key-value pairs as tags on the span.
// Every value is propagated as an interface.
func (s *span) SetAttributes(kv ...attribute.KeyValue) {
for _, attr := range kv {
s.SetTag(string(attr.Key), attr.Value.AsInterface())
}
}