I Can See Your Goose! AAIF's Goose agent now supports OpenTelemetry observability, enabling developers to trace and understand AI agent decision-making. The integration, detailed in a tutorial by AAIF, uses Goose v1.43.0 with the Claude ACP provider and Rust's OpenTelemetry SDK to provide visibility into agent actions, addressing common frustrations with opaque AI behavior. I Can See Your Goose Making AAIF's Goose Observable with OpenTelemetry If you’ve used AI agents for tech-y things software development, SRE type stuff, etc , you’ve probably found yourself in a yelling match at some point with your agent trying to understand why the hell it decided to take a particular course of action. Especially if that course of action is destructive or mischievous, like making code changes without permission, for example. Which is why having observable agents is important. Now, this is a fairly broad topic. There are lots of different agentic ecosystems out there. So today, I’ll dig into one specific use case: enabling OpenTelemetry OTel https://opentelemetry.io on AAIF’s Goose https://goose-docs.ai to help make your Goose agents observable. Let’s dig in Goose Overview Enabling OTel on Goose For your convenience, I have a lovely GitHub repository https://github.com/avillela/goose-otel-enablement to accompany this tutorial, including the dev container /tag/devcontainers configuration. If you choose to use the dev container, you will have all of the tools that you need for this tutorial. Set up Goose The instructions in this section are for setting up Goose with with the Claude ACP https://goose-docs.ai/docs/guides/acp-providers claude-acp provider, which is a wrapper for Claude Code. ✨ FUN FACT:ACP stands for Agent Client Protocol. It’s an open standard that allows AI agents to talk to each other. 1. Install Goose First things first, you need to set up Goose. When the dev container first builds, it donwloads and installs Goose, and then Goose prompts you to set it up. You don’t need to run the code snippet below, as it’s already done for you on dev container initial build, but I’ve included it for you so you can see how it’s being installed. GOOSE RELEASE="v1.43.0" curl -fsSL https://github.com/aaif-goose/goose/releases/download/${GOOSE RELEASE}/download cli.sh | bash 2. Configure your provider These steps show you how to set up Goose with the Claude Code provider. NOTE:If you need to configure your provider again, run goose configure How would you like to set up your provider? : Manual Configuration Which model provider should we use? : Claude Code CLI Provider Claude Code CLI requires CLAUDE CODE COMMAND, please enter a value : claude Select model : claude-sonnet-4-6 Select thinking effort : Low - Better latency, lighter reasoning Success Unfortunately, as you were making your selection, you may have noticed that the Claude CLI is deprecated. On top of that, we don’t have the option to select Claude ACP . ARRGH Fortunately, I found a workaround. First, open your Goose config.yaml ~/.config/goose/config.yaml file, and locate the code snipped below: active provider: claude-code providers: claude-code: enabled: true model: claude-sonnet-4-6 configured: true Replace claude-code with claude-acp . Your modified block should look like this: active provider: claude-acp providers: claude-acp: enabled: true model: sonnet configured: true You also need to globally install the Claude ACP npm package: npm install -g @agentclientprotocol/claude-agent-acp For more info, check out out the Goose ACP docs https://goose-docs.ai/docs/guides/acp-providers/ claude-acp-configuration . 3. Login to Claude Open a new terminal window, then start a Claude Code session via the Claude CLI by typing claude , and following the login instructions provided. 4. Start Goose Once Claude is authenticated, open a new terminal window and start Goose: goose session Feel free to ask it a test question. I always like to ask “how many folders in my directory?”. Enabling OpenTelemetry for Goose Fun fact: Goose is written in Rust, and is it so happens that it ships with built-in OpenTelemetry instrumentation, courtesy of the Rust OTel SDK https://opentelemetry.io/docs/languages/rust/ . This allows Goose to emit OTel traces, logs, and metrics. But you need to enable it. 1. Enable telemetry Upon initial setup, Goose may have given you the following option: Enable or disable anonymous usage data collection It’s kind of confusing, and if you’re like me, you might have disabled it initially. It turns out that you need this setting enabled for OTel telemetry emission. Never fear, because there’s a way to enable it after the fact. Open ~/.config/goose/config.yaml ~/.config/goose/config.yaml , and find the configuration called GOOSE TELEMETRY ENABLED , and set it to true : Before GOOSE TELEMETRY ENABLED: false After GOOSE TELEMETRY ENABLED: true But that’s only part of the story. We also need to configure a few OTel environment variables. 2. Configure environment variables The OTel environment variables tell OTel where to send the telemetry. These are the ones we need: OTEL EXPORTER OTLP ENDPOINT="http://localhost:4318" OTEL EXPORTER OTLP PROTOCOL="http/protobuf" Where: OTEL EXPORTER OTLP ENDPOINT is the destination of our telemetry. In our case, we’re sending the telemetry to an OTel Collector https://opentelemetry.io/docs/collector/ . The Collector is configured to send telemetry data to Jaeger https://www.jaegertracing.io/ traces only and Dynatrace https://dt-url.net/dt-trial traces, logs, metrics . OTEL EXPORTER OTLP PROTOCOL is the protocol we’re using for sending our OTel data. OTel supports both gRPC and HTTP. In this case, we’re using HTTP. If we were sending via gRPC, the value of OTEL EXPORTER OTLP PROTOCOL would be grpc , and our endpoint would be http://localhost:4317 the OTel Collector listens on port 4317 for gRPC . Full disclosure:I work at Dynatrace and therefore have access to a Dynatrace environment. Obviously, you don’t have to go that route. To make things easier, I have split off the Dynatrace configuration so that you can exclude it if you won’t want to use it. To configure the environment variables, create a copy of .env.example .env.example : cp .env.example .env If you don’t want to go the Dynatrace route, you don’t need to make any other environment variable configurations. If you choose to also use Dynatrace https://dt-url.net/dt-trial as your observability backend, uncomment and replace the details for the following environment variables: DT ENDPOINT : your Dynatrace endpoint https://www.dynatrace.com/news/blog/send-opentelemetry-data-to-dynatrace/ your-dynatrace-tenant DT API TOKEN : your Dynatrace API token https://www.dynatrace.com/news/blog/send-opentelemetry-data-to-dynatrace/ create-a-dynatrace-access-token 3. Start OTel components Now we’re ready to start the OTel Collector and Jaeger. This is done via a docker-compose.yaml https://github.com/avillela/goose-otel-enablement/blob/main/docker-compose.yml , which starts two containers: | Service | Image | Exposed ports | |---|---|---| otel-collector | otel/opentelemetry-collector-contrib:0.128.0 | 4317 gRPC , 4318 HTTP | jaeger | jaegertracing/jaeger:2.7.0 | 16686 UI | NOTE:If you would like to exclude the Dynatrace components, comment out lines referencing the Dynatrace Collector configuration from : docker-compose.yaml Before: otel-collector: image: otel/opentelemetry-collector-contrib:0.128.0 command: - "--config=/etc/otel/config.yaml" - "--config=/etc/otel/config-dt.yaml" volumes: - ./src/otel/otel-collector-config.yaml:/etc/otel/config.yaml - ./src/otel/otel-collector-config-dt.yaml:/etc/otel/config-dt.yaml After: otel-collector: image: otel/opentelemetry-collector-contrib:0.128.0 command: - "--config=/etc/otel/config.yaml" - "--config=/etc/otel/config-dt.yaml" volumes: - ./src/otel/otel-collector-config.yaml:/etc/otel/config.yaml - ./src/otel/otel-collector-config-dt.yaml:/etc/otel/config-dt.yaml Now you’rea ready to run the script: ./src/scripts/start-otel.sh .env This starts up Jaeger and the OTel Collector. Jaeger UI: http://localhost:16686 http://localhost:16686 Dynatrace UI: https://