Repository navigation
Expand file tree
/
Copy pathdistributed.go
More file actions
117 lines (99 loc) · 3.71 KB
/
Copy pathdistributed.go
File metadata and controls
117 lines (99 loc) · 3.71 KB
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
package opik
import (
"context"
"net/http"
)
// DistributedTraceHeaders contains trace context for cross-service propagation.
type DistributedTraceHeaders struct {
TraceID string `json:"opik_trace_id"`
ParentSpanID string `json:"opik_parent_span_id"`
}
// Header names for distributed tracing.
const (
HeaderTraceID = "X-Opik-Trace-ID"
HeaderParentSpanID = "X-Opik-Parent-Span-ID"
)
// GetDistributedTraceHeaders returns the current trace context from the context.
// This can be used to propagate trace context across service boundaries.
func GetDistributedTraceHeaders(ctx context.Context) DistributedTraceHeaders {
headers := DistributedTraceHeaders{}
if trace := TraceFromContext(ctx); trace != nil {
headers.TraceID = trace.ID()
}
if span := SpanFromContext(ctx); span != nil {
headers.ParentSpanID = span.ID()
if headers.TraceID == "" {
headers.TraceID = span.TraceID()
}
}
return headers
}
// InjectDistributedTraceHeaders adds trace context headers to an HTTP request.
func InjectDistributedTraceHeaders(ctx context.Context, req *http.Request) {
headers := GetDistributedTraceHeaders(ctx)
if headers.TraceID != "" {
req.Header.Set(HeaderTraceID, headers.TraceID)
}
if headers.ParentSpanID != "" {
req.Header.Set(HeaderParentSpanID, headers.ParentSpanID)
}
}
// ExtractDistributedTraceHeaders extracts trace context from HTTP request headers.
func ExtractDistributedTraceHeaders(req *http.Request) DistributedTraceHeaders {
return DistributedTraceHeaders{
TraceID: req.Header.Get(HeaderTraceID),
ParentSpanID: req.Header.Get(HeaderParentSpanID),
}
}
// ContinueTrace creates a new span that continues from distributed trace headers.
// This is used when receiving a request from another service that has trace context.
func (c *Client) ContinueTrace(ctx context.Context, headers DistributedTraceHeaders, spanName string, opts ...SpanOption) (context.Context, *Span, error) {
if headers.TraceID == "" {
// No trace context, start a new trace and return the first span
ctx, trace, err := StartTrace(ctx, c, spanName)
if err != nil {
return ctx, nil, err
}
span, err := trace.Span(ctx, spanName, opts...)
if err != nil {
return ctx, nil, err
}
newCtx := ContextWithSpan(ctx, span)
return newCtx, span, nil
}
// Create a span that continues the distributed trace
span, err := c.createSpanWithParent(ctx, headers.TraceID, headers.ParentSpanID, spanName, opts...)
if err != nil {
return ctx, nil, err
}
newCtx := ContextWithSpan(ctx, span)
return newCtx, span, nil
}
// createSpanWithParent creates a span with explicit trace and parent span IDs.
func (c *Client) createSpanWithParent(ctx context.Context, traceID, parentSpanID, name string, opts ...SpanOption) (*Span, error) {
return c.createSpan(ctx, traceID, parentSpanID, name, opts...)
}
// PropagatingRoundTripper wraps an http.RoundTripper to automatically inject
// distributed trace headers into outgoing requests.
type PropagatingRoundTripper struct {
transport http.RoundTripper
}
// NewPropagatingRoundTripper creates a new PropagatingRoundTripper.
func NewPropagatingRoundTripper(transport http.RoundTripper) *PropagatingRoundTripper {
if transport == nil {
transport = http.DefaultTransport
}
return &PropagatingRoundTripper{transport: transport}
}
// RoundTrip implements http.RoundTripper.
func (t *PropagatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
InjectDistributedTraceHeaders(req.Context(), req)
return t.transport.RoundTrip(req)
}
// PropagatingHTTPClient returns an http.Client that automatically propagates
// distributed trace headers to all outgoing requests.
func PropagatingHTTPClient() *http.Client {
return &http.Client{
Transport: NewPropagatingRoundTripper(nil),
}
}