CertSafari
    Anthropic Claude Certified Architect – Professional (CCAR-P)· Lessons

    Domain 3 · Lesson 15/38

    Agent Observability: Challenges, Signals and Traces

    Analyze observability challenges and select monitoring strategies at scale

    10 min read
    2.38% of exam
    3 sources
    Published 4 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Explain why agent systems need tracing rather than bug reports alone
    • Choose between metrics, log events and traces for a monitoring question
    • Read the Claude span hierarchy and use agent attributes to follow multi-agent runs
    • Configure telemetry per process or per agent and tag it by tenant

    Key concept

    Agent run tracing — Agents are non-deterministic and run many tool calls in sequence, so a final answer tells you little about why a run failed. A trace records each turn, model request and tool call as nested spans, which lets you see where in the run things went wrong.

    1.Why agent runs resist ordinary debugging

    In ordinary software, a bug usually reproduces: run the same input again and you get the same failure. Agents don't work that way. Anthropic's account of building its multi-agent research system says that agents "make dynamic decisions and are non-deterministic between runs, even with identical prompts." They also run for a long time and keep state across many tool calls, so a small early mistake carries through to the end. Users reported that agents were "not finding obvious information", and the team could not tell which of several causes was to blame: "Were the agents using bad search queries? Choosing poor sources? Hitting tool failures?"

    What fixed this was instrumentation, not more careful prompting: "Adding full production tracing let us diagnose why agents failed and fix issues systematically." The team also monitored "agent decision patterns and interaction structures" while deliberately not monitoring "the contents of individual conversations, to maintain user privacy." That tension between enough detail to diagnose and little enough to protect users runs through every strategy decision in this subdomain.

    For Claude-based agents, the supported route is OpenTelemetry. The Agent SDK exports traces, metrics and events to your own backend, so you can see which tools a run called, how long each model request took, how many tokens it spent, and where it failed.

    Checkpoint 1 of 5· Check yourself

    Users report that a research agent sometimes misses obvious information, and rerunning the same prompt doesn't reliably reproduce it. What does the source say let the team find the root cause?

    Running this across a fleet adds volume and privacy concerns, and Claude Code gives you levers for each. Three are worth knowing before you look at specific signals.

    First, which attributes ride on every metric datapoint. OTEL_METRICS_INCLUDE_SESSION_ID, OTEL_METRICS_INCLUDE_ACCOUNT_UUID and OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES all default to true. OTEL_METRICS_INCLUDE_VERSION, OTEL_METRICS_INCLUDE_ENTRYPOINT and OTEL_METRICS_INCLUDE_REPOSITORY default to false. Setting a default-true flag to false removes that attribute from metrics.

    Second, content volume. Content-bearing attributes such as model responses and tool content are truncated at 60 KB by default, and CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH can be lowered to cut telemetry volume. Prompt text and tool details are off unless you enable them.

    Third, who controls the configuration. When managed settings set OTEL_EXPORTER_OTLP_ENDPOINT, Claude Code removes every developer-set per-signal endpoint, so developers can't point one signal at a different collector. Exporter selectors still follow normal per-key precedence, so set them in managed settings too if you need them locked.

    Checkpoint 2 of 5· Exam question

    An enterprise is rolling out centrally managed OpenTelemetry export for Claude Code across 400 engineers on Kubernetes, and the observability team wants configuration that scales without per-developer setup and keeps backend storage costs predictable. Which combination of practices should they adopt? (Select all that apply)(Select 3)

    Sources123

    2.Metrics, log events and traces answer different questions

    Claude Code and the Agent SDK emit three OpenTelemetry signals. Each one has its own exporter selector, and you turn on only the ones you need. Choose by the question you're trying to answer. Metrics are aggregate counters, good for spotting cost spikes or unusual token consumption across a fleet. Log events are one structured record per occurrence, which supports tool success rates, average tool execution times and error patterns by tool type. Traces connect those occurrences into the shape of a single run.

    The three telemetry signals and how to enable each
    SignalWhat it containsEnable with
    MetricsCounters for tokens, cost, sessions, lines of code, and tool decisionsOTEL_METRICS_EXPORTER
    Log eventsStructured records for each prompt, API request, API error, and tool resultOTEL_LOGS_EXPORTER
    TracesSpans for each interaction, model request, tool call, and hook (beta)OTEL_TRACES_EXPORTER plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

    One caveat on the table: the hook span is the exception to its "plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1" rule. The interaction, model-request and tool spans need only the enhanced beta flag, but claude_code.hook requires detailed beta tracing, which means ENABLE_BETA_TRACING_DETAILED=1 and BETA_TRACING_ENDPOINT. That pair also changes where your logs and traces go.

    Checkpoint 3 of 5· Fill the gap

    This SDK configuration is meant to export all three signals. Which variable fills the blank?

    OTEL_ENV = {
        "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
        # Required for traces, which are in beta. Metrics and log events do not need this.
        " ? ": "1",
        # Choose an exporter per signal. Use otlp for the SDK; see the Note below.
        "OTEL_TRACES_EXPORTER": "otlp",
        "OTEL_METRICS_EXPORTER": "otlp",
        "OTEL_LOGS_EXPORTER": "otlp",
        # Standard OTLP transport configuration.
        "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
        "OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4318",
        "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",
    }

    Sources23

    3.Following one run, and its subagents, through spans

    A trace is a tree of spans. The root, claude_code.interaction, covers a single turn of the agent loop. Under it, each Claude API call and each tool call get their own spans. Tool spans are split into time spent waiting for permission and time spent executing, so a slow tool and a slow human approval look different. When the Agent tool starts a subagent, the subagent's own model-request and tool spans sit under that tool span.

    Spans in a Claude Code / Agent SDK trace
    SpanWhat it wraps
    claude_code.interactionA single turn of the agent loop, from receiving a prompt to producing a response
    claude_code.llm_requestEach call to the Claude API, with model name, latency, and token counts
    claude_code.toolEach tool invocation
    claude_code.tool.blocked_on_userThe permission wait inside a tool call
    claude_code.tool.executionThe tool execution itself
    claude_code.hookEach hook execution; requires detailed beta tracing

    In a multi-agent system, the attributes on the llm_request span are what make a run readable. agent_id identifies the subagent that issued a request and is absent on the main session. parent_agent_id identifies the agent that spawned it. query_source_safe gives a bounded subsystem label, such as repl_main_thread or agent.builtin.general-purpose, and is emitted even without detailed tracing. workflow.run_id ties agents back to the Workflow tool run that created them. For latency and failure analysis, the same span carries ttft_ms, attempt, error_class and stop_reason.

    Checkpoint 4 of 5· Match them up

    Match each llm_request span attribute to what it tells you

    Tap a term, then the definition that fits it.

    Sources23

    4.Where telemetry config lives, and tagging it per tenant

    There are two places to put the variables. In production, set them in the process environment: your shell, container or orchestrator. Every query() call then picks them up without any code change. Use per-call options (ClaudeAgentOptions.env in Python, options.env in TypeScript) when different agents in the same process need different telemetry settings. The two languages handle this differently. Python merges env on top of the inherited environment. TypeScript replaces the inherited environment entirely, so you have to spread process.env into the object, or you lose PATH and ANTHROPIC_API_KEY.

    At scale, you also need to know whose run you're looking at. OTEL_SERVICE_NAME names the agent, and OTEL_RESOURCE_ATTRIBUTES can carry deployment tags or per-request identity. By default these keys are also copied onto metric datapoints, controlled by OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES. Separately, token metrics already carry skill.name, plugin.name and agent.name, which lets you attribute spend to specific subagent types.

    That default has a cost. If you put identifiers such as enduser.id or tenant.id in OTEL_RESOURCE_ATTRIBUTES, they land on every metric datapoint, so your metrics backend stores them and metrics grow with the number of distinct users or tenants. Set OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES to false to keep them off metrics, and note that OTEL_METRICS_INCLUDE_SESSION_ID and OTEL_METRICS_INCLUDE_ACCOUNT_UUID are also true by default. The same identity tags still appear as resource attributes on the other signals. Decide per tenant and per signal how much identity you want in aggregate metrics, versus in logs and traces you query for a single run.

    Tagging each agent run with the end user and tenant from the incoming requestpython
    from urllib.parse import quote
    
    options = ClaudeAgentOptions(
        env={
            # ... exporter configuration from the Enable telemetry export example ...
            # request is the incoming request object from your web framework.
            "OTEL_RESOURCE_ATTRIBUTES": f"enduser.id={quote(request.user_id)},tenant.id={quote(request.tenant_id)}",
        },
    )

    Checkpoint 5 of 5· Check yourself

    A TypeScript service passes options: { env: otelEnv } to query(), where otelEnv holds only the OTEL variables. What goes wrong?

    Sources23

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Setting CLAUDE_CODE_ENABLE_TELEMETRY=1 and OTEL_TRACES_EXPORTER=otlp is enough to get traces.Why is that wrong?

      Traces are in beta and also need CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1. Metrics and log events don't.

      Covered in Metrics, log events and traces answer different questions

    2. 2.Per-call env behaves the same in the Python and TypeScript SDKs.Why is that wrong?

      Python merges env on top of the inherited environment. TypeScript replaces it entirely, so you must spread process.env.

      Covered in Where telemetry config lives, and tagging it per tenant

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “Agents make dynamic decisions and are non-deterministic between runs, even with identical prompts”
      ↩︎ Why agent runs resist ordinary debugging
      “all without monitoring the contents of individual conversations, to maintain user privacy”
      ↩︎ Why agent runs resist ordinary debugging
      “Adding full production tracing let us diagnose why agents failed and fix issues systematically”
      ↩︎ Checkpoint
    2. 2.
      “Export traces, metrics, and events from the Agent SDK to your observability backend using OpenTelemetry.”
      ↩︎ Why agent runs resist ordinary debugging
      “Structured records for each prompt, API request, API error, and tool result”
      ↩︎ Metrics, log events and traces answer different questions
      “Requires detailed beta tracing (ENABLE_BETA_TRACING_DETAILED=1 and BETA_TRACING_ENDPOINT)”
      ↩︎ Metrics, log events and traces answer different questions
      “claude_code.llm_request: wraps each call to the Claude API, with model name, latency, and token counts as attributes.”
      ↩︎ Following one run, and its subagents, through spans
      “Every query() call picks them up automatically with no code change. This is the recommended approach for production deployments.”
      ↩︎ Where telemetry config lives, and tagging it per tenant
      “Spans for each interaction, model request, tool call, and hook (beta)”
      ↩︎ Key concept
      “Required for traces, which are in beta. Metrics and log events do not need this.”
      ↩︎ Exam trap 1
      “In TypeScript, env replaces the inherited environment entirely, so include ...process.env in the object you pass.”
      ↩︎ Exam trap 2
      “Required for traces, which are in beta. Metrics and log events do not need this.”
      ↩︎ Prediction
      “In TypeScript, env replaces the inherited environment entirely, so include ...process.env in the object you pass.”
      ↩︎ Checkpoint
    3. 3.
      “Include keys from OTEL_RESOURCE_ATTRIBUTES as attributes on metric datapoints”
      ↩︎ Why agent runs resist ordinary debugging
      “or lower it to cut telemetry volume”
      ↩︎ Why agent runs resist ordinary debugging
      “Claude Code removes every developer-set per-signal endpoint”
      ↩︎ Why agent runs resist ordinary debugging
      “Identifier of the subagent or teammate that issued the request. Absent on the main session”
      ↩︎ Following one run, and its subagents, through spans
      “Attributing spend to specific skills, plugins, or subagent types via the skill.name, plugin.name, and agent.name attributes”
      ↩︎ Where telemetry config lives, and tagging it per tenant
      “Include keys from OTEL_RESOURCE_ATTRIBUTES as attributes on metric datapoints”
      ↩︎ Where telemetry config lives, and tagging it per tenant
      “Identifier of the agent that spawned this one. Absent for the main session and for agents spawned directly from it”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    Monitoring Strategy for Claude Agents: Privacy, Collectors and Reporting APIs

    Spotted a mistake, or was something unclear? Tell us.