/
config_span.go
146 lines (128 loc) · 4.1 KB
/
config_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
package otelcli
import (
"encoding/hex"
"fmt"
"io"
"time"
"github.com/equinix-labs/otel-cli/otlpclient"
"github.com/equinix-labs/otel-cli/w3c/traceparent"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
// NewProtobufSpan creates a new span and populates it with information
// from the config struct.
func (c Config) NewProtobufSpan() *tracepb.Span {
span := otlpclient.NewProtobufSpan()
if c.GetIsRecording() {
span.TraceId = otlpclient.GenerateTraceId()
span.SpanId = otlpclient.GenerateSpanId()
}
span.Name = c.SpanName
span.Kind = otlpclient.SpanKindStringToInt(c.Kind)
span.Attributes = otlpclient.StringMapAttrsToProtobuf(c.Attributes)
now := time.Now()
if c.SpanStartTime != "" {
st := c.ParseSpanStartTime()
span.StartTimeUnixNano = uint64(st.UnixNano())
} else {
span.StartTimeUnixNano = uint64(now.UnixNano())
}
if c.SpanEndTime != "" {
et := c.ParseSpanEndTime()
span.EndTimeUnixNano = uint64(et.UnixNano())
} else {
span.EndTimeUnixNano = uint64(now.UnixNano())
}
if c.GetIsRecording() {
tp := c.LoadTraceparent()
if tp.Initialized {
span.TraceId = tp.TraceId
span.ParentSpanId = tp.SpanId
}
} else {
span.TraceId = otlpclient.GetEmptyTraceId()
span.SpanId = otlpclient.GetEmptySpanId()
}
// --force-trace-id, --force-span-id and --force-parent-span-id let the user set their own trace, span & parent span ids
// these work in non-recording mode and will stomp trace id from the traceparent
var err error
if c.ForceTraceId != "" {
span.TraceId, err = parseHex(c.ForceTraceId, 16)
c.SoftFailIfErr(err)
}
if c.ForceSpanId != "" {
span.SpanId, err = parseHex(c.ForceSpanId, 8)
c.SoftFailIfErr(err)
}
if c.ForceParentSpanId != "" {
span.ParentSpanId, err = parseHex(c.ForceParentSpanId, 8)
c.SoftFailIfErr(err)
}
otlpclient.SetSpanStatus(span, c.StatusCode, c.StatusDescription)
return span
}
// LoadTraceparent follows otel-cli's loading rules, start with envvar then file.
// If both are set, the file will override env.
// When in non-recording mode, the previous traceparent will be returned if it's
// available, otherwise, a zero-valued traceparent is returned.
func (c Config) LoadTraceparent() traceparent.Traceparent {
tp := traceparent.Traceparent{
Version: 0,
TraceId: otlpclient.GetEmptyTraceId(),
SpanId: otlpclient.GetEmptySpanId(),
Sampling: false,
Initialized: true,
}
if !c.TraceparentIgnoreEnv {
var err error
tp, err = traceparent.LoadFromEnv()
if err != nil {
Diag.Error = err.Error()
}
}
if c.TraceparentCarrierFile != "" {
fileTp, err := traceparent.LoadFromFile(c.TraceparentCarrierFile)
if err != nil {
Diag.Error = err.Error()
} else if fileTp.Initialized {
tp = fileTp
}
}
if c.TraceparentRequired {
if tp.Initialized {
return tp
} else {
c.SoftFail("failed to find a valid traceparent carrier in either environment for file '%s' while it's required by --tp-required", c.TraceparentCarrierFile)
}
}
return tp
}
// PropagateTraceparent saves the traceparent to file if necessary, then prints
// span info to the console according to command-line args.
func (c Config) PropagateTraceparent(span *tracepb.Span, target io.Writer) {
var tp traceparent.Traceparent
if c.GetIsRecording() {
tp = otlpclient.TraceparentFromProtobufSpan(span, c.GetIsRecording())
} else {
// when in non-recording mode, and there is a TP available, propagate that
tp = c.LoadTraceparent()
}
if c.TraceparentCarrierFile != "" {
err := tp.SaveToFile(c.TraceparentCarrierFile, c.TraceparentPrintExport)
c.SoftFailIfErr(err)
}
if c.TraceparentPrint {
tp.Fprint(target, c.TraceparentPrintExport)
}
}
// parseHex parses hex into a []byte of length provided. Errors if the input is
// not valid hex or the converted hex is not the right number of bytes.
func parseHex(in string, expectedLen int) ([]byte, error) {
out, err := hex.DecodeString(in)
if err != nil {
return nil, fmt.Errorf("error parsing hex string %q: %w", in, err)
}
if len(out) != expectedLen {
return nil, fmt.Errorf("hex string %q is the wrong length, expected %d bytes but got %d", in, expectedLen, len(out))
}
return out, nil
}