How to Configure OpenTelemetry Exports for Goose Observability
Goose provides built-in OpenTelemetry support that exports traces, metrics, and logs via OTLP through environment variables like OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_TRACES_EXPORTER, requiring no code changes when the otel feature is enabled.
Goose (the block/goose repository) ships with native OpenTelemetry integration that enables full observability of AI agent execution through standardized telemetry protocols. You can configure OpenTelemetry exports for Goose observability entirely through environment variables, routing telemetry data to any OTLP-compatible collector such as Jaeger, Prometheus, or Grafana Tempo without modifying source code.
Understanding Goose's Three-Layer Telemetry Architecture
Goose implements OpenTelemetry through three distinct layers in crates/goose/src/otel/otlp.rs, each handling a specific signal type:
| Layer | Function | Purpose |
|---|---|---|
| Tracer Layer | create_otlp_tracing_layer() (lines 45-73) |
Captures distributed tracing spans generated by Goose agents by selecting an exporter (Otlp, Console, or None) and registering with the global provider. |
| Metrics Layer | create_otlp_metrics_layer() (lines 89-111) |
Emits Goose-generated metrics (e.g., tool-call counts, token usage) with configurable temporalities via OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE. |
| Logs Layer | create_otlp_logs_layer() (lines 119-141) |
Sends structured logs produced by the tracing subscriber to an OTLP log endpoint by bridging to the OTel log exporter. |
These layers are conditionally added to Goose's global tracing_subscriber when the otel feature is enabled. The CLI (crates/goose-cli/src/logging.rs lines 71-74) and server (crates/goose-server/src/logging.rs lines 60-61) both call otlp::init_otlp_layers(goose::config::Config::global()) while building their respective subscribers.
Environment Variable Configuration
Goose mirrors the official OpenTelemetry SDK specification, allowing complete configuration through environment variables. The init_otlp_layers() function processes these variables to determine exporter behavior.
Core Configuration Variables
OTEL_EXPORTER_OTLP_ENDPOINT— Base endpoint (e.g.,http://localhost:4318) used for all signals.OTEL_EXPORTER_OTLP_{SIGNAL}_ENDPOINT— Override endpoint for a specific signal (TRACES,METRICS,LOGS).OTEL_{SIGNAL}_EXPORTER— Select exporter type per signal:otlp(default),console(stdout), ornone(disable).OTEL_TRACES_SAMPLER/OTEL_TRACES_SAMPLER_ARG— Control trace sampling to reduce volume (e.g.,parentbased_traceidratiowith0.1).OTEL_EXPORTER_OTLP_TIMEOUT— HTTP timeout in milliseconds for OTLP calls.OTEL_SERVICE_NAME— Logical service name appearing in the collector UI.OTEL_RESOURCE_ATTRIBUTES— Additional key-value pairs (e.g.,deployment.environment=prod).OTEL_SDK_DISABLED— Completely disables OTel when set totrue.
Configuration Precedence
When init_otlp_layers executes, it first promotes config-file values (otel_exporter_otlp_endpoint, otel_exporter_otlp_timeout) to environment variables if the corresponding env vars are missing. This makes the Goose CLI config file a source of truth while allowing environment overrides.
The function then calls the three create_*_layer helpers. Each helper determines the exporter type via signal_exporter(signal) (lines 44-71), which checks OTEL_SDK_DISABLED, OTEL_{SIGNAL}_EXPORTER, and endpoint variables before instantiating concrete SDK exporters (opentelemetry_otlp::SpanExporter, MetricExporter, LogExporter) or stdout alternatives.
The resource builder (create_resource lines 97-113) adds standard attributes (service.name, service.version, service.namespace) while respecting OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES.
Practical Configuration Examples
Local Collector Setup with Docker
Start an OTLP collector and export all signals:
docker run -d -p 4318:4318 \
-v $(pwd)/collector.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector-contrib \
--config /etc/otel-collector-config.yaml
Configure Goose to export to the local collector:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"
export OTEL_SERVICE_NAME="goose-cli"
goose run --recipe my-recipe.yaml
Export Only Traces with Sampling
Disable metrics and logs while sampling 5% of traces:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4318"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.05"
goose run --recipe hello-world.yaml
Console Exporter for Debugging
Route telemetry to stdout without requiring a collector:
export OTEL_TRACES_EXPORTER="console"
export OTEL_METRICS_EXPORTER="console"
export OTEL_LOGS_EXPORTER="console"
goose run --recipe debug.yaml
Traces appear as JSON on stdout, metrics as periodic JSON, and logs as human-readable lines.
Separate Metrics Endpoint
Use different collectors for different signals:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://collector-all:4318"
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT="http://prometheus-collector:4318"
export OTEL_METRICS_EXPORTER="otlp"
goose run --recipe-metrics-only.yaml
Programmatic Configuration
For custom Rust tools embedding Goose, invoke the same helpers directly:
use goose::otel::otlp::{init_otlp_layers, shutdown_otlp};
use goose::config::Config;
let layers = init_otlp_layers(&config);
// Attach layers to your tracing_subscriber::Registry
// On shutdown:
shutdown_otlp();
The shutdown_otlp() function (defined in crates/goose/src/otel/otlp.rs) flushes all providers to ensure pending data reaches the collector before the process exits.
Key Implementation Files
crates/goose/src/otel/otlp.rs— Containscreate_otlp_tracing_layer(),create_otlp_metrics_layer(),create_otlp_logs_layer(), andinit_otlp_layers(), plus the resource builder and shutdown logic.crates/goose-cli/src/logging.rs— Adds OTel layers to the CLI subscriber when theotelfeature is enabled.crates/goose-server/src/logging.rs— Same initialization for the server binary.documentation/docs/guides/environment-variables.md— User-facing documentation for all OTel-related variables.
Summary
- Goose implements OpenTelemetry through three layers (traces, metrics, logs) in
crates/goose/src/otel/otlp.rs, activated via theotelfeature flag. - Configure exports entirely through standard OTel environment variables like
OTEL_EXPORTER_OTLP_ENDPOINTandOTEL_TRACES_EXPORTER. - The
init_otlp_layers()function automatically selects exporters (otlp,console, ornone) and registers global providers. - Resource attributes including
service.namederive fromOTEL_SERVICE_NAMEandOTEL_RESOURCE_ATTRIBUTES. - Always use
shutdown_otlp()on termination to ensure complete data flushing to collectors.
Frequently Asked Questions
How do I completely disable OpenTelemetry in Goose?
Set OTEL_SDK_DISABLED=true or configure individual signals to none (e.g., OTEL_TRACES_EXPORTER="none"). This prevents the init_otlp_layers() function from instantiating exporters, effectively disabling telemetry collection without requiring a rebuild.
Can I use different endpoints for traces and metrics?
Yes. While OTEL_EXPORTER_OTLP_ENDPOINT sets a base endpoint for all signals, you can override specific signals using OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, or OTEL_EXPORTER_OTLP_LOGS_ENDPOINT. This enables routing traces to Jaeger while sending metrics to Prometheus, for example.
What happens to telemetry data when Goose shuts down?
The shutdown_otlp() function in crates/goose/src/otel/otlp.rs flushes all pending spans, metrics, and logs to the configured collectors before the process exits. This ensures no data loss during short-lived CLI runs or server restarts.
Can I configure OpenTelemetry through the Goose config file instead of environment variables?
Yes. Goose promotes configuration values such as otel_exporter_otlp_endpoint and otel_exporter_otlp_timeout from the config file to environment variables if the corresponding env vars are missing. This allows you to store persistent OTel settings in your Goose configuration while still respecting environment overrides for sensitive or dynamic values.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →