BLOG
Datadog/Browser Agent: Observability for Hosted Chromium
Learn how a Datadog/browser agent setup works for AI browser automation, what RUM can and cannot see, and how to instrument hosted Chromium sessions.

A Datadog/browser agent is two different things depending on who is asking. For frontend teams, it means the Datadog Browser SDK (RUM) that runs inside a page and reports Core Web Vitals, errors, and user actions. For teams running AI browser agents, it means something harder: getting telemetry out of a Chromium session that nobody is watching, on infrastructure you don't own. This post covers both, explains where they overlap, and shows how to instrument a hosted Chromium runtime so your agent's failures are visible in Datadog instead of buried in a log file.
If you are evaluating a runtime for this, the short version: RUM inside the page tells you what the site did; CDP-level telemetry tells you what your agent did. You need both, and they arrive through different pipelines.
What "Datadog/browser agent" actually refers to
The phrase gets used for three distinct things:
- Datadog RUM Browser SDK. A JavaScript library injected into a web page. It captures page views, resources, errors, long tasks, and user interactions, then ships them to Datadog intake. It only sees what happens inside that page's JavaScript context.
- A Datadog Agent running on a host. The
datadog-agentdaemon collects system metrics and logs from a machine. If you self-host Chromium, this is how you get CPU, memory, and container metrics into Datadog. - An AI browser agent whose telemetry you want in Datadog. This is the case most readers of this site care about: a Playwright, Puppeteer, or browser-use agent driving a remote Chromium session, and you want traces, logs, and session metadata in Datadog.
The third case is where the real engineering work lives, because you control neither the page nor, usually, the machine. A hosted runtime like Remote Browser gives you CDP access and a live viewer, but it does not automatically emit Datadog events. You have to design that pipeline.
Why observability breaks when the browser moves to the cloud
Locally, you debug an agent by watching it. In production, sessions run in parallel, on ephemeral hosts, often triggered by a queue. Three things go wrong:
- The browser is not on your machine. A host-level Datadog Agent cannot see a container you don't run. You need application-level instrumentation that travels with the session.
- The page is third-party. You cannot add the RUM SDK to a site you don't own. Any in-page telemetry has to come from the site's own instrumentation or from what you can observe over CDP.
- The interesting failures are agent-side. A selector that timed out, a navigation that hit a CAPTCHA, a step that retried four times — none of that appears in page-level RUM. It lives in your agent's control loop.
So the instrumentation strategy splits cleanly: page-level signals from RUM where available, agent-level signals from your own code and CDP events.
The two telemetry planes
| Plane | Source | What it captures | Where it goes |
|---|---|---|---|
| Page-level (RUM) | Datadog Browser SDK inside the page | Page views, JS errors, resource timing, Core Web Vitals, user actions | Datadog RUM intake via browser-intake-* endpoints |
| Agent-level | Your agent code, CDP events, runtime metadata | Step traces, selector failures, navigation timing, session ID, retries | Datadog APM/logs via your own exporter |
| Host-level | Datadog Agent on the machine | CPU, memory, disk, container stats | Datadog Agent intake |
On a hosted runtime you typically only control the middle row. That is fine — it is also the row that answers "why did this task fail." Host-level metrics matter when you self-host; if you use a managed runtime, ask what metrics the provider exposes and whether they are per-session or aggregate.
Instrumenting the agent loop
The most useful thing you can emit is a trace per task, with a span per browser step. Use the Datadog tracer in your agent process and attach the session ID as a tag so you can pivot from a failed trace to the live viewer.
import { chromium, type Browser, type Page } from "playwright";
import tracer from "dd-trace";
tracer.init({ service: "browser-agent", env: process.env.DD_ENV });
async function runTask(taskId: string, cdpUrl: string) {
return tracer.trace("browser.task", { resource: taskId }, async (span) => {
span?.setTag("task.id", taskId);
const browser: Browser = await chromium.connectOverCDP(cdpUrl);
const context = browser.contexts()[0] ?? (await browser.newContext());
const page: Page = await context.newPage();
// Tag the session so traces line up with the runtime's live viewer.
span?.setTag("browser.session_id", cdpUrl.split("/").pop());
page.on("console", (msg) => {
if (msg.type() === "error") {
span?.setTag("browser.console_errors", true);
}
});
await tracer.trace("browser.navigate", async (nav) => {
const start = Date.now();
await page.goto("https://example.com/login", { waitUntil: "domcontentloaded" });
nav?.setTag("nav.duration_ms", Date.now() - start);
});
await tracer.trace("browser.step", { resource: "fill-login" }, async (step) => {
try {
await page.fill("#email", process.env.AGENT_EMAIL!);
await page.fill("#password", process.env.AGENT_PASSWORD!);
await page.click("button[type=submit]");
} catch (err) {
step?.setTag("error.type", (err as Error).name);
step?.setTag("error.message", (err as Error).message);
throw err;
}
});
await browser.close();
return page.url();
});
}A few details that matter in practice:
- Tag the session ID. Without it, a trace is just a timestamp. With it, an on-call engineer can open the runtime's viewer and replay what the agent saw.
- Record navigation duration separately. Slow navigations and slow selectors are different problems with different fixes.
- Do not log credentials. The example reads from env vars; keep it that way. Datadog log scrubbing is a backstop, not a strategy.
- Close the browser in a `finally`. Leaked sessions are the most common cause of surprise spend on metered runtimes.
For a broader treatment of how hosted sessions behave under load, see Remote browsers for AI agents.
What CDP gives you that RUM cannot
The Chrome DevTools Protocol exposes events the page never surfaces to JavaScript. The ones worth forwarding to Datadog:
Network.requestWillBeSent/Network.responseReceived— status codes, redirect chains, and timing for every request, including ones blocked before the page's JS runs.Runtime.exceptionThrown— uncaught exceptions with stack traces, even when the page's own error handler swallows them.Log.entryAdded— browser-level log entries (CSP violations, mixed content, deprecations).Page.frameNavigated— the actual navigation graph, including redirects your agent did not initiate.
You can subscribe to these over the same CDP connection your agent already uses. The Chrome DevTools Protocol documentation lists the full event surface; the four above cover most production debugging needs.
The trade-off: CDP events are high-volume. Forwarding every network event to Datadog will cost more than the browser sessions. Sample or aggregate — counts of 4xx/5xx per session, p95 navigation time, exception counts — rather than shipping raw events.
When RUM is the right tool
If you own the site your agent is testing or scraping, RUM is genuinely useful. It gives you real-user-style metrics for the pages your agent loads, and it correlates with your production frontend monitoring. Two caveats:
- Consent and sampling. RUM SDKs are designed for human users. Running them inside automated sessions inflates your user counts unless you tag those sessions and filter them out. Most teams set a custom attribute like
agent.session=trueand exclude it from product dashboards. - It will not see the agent. RUM knows a page loaded slowly. It does not know your agent retried the click three times because the button was covered by a cookie banner.
For third-party sites, RUM is simply unavailable. That is the common case for AI agents, and it is why agent-level tracing is the primary signal.
Production criteria for a runtime you can observe
If you are choosing a hosted Chromium provider and observability matters, ask these questions before you commit:
- Is CDP access raw? If the provider proxies or restricts CDP, you lose the event stream above. You want a real CDP endpoint you can attach Playwright or Puppeteer to.
- Is there a live viewer? Traces tell you a step failed; a viewer tells you what the page looked like. Both should be linkable from the same session ID.
- Are sessions isolated? Shared browser state makes traces lie — a failure in session A can be caused by session B. Isolation is a correctness requirement, not a nice-to-have.
- Can you pin profiles? Persistent profiles let you separate "logged-in state broke" from "selector broke." See Remote browser online for how session lifecycle affects debugging.
- What does metering look like? Per-browser-hour billing means a leaked session is a real cost. Check pricing for current rates and confirm how idle sessions are handled.
None of these are exotic. They are the difference between a runtime you can operate and one you can only hope works.
A practical instrumentation checklist
Before you ship an agent to production, confirm you have:
- [ ] A trace per task, with a span per browser step and the session ID as a tag.
- [ ] Navigation duration and selector-failure counts as metrics, not just logs.
- [ ] CDP exception and network-error events sampled into Datadog.
- [ ] Log scrubbing configured for credentials and PII.
- [ ] A
finallyblock that closes the browser on every code path. - [ ] A dashboard that joins agent traces to runtime session IDs.
- [ ] Alerts on failure rate and on sessions exceeding an expected duration.
That last item catches the failure mode nobody instruments for: an agent that is not erroring, just stuck. A session that has been open for an hour when your p95 is four minutes is a bug, and it is also a bill.
Where this leaves you
The Datadog/browser agent question has a clean answer once you separate the planes. RUM is for pages you own and want to monitor like production frontend. The Datadog Agent is for hosts you run. Neither covers the case that matters most for AI automation — an agent driving a remote browser — so you instrument the agent loop yourself and forward CDP events selectively.
A hosted runtime makes that easier by giving you a stable CDP endpoint, session IDs you can correlate, and a viewer for the moments when a trace is not enough. It does not do the instrumentation for you, and any provider claiming otherwise is probably shipping a lot of noise to your Datadog bill. Start with the trace-per-task pattern above, add CDP events once you know which ones you actually read, and keep the session ID as the join key across every system. If you want to see how the runtime side works, the documentation covers CDP connection, profiles, and the live viewer, and remote control browser walks through driving sessions from outside your own machine.