addons/observability enables generated GOWDK Trace wiring for debug builds.
It registers FeatureObservability; runtime/trace remains the dependency-free
root runtime, and optional OTLP export lives in the nested
runtime/trace/otel module.
Enable it:
gowdk add observability
gowdk build --debug --app /tmp/gowdk-app pages/home.page.gwdk
Generated development builds mount the local trace viewer at:
/_gowdk/traces
The viewer is off unless the addon is enabled and Build.DebugAssets() is true
(Build.Mode != gowdk.Production). Outside dev, generated apps mount the viewer
behind runtime/app.LocalTraceAccess, which only allows direct localhost or
loopback requests and rejects forwarded reverse-proxy requests unless the app
supplies an explicit TraceAccess function.
Current generated instrumentation:
- Backend request route spans extract incoming
traceparent. - Generated SSR route and
load {}spans record route IDs, render lane, source refs, response status, and load errors without storing raw request bodies or headers. - Generated action, API, fragment, command, and query routes record handler
spans with
.gwdksource refs when debug metadata is enabled. - Guards and contract command/query/job/event/worker operations record child spans when a tracer is present in context.
runtime/contracts.EventEnvelopeand file outbox records carry an optionaltraceparent; old records without it remain readable.- Generated browser runtime spans partial submits and SPA navigation, injects
traceparent, and posts frontend spans to the local collector. - JS islands, WASM island loaders, and page-level client Go WASM loaders reuse
window.__gowdkTrace.
The generated local collector keeps a bounded in-memory ring of 1024 completed spans. Generated code records stable route/endpoint IDs and source metadata, and uses runtime redaction helpers for query strings, error messages, and app-owned trace events.
For app-owned Go handlers, record a user event on the active span:
app.Trace(ctx, "loaded patient", map[string]any{"patientID": id})
Export to OTLP from an app that opts into the nested module:
sink, err := otel.NewSink(ctx, otel.WithEndpoint("localhost:4318"), otel.WithInsecure())
if err != nil {
return err
}
defer sink.Shutdown(ctx)
tracer := trace.NewTracer(trace.WithSink(sink))
Do not treat the local collector or viewer as a production observability backend. Production deployments should set sampling deliberately, keep viewer access gated or disabled, and send spans to app-owned telemetry infrastructure.