Skip to content

Getting started

This walkthrough stands up an OTLP tracer provider, wires it into the global OpenTelemetry API, and shuts it down cleanly. Metrics and logs follow the same shape.

Install

go get gitlab.com/phpboyscout/go/observability

The module carries the OpenTelemetry SDK and cockroachdb/errors — nothing else.

Build a tracer provider

Two ingredients: a resource identifying your service, and a Settings describing the OTLP target. Then tracing.NewProvider assembles the exporter and provider:

package main

import (
    "context"
    "log"

    "go.opentelemetry.io/otel"

    "gitlab.com/phpboyscout/go/observability/otelcore"
    "gitlab.com/phpboyscout/go/observability/tracing"
)

func main() {
    ctx := context.Background()

    res := otelcore.Resource("my-service", "1.4.0")

    settings := otelcore.Settings{
        Enabled:  true,
        Endpoint: "https://otel-collector.internal:4318",
    }

    tp, err := tracing.NewProvider(ctx, res, settings, tracing.WithSampling(0.1))
    if err != nil {
        log.Fatal(err)
    }
    defer func() { _ = tp.Shutdown(ctx) }()

    otel.SetTracerProvider(tp)

    // ... your service; spans now export to the collector.
}

WithSampling(0.1) samples 10% of traces. That is also the default — omitting the option gives you the same ratio, not "record everything". Pass 1.0 while you are getting the wiring working, or nothing you produce may be recorded.

otel.SetTracerProvider is not optional if anything else in the process creates spans. NewProvider deliberately registers nothing globally, so otelhttp, otelgrpc and any library reading otel.Tracer(…) see a no-op provider until you make that call.

Let the environment configure it

Leave Endpoint empty and the exporter falls back to the standard OpenTelemetry environment variables — so an operator can point the service at a collector without touching your config:

settings := otelcore.Settings{Enabled: true} // Endpoint == "" ⇒ OTEL_* env fallback
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.internal:4318

This is deliberate, not an oversight — see the config model. One thing to know before relying on it: with neither the field nor the variable set, the exporter does not disable itself. It falls back to localhost:4318 and fails there quietly, on a background goroutine.

What Enabled does

Nothing, on its own. Settings.Enabled is a value for you to act on — no code in this module reads it, and NewProvider builds a working provider either way:

if settings.Enabled {
    tp, err := tracing.NewProvider(ctx, res, settings)
    // ...
}

Next steps