otelkit

package module
v0.0.0-...-acdfae7 Latest Latest
Warning

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

Go to latest
Published: Jun 21, 2026 License: Apache-2.0 Imports: 38 Imported by: 0

README

ubgo/otelkit

Go Reference Go Report Card coverage License Go

The boring, correct, loud way to turn OpenTelemetry on in a Go service — one constructor, vendor presets, and failures you can actually see.

otelkit stands up the OpenTelemetry trace/metric/log pipeline — providers, exporters, resource, propagators, and an ordered shutdown — from one explicit constructor. Point it at a backend (HyperDX, Grafana Cloud, Honeycomb, Datadog, New Relic, an OTLP collector, or stdout) and get correct, shutdown-safe telemetry without re-writing the usual ~150 lines of fiddly, failure-silent setup. The core has zero application dependencies; OTLP/gRPC ships as an opt-in contrib/ module so your dependency graph stays lean.

It is a bootstrap, not an SDK: it wires the official go.opentelemetry.io/otel SDK rather than reimplementing it. Writing log lines is a logger's job — otelkit exposes the LoggerProvider that github.com/ubgo/logger (and any OTEL log bridge) consumes.

Why otelkit

OpenTelemetry's Go SDK ships excellent primitives and no opinionated bootstrap, and its dominant failure mode is silence: a wrong port (4317 vs 4318), wrong protocol, a missing /v1/<signal> path, an unflushed batch on exit, or a cumulative-vs-delta mismatch all fail without an error — producing empty dashboards and no clue why. otelkit fixes that:

  • Spec-compliant — honors the standard OTEL_* environment variables and defaults.
  • Vendor presets as data — switch backends in one line; the preset encodes the endpoint, auth header name/format, path quirk, and metric temporality.
  • Loud, not silent — an export-error handler, a connectivity probe, a dry-run mode, and an opt-in boot self-test turn silent misconfiguration into a specific startup error.
  • One knob, no footgunsotelkit owns all port + /v1/<signal> path construction.
  • One handle, one ordered Shutdown + a ready-made signal helper; a real no-op on OTEL_SDK_DISABLED.
  • Future-proof — delegates to the now-stable declarative config (otelconf) when OTEL_CONFIG_FILE is set.
  • Zero application dependencies.

Install

go get github.com/ubgo/otelkit

OTLP/gRPC support is an opt-in module (keeps google.golang.org/grpc out of the core):

go get github.com/ubgo/otelkit/contrib/otelkit-grpc

Quick start

package main

import (
	"context"
	"log"

	"github.com/ubgo/otelkit"
)

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

	tel, err := otelkit.Init(ctx,
		otelkit.WithService("checkout", "1.4.2"),
		otelkit.WithEnvironment("prod"),
		otelkit.WithPreset(otelkit.PresetHyperDX("<ingestion-key>", "")),
	)
	if err != nil {
		log.Fatal(err)
	}
	tel.SetGlobal()
	defer tel.Shutdown(ctx)

	// ... run your service; create spans/metrics via the OTEL globals ...
}

For a long-running service, replace the defer with the signal helper:

go runServer()
if err := tel.RunOnSignal(ctx); err != nil { // blocks until SIGTERM/SIGINT, then flushes
	log.Printf("shutdown errors: %v", err)
}

Modules

otelkit is split so the core stays dependency-light. Add the gRPC module only if your backend needs OTLP/gRPC.

Module Import
core github.com/ubgo/otelkit
gRPC exporters github.com/ubgo/otelkit/contrib/otelkit-grpc

The core ships OTLP/HTTP + stdout exporters and depends only on go.opentelemetry.io/otel/*. The gRPC module adds OTLP/gRPC (pulling in google.golang.org/grpc) and self-registers on a blank import. Selecting TransportGRPC without it returns otelkit.ErrGRPCNotLinked — loud, not silent.

Vendor presets

Every observability backend wants OTLP data in a slightly different shape — a different endpoint, a different auth header name (there's no Bearer consensus), a path quirk, and sometimes delta-vs-cumulative metrics. Get one detail wrong and your data silently never arrives. A preset is a one-line constructor that fills all of that in correctly for a specific vendor — so pointing at HyperDX vs Grafana vs Datadog is a single line, not a research project.

Preset Auth header Notes
PresetStdout() Local dev; all signals to stdout.
PresetHyperDX(key, endpoint) authorization (raw key, no Bearer) Defaults to https://in-otel.hyperdx.io.
PresetGrafanaCloud(instanceID, token, endpoint) Authorization: Basic <b64> Endpoint is the /otlp base; /v1/<signal> is appended automatically.
PresetHoneycomb(key, dataset, endpoint) x-honeycomb-team Metrics additionally send x-honeycomb-dataset.
PresetDatadog(key, endpoint) dd-api-key Forces delta temporality (Datadog rejects cumulative).
PresetNewRelic(key, endpoint) api-key Prefers delta temporality.
PresetCollector(endpoint, transport) Generic OTLP, no auth. The vendor-neutral escape hatch.

Switching backend is a one-line change:

otelkit.WithPreset(otelkit.PresetGrafanaCloud("123456", "<token>", "https://otlp-gateway-prod-eu-west-2.grafana.net/otlp"))

Full matrix and the reasoning behind each quirk: docs/presets.md.

Diagnostics — see failures instead of empty dashboards

The OpenTelemetry SDK drops exports silently: a wrong key, endpoint, protocol, or TLS setting just loses data with no error, and you find out hours later from blank dashboards. otelkit gives you four ways to make those failures visible — at startup, not in production:

tel, err := otelkit.Init(ctx,
	otelkit.WithPreset(otelkit.PresetHoneycomb(key, "metrics", "")),
	otelkit.WithSelfTest(),                 // send one span synchronously; error if the backend is unreachable
	otelkit.WithErrorHandler(myLogger),     // route export failures into your logs (default: stderr)
)
  • WithSelfTest() — sends one span through the real pipeline at startup and returns the export error the async batcher would otherwise hide.
  • Connectivity probeotelkit.ProbeEndpoint(ctx, endpoint, transport, tlsMode) diagnoses DNS / port / protocol / TLS problems with a human-readable message.
  • WithDryRun() — prints the resolved effective config (auth headers redacted) and routes telemetry to stdout, so you can verify wiring with no backend.
  • Export-error handler — installed by default (stderr); override with WithErrorHandler.

More: docs/diagnostics.md.

gRPC

import (
	"github.com/ubgo/otelkit"
	_ "github.com/ubgo/otelkit/contrib/otelkit-grpc" // blank import enables gRPC
)

tel, _ := otelkit.Init(ctx,
	otelkit.WithPreset(otelkit.PresetCollector("localhost:4317", otelkit.TransportGRPC)),
)

Without the contrib import, selecting TransportGRPC returns otelkit.ErrGRPCNotLinked — loud, not silent.

Configuration sources

otelkit accepts config from three independent routes (precedence: preset < options < env):

  1. ProgrammaticWithService, WithPreset, WithProtocol, WithSampler, WithTLS, … (map your own config system, e.g. PKL, into these).
  2. OTEL_* environment variables — the full standard surface (protocol, endpoint, headers, timeout, sampler, propagators, temporality). Set WithEnvOverrides(false) to make programmatic values authoritative.
  3. Declarative config file — set OTEL_CONFIG_FILE and otelkit delegates to the stable otelconf loader (file wins; flat env is ignored except ${ENV} substitution).

Migrating from a hand-rolled bootstrap

If you have the familiar ~150-line bootstrap (build three providers, attach OTLP exporters, set globals, wire shutdown): replace it with one otelkit.Init(...) call plus the matching preset. otelkit adds gRPC, vendor presets, loud diagnostics, the full OTEL_* surface, a real no-op on OTEL_SDK_DISABLED, declarative-config delegation, and an ordered Shutdown that flushes all three signals (instead of returning on the first error). Full before/after in docs/migration.md.

Documentation

Guide Covers
Getting started Zero to correlated telemetry in three lines.
Configuration Options, the full OTEL_* env surface, precedence.
Presets Vendor presets and what each encodes.
Diagnostics Self-test, probe, dry-run, error handler.
Declarative config OTEL_CONFIG_FILE delegation.
Migration Replacing a hand-rolled bootstrap.
Architecture How it fits; the endpoint/path rules.
ADRs · Snippets · Coverage Decisions, copy-paste, test coverage.

API reference: pkg.go.dev/github.com/ubgo/otelkit.

Examples

Runnable programs in examples/ — each in its own directory (go run ./01-basic):

01-basic · 02-all-signals · 03-k8s-prestop · 04-presets · 05-self-test · 06-dry-run · 07-declarative · 08-grpc

Quality

Both modules are held at 100% line coverage with the race detector, gated in CI. See COVERAGE.md.

FAQ

Does otelkit replace the OpenTelemetry SDK? No — it's a bootstrap that wires the official SDK. You still create spans/metrics the normal OTEL way.

Does it write my logs? No. It builds the LoggerProvider; a logger (e.g. github.com/ubgo/logger) writes log lines through it and correlates them with traces.

Why is gRPC a separate module? To keep google.golang.org/grpc out of the core dependency graph for HTTP-only deployments. A blank import of contrib/otelkit-grpc enables it.

My backend isn't a preset. Use PresetCollector(endpoint, transport) (no auth) or set headers/endpoint via WithConfig / OTEL_EXPORTER_OTLP_*. Presets are a convenience, not a requirement.

Nothing reaches my backend and there's no error. That's exactly what otelkit fixes — add WithSelfTest() to fail at startup, or WithDryRun() to print the resolved config. See diagnostics.md.

How it compares

otelkit is the vendor-neutral, batteries-included bootstrap. The honest landscape (otelconf is the OTEL declarative-config standard — otelkit delegates to it, so they're complementary):

otelkit setupOTelSDK (docs snippet) otelconf Vendor distros (uptrace-go, …)
Vendor-neutral ❌ backend-locked
Vendor presets — 1-line backend swap only their own
Owns endpoint/port//v1/ path (no footguns) partial
Loud diagnostics (self-test · probe · dry-run)
Full OTEL_* env compliance partial partial
Declarative OTEL_CONFIG_FILE ✅ (delegates to otelconf) ✅ (it is this)
Real no-op on OTEL_SDK_DISABLED
Ordered Shutdown + SIGTERM helper partial partial partial
All three signals (traces/metrics/logs) varies
Versioned library, not a copy-paste snippet
Coverage 100% n/a varies

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package otelkit is an OpenTelemetry SDK bootstrap for Go.

otelkit stands up the OTEL trace/metric/log pipeline — providers, exporters, resource, propagators, and an ordered shutdown — from one explicit constructor, so an application points at a backend (HyperDX, Grafana, Honeycomb, Datadog, New Relic, a collector, or stdout) and gets correct, shutdown-safe telemetry without re-writing the usual ~150 lines of fiddly, failure-silent setup.

otelkit is a bootstrap, not an SDK: it never reimplements spans, meters, or the log data model — it wires the official go.opentelemetry.io/otel SDK. Writing log lines is a logger's job (see github.com/ubgo/logger, which consumes the LoggerProvider this package builds). Auto-instrumentation lives in the OTEL contrib ecosystem.

Design goals

  • Spec-compliant: honors the standard OTEL_* environment variables.
  • Loud, not silent: opt-in boot self-test, connectivity probe, an error handler that surfaces export failures, and a dry-run mode — because the SDK's default failure mode is silence.
  • Vendor presets as data: switch backends by changing one line.
  • One handle, one ordered Shutdown; a real no-op on OTEL_SDK_DISABLED.
  • Zero application dependencies.

Quick start

tel, err := otelkit.Init(ctx,
    otelkit.WithService("checkout", "1.4.2"),
    otelkit.WithPreset(otelkit.PresetCollector("localhost:4318", otelkit.TransportHTTP)),
)
if err != nil {
    log.Fatal(err)
}
tel.SetGlobal()
defer tel.Shutdown(context.Background())

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrMissingServiceName is returned when no service name is set via
	// WithService, the Config, or OTEL_SERVICE_NAME. otelkit still falls back
	// to "unknown_service:<binary>" for the resource per spec, but an
	// explicitly-required service name (validation mode) surfaces this.
	ErrMissingServiceName = errors.New("otelkit: service name is required")

	// ErrMissingEndpoint is returned when a network transport (HTTP/gRPC) is
	// selected for an enabled signal but no endpoint was provided.
	ErrMissingEndpoint = errors.New("otelkit: endpoint is required for http/grpc transport")

	// ErrInvalidProtocol is returned for an OTEL_EXPORTER_OTLP_PROTOCOL value
	// that is not one of grpc, http/protobuf, http/json.
	ErrInvalidProtocol = errors.New("otelkit: invalid OTLP protocol")

	// ErrContradictoryTLS is returned when the TLS mode contradicts the
	// supplied TLS material (e.g. plaintext with a client certificate).
	ErrContradictoryTLS = errors.New("otelkit: contradictory TLS configuration")

	// ErrGRPCNotLinked is returned when TransportGRPC is requested but no gRPC
	// exporter factory has been registered. Import the contrib/otelkit-grpc
	// module (which registers itself) to enable gRPC.
	ErrGRPCNotLinked = errors.New("otelkit: gRPC transport requires importing github.com/ubgo/otelkit/contrib/otelkit-grpc")
)

Sentinel errors returned by Init and config validation. Callers can match them with errors.Is.

Functions

func ProbeEndpoint

func ProbeEndpoint(ctx context.Context, endpoint string, transport Transport, mode TLSMode) error

ProbeEndpoint checks that the OTLP endpoint is reachable, turning the SDK's opaque "context deadline exceeded" / gRPC "Unavailable" failures into a specific, human-readable diagnosis at startup. It performs a TCP dial (and, for TLS modes, a TLS handshake) against the endpoint's host:port.

endpoint accepts the same forms as SignalConfig.Endpoint (host, host:port, or URL). mode selects whether a TLS handshake is attempted. A nil return means the first hop is reachable; it does not guarantee the backend will accept or store the data.

Example

Probe an endpoint's reachability with a precise diagnosis.

package main

import (
	"context"
	"log"

	"github.com/ubgo/otelkit"
)

func main() {
	ctx := context.Background()
	if err := otelkit.ProbeEndpoint(ctx, "localhost:4318", otelkit.TransportHTTP, otelkit.TLSModePlaintext); err != nil {
		log.Printf("collector unreachable: %v", err)
	}
}

func RegisterGRPC

func RegisterGRPC(span SpanExporterFactory, metric MetricExporterFactory, log LogExporterFactory)

RegisterGRPC wires the OTLP/gRPC exporter factories. The contrib/otelkit-grpc module calls this from its package init, so applications enable gRPC simply by importing that module. Passing a nil factory leaves that signal's gRPC support unregistered.

Types

type Config

type Config struct {
	// ServiceName populates the required service.name resource attribute.
	// When empty, otelkit falls back to "unknown_service:<binary>".
	ServiceName string
	// ServiceVersion populates service.version (recommended).
	ServiceVersion string
	// Environment populates deployment.environment.name (optional).
	Environment string

	// Per-signal exporter configuration.
	Traces  SignalConfig
	Metrics SignalConfig
	Logs    SignalConfig

	// Sampler selects head sampling. Zero value = parentbased_always_on.
	Sampler Sampler
	// SamplerRatio is the [0,1] probability for ratio samplers. Default 1.0.
	SamplerRatio float64

	// MetricTemporality selects cumulative (default) or delta.
	MetricTemporality Temporality
	// MetricInterval is the periodic reader interval. 0 → 60s.
	MetricInterval time.Duration

	// TLS is the transport security mode for network exporters.
	TLS TLSMode
	// ResourceDetectors is a token list ("env,host,os,process,container",
	// "all", or "none"). Empty → the default set (process, os, host).
	ResourceDetectors string
	// contains filtered or unexported fields
}

Config is the full bootstrap configuration. Build it directly, or start from a Preset and override fields with the With* options.

type LogExporterFactory

type LogExporterFactory = func(ctx context.Context, sc SignalConfig, tlsMode TLSMode) (sdklog.Exporter, error)

LogExporterFactory builds an OTLP/gRPC log exporter.

type MetricExporterFactory

type MetricExporterFactory = func(ctx context.Context, sc SignalConfig, tlsMode TLSMode, temp Temporality) (sdkmetric.Exporter, error)

MetricExporterFactory builds an OTLP/gRPC metric exporter.

type Option

type Option func(*Config)

Option configures a Config before providers are built. Options are applied after any Preset and before the OTEL_* environment overlay (unless WithEnvOverrides(false) is set), giving the precedence: preset < options < env.

func WithConfig

func WithConfig(c Config) Option

WithConfig replaces the working Config wholesale. Use it when you already have a fully-formed Config (e.g. mapped from PKL); later options still apply on top.

func WithDryRun

func WithDryRun() Option

WithDryRun prints the resolved effective configuration (auth headers redacted) and routes telemetry to stdout instead of exporting. Use it to verify wiring without a backend.

Example

Verify wiring with no backend.

package main

import (
	"context"

	"github.com/ubgo/otelkit"
)

func main() {
	ctx := context.Background()
	tel, _ := otelkit.Init(ctx,
		otelkit.WithService("svc", "1"),
		otelkit.WithPreset(otelkit.PresetGrafanaCloud("123456", "token", "https://otlp-gateway.grafana.net/otlp")),
		otelkit.WithDryRun(),
		otelkit.WithEnvOverrides(false),
	)
	defer tel.Shutdown(ctx)
}

func WithEnvOverrides

func WithEnvOverrides(enabled bool) Option

WithEnvOverrides controls whether OTEL_* environment variables override programmatic/preset values. Default true (spec precedence). Set false to make options/PKL authoritative.

func WithEnvironment

func WithEnvironment(env string) Option

WithEnvironment sets deployment.environment.name (e.g. "prod", "staging").

func WithErrorHandler

func WithErrorHandler(fn func(error)) Option

WithErrorHandler overrides the default stderr handler for telemetry export errors (the loud-by-default diagnostic). Pass your logger here to route export failures into your logging pipeline.

func WithMetricInterval

func WithMetricInterval(d time.Duration) Option

WithMetricInterval sets the periodic metric reader interval.

func WithMetricTemporality

func WithMetricTemporality(t Temporality) Option

WithMetricTemporality selects cumulative (default) or delta aggregation.

func WithPreset

func WithPreset(p Preset) Option

WithPreset applies a vendor Preset. Presets run before other options, so an explicit With* still overrides a preset value (precedence: preset < options < env). A nil preset is a no-op.

Example

Switch backends in one line via a preset.

package main

import (
	"context"
	"log"

	"github.com/ubgo/otelkit"
)

func main() {
	ctx := context.Background()
	tel, err := otelkit.Init(ctx,
		otelkit.WithService("api", "2.0.0"),
		otelkit.WithEnvironment("prod"),
		otelkit.WithPreset(otelkit.PresetHyperDX("ingestion-key", "")),
	)
	if err != nil {
		log.Fatal(err)
	}
	tel.SetGlobal()
	defer tel.Shutdown(ctx)
}

func WithProtocol

func WithProtocol(t Transport) Option

WithProtocol sets the transport for every enabled signal in one call, deriving the correct port and path. A convenience over setting each SignalConfig.Transport.

func WithResourceAttrs

func WithResourceAttrs(attrs ...attribute.KeyValue) Option

WithResourceAttrs appends extra resource attributes, merged over the detected resource.

func WithResourceDetectors

func WithResourceDetectors(tokens string) Option

WithResourceDetectors sets the detector token list ("env,host,os,process, container", "all", or "none").

func WithSampler

func WithSampler(s Sampler) Option

WithSampler sets the head sampler. For ratio samplers, pair with WithSamplerRatio.

func WithSamplerRatio

func WithSamplerRatio(ratio float64) Option

WithSamplerRatio sets the [0,1] probability used by the ratio samplers.

func WithSelfTest

func WithSelfTest() Option

WithSelfTest enables the opt-in boot self-test: Init sends one span synchronously and surfaces any export error, so a misconfigured backend fails loudly at startup instead of silently dropping telemetry.

Example

Catch a misconfigured backend at startup.

package main

import (
	"context"
	"log"

	"github.com/ubgo/otelkit"
)

func main() {
	ctx := context.Background()
	tel, err := otelkit.Init(ctx,
		otelkit.WithPreset(otelkit.PresetCollector("localhost:4318", otelkit.TransportHTTP)),
		otelkit.WithSelfTest(),
	)
	if err != nil {
		log.Printf("telemetry self-test failed: %v", err)
		return
	}
	defer tel.Shutdown(ctx)
}

func WithService

func WithService(name, version string) Option

WithService sets service.name and service.version.

func WithTLS

func WithTLS(mode TLSMode) Option

WithTLS sets the transport security mode for network exporters.

type Preset

type Preset func(*Config)

Preset configures a Config for a specific backend, encoding that vendor's endpoint, transport, auth-header name/format, path quirk, and temporality as data — so switching backends is a one-line change. Apply with WithPreset.

func PresetCollector

func PresetCollector(endpoint string, transport Transport) Preset

PresetCollector configures a generic OTLP collector with no auth — the vendor-neutral escape hatch. Choose the transport (HTTP or gRPC); gRPC requires importing contrib/otelkit-grpc.

func PresetDatadog

func PresetDatadog(apiKey, endpoint string) Preset

PresetDatadog configures Datadog OTLP intake. Auth is the API key in "dd-api-key". Datadog's direct intake rejects cumulative metrics, so the preset forces delta temporality. A blank endpoint defaults to the intake host.

func PresetGrafanaCloud

func PresetGrafanaCloud(instanceID, token, endpoint string) Preset

PresetGrafanaCloud configures Grafana Cloud OTLP. Auth is HTTP Basic with the base64 of "instanceID:token". The endpoint must be the "/otlp" base; otelkit appends "/v1/<signal>" automatically.

func PresetHoneycomb

func PresetHoneycomb(apiKey, dataset, endpoint string) Preset

PresetHoneycomb configures Honeycomb ingest. Auth is the team/ingest key in "x-honeycomb-team"; metrics additionally require the dataset in "x-honeycomb-dataset". A blank endpoint defaults to the US gateway.

func PresetHyperDX

func PresetHyperDX(apiKey, endpoint string) Preset

PresetHyperDX configures HyperDX / ClickStack ingest. Auth is the raw ingestion key in the "authorization" header (no "Bearer" prefix). A blank endpoint defaults to the cloud gateway.

func PresetNewRelic

func PresetNewRelic(licenseKey, endpoint string) Preset

PresetNewRelic configures New Relic OTLP. Auth is the ingest license key in "api-key"; New Relic strongly prefers delta temporality. A blank endpoint defaults to the US collector.

func PresetStdout

func PresetStdout() Preset

PresetStdout routes all three signals to stdout — for local development.

type Sampler

type Sampler int

Sampler selects the head sampling strategy. Tail sampling is a Collector concern and is intentionally out of scope.

const (
	// SamplerParentBasedAlwaysOn samples every root span and respects the
	// parent's decision for child spans. This is the OTEL default and keeps
	// traces unbroken across services.
	SamplerParentBasedAlwaysOn Sampler = iota
	// SamplerAlwaysOn samples every span.
	SamplerAlwaysOn
	// SamplerAlwaysOff samples no spans.
	SamplerAlwaysOff
	// SamplerTraceIDRatio samples a fraction of root spans (see Config.SamplerRatio).
	SamplerTraceIDRatio
	// SamplerParentBasedTraceIDRatio is ratio sampling for roots, parent
	// decision for children.
	SamplerParentBasedTraceIDRatio
)

func (Sampler) String

func (s Sampler) String() string

String returns the spec OTEL_TRACES_SAMPLER value for the sampler.

type Signal

type Signal int

Signal identifies one of the three OpenTelemetry signals.

const (
	// SignalTraces is the distributed-tracing signal.
	SignalTraces Signal = iota
	// SignalMetrics is the metrics signal.
	SignalMetrics
	// SignalLogs is the logs signal.
	SignalLogs
)

func (Signal) String

func (s Signal) String() string

String returns the lowercase signal name ("traces"/"metrics"/"logs"), or "signal(N)" for an unknown value.

type SignalConfig

type SignalConfig struct {
	// Enabled turns the signal on. A zero Config has all signals disabled;
	// presets and With* options enable them.
	Enabled bool
	// Transport selects stdout / HTTP / gRPC.
	Transport Transport
	// Endpoint is a host, host:port, or URL. otelkit derives the correct port
	// and OTLP path from it (see resolveEndpoint).
	Endpoint string
	// EndpointIsURL marks Endpoint as a complete, per-signal URL to be used
	// verbatim — otelkit will NOT append /v1/<signal> to it. Mirrors the OTLP
	// per-signal-endpoint rule.
	EndpointIsURL bool
	// Headers carries auth and routing headers (e.g. authorization, dataset).
	Headers map[string]string
	// BatchTimeout overrides the batch/schedule delay for this signal. 0 →
	// the spec default.
	BatchTimeout time.Duration
}

SignalConfig configures one signal's exporter. A disabled signal yields a no-op provider.

func (SignalConfig) GRPCTarget

func (sc SignalConfig) GRPCTarget() (string, error)

GRPCTarget returns the bare host:port an OTLP/gRPC exporter should dial for this signal, applying the default OTLP gRPC port (4317) when none is present. It is exported for the contrib/otelkit-grpc module's exporter factories.

type SpanExporterFactory

type SpanExporterFactory = func(ctx context.Context, sc SignalConfig, tlsMode TLSMode) (sdktrace.SpanExporter, error)

SpanExporterFactory builds an OTLP/gRPC span exporter. It is the seam the contrib/otelkit-grpc module registers; core never imports google.golang.org/grpc.

type TLSMode

type TLSMode int

TLSMode is the transport security mode for OTLP exporters. The three modes are mutually exclusive, so a contradictory combination (e.g. plaintext with a client certificate) is rejected at construction rather than failing silently at export time.

const (
	// TLSModeTLS uses TLS with full server-certificate verification (the
	// default for non-stdout transports).
	TLSModeTLS TLSMode = iota
	// TLSModePlaintext disables TLS entirely (cleartext). Use only for a
	// local collector on a trusted network.
	TLSModePlaintext
	// TLSModeSkipVerify uses TLS but skips server-certificate verification.
	// Insecure; for self-signed collectors in dev only.
	TLSModeSkipVerify
)

func (TLSMode) String

func (m TLSMode) String() string

String returns the canonical TLS-mode name.

type Telemetry

type Telemetry struct {
	// contains filtered or unexported fields
}

Telemetry is the single bootstrap handle. It owns the three providers and the propagator, exposes accessors, and drives a single ordered Shutdown.

func Init

func Init(ctx context.Context, opts ...Option) (*Telemetry, error)

Init resolves configuration (preset < options < env), builds the enabled providers, and returns a handle. It performs no global registration; call SetGlobal to opt in. On OTEL_SDK_DISABLED=true it returns a fully no-op handle (never nil). When WithSelfTest is set it sends one span synchronously and returns the export error if the backend is unreachable.

Example

Minimal: stdout in local dev.

package main

import (
	"context"
	"log"

	"github.com/ubgo/otelkit"
)

func main() {
	ctx := context.Background()
	tel, err := otelkit.Init(ctx,
		otelkit.WithService("checkout", "1.4.2"),
		otelkit.WithPreset(otelkit.PresetStdout()),
		otelkit.WithEnvOverrides(false),
	)
	if err != nil {
		log.Fatal(err)
	}
	tel.SetGlobal()
	defer tel.Shutdown(ctx)
}

func (*Telemetry) Disabled

func (t *Telemetry) Disabled() bool

Disabled reports whether this handle is the no-op handle (OTEL_SDK_DISABLED).

func (*Telemetry) ForceFlush

func (t *Telemetry) ForceFlush(ctx context.Context) error

ForceFlush flushes any buffered telemetry across all providers.

func (*Telemetry) LoggerProvider

func (t *Telemetry) LoggerProvider() otellog.LoggerProvider

LoggerProvider returns the log provider — the seam github.com/ubgo/logger's OTEL sink consumes.

func (*Telemetry) MeterProvider

func (t *Telemetry) MeterProvider() otelmetric.MeterProvider

MeterProvider returns the metric provider (real or no-op).

func (*Telemetry) RunOnSignal

func (t *Telemetry) RunOnSignal(ctx context.Context) error

RunOnSignal blocks until ctx is cancelled or one of SIGINT/SIGTERM arrives, then runs Shutdown and returns its error. It is the one-call graceful-shutdown helper for long-running services.

Shutdown runs on a fresh background context — not the (possibly already cancelled) ctx that ended the wait — so a cancelled app context cannot abort the flush. For a bounded shutdown deadline, call Shutdown(ctxWithTimeout) directly instead.

func (*Telemetry) SelfTest

func (t *Telemetry) SelfTest(ctx context.Context) error

SelfTest sends one span synchronously and force-flushes it, surfacing the export error the async batch processor would otherwise hide. Used by WithSelfTest; safe to call directly.

func (*Telemetry) SetGlobal

func (t *Telemetry) SetGlobal()

SetGlobal registers the providers and propagator on the OTEL globals.

func (*Telemetry) Shutdown

func (t *Telemetry) Shutdown(ctx context.Context) error

Shutdown flushes and shuts down every provider in order — logs, then metrics, then traces — so a prior phase's errors still reach the collector before its provider closes. All providers are attempted regardless of individual failures; the errors are aggregated with errors.Join.

func (*Telemetry) Tracer

func (t *Telemetry) Tracer(name string) oteltrace.Tracer

Tracer is shorthand for TracerProvider().Tracer(name).

func (*Telemetry) TracerProvider

func (t *Telemetry) TracerProvider() oteltrace.TracerProvider

TracerProvider returns the trace provider (real or no-op).

type Temporality

type Temporality int

Temporality selects the metric aggregation temporality. Some backends (Datadog direct intake) reject cumulative and require delta; presets set this correctly per vendor.

const (
	// TemporalityCumulative is the OTEL default for all instrument kinds.
	TemporalityCumulative Temporality = iota
	// TemporalityDelta reports deltas between collections. Required by
	// Datadog direct OTLP intake; preferred by New Relic and Uptrace.
	TemporalityDelta
)

func (Temporality) String

func (t Temporality) String() string

String returns the canonical temporality name.

type Transport

type Transport int

Transport selects how a signal's OTLP exporter talks to the backend.

const (
	// TransportStdout writes telemetry to stdout — for local dev and the
	// dry-run path. No network, no auth.
	TransportStdout Transport = iota
	// TransportHTTP is OTLP over HTTP (default OTLP port 4318).
	TransportHTTP
	// TransportGRPC is OTLP over gRPC (default OTLP port 4317). The gRPC
	// exporters ship in the contrib/otelkit-grpc module to keep core's
	// dependency graph free of google.golang.org/grpc.
	TransportGRPC
)

func (Transport) String

func (t Transport) String() string

String returns the canonical transport name.

Jump to

Keyboard shortcuts

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