Claude
Skills
Sign in
Back

dt-obs-frontends

Included with Lifetime
$97 forever

Real User Monitoring (RUM), Web Vitals, user sessions, mobile crashes, page performance, user interactions, and frontend errors. Query web and mobile frontend telemetry.

Web Dev

What this skill does


# Frontend Observability Skill

Monitor web and mobile frontends using Real User Monitoring (RUM) with DQL queries.
This skill targets the new RUM experience only; do not use classic RUM data.

## Overview

This skill helps you:
- Monitor Core Web Vitals and frontend performance
- Track user sessions, engagement, and behavior
- Analyze errors and correlate with backend traces
- Optimize mobile app startup and stability
- Diagnose performance issues with detailed timing analysis

**Data Sources:**
- **Metrics**: `timeseries` with `dt.frontend.*` (trends, alerting)
- **Events**: `fetch user.events` (individual page views, requests, clicks, errors)
- **Sessions**: `fetch user.sessions` (session-level aggregates: duration, bounce, counts)


## Quick Reference

### Common Metrics
- `dt.frontend.user_action.count` - User action volume
- `dt.frontend.user_action.duration` - User action duration
- `dt.frontend.request.count` - Request volume
- `dt.frontend.request.duration` - Request latency (ms)
- `dt.frontend.error.count` - Error counts
- `dt.frontend.session.active.estimated_count` - Active sessions
- `dt.frontend.user.active.estimated_count` - Unique users
- `dt.frontend.web.page.cumulative_layout_shift` - CLS metric
- `dt.frontend.web.navigation.dom_interactive` - DOM interactive time
- `dt.frontend.web.page.first_input_delay` - FID metric (legacy; prefer INP)
- `dt.frontend.web.page.largest_contentful_paint` - LCP metric
- `dt.frontend.web.page.interaction_to_next_paint` - INP metric
- `dt.frontend.web.navigation.load_event_end` - Load event end
- `dt.frontend.web.navigation.time_to_first_byte` - Time to first byte

### Common Filters
- `frontend.name` - Filter by frontend name (e.g. `my-frontend`)
- `dt.rum.user_type` - Exclude synthetic monitoring
- `geo.country.iso_code` - Geographic filtering
- `device.type` - Mobile, desktop, tablet
- `browser.name` - Browser filtering

### Common Timeseries Dimensions
Use these for `dt.frontend.*` timeseries splits and breakdowns:
- `frontend.name` - Frontend name
- `geo.country.iso_code`
- `device.type`
- `browser.name`
- `os.name`
- `user_type` - `real_user`, `synthetic`, `robot`

```dql
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true
| summarize page_views = count(), by: {frontend.name}
| sort page_views desc
```

### Event Characteristics
- `characteristics.has_page_summary` - Page views (web)
- `characteristics.has_view_summary` - Views (mobile)
- `characteristics.has_navigation` - Navigation events
- `characteristics.has_user_interaction` - Clicks, forms, etc.
- `characteristics.has_request` - Network request events
- `characteristics.has_error` - Error events
- `characteristics.has_crash` - Mobile crashes
- `characteristics.has_long_task` - Long JavaScript tasks
- `characteristics.has_csp_violation` - CSP violations

Full event model: https://docs.dynatrace.com/docs/semantic-dictionary/model/rum/user-events

### Session Data (`user.sessions`)

`user.sessions` contains session-level aggregates produced by the session aggregation service from `user.events`. **Field names differ from `user.events`** — sessions use underscores where events use dots.

**Session identity and context:**
- `dt.rum.session.id` — Session ID (NOT `dt.rum.session_id`)
- `dt.rum.instance.id` — Instance ID
- `frontend.name` - array of frontends involved in session
- `dt.rum.application.type` — `web` or `mobile`
- `dt.rum.user_type` — `real_user`, `synthetic`, or `robot`

**Session aggregates (underscore naming — NOT dot):**

| Field | Description | ⚠️ NOT this |
|-------|-------------|-------------|
| `navigation_count` | Number of navigations | ~~`navigation.count`~~ |
| `user_interaction_count` | Clicks, form submissions | ~~`user_interaction.count`~~ |
| `user_action_count` | User actions | ~~`user_action.count`~~ |
| `request_count` | XHR/fetch requests | ~~`request.count`~~ |
| `event_count` | Total events in session | ~~`event.count`~~ |
| `page_summary_count` | Page views (web) | ~~`page_summary.count`~~ |
| `view_summary_count` | Views (mobile/SPA) | ~~`view_summary.count`~~ |

**Error fields (dot naming — same as events):**
- `error.count`, `error.exception_count`, `error.http_4xx_count`, `error.http_5xx_count`
- `error.anr_count`, `error.csp_violation_count`, `error.has_crash`

**Session lifecycle:**
- `start_time`, `end_time`, `duration` (nanoseconds)
- `end_reason` — `timeout`, `synthetic_execution_finished`, etc.
- `characteristics.is_bounce` — Boolean bounce flag
- `characteristics.has_replay` — Session replay available

**User identity:**
- `dt.rum.user_tag` — User identifier (typically email, username or customerId), set via `dtrum.identifyUser()` API call in the instrumented frontend. **Not always populated** — only present when the frontend explicitly calls `identifyUser()`.
- When `dt.rum.user_tag` is empty, `dt.rum.instance.id` is often the only user differentiator. The value is a random ID assigned by the RUM agent on the client side, so it is not personally identifiable but can be used to distinguish unique users when `user_tag` is not set. On web this is based on a persistent cookie, so it can be deleted by the user.
- The user tag is a **session-level field** — query it from `user.sessions`, not `user.events` (where it may be empty even if the session has one).

**Client/device context:**
- `browser.name`, `browser.version`, `device.type`, `os.name`
- `geo.country.iso_code`, `client.ip`, `client.isp`

**Synthetic-only fields:**
- `dt.entity.synthetic_test`, `dt.entity.synthetic_location`, `dt.entity.synthetic_test_step`

**Time window behavior:**
- `fetch user.sessions, from: X, to: Y` only returns sessions that **started** in `[X, Y]` — NOT sessions that were merely active during that window.
- Sessions can last 8h+ (the aggregation service waits 30+ minutes of inactivity before closing a session).
- To find all sessions active during a time window, extend the lookback by at least 8 hours: e.g., to cover events from the last 24h, query `fetch user.sessions, from: now() - 32h`.
- This matters for correlation queries (e.g., matching `user.events` to `user.sessions` by session ID) — a narrow `user.sessions` window will miss long-running sessions and produce false "orphans."

**Session creation delay:**
- The session aggregation service waits for ~30+ minutes of inactivity before closing a session and writing the `user.sessions` record.
- This means **recent events (last ~1 hour) will not yet have a matching `user.sessions` entry** — this is normal, not a data gap.
- When correlating `user.events` with `user.sessions`, exclude recent data (e.g., use `to: now() - 1h`) to avoid counting in-progress sessions as orphans.

**Zombie sessions (events without a `user.sessions` record):**
- Not every `dt.rum.session.id` in `user.events` will have a corresponding `user.sessions` record. The session aggregation service intentionally skips **zombie sessions** — sessions with no real user activity (zero navigations and zero user interactions).
- Zombie sessions contain only background, machine-driven activity (e.g., automatic XHR requests, heartbeats) with no page views or clicks. Serializing them would add no value to users.
- When correlating `user.events` with `user.sessions`, expect a large number of unmatched session IDs. This is **by design**, not a data gap. Filter to sessions with activity before diagnosing orphans:
  ```dql
  fetch user.events, from: now() - 2h, to: now() - 1h
  | filter isNotNull(dt.rum.session.id)
  | summarize navs = countIf(characteristics.has_navigation == true),
      interactions = countIf(characteristics.has_user_interaction == true),
      by: {dt.rum.session.id}
  | filter navs > 0 or interactions > 0
  ```

**Example — bounce rate and session quality:**
```dql
fetch user.sessions, from: now() - 24h
| filter dt.rum.user_type == "real_user"
| summarize
    total_sessions = count(),
    bounces = countIf(characteristics.is_bounce == true),
    zero_activi
Files: 25
Size: 140.6 KB
Complexity: 70/100
Category: Web Dev

Related in Web Dev