Skip to content

OTel Phase 0 — Tracer seam in forge-core (no-op default) #101

@initializ-mk

Description

@initializ-mk

Part of the Observability — OpenTelemetry Tracing v1 initiative (master tracking: #108). Effort: XS (~0.5 engineer-day). Risk: low (no behavior change). Depends on: nothing.

Goal

forge-core can import otel and exposes a tracer seam that defaults to no-op. Zero behavior change — purely additive infrastructure that subsequent phases hang implementations off.

Files

File Change
forge-core/go.mod Add otel deps (below)
forge-core/runtime/tracing.go New. Tracer seam: package-level provider holder defaulting to otel noop, SetTracerProvider, Tracer(), span helpers
forge-core/runtime/tracing_test.go New. Verify default is noop; Tracer().Start returns a non-recording span

Dependencies to add (forge-core/go.mod)

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

W3C tracecontext + baggage propagators are in core otel (go.opentelemetry.io/otel/propagation) — no contrib module needed.

tracing.go shape

  • The seam is otel's trace.TracerProvider interface. Default = go.opentelemetry.io/otel/trace/noop.NewTracerProvider().
  • var tracerProvider trace.TracerProvider = noop.NewTracerProvider() guarded by sync.RWMutex.
  • SetTracerProvider(tp trace.TracerProvider) — installs the real provider (called from cli wiring). Also call otel.SetTracerProvider(tp) and otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) so propagation works globally.
  • Tracer() trace.TracertracerProvider.Tracer("github.com/initializ/forge").
  • Instrumentation scope name constant: const InstrumentationName = "github.com/initializ/forge".

Do not hand-roll a span type — use otel's trace.Span. The noop provider already satisfies "no-op when disabled."

Verify

go work sync
go build ./...
go test ./forge-core/runtime/ -run Tracing -v
# Confirm: with no provider set, Tracer().Start(ctx,"x") yields span.SpanContext().IsValid()==false

Anti-patterns to avoid

  • Defining a custom Span interface (use otel's trace.Span).
  • Importing otel from cli call sites that should use the runtime seam.
  • Setting a real exporter here — that's Phase 1's job.

Architectural decision

forge-core owns the otel dependency behind a no-op TracerProvider seam. Real OTLP provider lives in a new forge-core/observability subpackage (Phase 1) and is injected from forge-cli (Phase 2) — same pattern as audit: defined in core, wired in cli.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions