Setup & Viewing Your Data
Transaction Search, the GenAI dashboard, and trace correlation
- Enable CloudWatch Transaction Search (console and CLI) and explain why it is the mandatory first step before any span is searchable
- Deploy and invoke a Runtime-hosted agent that is auto-instrumented, and drive the Sessions view by passing a runtimeSessionId
- Configure vended-log delivery for non-Runtime resources (Memory, Gateway, Tools, Identity) that do NOT get a log group for free
- Navigate the GenAI Observability dashboard (Agents, Sessions, Trace views) and locate the raw CloudWatch log groups, span store, and metrics namespace
- Correlate a single request across the stack using the session-ID header and the trace headers (X-Amzn-Trace-Id / traceparent / baggage)
- Identify and prevent the APPLICATION_LOGS PII/secret-leak gotcha before payloads are emitted
Observability data doesn't just appear — there's a concrete setup path. This lesson walks the one-time, REQUIRED prerequisite (enabling CloudWatch Transaction Search, or your spans are never searchable), shows why Runtime-hosted agents are automatic while Memory/Gateway/Tools/Identity need vended-log delivery wired up by hand, then teaches you where to actually find the data — the GenAI Observability dashboard's Agents/Sessions/Trace views, the raw CloudWatch locations, and how session and trace IDs stitch a single request across the whole stack.
- 1Step 1 (REQUIRED, one-time): enable Transaction Search
- 2Step 2: Runtime-hosted is automatic
- 3Step 3: non-Runtime resources need vended-log delivery
- 4Viewing data: the GenAI Observability dashboard
- 5Trace correlation: stitching one request across the stack
- 6The PII / secrets leak gotcha in APPLICATION_LOGS
Step 1 (REQUIRED, one-time): enable Transaction Search
Before you look at a single trace, there is one switch you must flip — and forgetting it is the #1 observability gotcha. CloudWatch Transaction Search is what makes spans searchable. AgentCore emits spans to the CloudWatch Logs group aws/spans (a.k.a. /aws/spans/default), but until Transaction Search is enabled, X-Ray won't index those spans into traces and the GenAI dashboard's Trace view will be empty.
This is a one-time, account-level action. After you enable it, allow ~10 minutes before spans become searchable.
Console path: CloudWatch → Settings → Account → X-Ray traces → Transaction Search → Enable, then set the index percentage. The first 1% of indexed spans is free; you can raise the percentage later (everything above 1% is billed at standard CloudWatch rates).
API / CLI path — three calls. First, a resource policy that lets X-Ray write events into the span log groups; then point trace segments at CloudWatch Logs; then set the indexing rule:
# 1) Allow xray.amazonaws.com to PutLogEvents into the span log groups
aws logs put-resource-policy \
--policy-name TransactionSearchAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "xray.amazonaws.com" },
"Action": "logs:PutLogEvents",
"Resource": [
"arn:aws:logs:<region>:<account-id>:log-group:aws/spans:*",
"arn:aws:logs:<region>:<account-id>:log-group:/aws/application-signals/data:*"
]
}]
}'
# 2) Send trace segments to CloudWatch Logs (the Transaction Search store)
aws xray update-trace-segment-destination --destination CloudWatchLogs
# 3) Set the indexing rule (1% is free). The exact --name/--rule
# shape is an X-Ray API detail — confirm it against the live
# configure-observability docs before scripting it.
aws xray update-indexing-rule \
--name "Default" \
--rule '{ "Probabilistic": { "DesiredSamplingPercentage": 1 } }'The two grant targets above — aws/spans and /aws/application-signals/data — are exactly where AgentCore's spans and Application Signals data land. Get this wrong and traces silently never index.
Watch out
No Transaction Search = no traces, full stop
This is the single most common reason an engineer says "observability isn't working." It is also a prerequisite for enabling tracing on other resources later — you cannot turn on Memory spans, for example, until Transaction Search is on. Enable it once, at the account level, before anything else.
Note
The 1%-free number is VOLATILE
The free indexing allotment (first 1% of spans) and the per-indexed-span rate above it are CloudWatch pricing details that change. Treat "1% free" as true as of writing and verify it on the live CloudWatch pricing page before you size a budget.
Step 2: Runtime-hosted is automatic
If your agent runs on AgentCore Runtime, you are mostly done after Step 1. Deploying via the CLI auto-instruments the agent with OpenTelemetry — no extra libraries, no env vars, no log-group creation. You get baseline traces, spans, sessions, and metrics for free, and Runtime auto-creates its log group.
# Deploy the auto-instrumented Runtime agent (starter toolkit verb shown; the
# newer @aws/agentcore CLI uses `agentcore deploy` the same way)
agentcore deploy
# Invoke it — pass a session id to group spans in the Sessions view
agentcore invoke '{"prompt":"summarize my open tickets"}' --session-id my-session-0001The critical detail for viewing data is the session id. Passing runtimeSessionId (the --session-id flag on the CLI, or runtimeSessionId in a boto3 InvokeAgentRuntime call) is what drives the Sessions view — it's the key the dashboard groups spans under. Omit it and every invocation looks like an orphan; pass a stable id across turns of one conversation and you get a clean session timeline.
Under the hood the auto-instrumentation captures the agent invocation sequence, LLM calls and responses, tool invocations and results, error paths, and your framework integration (Strands, LangChain, LangGraph, etc.). The span operation for the invocation is InvokeAgentRuntime, carrying attributes including session.id, latency_ms, and error_type.
Tip
Session IDs are ≥ 33 characters
Runtime session IDs have a minimum length (commonly cited as 33 characters). A too-short id is a frequent invoke failure. Use a UUID-derived string and you'll never hit it. Verify the exact minimum in the API reference if you're generating ids programmatically.
Watch out
Don't double-instrument
Because Runtime already auto-instruments with ADOT, manually adding your own OTEL distro on top of a Runtime-hosted agent produces duplicate spans. Auto-instrumentation is for Runtime; the manual ADOT path (env vars + opentelemetry-instrument) is for agents running OUTSIDE Runtime.
Step 3: non-Runtime resources need vended-log delivery
Here is the asymmetry that trips people up. Runtime auto-creates a log group. The other resources do NOT. Memory, Gateway, Built-in Tools, and Identity will not deliver spans or logs anywhere until you configure vended-log delivery yourself. Enable nothing and you'll wonder why your Gateway shows metrics but no traces.
Vended-log delivery is a standard CloudWatch Logs mechanism made of three pieces, wired with the logs client:
import boto3
logs = boto3.client("logs")
resource_arn = "arn:aws:bedrock-agentcore:<region>:<account-id>:gateway/<gateway-id>"
# 1) The delivery SOURCE: which resource + what kind of telemetry
logs.put_delivery_source(
name="agentcore-gw-traces",
resourceArn=resource_arn,
logType="TRACES", # or "APPLICATION_LOGS"
)
# 2) The delivery DESTINATION: where it goes (CloudWatch Logs / X-Ray / S3 / Firehose)
logs.put_delivery_destination(
name="agentcore-gw-dest",
deliveryDestinationType="XRAY", # "CWL" for CloudWatch Logs, "XRAY" for traces
deliveryDestinationConfiguration={
"destinationResourceArn": "arn:aws:logs:<region>:<account-id>:log-group:/aws/vendedlogs/..."
},
)
# 3) The DELIVERY: bind source to destination
logs.create_delivery(
deliverySourceName="agentcore-gw-traces",
deliveryDestinationArn="arn:aws:logs:<region>:<account-id>:delivery-destination:agentcore-gw-dest",
)The two logType values you'll use are APPLICATION_LOGS (stdout/stderr + app logs, including full request/response payloads) and TRACES (spans). Destinations can be CloudWatch Logs (CWL), X-Ray (XRAY), S3, or Data Firehose. When delivered to CloudWatch Logs, the default group follows the pattern:
/aws/vendedlogs/bedrock-agentcore/{resource-type}/APPLICATION_LOGS/{resource-id}
Memory has an extra step: Memory spans require enabling Tracing on the Memory resource (a toggle in the console) — and, like everything else, that depends on Transaction Search already being on. So the order for Memory tracing is: Transaction Search → enable Tracing on the memory store → configure vended-log delivery for the spans.
Key insight
Why only Runtime is hands-off
Runtime owns the execution sandbox, so it can wire its own log group at deploy time and auto-instrument the process. Memory, Gateway, Tools, and Identity are managed control-plane services you call into — there's no process of yours to instrument, so AWS exposes their telemetry through the generic vended-log delivery plumbing instead. Same data store (CloudWatch), different on-ramp.
Viewing data: the GenAI Observability dashboard
With data flowing, the primary lens is the GenAI Observability dashboard in CloudWatch:
CloudWatch →
#gen-ai-observability→ Bedrock AgentCore tab → Agents View / Sessions View / Trace View
- Agents View — your Runtime agents with rolled-up metrics: session count, latency, duration, token usage, error rates.
- Sessions View — invocations grouped by session id. This is why passing
runtimeSessionIdmatters; without it the grouping collapses. - Trace View — the per-request trace trajectory + timeline, with span details and error breakdowns. This is where you click into one slow request and see exactly which tool call or LLM step ate the latency.
A hard limit to internalize: only Agent (Runtime) shows up in the GenAI dashboard. (Policy also appears, but under the Gateway tab.) Memory, Gateway, and Built-in Tools live in plain CloudWatch only — you'll find their metrics and any spans/logs you enabled in regular CloudWatch Metrics and Logs, not in the GenAI dashboard.
Raw CloudWatch locations (when you need to grep logs or build a custom widget):
| Telemetry | Where to look |
|---|---|
| Standard runtime logs | /aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint>/[runtime-logs]... |
| OTEL runtime logs | .../runtime-logs (one doc shows .../otel-rt-logs — the suffix is inconsistent across docs; check the live page) |
| Spans / traces | /aws/spans/default, surfaced via Transaction Search |
| Metrics | CloudWatch Metrics namespace bedrock-agentcore |
| Non-Runtime vended logs | /aws/vendedlogs/bedrock-agentcore/{resource-type}/APPLICATION_LOGS/{resource-id} |
Watch out
"My Gateway/Memory traces aren't in the dashboard"
They never will be. The GenAI Observability dashboard surfaces Runtime (and Policy under the Gateway tab) only. For Memory, Gateway, and Tools, open plain CloudWatch Logs/Metrics and look under the bedrock-agentcore namespace and the /aws/vendedlogs/... groups.
Note
The OTEL log-group suffix is VOLATILE
AWS docs are inconsistent about whether OTEL runtime logs land under .../runtime-logs or .../otel-rt-logs. Don't hard-code either in tooling without confirming against your own account and the live observability-view documentation.
Trace correlation: stitching one request across the stack
A single user request might hop through Runtime → Gateway → a tool → Memory. To see it as one story, two kinds of identifiers must propagate: a session id (groups many requests in a conversation) and trace context (stitches the spans of one request together).
Session id. On Runtime, it rides the header X-Amzn-Bedrock-AgentCore-Runtime-Session-Id (the runtimeSessionId parameter in boto3). For code running outside Runtime, set it via OTEL baggage so it lands on the session.id span attribute:
from opentelemetry import baggage, context
# Outside Runtime: attach the session id so spans carry session.id
ctx = baggage.set_baggage("session.id", "my-session-0001")
context.attach(ctx)Trace context. The headers AgentCore honors for trace propagation are:
X-Amzn-Trace-Id
traceparent
tracestate
baggage
X-Amzn-Bedrock-AgentCore-Runtime-Session-Id
mcp-session-idX-Amzn-Trace-Id is the AWS-native form; traceparent / tracestate are the W3C Trace Context standard; baggage carries arbitrary key/values like session.id. These same custom headers are accepted on the Built-in Tools APIs (StartCodeInterpreterSession, InvokeCodeInterpreter, StartBrowserSession, StopBrowserSession) and the Identity APIs (GetWorkloadAccessToken*, GetResourceOauth2Token, GetResourceAPIKey) — so if you forward the trace headers when your agent calls those services, their spans join the same trace.
In the Trace View, correlation shows up as a single trajectory: the InvokeAgentRuntime span at the root, child spans for each LLM call and tool invocation, all sharing one trace id and tagged with the same session.id.
Tip
Forward the headers and you get correlation for free
The mechanics are just HTTP header propagation. If your agent passes traceparent / X-Amzn-Trace-Id / baggage through to its downstream calls (Gateway, tools, Identity), the spans automatically chain into one trace. Drop the headers and you get disconnected single-span traces that are far harder to debug.
The PII / secrets leak gotcha in APPLICATION_LOGS
AgentCore's APPLICATION_LOGS are deliberately rich for auditability: they capture the full request_payload and response_payload alongside trace_id, span_id, and session_id. That is exactly what you want when reconstructing what an agent did — and exactly the place a secret or a customer's PII leaks if you aren't careful.
The payloads are verbatim. If your prompt contains an API key, a Social Security number, a full email thread, or a bearer token a user pasted in, it lands in CloudWatch Logs (and, if you configured S3/Firehose delivery, anywhere those flow). Treat application logs as a data-classification surface, not just a debugging convenience.
The fix is to redact before emission, inside your entrypoint, before anything is logged:
import re
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
SECRET_RE = re.compile(r"(sk-[A-Za-z0-9]{20,}|Bearer\s+\S+|\b\d{3}-\d{2}-\d{4}\b)")
def redact(text: str) -> str:
return SECRET_RE.sub("[REDACTED]", text)
@app.entrypoint
def handler(payload):
prompt = redact(payload.get("prompt", ""))
# ... run the agent on the sanitized prompt; never log the raw payload ...
return {"result": prompt}For a managed layer, combine this with Bedrock Guardrails (PII filters) so sensitive content is caught even when your regex misses it. The principle from the security model is blunt: filter PII/secrets before emission — once it's in the logs it's durable.
Watch out
Auditability cuts both ways
Full request/response capture is great for forensics and terrible for compliance if unmanaged. Logs are durable to CloudWatch Logs, S3, and Data Firehose and may be replicated cross-account. Decide what is allowed to land there BEFORE you ship, and redact at the entrypoint.
Try it: Turn on the lights: from blank dashboard to a correlated trace
Goal: enable observability end-to-end, prove a Runtime agent is auto-instrumented, wire telemetry for one non-Runtime resource, and read a correlated trace — while keeping secrets out of your logs.
- Enable Transaction Search (once). In the console: CloudWatch → Settings → Account → X-Ray traces → Transaction Search → Enable, set the index percentage to 1%. (Or run the three CLI calls:
aws logs put-resource-policy ...,aws xray update-trace-segment-destination --destination CloudWatchLogs,aws xray update-indexing-rule ....) Note the time — spans take about 10 minutes to become searchable. - Deploy and invoke a Runtime agent.
agentcore deploy, thenagentcore invoke '{"prompt":"..."}' --session-id <a-33+char-id>three times with the SAME session id, then once more with a DIFFERENT id. - Read the dashboard. Open CloudWatch →
#gen-ai-observability→ Bedrock AgentCore tab. In the Sessions view, confirm your three same-id invocations group into one session and the fourth is separate. Open the Trace view on one invocation and find theInvokeAgentRuntimeroot span, itssession.id/latency_msattributes, and the child LLM/tool spans. - Locate the raw data. Find the standard log group under
/aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint>/..., confirm spans under/aws/spans/default, and find a metric in thebedrock-agentcorenamespace. - Wire a non-Runtime resource. For a Gateway (or Memory) in your account, call
put_delivery_source(logTypeTRACES),put_delivery_destination(typeXRAY), andcreate_delivery. For Memory, also enable Tracing on the resource. Confirm a new/aws/vendedlogs/bedrock-agentcore/...group (or X-Ray spans) appears — and confirm these do NOT show in the GenAI dashboard, only in plain CloudWatch. - Prove correlation. Make your agent call the downstream resource forwarding the trace headers (
traceparent/X-Amzn-Trace-Id/baggage). In the Trace view, verify the downstream span joins the same trace id. - Close the leak. Add a redaction step in your
@app.entrypointthat strips API-key/SSN/bearer-token patterns before anything is processed or logged. Send a prompt containing a fakesk-...token and confirm[REDACTED](not the token) appears inAPPLICATION_LOGS. - Reflect. Write three sentences: which step would have left you with an empty dashboard if skipped, why Memory needed an extra toggle Runtime didn't, and one place a real secret could still slip into logs despite your redaction.
Key takeaways
- 1Step 1 is non-negotiable: enable CloudWatch Transaction Search once at the account level (console, or `aws logs put-resource-policy` + `aws xray update-trace-segment-destination --destination CloudWatchLogs` + `update-indexing-rule`). Spans aren't searchable until it's on, and it takes ~10 minutes; the first 1% of indexed spans is free.
- 2Runtime-hosted agents are auto-instrumented on `agentcore deploy` and auto-create their log group — no extra setup. Pass `runtimeSessionId` (the `--session-id` flag) to drive the Sessions view.
- 3Memory, Gateway, Built-in Tools, and Identity do NOT get a log group for free — configure vended-log delivery with `put_delivery_source` / `put_delivery_destination` / `create_delivery` (logType APPLICATION_LOGS or TRACES; destination CWL/XRAY/S3/Firehose). Memory spans additionally require enabling Tracing on the resource.
- 4View data in the GenAI Observability dashboard: CloudWatch → `#gen-ai-observability` → Bedrock AgentCore tab → Agents / Sessions / Trace views. Only Runtime (and Policy, under the Gateway tab) appears there; Memory/Gateway/Tools are in plain CloudWatch only.
- 5Raw locations: standard logs `/aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint>/...`, spans `/aws/spans/default` via Transaction Search, metrics namespace `bedrock-agentcore`, non-Runtime vended logs `/aws/vendedlogs/bedrock-agentcore/{resource-type}/APPLICATION_LOGS/{resource-id}`.
- 6Correlate across the stack with the session id (`X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`, or OTEL baggage `session.id` outside Runtime) and trace headers (`X-Amzn-Trace-Id`, `traceparent`/`tracestate`, `baggage`, `mcp-session-id`). Forward them downstream and spans chain into one trace.
- 7`APPLICATION_LOGS` capture full `request_payload`/`response_payload` — a PII/secret leak surface. Redact at the entrypoint before emission and pair with Bedrock Guardrails PII filters.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.You deployed a Runtime agent, invoked it several times, but the Trace view in the GenAI dashboard is completely empty. Metrics show up fine. What's the most likely cause?
2.Your Gateway shows metrics in CloudWatch but produces no spans or application logs anywhere. Runtime agents in the same account trace perfectly. What do you need to do for the Gateway?
3.Your agent runs OUTSIDE AgentCore Runtime (on EKS) and you want its spans to be grouped by conversation in the Sessions view. How do you set the session id?
4.During a security review, a colleague finds customer API keys sitting in CloudWatch Logs from your agent. Where did they come from, and what's the correct fix?
Go deeper
Hand-picked sources to keep learning
The conceptual entry point: telemetry model, what's emitted per resource, and how it maps to CloudWatch.
The setup reference for enabling Transaction Search and configuring delivery for non-Runtime resources.
The GenAI dashboard views and raw log-group/metric locations — confirm the VOLATILE OTEL log-group suffix here.
What's New post for the #gen-ai-observability dashboard expanding beyond Runtime to Tools/Gateways/Memory/Identity (GA Oct 13 2025 — VOLATILE).
AWS deep-dive blog covering the end-to-end setup and viewing workflow with examples.
Runnable examples, including observability/instrumentation patterns you can adapt for the lab.