Setting up native tracing for Go-based AWS Lambda functions
You can set up native tracing for AWS Lambda functions written in Go by installing and configuring Instana Go Tracer.
- Supported runtimes
- Installing Go Tracer
- Updating Go Tracer
- Configuring the AWS Lambda function
- Enabling tracing
Supported runtimes
Instana supports native tracing for go1.x
runtime that uses Go 1.8 or later.
Installing Go Tracer
You need to install Instana Go Tracer to collect tracing data from the AWS Lambda functions.
Before you install the Go Tracer, ensure that the Instana AWS sensor for Lambda monitoring is configured to collect versions and runtime metrics from AWS Lambda. For more information, see AWS sensor for Lambda monitoring.
To install the Instana Go Tracer, complete the following steps:
-
Download the Go Tracer module:
go get github.com/instana/go-sensor
-
Add the Go Tracer module to your
go.mod
file.
Updating Go Tracer
Ensure that you use the latest version of the tracer.
To make sure that you are using the latest version of the Go Tracer, check the go.mod
file in your project or run the following command:
go get github.com/instana/go-sensor@latest
If you do not have the latest Go Tracer, download the latest version and update the version in the go.mod
file.
Configuring the AWS Lambda function
After you install the Instana Go Tracer, it automatically detects that a service is running on AWS Lambda and switches to serverless mode. Instead of sending collected traces to the host agent, the tracer submits them directly to the Instana serverless acceptor endpoint in the Instana backend.
To send collected traces to the Instana backend, set the following environment variables to your AWS Lambda function:
**INSTANA_ENDPOINT_URL**
**INSTANA_AGENT_KEY**
To provide these variables to the AWS handler in the AWS Console UI, complete the following steps:
-
On the "AWS Lambda configuration" page, click your Lambda function.
-
In the Environment variables section, click Edit and add the following variables:
INSTANA_ENDPOINT_URL
: Specify the URL of your Instana backend endpoint.INSTANA_AGENT_KEY
: Specify your agent key.
You can use optional environment variables to change the default settings in the tracer, such as the list of HTTP headers to collect or a custom service name to use.
Enabling tracing
With the aws-lambda-go
package, you can run the Go code on AWS Lambda.
To trace AWS Lambda, trigger events, and monitor internal and external calls that are made within the AWS handler function by using Instana, you must instrument your handler code first.
You can instrument your handler code by using the middleware wrappers that are provided by the Instana instrumentation module instalambda
.
Adding the Instana instrumentation module
To add the instalambda
module to your Go Lambda project, run the following command from the folder that contains the go.mod
file:
go get github.com/instana/go-sensor/instrumentation/instalambda
This command adds the instrumentation module to your project dependencies list and to the main Go Tracer.
Instrumenting a handler function
The following code snippet shows a typical AWS Lambda function that is written in Go:
package main
import (
"github.com/aws/aws-lambda-go/lambda"
)
func main() {
lambda.Start(Handle)
}
func Handle() (string, error) {
// handler code
}
A handler function can take and return up to two arguments, with a limitation that if the handler function takes two arguments, the first
argument must implement context.Context
.
To instrument a handler function, complete the following steps:
- Create an instrumented handler from the original handler function by using
instalambda.NewHandler()
. - Pass the instrumented handler to
lambda.StartHandler()
. The following snippet shows the code changes:
package main
import (
"github.com/aws/aws-lambda-go/lambda"
// Import the in-process sensor and instrumentation packages
instana "github.com/instana/go-sensor"
"github.com/instana/go-sensor/instrumentation/instalambda"
)
func main() {
// Initialize the instana.Sensor instance
sensor := instana.NewSensor("my-lambda-handler")
// Create an instrumented handler from your handler function
h := instalambda.NewHandler(Handle, sensor)
// Pass the handler to the lambda.StartHandler() invoke loop
lambda.StartHandler(h)
}
func Handle() (string, error) {
// handler code
}
Instrumenting a lambda.Handler
The following code snippet shows a typical AWS Lambda handler that is implemented as lambda.Handler
:
package main
import (
"github.com/aws/aws-lambda-go/lambda"
)
func main() {
h := &Handler{
// handler configuration
}
lambda.StartHandler(h, sensor)
}
type Handler struct {
// ...
}
func (h *Handler) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
// handler code
}
To instrument such a handler, wrap it with instalambda.WrapHandler()
and pass to labmda.StartHandler()
:
package main
import (
"github.com/aws/aws-lambda-go/lambda"
// Import the in-process sensor and instrumentation packages
instana "github.com/instana/go-sensor"
"github.com/instana/go-sensor/instrumentation/instalambda"
)
func main() {
// Initialize the instana.Sensor instance
sensor := instana.NewSensor("my-lambda-handler")
h := &Handler{
// handler configuration
}
// Wrap and pass the handler to the lambda.StartHandler() invoke loop
lambda.StartHandler(instalambda.WrapHandler(h, sensor))
}
type Handler struct {
// ...
}
func (h *Handler) Invoke(ctx context.Context, payload []byte) ([]byte, error) {
// handler code
}
Enabling trace context propagation
To enable trace context propagation, you need minimal instrumentation that involves making a few changes in your main()
function without updating your handler code. However, you can add context.Context
to the list
of arguments of your handler function. When you add context.Context
as an argument, instalambda
injects the spans of the Lambda trigger event into the context. You can retrieve these spans with instana.SpanFromContext()
and use them as a parent to trace internal and external calls that are made within the handler:
func MyHandler(ctx context.Context) error {
// Pass the handler context to a subcall to trace its execution
subCall(ctx)
// ...
// Propagate the trace context within an HTTP request to another service monitored with Instana
// using an instrumented http.Client
req, err := http.NewRequest("GET", url, nil)
client := &http.Client{
Transport: instana.RoundTripper(sensor, nil),
}
client.Do(req.WithContext(ctx))
// ...
}
func subCall(ctx context.Context) {
if parent, ok := instana.SpanFromContext(ctx); ok {
// start a new span, using the Lambda entry span as a parent
sp = parent.Tracer().StartSpan(/* ... */)
defer sp.Finish()
}
// ...
}