• Guide Content

OpenTelemetry with Go: Instrumenting Golang Apps, Step by Step

What Is OpenTelemetry?

OpenTelemetry, a Cloud Native Computing Foundation project, is a framework for observability. It integrates metrics, traces, and logs to provide a holistic view of your system’s performance and behavior. As a single, vendor-neutral framework, OpenTelemetry eliminates the need to integrate multiple observability systems within your development toolchains. This reduces complexity and makes observability easier as part of gaining a system wide view of deployed applications

OpenTelemetry provides libraries, agents, and other components that you can use to measure and collect performance data from your services and software. It enables the ability to build standardized and interoperable observability pipelines into development workflows, which can be used across multiple programming languages, platforms, and span multiple teams.

One of the supported programming languages in OpenTelemetry is Golang. Using OpenTelemetry with Golang provides a robust set of tools to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) in Go applications.

Adding Observability to Go Apps with OpenTelemetry: What Is Involved?

To add observability to Go applications using OpenTelemetry, you’ll need to follow a multi-step process involving setup, instrumentation, and data export. The initial step involves integrating the OpenTelemetry Go SDK into your application, which requires setting up the appropriate libraries and dependencies. This SDK is then used to collect telemetry data.

The next step is the instrumentation of the application. This step involves modifying the application code to generate telemetry data, such as metrics, logs, and traces. For Golang applications, OpenTelemetry provides specific APIs and libraries that facilitate this process. You can instrument various parts of the application, including HTTP handlers, database calls, and custom logic. This granularity enables detailed insight into the application’s performance and behavior.

Finally, the configured data needs to be exported to an observability backend for analysis. OpenTelemetry supports various exporters that allow sending telemetry data to different analysis tools and services. This means you can use OpenTelemetry with your preferred monitoring, tracing, and logging platforms. 

The process of setting up these exporters involves specifying the endpoint of the observability backend and configuring the relevant authentication and data format settings. Once set up, the application will continuously send the collected telemetry data to the specified backend, allowing you to monitor, debug, and optimize applications more effectively as part of the software development lifecycle. 

Note: In this tutorial, we will focus more on instrumenting the application and won’t go into the process of setting up exporters. You can learn more about setting up exporters in the OpenTelemetry documentation.

Related content: Read our guide to OpenTelemetry architecture

Tutorial: Creating an Example Golang Application and Adding OpenTelemetry Instrumentation 

Step 1: Setup

To get started, ensure that you have Go installed in your development environment. If Go is not yet installed, follow the instructions on this page. For this tutorial, you will need at least version 1.20 or newer installed, which can be found in the ‘all releases’ page here

Once Go is installed, you can verify the installation by running go version in your terminal.

Set up your Go workspace. You can do this by creating a new directory where you’ll be writing your code. Once you’ve created this directory, navigate to it in your terminal and initialize it as a Go module by running go mod init <module-name>. This will create a new go.mod file in your directory.

Next, you’ll need to install the OpenTelemetry Golang SDK. This can be done by running the command go get go.opentelemetry.io/otel. This will download and install the OpenTelemetry SDK.

Step 2: Create and Launch an HTTP Server

Now, let’s create a simple HTTP server in Go. In your project directory, create a new file named main.go. In the main.go file, write the following code:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Hello, OpenTelemetry!")
    })

    http.ListenAndServe("0.0.0.0:8080", nil)
}

This code creates a new HTTP server that listens on port 8080 and responds with “Hello, OpenTelemetry!” when accessed. You can run the server by executing go run main.go in your terminal. Visit http://<YOUR IP>:8080 in your browser to see your server in action.

Step 3: Add Dependencies

Now that our HTTP server is running, we need to add the OpenTelemetry Golang dependencies. In your terminal, run the following commands:

go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get go.opentelemetry.io/otel/sdk/resource
go get go.opentelemetry.io/otel/trace

These commands add the core OpenTelemetry library, the OTLP trace exporter, the resource SDK, and the trace API to your project.

Step 4: Initialize the OpenTelemetry SDK

With the dependencies added, we can now initialize the OpenTelemetry SDK. Update your main.go file with the following code:

package main

import (
    // ...other imports...
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
    "go.opentelemetry.io/otel/attribute"
   "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func main() {
    // Initialize OpenTelemetry SDK
    ctx := context.Background()
    exporter, err := otlptracegrpc.New(ctx)
    handleErr(err, "Failed to create exporter")
    openTelemetryURL := attribute.KeyValue{
                Key:   attribute.Key("opentelemetry.io/schemas"),
                Value: attribute.StringValue("1.7.0"),
        }



    resource, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.SchemaURL,
		  openTelemetryURL,
        ),
    )
    handleErr(err, "Failed to create resource")

    tracerProvider := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(resource),
    )

    otel.SetTracerProvider(tracerProvider)
    otel.SetTextMapPropagator(propagation.TraceContext{})
}

This code initializes the OpenTelemetry SDK, sets up the OTLP trace exporter, and configures the resource attributes for our service.

Step 5: Instrument the HTTP Server

Before proceeding, add another module called otelhttp using the following command:

     go get
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp

Now you can instrument the HTTP server to generate traces. Update the main.go file with the following code:

package main

import (
    // ...other imports...
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func main() {
    // ...other code...

    http.Handle("/", otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Hello, OpenTelemetry!")
    }), "/"))

    http.ListenAndServe(":8080", nil)
}

This code wraps our HTTP handler with the otelhttp.NewHandler function, which automatically generates traces for incoming requests.

Step 6: Add Custom Instrumentation

For more detailed observability, we can add custom instrumentation to our application. This allows us to generate traces for specific operations in our code. Update the main.go file with the following code:

package main

import (
    // ...other imports...
    "go.opentelemetry.io/otel/trace"
)

func main() {
    // ...other code...
    tracer := otel.GetTracerProvider().Tracer("example")	
    http.Handle("/", otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        _, span := tracer.Start(r.Context(), "my-operation")
        defer span.End()

        fmt.Fprint(w, "Hello, OpenTelemetry!")
    }), "/"))

    http.ListenAndServe(":8080", nil)
}

This code creates a new trace span for the operation named “my-operation”. The span is automatically linked to the incoming request context and is ended when the operation is completed.

Step 7: Run the Application

With everything set up, we can now run our instrumented application. Execute go run main.go in your terminal. Visit http://<your IP Address>:8080 in your browser to generate traces. You can view the generated traces in your OpenTelemetry collector or observability backend.

Microservices Monitoring with Lumigo

OpenTelemetry offers a pluggable architecture that enables you to add technology protocols and formats easily. Using OpenTelemtry, Lumigo provides containerized applications with end-to-end observability through automated distributed tracing.