otel

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: MIT Imports: 20 Imported by: 0

README

github.com/plinth-dev/sdk-go/otel

Plinth's OpenTelemetry SDK initialisation. One Init call wires the global tracer provider with Plinth's standard resource attributes (service.name, service.version, module.name, deployment.environment), the OTLP exporter, and the W3C trace-context propagator.

Design rationale: https://plinth.run/sdk/go/otel/.

Install

go get github.com/plinth-dev/sdk-go/otel@latest

Minimum example

package main

import (
    "context"
    "log"
    "net/http"

    plinthotel "github.com/plinth-dev/sdk-go/otel"
)

func main() {
    shutdown, err := plinthotel.Init(context.Background(), plinthotel.Options{
        ServiceName:    "items-api",
        ServiceVersion: version, // injected via -ldflags '-X main.version=...'
        ModuleName:     "items",
    })
    if err != nil {
        log.Fatal(err)
    }
    defer func() { _ = shutdown(context.Background()) }()

    mux := http.NewServeMux()
    mux.Handle("/", plinthotel.HTTPMiddleware(myHandler()))
    http.ListenAndServe(":8080", mux)
}

The package name is otel (matching its directory). To avoid colliding with the official go.opentelemetry.io/otel package, alias the import: plinthotel, pothel, or whatever fits your style.

Defaults

  • ExporterEndpoint: http://otel-collector.observability:4318 (the in-cluster collector address).
  • ExporterProtocol: "http" (OTLP/HTTP). Switch to "grpc" if you have gRPC available end-to-end.
  • Environment: os.Getenv("ENV"), falling back to "dev".
  • Sampling: parent-based ratio, 0.05 in production / 0.5 in staging / 1.0 elsewhere. Override via TracesSamplerArg or env var OTEL_TRACES_SAMPLER_ARG.
  • Propagator: W3C TraceContext + Baggage.

Recording errors and module attributes

import plinthotel "github.com/plinth-dev/sdk-go/otel"

// In a handler — tags the current span with the error and sets status.
if err := svc.Do(ctx, in); err != nil {
    plinthotel.RecordError(ctx, err)
    return err
}

// Manual attribute on a custom span.
ctx, span := tracer.Start(ctx, "items.publish",
    trace.WithAttributes(plinthotel.AttrModule("items")))
defer span.End()

Testing

Options.Exporter accepts any sdktrace.SpanExporter. Pass tracetest.NewInMemoryExporter() for unit tests; Init automatically uses a SimpleSpanProcessor (synchronous flush) when Exporter is set, so spans are visible immediately after span.End().

import (
    plinthotel "github.com/plinth-dev/sdk-go/otel"
    "go.opentelemetry.io/otel/sdk/trace/tracetest"
)

func TestSomething(t *testing.T) {
    rec := tracetest.NewInMemoryExporter()
    shutdown, _ := plinthotel.Init(context.Background(), plinthotel.Options{
        ServiceName:      "test",
        Exporter:         rec,
        TracesSamplerArg: 1.0, // pin sampler so single-span tests aren't dropped
    })
    defer shutdown(context.Background())

    // … emit a span …

    spans := rec.GetSpans()
    // … assert …
}

Don't call shutdown() before reading spanstracetest.InMemoryExporter.Shutdown calls Reset() and clears recorded spans. Defer it; it runs after the assertions.

Compatibility

  • Go 1.25+ (transitive OpenTelemetry deps require it).
  • Wraps go.opentelemetry.io/otel (v1.43+) and go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp (v0.68+).
  • Uses semconv v1.40.0 (matches the SDK's default detector schema URL).

License

MIT — see LICENSE.

Documentation

Overview

Package otel is Plinth's OpenTelemetry SDK initialisation.

One Init call in main configures the global tracer provider with the resource attributes Plinth expects (service.name, service.version, module.name, deployment.environment), wires the OTLP exporter, and installs the W3C trace-context propagator. The caller defers the returned shutdown.

The package name is `otel`, identical to the official OpenTelemetry Go module's root package. Consumers naming their import alias `otel` gets our package; our own source file aliases the SDK as `sdkotel` internally to disambiguate. See https://plinth.run/sdk/go/otel/.

Index

Constants

View Source
const (
	DefaultExporterEndpoint = "http://otel-collector.observability:4318"
	DefaultExporterProtocol = "http"

	// EnvOTELSamplerArg overrides Options.TracesSamplerArg.
	EnvOTELSamplerArg = "OTEL_TRACES_SAMPLER_ARG"
	// EnvENV is read when Options.Environment is empty.
	EnvENV = "ENV"
)

Defaults; exposed so callers can compute "is this the default?" if needed.

View Source
const AttrModuleKey = attribute.Key("module.name")

AttrModuleKey is the OTel attribute key for "module.name" — Plinth's per-module dimension. Use AttrModule to build the attribute.

Variables

This section is empty.

Functions

func AttrModule

func AttrModule(name string) attribute.KeyValue

AttrModule returns the AttrModuleKey attribute populated with name. Use for manual span tagging when the module name isn't already on the resource (e.g. cross-module spans).

func HTTPMiddleware

func HTTPMiddleware(next http.Handler, opts ...HTTPOption) http.Handler

HTTPMiddleware wraps an http.Handler with otelhttp.NewHandler plus Plinth-specific span-name customisation (METHOD path).

For chi routers, callers can wire WithRouteFromContext (future option) to use the matched route pattern instead of the literal path — that's more useful for cardinality control.

func Init

func Init(ctx context.Context, opts Options) (shutdown func(context.Context) error, err error)

Init configures the global tracer provider, propagator, and resource. Returns a shutdown closure that flushes pending spans; defer it from main.

Standard usage:

shutdown, err := otel.Init(ctx, otel.Options{
    ServiceName:    "items-api",
    ServiceVersion: version,
    ModuleName:     "items",
})
if err != nil { log.Fatal(err) }
defer func() { _ = shutdown(context.Background()) }()

Init returns an error for any configuration problem (missing ServiceName, unknown protocol, malformed endpoint). The OTLP exporter doesn't dial eagerly; an unreachable collector at startup doesn't fail Init — the SDK retries each batch internally.

func RecordError

func RecordError(ctx context.Context, err error)

RecordError tags the current span with the error and sets its status. No-op if ctx has no span or err is nil.

Convenience over the verbose otel/codes + span.RecordError + span.SetStatus combination most call sites end up writing.

Types

type HTTPOption

type HTTPOption func(*httpConfig)

HTTPOption customizes HTTPMiddleware.

func WithOperationName

func WithOperationName(name string) HTTPOption

WithOperationName overrides the default span-name prefix ("http.server").

type Options

type Options struct {
	// ServiceName is the OTel `service.name` resource attribute. Required.
	// Use the running module's identifier (e.g. "items-api").
	ServiceName string

	// ServiceVersion populates `service.version`. Defaults to "unknown" if empty.
	// Inject from build-time ldflags: -X 'main.version=$(git describe --tags)'.
	ServiceVersion string

	// Environment populates `deployment.environment.name`. Defaults to
	// os.Getenv(EnvENV) → "dev". Recognised values: "production", "staging", "dev".
	Environment string

	// ModuleName populates the Plinth-specific `module.name` attribute.
	// Optional; matches the convention used by sdk-go/audit and sdk-go/errors.
	ModuleName string

	// ExporterEndpoint is the OTLP target. Defaults to the in-cluster
	// collector at http://otel-collector.observability:4318.
	ExporterEndpoint string

	// ExporterProtocol selects "http" (OTLP/HTTP, default) or "grpc".
	ExporterProtocol string

	// ExporterHeaders are sent on every export. Useful for hosted backends.
	ExporterHeaders map[string]string

	// TracesSamplerArg is the parent-based ratio sampler arg in [0, 1].
	// Defaults: 1.0 in dev, 0.5 in staging, 0.05 in production.
	// Set OTEL_TRACES_SAMPLER_ARG in the env to override this (and any
	// explicit value passed in code — env wins, matching the OpenTelemetry
	// spec). Pointer so zero is distinguishable from unset; pass nil
	// (or leave zero-value) to use env-or-default, &0 for explicit
	// no-sampling.
	TracesSamplerArg *float64

	// BatchTimeout caps how long the BatchSpanProcessor buffers before flushing.
	// Defaults to the SDK's default (5 seconds).
	BatchTimeout time.Duration

	// Logger receives init / exporter / shutdown messages. Defaults to slog.Default().
	Logger *slog.Logger

	// Exporter overrides the OTLP exporter selection. Tests pass in-memory
	// recorders here; production typically leaves this nil and lets Init
	// build an OTLP exporter from ExporterEndpoint / ExporterProtocol.
	Exporter sdktrace.SpanExporter
}

Options configure Init. ServiceName is the only required field; sensible defaults fill the rest.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL