-
Notifications
You must be signed in to change notification settings - Fork 40
/
listener.go
146 lines (123 loc) · 4.79 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
/*
* 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 2021 Datadog, Inc.
*/
package trace
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/DataDog/datadog-lambda-go/internal/extension"
"github.com/DataDog/datadog-lambda-go/internal/logger"
"github.com/DataDog/datadog-lambda-go/internal/version"
"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
extensionManager *extension.ExtensionManager
traceContextExtractor ContextExtractor
}
// Config gives options for how the Listener should work
Config struct {
DDTraceEnabled bool
MergeXrayTraces bool
TraceContextExtractor ContextExtractor
}
)
// The function execution span is the top-level span representing the current Lambda function execution
var functionExecutionSpan ddtrace.Span
var tracerInitialized = false
// MakeListener initializes a new trace lambda Listener
func MakeListener(config Config, extensionManager *extension.ExtensionManager) Listener {
return Listener{
ddTraceEnabled: config.DDTraceEnabled,
mergeXrayTraces: config.MergeXrayTraces,
extensionManager: extensionManager,
traceContextExtractor: config.TraceContextExtractor,
}
}
// HandlerStarted sets up tracing and starts the function execution span if Datadog tracing is enabled
func (l *Listener) HandlerStarted(ctx context.Context, msg json.RawMessage) context.Context {
if !l.ddTraceEnabled {
return ctx
}
ctx, _ = contextWithRootTraceContext(ctx, msg, l.mergeXrayTraces, l.traceContextExtractor)
if !tracerInitialized {
tracer.Start(
tracer.WithService("aws.lambda"),
tracer.WithLambdaMode(!l.extensionManager.IsExtensionRunning()),
tracer.WithGlobalTag("_dd.origin", "lambda"),
)
tracerInitialized = true
}
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 ends the function execution span and stops the tracer
func (l *Listener) HandlerFinished(ctx context.Context, err error) {
if functionExecutionSpan != nil {
functionExecutionSpan.Finish(tracer.WithError(err))
}
tracer.Flush()
}
// 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)
rootTraceContext, ok := ctx.Value(traceContextKey).(TraceContext)
if !ok {
logger.Error(fmt.Errorf("Error extracting trace context from context object"))
}
functionArn := lambdaCtx.InvokedFunctionArn
functionArn = strings.ToLower(functionArn)
functionArn, functionVersion := separateVersionFromFunctionArn(functionArn)
// Set the root trace context as the parent of the function execution span
var parentSpanContext ddtrace.SpanContext
convertedSpanContext, err := ConvertTraceContextToSpanContext(rootTraceContext)
if err == nil {
parentSpanContext = convertedSpanContext
}
span := tracer.StartSpan(
"aws.lambda", // This operation name will be replaced with the value of the service tag by the Forwarder
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),
tracer.Tag("functionname", strings.ToLower(lambdacontext.FunctionName)),
tracer.Tag("datadog_lambda", version.DDLambdaVersion),
tracer.Tag("dd_trace", version.DDTraceVersion),
)
if parentSpanContext != nil && mergeXrayTraces {
// This tag will cause the Forwarder to drop the span (to avoid redundancy with X-Ray)
span.SetTag("_dd.parent_source", "xray")
}
return span
}
func separateVersionFromFunctionArn(functionArn string) (arnWithoutVersion string, functionVersion string) {
arnSegments := strings.Split(functionArn, ":")
if cap(arnSegments) < 7 {
return "", ""
}
functionVersion = "$LATEST"
arnWithoutVersion = strings.Join(arnSegments[0:7], ":")
if len(arnSegments) > 7 {
functionVersion = arnSegments[7]
}
return arnWithoutVersion, functionVersion
}