Skip to content

Commit d7c65e2

Browse files
committed
Merge branch 'otel'
This migrates AWS SDK instrumentation from the deprecated X-Ray SDK to OpenTelemetry. OTel is better about not crashing the program when there's no parent span or the collected traces aren't going anywhere, so in general the instrumentation is always enabled but X-Ray exports are only active if set in the environment. In theory, this really opens up what we can do on the randomizer-server side, but I'm not sure how many options are out there to export traces.
2 parents bb72c75 + 31fb777 commit d7c65e2

File tree

894 files changed

+84626
-340745
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

894 files changed

+84626
-340745
lines changed

CloudFormation.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Resources:
7373
DYNAMODB_TABLE: !Ref GroupsTable
7474
SLACK_TOKEN_SSM_NAME: !Sub '/${SlackTokenSSMName}'
7575
SLACK_TOKEN_SSM_TTL: !Ref SlackTokenSSMTTL
76-
AWS_CLIENT_XRAY_TRACING: !If [HasXRayTracingEnabled, '1', !Ref AWS::NoValue]
76+
AWS_XRAY_TRACING_ENABLED: !If [HasXRayTracingEnabled, '1', !Ref AWS::NoValue]
7777
AWS_CLIENT_EMBEDDED_TLS_ROOTS: !If [HasAWSClientEmbeddedTLSRoots, '1', !Ref AWS::NoValue]
7878
FunctionUrlConfig:
7979
AuthType: NONE

SERVERLESS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ Run `./hfc help` to learn more about additional commands that might be useful.
132132
isn't eligible for the AWS Free Tier. See the [Read/Write Capacity
133133
Mode][capacity mode] documentation for details.
134134
- The default configuration enables [AWS X-Ray][x-ray] tracing for the function
135-
and its requests to DynamoDB. X-Ray is free for up to 100,000 traces per month
136-
for every AWS account, and it's useful to see where each request is spending
137-
time. However, you can turn it off by passing `XRayTracingEnabled=false` to
138-
the deployment script.
135+
and its AWS SDK requests. Every AWS account can collect up to 100,000 traces
136+
per month for free, which is useful to see where requests are spending time.
137+
However, you can turn this off by passing `XRayTracingEnabled=false` to the
138+
deployment script.
139139
- My co-workers and I collectively make a little over 500 requests to the
140140
randomizer per month, and at that small of a volume it's essentially free to
141141
run on AWS even without the 12 month free tier. My _rough_ estimate is that

cmd/randomizer-lambda/main.go

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,25 @@ import (
1818
"log/slog"
1919
"os"
2020

21+
"github.com/aws-observability/aws-otel-go/exporters/xrayudp"
2122
"github.com/aws/aws-lambda-go/lambda"
2223
"github.com/awslabs/aws-lambda-go-api-proxy/httpadapter"
24+
lambdadetector "go.opentelemetry.io/contrib/detectors/aws/lambda"
25+
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
26+
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"
27+
xraypropagator "go.opentelemetry.io/contrib/propagators/aws/xray"
28+
"go.opentelemetry.io/otel"
29+
"go.opentelemetry.io/otel/attribute"
30+
"go.opentelemetry.io/otel/sdk/resource"
31+
"go.opentelemetry.io/otel/sdk/trace"
32+
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
2333

2434
"github.com/featherbread/randomizer/internal/slack"
2535
"github.com/featherbread/randomizer/internal/store/dynamodb"
2636
)
2737

2838
func main() {
39+
ctx := context.Background()
2940
logger := slog.New(slog.NewJSONHandler(os.Stderr, nil))
3041

3142
tokenProvider, err := slack.TokenProviderFromEnv()
@@ -34,16 +45,76 @@ func main() {
3445
os.Exit(2)
3546
}
3647

37-
storeFactory, err := dynamodb.FactoryFromEnv(context.Background())
48+
storeFactory, err := dynamodb.FactoryFromEnv(ctx)
3849
if err != nil {
3950
logger.Error("Failed to create DynamoDB store", "err", err)
4051
os.Exit(2)
4152
}
4253

54+
// OpenTelemetry is always active, but traces are only exported to AWS X-Ray
55+
// (and charged for usage) if enabled in the environment.
56+
tp := initTracerProvider(ctx, logger)
57+
otel.SetTracerProvider(tp)
58+
if xrayTracingEnabled {
59+
otel.SetTextMapPropagator(xraypropagator.Propagator{})
60+
}
61+
defer func() {
62+
err := tp.Shutdown(ctx)
63+
if err != nil {
64+
logger.Warn("Failed to shut down tracer provider", "err", err)
65+
}
66+
}()
67+
4368
app := slack.App{
4469
TokenProvider: tokenProvider,
4570
StoreFactory: storeFactory,
4671
Logger: logger,
4772
}
48-
lambda.Start(httpadapter.NewV2(app).ProxyWithContext)
73+
appHandler := httpadapter.NewV2(app).ProxyWithContext
74+
handler := otellambda.InstrumentHandler(appHandler, xrayconfig.WithRecommendedOptions(tp)...)
75+
lambda.Start(handler)
76+
}
77+
78+
var xrayTracingEnabled = os.Getenv("AWS_XRAY_TRACING_ENABLED") == "1"
79+
80+
func initTracerProvider(ctx context.Context, logger *slog.Logger) *trace.TracerProvider {
81+
traceResource := initTraceResource(ctx, logger)
82+
tp := trace.NewTracerProvider(trace.WithResource(traceResource))
83+
84+
if !xrayTracingEnabled {
85+
return tp
86+
}
87+
88+
exporter, err := xrayudp.NewSpanExporter(ctx)
89+
if err != nil {
90+
logger.Warn("Failed to initialize X-Ray span exporter", "err", err)
91+
return tp
92+
}
93+
94+
tp.RegisterSpanProcessor(trace.NewSimpleSpanProcessor(exporter))
95+
return tp
96+
}
97+
98+
func initTraceResource(ctx context.Context, logger *slog.Logger) *resource.Resource {
99+
baseResource := resource.NewWithAttributes(semconv.SchemaURL, attribute.KeyValue{
100+
Key: semconv.ServiceNameKey,
101+
Value: attribute.StringValue(os.Getenv("AWS_LAMBDA_FUNCTION_NAME"))})
102+
103+
if !xrayTracingEnabled {
104+
return baseResource
105+
}
106+
107+
lambdaResource, err := lambdadetector.NewResourceDetector().Detect(ctx)
108+
if err != nil {
109+
logger.Warn("Skipping Lambda resources in traces", "err", err, "step", "detect")
110+
return baseResource
111+
}
112+
113+
mergedResource, err := resource.Merge(lambdaResource, baseResource)
114+
if err != nil {
115+
logger.Warn("Skipping Lambda resources in traces", "err", err, "step", "merge")
116+
return baseResource
117+
}
118+
119+
return mergedResource
49120
}

go.mod

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@ go 1.25.0
44

55
require (
66
cloud.google.com/go/firestore v1.18.0
7-
github.com/aws/aws-lambda-go v1.49.0
8-
github.com/aws/aws-sdk-go-v2 v1.38.3
7+
github.com/aws-observability/aws-otel-go/exporters/xrayudp v1.0.0
8+
github.com/aws/aws-lambda-go v1.51.1
9+
github.com/aws/aws-sdk-go-v2 v1.40.1
910
github.com/aws/aws-sdk-go-v2/config v1.31.6
1011
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.8.9
11-
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.1
12+
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.53.3
1213
github.com/aws/aws-sdk-go-v2/service/ssm v1.64.2
13-
github.com/aws/aws-xray-sdk-go/v2 v2.0.0
1414
github.com/awslabs/aws-lambda-go-api-proxy v0.16.2
1515
github.com/googleapis/gax-go/v2 v2.15.0
1616
github.com/spf13/cobra v1.10.1
1717
go.etcd.io/bbolt v1.4.3
18-
google.golang.org/grpc v1.75.0
18+
go.opentelemetry.io/contrib/detectors/aws/lambda v0.64.0
19+
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda v0.64.0
20+
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig v0.64.0
21+
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.64.0
22+
go.opentelemetry.io/contrib/propagators/aws v1.39.0
23+
go.opentelemetry.io/otel v1.39.0
24+
go.opentelemetry.io/otel/sdk v1.39.0
25+
google.golang.org/grpc v1.78.0
1926
)
2027

2128
require (
@@ -24,48 +31,53 @@ require (
2431
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
2532
cloud.google.com/go/compute/metadata v0.9.0 // indirect
2633
cloud.google.com/go/longrunning v0.6.7 // indirect
27-
github.com/andybalholm/brotli v1.2.0 // indirect
2834
github.com/aws/aws-sdk-go-v2/credentials v1.18.10 // indirect
2935
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.9 // indirect
3036
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.6 // indirect
31-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect
32-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect
37+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect
38+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect
3339
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
3440
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.30.2 // indirect
35-
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
36-
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.6 // indirect
41+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
42+
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.15 // indirect
3743
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.6 // indirect
44+
github.com/aws/aws-sdk-go-v2/service/sns v1.39.8 // indirect
45+
github.com/aws/aws-sdk-go-v2/service/sqs v1.42.18 // indirect
3846
github.com/aws/aws-sdk-go-v2/service/sso v1.29.1 // indirect
3947
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.2 // indirect
4048
github.com/aws/aws-sdk-go-v2/service/sts v1.38.2 // indirect
41-
github.com/aws/smithy-go v1.23.0 // indirect
49+
github.com/aws/smithy-go v1.24.0 // indirect
50+
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
51+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
52+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
4253
github.com/felixge/httpsnoop v1.0.4 // indirect
4354
github.com/go-logr/logr v1.4.3 // indirect
4455
github.com/go-logr/stdr v1.2.2 // indirect
4556
github.com/google/s2a-go v0.1.9 // indirect
57+
github.com/google/uuid v1.6.0 // indirect
4658
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
59+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
4760
github.com/inconshreveable/mousetrap v1.1.0 // indirect
48-
github.com/klauspost/compress v1.18.0 // indirect
49-
github.com/pkg/errors v0.9.1 // indirect
61+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
5062
github.com/spf13/pflag v1.0.9 // indirect
51-
github.com/valyala/bytebufferpool v1.0.0 // indirect
52-
github.com/valyala/fasthttp v1.65.0 // indirect
53-
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
54-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
55-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
56-
go.opentelemetry.io/otel v1.38.0 // indirect
57-
go.opentelemetry.io/otel/metric v1.38.0 // indirect
58-
go.opentelemetry.io/otel/trace v1.38.0 // indirect
59-
golang.org/x/crypto v0.45.0 // indirect
60-
golang.org/x/net v0.47.0 // indirect
63+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
64+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect
65+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
66+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
67+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect
68+
go.opentelemetry.io/otel/metric v1.39.0 // indirect
69+
go.opentelemetry.io/otel/trace v1.39.0 // indirect
70+
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
71+
golang.org/x/crypto v0.46.0 // indirect
72+
golang.org/x/net v0.48.0 // indirect
6173
golang.org/x/oauth2 v0.33.0 // indirect
62-
golang.org/x/sync v0.18.0 // indirect
63-
golang.org/x/sys v0.38.0 // indirect
64-
golang.org/x/text v0.31.0 // indirect
74+
golang.org/x/sync v0.19.0 // indirect
75+
golang.org/x/sys v0.39.0 // indirect
76+
golang.org/x/text v0.32.0 // indirect
6577
golang.org/x/time v0.14.0 // indirect
6678
google.golang.org/api v0.248.0 // indirect
6779
google.golang.org/genproto v0.0.0-20250826171959-ef028d996bc1 // indirect
68-
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 // indirect
69-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
70-
google.golang.org/protobuf v1.36.8 // indirect
80+
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
81+
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
82+
google.golang.org/protobuf v1.36.11 // indirect
7183
)

0 commit comments

Comments
 (0)