-
Notifications
You must be signed in to change notification settings - Fork 39
/
listener.go
154 lines (128 loc) · 4.93 KB
/
listener.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
147
148
149
150
151
152
153
154
/*
* 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 2019 Datadog, Inc.
*/
package trace
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/DataDog/datadog-lambda-go/internal/logger"
"github.com/aws/aws-lambda-go/lambdacontext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)
type (
// Listener creates a function execution span and injects it into the context
Listener struct {
ddTraceEnabled bool
mergeXrayTraces bool
}
// Config gives options for how the Listener should work
Config struct {
DDTraceEnabled bool
MergeXrayTraces bool
}
)
// The function execution span is the top-level span representing the current Lambda function execution
var functionExecutionSpan ddtrace.Span
// MakeListener initializes a new trace lambda Listener
func MakeListener(config Config) Listener {
return Listener{
ddTraceEnabled: config.DDTraceEnabled,
mergeXrayTraces: config.MergeXrayTraces,
}
}
// HandlerStarted creates the function execution span representing the Lambda function execution
// and adds that span to the context so that the user can create child spans (if Datadog tracing is enabled)
func (l *Listener) HandlerStarted(ctx context.Context, msg json.RawMessage) context.Context {
ctx, _ = ContextWithTraceContext(ctx, msg)
if l.ddTraceEnabled {
tracer.Start(
tracer.WithService("aws.lambda"),
tracer.WithLambdaMode(true),
tracer.WithDebugMode(true),
tracer.WithGlobalTag("__dd.origin", "lambda"),
)
functionExecutionSpan = startFunctionExecutionSpan(ctx, l.mergeXrayTraces)
// Add the span to the context so the user can create child spans
ctx = tracer.ContextWithSpan(ctx, functionExecutionSpan)
}
return ctx
}
// HandlerFinished finishes the function execution span (if it was started) and stops the tracer
func (l *Listener) HandlerFinished(ctx context.Context) {
if functionExecutionSpan != nil {
functionExecutionSpan.Finish()
}
// Stop the tracer, forcing it to flush any traces it's holding
// Without this, we might drop traces
tracer.Stop()
}
// startFunctionExecutionSpan starts a span that represents the current Lambda function execution
// and returns the span so that it can be finished when the function execution is complete
func startFunctionExecutionSpan(ctx context.Context, mergeXrayTraces bool) tracer.Span {
// Extract information from context
lambdaCtx, _ := lambdacontext.FromContext(ctx)
var traceSource string
traceContext, ok := ctx.Value(traceContextKey).(TraceContext)
if ok {
traceSource = traceContext[sourceType]
} else {
logger.Error(fmt.Errorf("Error extracting Datadog trace context from context"))
}
functionArn := lambdaCtx.InvokedFunctionArn
functionArn = strings.ToLower(functionArn)
functionArn, functionVersion := separateVersionFromFunctionArn(functionArn)
// The function execution span must be made a child of the current span if the trace context came from an event OR merge X-Ray traces is enabled
// In other words, if merge X-Ray traces is NOT enabled and the trace context came from X-Ray, we should NOT make the execution span a child of the X-Ray span
var parentSpanContext ddtrace.SpanContext
if (traceSource == fromEvent) || mergeXrayTraces {
convertedSpanContext, err := convertTraceContextToSpanContext(traceContext)
if err == nil {
parentSpanContext = convertedSpanContext
}
}
span := tracer.StartSpan(
"aws.lambda",
tracer.SpanType("serverless"),
tracer.ChildOf(parentSpanContext),
tracer.ResourceName(lambdacontext.FunctionName),
tracer.Tag("cold_start", ctx.Value("cold_start")),
tracer.Tag("function_arn", functionArn),
tracer.Tag("function_version", functionVersion),
tracer.Tag("request_id", lambdaCtx.AwsRequestID),
tracer.Tag("resource_names", lambdacontext.FunctionName),
)
if traceSource == fromXray && mergeXrayTraces {
span.SetTag("_dd.parent_source", traceSource)
}
return span
}
func separateVersionFromFunctionArn(functionArn string) (arnWithoutVersion string, functionVersion string) {
arnSegments := strings.Split(functionArn, ":")
functionVersion = "$LATEST"
arnWithoutVersion = strings.Join(arnSegments[0:7], ":")
if len(arnSegments) > 7 {
functionVersion = arnSegments[7]
}
return arnWithoutVersion, functionVersion
}
func convertTraceContextToSpanContext(traceCtx TraceContext) (ddtrace.SpanContext, error) {
spanCtx, err := propagator.Extract(tracer.TextMapCarrier(traceCtx))
if err != nil {
logger.Error(fmt.Errorf("Error extracting Datadog trace context from context: %v", err))
return nil, err
}
return spanCtx, nil
}
// propagator is able to extract a SpanContext object from a TraceContext object
var propagator = tracer.NewPropagator(&tracer.PropagatorConfig{
TraceHeader: traceIDHeader,
ParentHeader: parentIDHeader,
PriorityHeader: samplingPriorityHeader,
})