Deployment Integrations
On-premise Integrations
Platform Integrations

Enrich Tracing

Manual Instrumentation with Open Tracing

Thundra uses the OpenTracing API to implement instrumentation. Thus, you can manually instrument your code by following OpenTracing API instructions as Thundra’s agents are compliant with the OpenTracing API.

To manually instrument your Go functions in order to see detailed spans, you will have to create span instances using the OpenTracinig's StartSpan methods. OpenTracing API provides two different methods: opentracing.StartSpan and opentracing.StartSpanFromContext that you can use to start new spans in your function. We suggest using opentracing.StartSpanFromContext method for creating new spans by passing it a context, an operation name, and optional options parameters. Refer the section about using contexts to learn about more.

You don't need to create a span for main handler function

Thundra automatically creates a root span representing the main Lambda handler function and finishes this root span when your Lambda function is done. Thus, you don't need to create a root span representing your handler function. Any other span you will create in your main handler or in other functions will be the child of this root span.

The following code snippet shows the example usage of OpenTracing API to create new spans.

Creating a new span
// Pass a context parameter to the function
func sayHello(ctx context.Context, name string) {
// Create a new span to represent operations made in this function
span, _ := opentracing.StartSpanFromContext(ctx, "sayHello")
// Finish the span when function is done
defer span.Finish()
// Perform actual function logic
fmt.Printf("Hello, %s!\n", name)

Using contexts while creating new spans

Currently, Golang does not provide a thread local storage or a similar construct. For this reason, OpenTracing API handles parent-child relations between the spans using Golang's context objects.

Since Thundra creates a root span representing your main handler function, we strongly suggest you create your main handler function such that it accepts a context object as its first argument. This way you will be able to access the root span that Thundra have created to represent your main handler function and use the passed context variable to create child spans of this root span.

The following code snippet shows how context objects can be used to create child spans.

package main
import (
opentracing ""
// Your main lambda handler
func handler(ctx context.Context) (string, error) {
// Currently ctx object contains the root span that
// Thundra have created for you
// Sleep some amount, representing the operations
// that you make in your handler function before
// creating a new child span
time.Sleep(time.Millisecond * 100)
// Say you continue to make some operations in your
// main handler but this time create a childSpan
// to represent these operations.
aSpan, _ := opentracing.StartSpanFromContext(ctx, "childSpan-1")
time.Sleep(time.Millisecond * 100)
// Make a call to another function. Note that this function
// also accepts a context object. We are using this context
// object to carry the parent span information.
// Creating another span representing the another operations
// that we are doing inside the root span.
anotherSpan, _ := opentracing.StartSpanFromContext(ctx, "childSpan-3")
time.Sleep(time.Millisecond * 100)
// Say we have also some operations before our handler finishes
time.Sleep(time.Millisecond * 50)
return "Hello ƛ!", nil
func aFunction(ctx context.Context) {
span, _ := opentracing.StartSpanFromContext(ctx, "childSpan-2")
defer span.Finish()
time.Sleep(time.Millisecond * 100)
func main() {
// Wrap your lambda handler with Thundra

The resulting trace chart that you will see in Thundra web console for this function, will be like the following image.

As you can see, there is a root span that Thundra automatically created, and other spans are the child of the root span, as they should be.