OTLP Log Export, Shared OTLP Connection, Telemetry Accessors
v1.38.0 completes Microbus's OpenTelemetry story: logs join traces and metrics as a first-class OTLP signal. When a logs endpoint is configured, every record emitted through the connector's structured logger is exported over OTLP — trace-correlated automatically via native TraceId/SpanId — alongside the existing console output and inline span-event view. Under the hood, all OTLP exporters in one executable that target the same collector now share a single reference-counted connection instead of each opening its own, collapsing up to 3N connections (N services × three signals) down to one per distinct target. The connector also exposes its underlying telemetry handles — Logger(), TracerProvider() and MeterProvider() — so application code can instrument third-party libraries against the same pipelines and resource as the framework. The release is purely additive; there are no breaking changes.
Highlights
- Logs export over OTLP. When
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT(or the genericOTEL_EXPORTER_OTLP_ENDPOINT) is set, the connector exports structured logs to OpenTelemetry. The slog handler fans each record out to the deployment's terminal handler (colorful inLOCAL, text inTESTING, JSON inLAB/PROD) and anotelslogbridge backed by an SDKLoggerProvider, built over the shared OTLP connection with the common resource. - Automatic trace correlation for logs. The OTLP bridge stamps native
TraceId/SpanIdfrom the context, so backends correlate logs with traces without a string field. Thetracestring attribute is now added only on the console leg, where it remains the sole correlation handle. Inline span-event mirroring is retained as a separate, in-trace view that survives thePRODtail sampler even with no logs pipeline configured. - Shared, reference-counted OTLP connection. All OTLP exporters in one executable that export to the same endpoint over the same protocol share one connection (a gRPC
*grpc.ClientConnor a pooled*http.Client), created lazily on first acquire and torn down on last release. A bundle of microservices exporting three signals each would otherwise open up to 3N connections to the collector; sharing collapses that to one per distinct target. Connector.Logger()accessor. Returns the microservice's structured*slog.Logger. Logging through its context-aware methods (InfoContext,ErrorContext, …) enriches each record identically toLogInfo/LogError, because both flow through the same root handler. BeforeStartupit returns a discard logger, so the accessor never returnsnil.TracerProvider()andMeterProvider()accessors. Return the live SDK providers — or a no-op provider (nevernil) when the signal is disabled — so application code can instrument OTEL-aware third-party libraries against the framework's pipelines and resource.- Per-call log enrichment moved into the handler. Trace-ID and actor-claim stamping, span-event mirroring, the
microbus_log_messagescount, and developer-deployment stderr error dumps now live in the connector's root slog handler rather than in eachLogXXXmethod body.microbus_log_messagesconsequently counts every record emitted through the logger, including a bareLogger().Info(...), not just the convenience methods. - OTLP transport security from the endpoint and env. Transport security follows the endpoint URL scheme (
httpsis secured), withOTEL_EXPORTER_OTLP[_signal]_INSECUREoverriding it. Because the connector now builds the connection itself, it also honors the OTLP certificate env vars directly: a custom CA (..._CERTIFICATE) and a client certificate for mTLS (..._CLIENT_CERTIFICATE/..._CLIENT_KEY). - Ordered shutdown teardown. The final "Shutdown" log is recorded while the meter and tracer are still live (it feeds both a log counter and a span event), then the providers are torn down in reverse of init order — logs, then traces, then metrics — so that last log's counter increment and span event are captured rather than dropped.
- gRPC is now a direct dependency, attributed in
ATTRIBUTION.md, since the connector dials the shared OTLP gRPC connection itself.
New Features
OTLP Log Export
Set a logs endpoint — per-signal or via the generic endpoint that drives all three signals — to stream structured logs to an OpenTelemetry collector:
# env.yaml
# The generic endpoint drives all three signals (traces, metrics, logs).
OTEL_EXPORTER_OTLP_ENDPOINT: http://127.0.0.1:4317
# ...or set just the logs signal to override one:
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: http://127.0.0.1:4317No code change is required. Logging continues through the same LogInfo/LogWarn/LogError/LogDebug methods (or Logger()), and each record is now mirrored to the collector in addition to the console and the active span. The otelslog bridge stamps the trace and span IDs natively, so logs land already correlated with their traces in Grafana's LGTM stack. Export is best-effort sideband: a configured-but-unreachable collector never affects service health — exports are single-shot (retries disabled) and time out per OTEL_EXPORTER_OTLP_TIMEOUT rather than stalling startup or shutdown.
Telemetry Provider Accessors
Three accessors expose the connector's underlying telemetry handles so application code can wire third-party, OTEL-instrumented libraries into the same pipelines and resource the framework uses:
// Instrument a third-party library against the framework's own pipelines.
logger := svc.Logger() // *slog.Logger, enriched like LogXXX via *Context methods
tracer := svc.TracerProvider().Tracer("my-lib")
meter := svc.MeterProvider().Meter("my-lib")Logger() returns a discard logger before Startup; TracerProvider() / MeterProvider() return a no-op provider when their signal is disabled. None of the three ever returns nil. Fetch them at point of use rather than caching across Startup — the placeholders are replaced with the real handles during Startup. They are concrete methods on the connector and are reachable via the embedded connector on the concrete *Service.
Shared OTLP Connection
A registry in the connector keys shared connections on protocol|endpoint|caFile|clientCert|clientKey|insecure. The TLS material and the insecure override are part of the key, so two signals pointed at the same endpoint but secured differently never alias onto one connection. Each signal of each connector holds one reference; the connection is dialed on first export (preserving the non-blocking-Startup guarantee) and closed on last release. The connector — not the SDK — owns the connection: exporters are constructed with WithGRPCConn / WithHTTPClient, and the SDK never closes a caller-provided connection, so one connector's shutdown cannot pull the connection out from under another that still holds a reference.
Breaking Changes
None. v1.38.0 is purely additive — the new accessors and the logs signal layer on top of the existing logging, tracing, and metrics APIs. The slog handler internals were reorganized, but the service.Logger interface (LogDebug/LogInfo/LogWarn/LogError) is unchanged. The only observable behavior change is that microbus_log_messages now counts records emitted via a bare Logger().Info(...) in addition to the convenience methods.
Migration
From inside a Microbus project, ask Claude Code to upgrade Microbus:
{{< prompt >}}
Get the latest version of Microbus.
{{< /prompt >}}
The upgrade skill handles the version bump end-to-end:
- Bump
go.modtov1.38.0andgo mod tidy. - Refresh
.claude/rules/,.claude/skills/, and project-wide framework-managed files. - Run
go vet ./... && go test ./....
No source rewrites are required. To start exporting logs over OTLP, add OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (or rely on the generic OTEL_EXPORTER_OTLP_ENDPOINT) to env.yaml.
Documentation
- Updated: Structured Logging for OTLP log export, automatic trace correlation, and the
Logger()accessor. - Updated: Distributed Tracing and Metrics for the
TracerProvider()/MeterProvider()accessors and the shared OTLP connection. - Updated: Environment Variables for the logs endpoint and the OTLP transport-security env vars (
..._INSECURE,..._CERTIFICATE,..._CLIENT_CERTIFICATE/..._CLIENT_KEY). - Updated: Package
connectorfor the newotel.goshared-connection helpers.