Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To add distributed tracing to a Go application, initialize the OpenTelemetry SDK, attach a stable service identity, instrument incoming and outgoing work, propagate trace context across requests, and export spans—usually through OTLP. Use library instrumentation for supported dependencies and add manual spans for application-specific operations. Choose sampling deliberately, and shut down the tracer provider cleanly so buffered spans can be flushed.

Prerequisites and Go packages

The OpenTelemetry Go getting-started guide lists Go 1.23 or newer as a prerequisite; check the current guide for any changes before adopting it. The official status page lists Go traces and metrics as stable and logs as release candidate; status can change, so consult the Go documentation for the current status.

An application that emits telemetry needs the OpenTelemetry SDK. Instrumentation libraries typically depend on the API, allowing them to create telemetry when used by an SDK-enabled application. For manual tracing, the core packages include go.opentelemetry.io/otel, go.opentelemetry.io/otel/trace, and go.opentelemetry.io/otel/sdk. Add an exporter package that matches the protocol and destination you select. Avoid pinning versions from an older example; use versions that are compatible with your Go toolchain and each other.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I add OpenTelemetry tracing to a Go application?

Build the tracing pipeline in a deliberate order: choose an exporter, identify the service with resource attributes, configure a tracer provider and span processor, register the provider when appropriate, then acquire a tracer and create spans. Plan for shutdown as part of application lifecycle management, not as an afterthought.

Initialize the provider and resource

The following illustrates the setup shape using OTLP over HTTP. It is a structural example rather than a complete, copy-paste application: select the exporter package and configuration that match your collector, add your application’s error and signal handling, and check current package documentation for exact APIs.

ctx := context.Background()

// Create an OTLP trace exporter configured for your collector.
// Check the current exporter documentation for the selected transport.
exp, err := newTraceExporter(ctx)
if err != nil {
    return fmt.Errorf("create trace exporter: %w", err)
}

res, err := resource.New(ctx,
    resource.WithAttributes(
        semconv.ServiceName("orders-api"),
    ),
)
if err != nil {
    return fmt.Errorf("create telemetry resource: %w", err)
}

provider := sdktrace.NewTracerProvider(
    sdktrace.WithResource(res),
    sdktrace.WithBatcher(exp),
)
otel.SetTracerProvider(provider)

tracer := provider.Tracer("example.com/orders")

The exporter constructor above is intentionally illustrative; it is not a real function supplied by the SDK. Use the selected exporter’s documented constructor and handle its initialization errors. A resource identifies the service that produced telemetry; set a stable service.name so traces from different deployments can be distinguished. The instrumentation scope name passed to Tracer identifies the code or instrumentation library creating spans.

A batch span processor buffers and exports spans in batches, reducing the need to export each span synchronously. Registering the provider globally is useful when instrumentation libraries obtain it through the OpenTelemetry global API. If your code passes a provider or tracer explicitly, follow that design consistently rather than adding global state without need.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Flush spans during shutdown

When the process is terminating, shut down the provider with a context that allows export to complete. A common lifecycle pattern is to call provider.Shutdown(ctx) from the application’s graceful-shutdown path and report any returned error. Do not use an already-canceled request context for this operation: shutdown needs time to flush buffered spans and close exporter resources. Coordinate the shutdown deadline with the rest of the application’s termination sequence.

Instrument requests and application work

Use dependency instrumentation and manual spans together. Instrumentation libraries can create spans and metrics for supported frameworks and clients; they cannot infer the meaning of your business operations. The OpenTelemetry Go instrumentation documentation describes net/http instrumentation that automatically produces spans and metrics for HTTP requests.

Start with supported dependencies

Add the instrumentation library for the server, client, database, or framework in use, following that library’s current setup instructions. This captures standard boundaries such as inbound requests and outbound calls. Avoid adding another span around the same operation if middleware or a dependency library already creates one; duplicate spans make a trace harder to read and can inflate volume.

Add manual spans for meaningful operations

Use manual spans where they explain application-specific work, such as validating an order, applying a business rule, or coordinating multiple downstream calls. Start them from the active context so they become children of the request span, and end them when the operation finishes. Record useful attributes or errors in line with your data-handling policies; avoid placing secrets or sensitive personal data in span attributes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
func (s *Service) ProcessOrder(ctx context.Context, id string) error {
    ctx, span := tracer.Start(ctx, "process_order")
    defer span.End()

    span.SetAttributes(attribute.String("order.id", id))
    return s.process(ctx, id)
}

Pass the derived context to downstream operations. If a downstream call receives the original context instead, its spans may not be children of this operation.

How do I propagate trace context between Go services?

A trace crosses service boundaries only when the active trace context travels with the request. OpenTelemetry Context is an execution-scoped propagation mechanism and is immutable: operations return a derived context rather than changing the existing one. For HTTP, configure a propagator and use instrumentation that extracts context from inbound request headers and injects it into outbound requests. Confirm the exact APIs for the instrumentation and OpenTelemetry package versions you use rather than borrowing propagation code from another language.

At a conceptual level, the inbound server instrumentation extracts the remote trace context into the request context. Your handler and manual spans continue from that context. Outbound client instrumentation injects the current context into request headers, allowing the receiving service to continue the trace. If automatic instrumentation is not being used, apply the configured propagator explicitly at the HTTP boundary.

For a deployment that combines manual spans with eBPF-based Go zero-code instrumentation such as OBI, do not assume the ordinary global-provider setup is appropriate. Follow the OpenTelemetry Go Auto SDK guidance for that deployment model.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I export Go OpenTelemetry traces using OTLP?

OTLP preserves the OpenTelemetry data model and is supported by Go exporters over HTTP and gRPC. The Go exporter documentation recommends the OpenTelemetry Collector for production environments; the Collector can receive telemetry from applications and route it to a visualization system or vendor backend. Jaeger, Zipkin, Prometheus, and vendor-specific backends are among the tools and destinations described by the documentation, but their supported signals and ingestion options differ.

Choose a matching transport and endpoint

Choice Endpoint form Practical consideration
OTLP/HTTP HTTP endpoint; signal paths such as /v1/traces are used as applicable. Use the HTTP exporter and its documented endpoint configuration.
OTLP/gRPC gRPC target, without HTTP signal paths such as /v1/traces. Use the gRPC exporter and its documented target and security settings.

Do not combine an HTTP signal URL with a gRPC exporter. Configure the transport and endpoint as a pair, and confirm whether the receiver expects TLS and authentication. The Go exporter guide describes the supported OTLP paths and environment-based configuration options.

Use a Collector when you need an intermediary

A Collector provides a separate place to receive, process, and export telemetry rather than tying the application directly to a particular destination. This can simplify changing backends or centralizing telemetry routing. Direct export may be sufficient for a small setup, but it couples application configuration more closely to the receiving endpoint. Select based on operational needs, security requirements, and the receiving system’s capabilities.

The Go exporter documentation also describes contrib’s autoexport for environment-based exporter selection, including selectors such as OTEL_TRACES_EXPORTER. Supported values and variable support depend on the exporter and configuration path. The Go SDK documentation states that OTEL_SDK_DISABLED is not currently supported, so do not rely on it as a universal switch for Go telemetry.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose a sampling policy

Sampling controls how many traces are recorded and exported. It trades telemetry volume and associated processing/storage costs against the chance of retaining diagnostic detail. There is no universally correct sampling percentage: choose according to traffic, retention requirements, incident needs, and backend capacity.

Policy Useful context Important behavior
AlwaysSample Useful for development and controlled environments, according to the Go sampling guidance. Records every trace presented to the sampler, so volume can be high.
NeverSample Useful when intentionally disabling recording in a controlled case. Does not retain diagnostic traces.
Parent-based with trace-ID ratio sampling A production option the Go sampling guidance recommends considering. Respects the parent decision while applying a ratio decision where appropriate.

Sampling decisions should be made at the start of a trace and propagated so services do not produce inconsistent fragments of the same trace. If you implement a custom sampler, preserve the parent tracestate and keep synchronous ShouldSample work inexpensive.

Implementation checklist

  • Confirm the current Go prerequisite, SDK status, and compatible module versions in the official Go documentation.
  • Set a stable service.name resource attribute and an intentional instrumentation scope name.
  • Use supported instrumentation for framework and dependency boundaries, then add manual spans for meaningful internal operations without duplicating existing spans.
  • Keep the active context flowing through handlers and downstream calls; configure extraction and injection at service boundaries.
  • Match the OTLP exporter to the receiver’s HTTP or gRPC endpoint form, and decide whether a Collector belongs in the pipeline.
  • Select sampling deliberately, and include provider shutdown in graceful termination so buffered spans can be flushed.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.