AWS Lambda
Middleware instruments AWS Lambda functions to collect application telemetry and help investigate function execution, errors, and requests across serverless services. Available signals and export configuration depend on the runtime and instrumentation method described below.
Middleware supports AWS Lambda instrumentation for Node.js, Python, Java, Go, and .NET.
How it works#
Middleware uses OpenTelemetry to collect telemetry from AWS Lambda functions.
Depending on the runtime, instrumentation works through:
- OpenTelemetry Lambda layers: Automatically instrument Node.js, Python, and Java functions using runtime-specific layers and, where required, an OpenTelemetry Collector extension.
- OpenTelemetry SDK: Instrument Go Lambda handlers and export traces directly to Middleware.
- Middleware APM SDK: Configure .NET applications to send telemetry to Middleware.
Lambda execution environments can freeze after an invocation. Configure telemetry flushing to ensure spans are exported before the function stops processing.

Supported telemetry#
| Traces | Metrics | App Logs | Custom Logs | Profiling |
|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✖ |
Telemetry availability depends on the runtime and the instrumentation configuration.
Prerequisites#
Before instrumenting AWS Lambda, verify the following:
- An AWS account with an existing Lambda function.
- Permissions to update Lambda layers, environment variables, and function code.
- A Middleware account with an API key and target URL.
- Outbound HTTPS access from the Lambda function to your Middleware endpoint on port 443.
- The appropriate OpenTelemetry Lambda layer for your runtime, AWS Region, and processor architecture, where applicable.
Optional: Enable AWS X-Ray Active Tracing if you need X-Ray integration and compatible trace context propagation.
Select your runtime#
| Runtime | Installation method |
|---|---|
| Node.js | OpenTelemetry Collector and auto-instrumentation layers |
| Python | OpenTelemetry Collector and auto-instrumentation layers |
| Java | OpenTelemetry Java instrumentation layer |
| Go | OpenTelemetry Go SDK and Lambda handler wrapper |
| .NET | Middleware APM SDK |
Note: OpenTelemetry Lambda layer ARNs are Region- and architecture-specific. Use the appropriate published version from the OpenTelemetry Lambda releases rather than relying on an older example ARN.
Installation#
Use OpenTelemetry Lambda layers to automatically instrument your Node.js function and send traces to Middleware.
1 Get the OpenTelemetry layer ARNs#
Open the OpenTelemetry Lambda repository.
Find the compatible versions of:
- OpenTelemetry Collector Lambda layer
- OpenTelemetry Node.js auto-instrumentation layer
Select the layer ARNs for your AWS Region, runtime, and architecture.
2 Add the layers to your Lambda function#
- Open AWS Console → Lambda → Functions.
- Select the function you want to instrument.
- Scroll to Layers.
- Click Add a layer → Specify an ARN.
- Add the Collector layer.
- Add the Node.js auto-instrumentation layer
Add the layers in the order specified above.
3 Configure environment variables#
Go to Configuration → Environment variables → Edit.
Add the following:
1AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
2NODE_OPTIONS=--require ./lambda-config.js
3OTEL_SERVICE_NAME=your-service-name
4OTEL_EXPORTER_OTLP_ENDPOINT=https://<MW_UID>.middleware.io:443
5OTEL_RESOURCE_ATTRIBUTES=mw.account_key=<MW_API_KEY>
6OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION=true
7OTEL_PROPAGATORS=tracecontextReplace the placeholders with your Middleware account details and service name.
Note: For ESM-based Node.js applications, use the appropriate --import configuration instead of --require.
4 Configure trace exporting#
Create a file named lambda-config.js in your Lambda function's root directory.
1const { SimpleSpanProcessor } = require("@opentelemetry/sdk-trace-base");
2const {
3 OTLPTraceExporter,
4} = require("@opentelemetry/exporter-trace-otlp-proto");
5
6global.configureTracerProvider = (tracerProvider) => {
7 const spanProcessor = new SimpleSpanProcessor(new OTLPTraceExporter());
8 tracerProvider.addSpanProcessor(spanProcessor);
9};This configures the trace provider to export spans as they finish, reducing the risk of losing spans when Lambda freezes the execution environment.
Ensure the required OpenTelemetry dependencies are included in your application package.
5 Deploy and verify#
Deploy the updated Lambda function and invoke it.
Go to APM → Services in Middleware and verify that your configured service appears.
Add custom spans (optional)#
You can use the OpenTelemetry API to capture custom operations in your Lambda function.
1const { trace } = require("@opentelemetry/api");
2
3const tracer = trace.getTracer("lambda-handler", "1.0.0");
4
5exports.handler = async (event) => {
6 const span = tracer.startSpan("process-request");
7
8 try {
9 span.setAttribute("function.operation", "process-request");
10
11 // Your application logic here
12
13 return {
14 statusCode: 200,
15 body: JSON.stringify({ message: "Success" }),
16 };
17 } catch (error) {
18 span.recordException(error);
19 throw error;
20 } finally {
21 span.end();
22 }
23};Use custom spans to capture application-specific operations that automatic instrumentation does not cover.
Use OpenTelemetry Lambda layers to collect traces and logs from Python functions.
1 Get the OpenTelemetry layer ARNs#
From the OpenTelemetry Lambda repository find the compatible:
- OpenTelemetry Collector Lambda layer
- OpenTelemetry Python auto-instrumentation layer
Select the correct ARNs for your AWS Region and architecture.
2 Create the Collector configuration#
Create a file named collector.yaml in your Lambda deployment package.
1receivers:
2 telemetryapi: {}
3 otlp:
4 protocols:
5 grpc:
6 endpoint: "localhost:9319"
7 http:
8 endpoint: "localhost:9320"
9
10exporters:
11 debug:
12 verbosity: detailed
13 otlp:
14 endpoint: "https://<MW_UID>.middleware.io:443"
15 headers:
16 authorization: <MW_API_KEY>
17
18processors:
19 decouple: {}
20 resource:
21 attributes:
22 - action: upsert
23 key: service.name
24 value: my-service-name
25
26service:
27 pipelines:
28 traces:
29 receivers:
30 - telemetryapi
31 - otlp
32 processors:
33 - resource
34 - decouple
35 exporters:
36 - otlp
37
38 logs:
39 receivers:
40 - telemetryapi
41 - otlp
42 processors:
43 - resource
44 - decouple
45 exporters:
46 - otlpReplace the Middleware credentials and service name.
The Collector receives telemetry locally and exports it to Middleware.
Note: This configuration requires a Collector distribution that supports the telemetryapi receiver and decouple processor. Verify compatibility with the selected Collector Lambda layer.
3 Add the Lambda layers#
- Open AWS Console → Lambda → Functions.
- Select your Python Lambda function.
- Go to Layers → Add a layer.
- Add the Collector layer.
- Add the Python auto-instrumentation layer.
Use the appropriate ARNs for your deployment.
4 Configure environment variables#
Under Configuration → Environment variables, add:
1AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument
2OPENTELEMETRY_COLLECTOR_CONFIG_URI=/var/task/collector.yaml
3OPENTELEMETRY_EXTENSION_LOG_LEVEL=info
4OTEL_BSP_SCHEDULE_DELAY=500
5OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:9320
6OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION=true
7OTEL_PROPAGATORS=tracecontextThe application sends telemetry to the local Collector, which forwards it to Middleware.
5 Enable JSON logging#
To collect structured application logs:
- Open your Lambda function.
- Go to Configuration → Monitoring and operations tools.
- Click Edit.
- Set the log format to JSON.
- Save the configuration.
6 Configure application logging (optional)#
Use Python's logging module to emit application logs.
1import json
2import logging
3
4def lambda_handler(event, context):
5 logger1 = logging.getLogger("logger1")
6 logger1.setLevel(logging.DEBUG)
7
8 logger1.debug("This is a debug message from aws lambda")
9 logger1.info("This is an info message from aws lambda")
10
11 logger1.propagate = False
12
13 logging.shutdown()
14
15 return {
16 "statusCode": 200,
17 "body": json.dumps("Hello from Lambda!")
18 }Set logger1.propagate = False in this configuration to avoid duplicate log records.
Deploy the function and invoke it to generate telemetry.
Use the OpenTelemetry Java Lambda instrumentation layer to collect application traces.
1 Add the Java instrumentation layer#
- Open AWS Console → Lambda → Functions.
- Select your Java Lambda function.
- Go to Layers → Add a layer.
- Add the compatible OpenTelemetry Java instrumentation layer ARN for your AWS Region and runtime.
Find the current layer information in the OpenTelemetry Lambda repository.
2 Configure environment variables#
Add the following variables to your Lambda function:
1AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
2OTEL_EXPORTER_OTLP_ENDPOINT=https://<MW_UID>.middleware.io:443
3OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION=true
4OTEL_PROPAGATORS=tracecontext
5OTEL_RESOURCE_ATTRIBUTES=mw.account_key=<YOUR_MW_ACCOUNT_KEY>
6OTEL_SERVICE_NAME=<JAVA_LAMBDA_SERVICE>Replace the placeholders with your Middleware credentials and service name.
3 Add custom instrumentation (optional)#
Automatic instrumentation captures supported Lambda operations. To create custom spans, add the OpenTelemetry API dependency.
Gradle
1dependencies {
2 implementation 'io.opentelemetry:opentelemetry-api:1.28.0'
3 implementation 'com.amazonaws:aws-lambda-java-core:1.2.1'
4}Maven
1<dependencies>
2 <dependency>
3 <groupId>io.opentelemetry</groupId>
4 <artifactId>opentelemetry-api</artifactId>
5 <version>1.28.0</version>
6 </dependency>
7 <dependency>
8 <groupId>com.amazonaws</groupId>
9 <artifactId>aws-lambda-java-core</artifactId>
10 <version>1.2.1</version>
11 </dependency>
12</dependencies>Use dependency versions compatible with your Java runtime and OpenTelemetry instrumentation layer.
4 Deploy and verify#
Deploy the updated function and invoke it.
Go to APM → Services in Middleware to verify trace collection.
Instrument Go Lambda functions using the OpenTelemetry Go SDK and the official Lambda handler wrapper.
Unlike the layer-based methods, this setup exports traces directly to Middleware using OTLP/HTTP.
1 Install the required packages#
Add the OpenTelemetry and AWS Lambda packages to your Go project.
1go get github.com/aws/aws-lambda-go/lambda
2go get go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda
3go get go.opentelemetry.io/otel
4go get go.opentelemetry.io/otel/sdk
5go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp2 Configure the trace exporter#
Create a tracer provider that exports spans to Middleware.
1func initTracerProvider(ctx context.Context) (*trace.TracerProvider, error) {
2 exp, err := otlptracehttp.New(ctx,
3 otlptracehttp.WithEndpointURL(
4 "https://<MW_UID>.middleware.io:443/v1/traces",
5 ),
6 )
7 if err != nil {
8 return nil, err
9 }
10
11 res := resource.NewSchemaless(
12 attribute.String("service.name", "go-lambda-service"),
13 attribute.String("mw.account_key", "<YOUR_MW_ACCOUNT_KEY>"),
14 attribute.String("library.language", "go"),
15 )
16
17 tp := trace.NewTracerProvider(
18 trace.WithBatcher(exp),
19 trace.WithResource(res),
20 )
21
22 otel.SetTracerProvider(tp)
23
24 return tp, nil
25}The WithEndpointURL option requires the complete OTLP/HTTP traces URL, including /v1/traces.
3 Instrument the Lambda handler#
Add a custom span to your Lambda handler.
1type MyEvent struct {
2 Name string `json:"name"`
3 Age int `json:"age"`
4}
5
6type MyResponse struct {
7 Message string `json:"message"`
8}
9
10func HandleLambdaEvent(ctx context.Context, event *MyEvent) (*MyResponse, error) {
11 tracer := otel.Tracer("lambda")
12 _, span := tracer.Start(ctx, "HandleLambdaEvent")
13 defer span.End()
14
15 span.SetAttributes(
16 attribute.String("user.name", event.Name),
17 attribute.Int("user.age", event.Age),
18 )
19
20 return &MyResponse{Message: "ok"}, nil
21}4 Configure trace flushing#
Wrap the handler using otellambda.InstrumentHandler.
1func main() {
2 ctx := context.Background()
3
4 tp, err := initTracerProvider(ctx)
5 if err != nil {
6 log.Fatalf("failed to init tracer provider: %v", err)
7 }
8
9 defer tp.Shutdown(ctx)
10
11 lambda.Start(otellambda.InstrumentHandler(
12 HandleLambdaEvent,
13 otellambda.WithFlusher(tp),
14 ))
15}WithFlusher helps export completed spans before the Lambda execution environment freezes.
Note: Include the required imports from the existing Go example and verify that the selected exporter supports your Middleware authentication configuration.
5 Deploy and verify#
Build and deploy your Go Lambda function.
Invoke the function and verify traces under APM → Services in Middleware.
Use the Middleware APM package to instrument supported .NET applications.
1 Verify the .NET version#
Ensure .NET 6 or later is installed in your development environment.
1dotnet --version2 Install the Middleware APM package#
1dotnet add package MW.APM3 Configure instrumentation#
Add the Middleware instrumentation configuration to your application startup.
For an ASP.NET Core application using the minimal hosting model:
1var builder = WebApplication.CreateBuilder(args);
2
3var configuration = new ConfigurationBuilder()
4 .SetBasePath(Directory.GetCurrentDirectory())
5 .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
6 .AddEnvironmentVariables()
7 .Build();
8
9builder.Services.ConfigureMWInstrumentation(configuration);
10
11builder.Logging.AddConfiguration(configuration.GetSection("Logging"));
12builder.Logging.AddConsole();
13builder.Logging.SetMinimumLevel(LogLevel.Debug);
14
15var app = builder.Build();
16
17Logger.Init(app.Services.GetRequiredService<ILoggerFactory>());
18
19app.Run();4 Configure Middleware credentials#
Add the following configuration to appsettings.json:
1{
2 "MW": {
3 "ApiKey": "<YOUR_MW_ACCOUNT_KEY>",
4 "TargetURL": "<MW_UID>.middleware.io:443",
5 "ServiceName": "<SERVICE_NAME>"
6 },
7 "Logging": {
8 "LogLevel": {
9 "Default": "Information",
10 "Microsoft.AspNetCore": "Warning"
11 }
12 }
13}Replace the placeholders with your Middleware account details.
Note: The startup example above is for ASP.NET Core. A Lambda project using a different hosting model may require a different initialization method.
5 Deploy and verify#
Deploy the updated application to AWS Lambda.
Invoke the function and check APM → Services in Middleware for the configured service.
Verify Installation#
After instrumenting your Lambda function, invoke it to generate telemetry.
1 Invoke the function#
Use the AWS Lambda console to create and run a test event.
Alternatively, invoke the function using the AWS CLI:
1aws lambda invoke \
2 --function-name <FUNCTION_NAME> \
3 --payload '{}' \
4 --cli-binary-format raw-in-base64-out \
5 response.json2 Verify traces#
- Log in to Middleware.
- Go to APM → Services.
- Find the service name configured using
OTEL_SERVICE_NAMEor the Middleware SDK configuration. - Open the service to view its traces, errors, and latency.
3 Verify logs#
Go to Log Monitoring
If logs do not appear, verify that logging is enabled and that the configured Collector or SDK exports logs to Middleware.
AWS X-Ray tracing (optional)#
AWS X-Ray provides additional tracing context for Lambda functions and their AWS service dependencies.
To enable Active Tracing:
- Open AWS Console → Lambda → Functions.
- Select your Lambda function.
- Go to Configuration → Monitoring and operations tools.
- Under AWS X-Ray, enable Active tracing.
- Save the changes.
For W3C trace context propagation, use:
1OTEL_PROPAGATORS=tracecontextIf you need AWS X-Ray interoperability, configure compatible propagators rather than disabling AWS context propagation.
Environment variables reference#
| Variable | Description |
|---|---|
AWS_LAMBDA_EXEC_WRAPPER | Starts runtime auto-instrumentation before the Lambda handler |
OTEL_SERVICE_NAME | Service name displayed in Middleware |
OTEL_RESOURCE_ATTRIBUTES | Resource metadata, including Middleware account identification |
OTEL_PROPAGATORS | Trace context propagation format |
OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION | Controls AWS-specific context propagation |
OTEL_EXPORTER_OTLP_ENDPOINT | Base endpoint for OTLP export |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | Signal-specific endpoint for traces |
OTEL_BSP_SCHEDULE_DELAY | Span batch processor delay in milliseconds |
NODE_OPTIONS | Loads the Node.js instrumentation configuration |
OPENTELEMETRY_COLLECTOR_CONFIG_URI | Path to the Collector configuration file |
OPENTELEMETRY_EXTENSION_LOG_LEVEL | Logging level for the Collector extension |
OTEL_LOGS_EXPORTER | Configures the OpenTelemetry logs exporter |
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED | Enables Python logging instrumentation |
Troubleshooting#
No traces in Middleware#
If traces do not appear after invoking your Lambda function:
- Verify that the correct instrumentation layer is attached for your runtime, Region, and architecture.
- Confirm that the required environment variables are configured.
- Check that your Middleware target URL and account key are correct.
- Verify that the Lambda function can reach Middleware over HTTPS.
- Review the function's CloudWatch Logs for instrumentation or exporter errors.
Traces are missing or delayed#
Lambda functions can freeze before buffered spans are exported.
- Reduce the batch span processor delay, for example
OTEL_BSP_SCHEDULE_DELAY=500. - For Go, use
otellambda.WithFlusher(tp)to flush spans after invocations. - Verify that the function timeout allows sufficient time for telemetry export.
Collector layer is not exporting data#
If you use the Collector Lambda layer:
- Verify that
collector.yamlis included in the deployment package. - Confirm that
OPENTELEMETRY_COLLECTOR_CONFIG_URIpoints to the correct file. - Check the Collector exporter configuration and Middleware credentials.
- Review CloudWatch Logs for Collector startup or export errors.
Node.js instrumentation is not loading#
If the Node.js wrapper starts but the bootstrap file does not load:
- Verify that
lambda-config.jsis included in the deployment package. - Check the
NODE_OPTIONSpath. - For ESM applications, use a compatible
--importconfiguration rather than CommonJS--require.
Python logs are missing#
If traces appear but Python application logs are missing:
- Enable OpenTelemetry Python logging instrumentation.
- Set
OTEL_LOGS_EXPORTER=otlpwhen required. - Verify that the Collector has a logs pipeline.
- Check for duplicate log propagation if logs appear more than once.
Go traces are not exported#
If Go instrumentation is enabled but traces do not appear:
- Verify that the OTLP/HTTP traces endpoint includes
/v1/traces. - Check that the exporter sends the required Middleware account information.
- Confirm that the Lambda handler uses
otellambda.InstrumentHandler. - Verify that spans are flushed before the invocation ends.
Lambda function cannot connect to Middleware#
If the function runs inside a VPC:
- Verify that its subnet routing provides outbound access.
- Configure a NAT Gateway or another appropriate egress path if required.
- Confirm that security groups and network ACLs allow outbound HTTPS traffic.
Traces are not connected across services#
If traces appear separately instead of forming a distributed trace:
- Use a consistent propagation format across services.
- Verify that upstream services forward the required tracing headers.
- Check AWS X-Ray and W3C propagation settings for compatibility.
If the issue persists, contact Middleware Support at [email protected].
Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us or join our Slack channel.
What did you think of this content?