
Dt Obs Frontends
- 1.4k installs
- 119 repo stars
- Updated July 29, 2026
- dynatrace/dynatrace-for-ai
dt-obs-frontends provides documented workflows for Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors,
About
The dt-obs-frontends skill real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors, and trace correlation. Query via `user.events`, `user.sessions`, and `dt.frontend.*` metrics. Does NOT cover synthetic monitoring (HTTP/browser/network checks) - that's a separate domain. # Frontend Observability (RUM) Monitor web, mobile, and hybrid frontends using Real User Monitoring with DQL. Targets the **new RUM experience only** - do not use classic RUM data. ## Data Model Three data sources, each for a different question: | Source | Use for | Granularity | |--------|---------|-------------| | `timeseries dt.frontend.*` | Trends, dashboards, alerting | Aggregated metric | | `fetch user.events` | Root cause, individual page views / requests / clicks / errors | Per-event | | `fetch user.sessions` | Bounce rate, session duration, session-level aggregates | Per-session | **Rule of thumb**: start with metrics for the shape of the problem, drill into events for the why. Use sessions when the question is about user journeys, not individual interactions.
- `frontend.name` - frontend identifier (e.g. `my-frontend`)
- `dt.rum.user_type` - `real_user`, `synthetic`, `robot`
- `dt.rum.application.type` - `web` or `mobile`
- `device.type`, `browser.name`, `os.name`, `geo.country.iso_code`
- **LCP**: Good < 2.5 s | Poor > 4.0 s
Dt Obs Frontends by the numbers
- 1,425 all-time installs (skills.sh)
- +82 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #155 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
dt-obs-frontends capabilities & compatibility
- Capabilities
- `frontend.name` frontend identifier (e.g. `my · `dt.rum.user_type` `real_user`, `synthetic`, ` · `dt.rum.application.type` `web` or `mobile` · `device.type`, `browser.name`, `os.name`, `geo.c · **lcp**: good < 2.5 s | poor > 4.0 s
- Use cases
- documentation
What dt-obs-frontends says it does
# Frontend Observability (RUM) Monitor web, mobile, and hybrid frontends using Real User Monitoring with DQL.
npx skills add https://github.com/dynatrace/dynatrace-for-ai --skill dt-obs-frontendsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.4k |
|---|---|
| repo stars | ★ 119 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 29, 2026 |
| Repository | dynatrace/dynatrace-for-ai ↗ |
How do I use dt-obs-frontends for the task described in its SKILL.md triggers?
Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors, and trace correlation. Query via `use.
Who is it for?
Teams invoking dt-obs-frontends when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors, and trace correlation. Query via `user.events`, `user.sess
What you get
Step-by-step guidance grounded in dt-obs-frontends documentation and reference files.
- DQL query templates
- Regional latency breakdowns
- Regression analysis views
By the numbers
- Example DQL windows use a 4-hour lookback (now() - 4h)
- Groups metrics by frontend.name and geo.country.iso_code
Files
Frontend Observability (RUM)
Monitor web, mobile, and hybrid frontends using Real User Monitoring with DQL. Targets the new RUM experience only — do not use classic RUM data.
Data Model
Three data sources, each for a different question:
| Source | Use for | Granularity |
|---|---|---|
timeseries dt.frontend.* | Trends, dashboards, alerting | Aggregated metric |
fetch user.events | Root cause, individual page views / requests / clicks / errors | Per-event |
fetch user.sessions | Bounce rate, session duration, session-level aggregates | Per-session |
Rule of thumb: start with metrics for the shape of the problem, drill into events for the why. Use sessions when the question is about user journeys, not individual interactions.
Full event model: https://docs.dynatrace.com/docs/semantic-dictionary/model/rum/user-events
Common Filters
frontend.name— frontend identifier (e.g.my-frontend)dt.rum.user_type—real_user,synthetic,robotdt.rum.application.type—webormobiledevice.type,browser.name,os.name,geo.country.iso_code
Workflows
Each workflow maps to one or more references. Load the reference when you start the workflow, not upfront.
| Workflow | Reference |
|---|---|
| Core Web Vitals (LCP, INP, CLS) | references/web-vitals.md |
| Session, bounce, engagement analysis | references/user-sessions.md |
| User actions — interaction lifecycle, completion reasons, timeouts | references/user-actions.md |
| Errors, exceptions, trace correlation | references/error-tracking.md |
| CSP violations — security policy enforcement and blocked resources | references/csp-violations.md |
| Mobile app starts, crashes, ANR, native signals | references/mobile-monitoring.md |
| Request latency, long tasks, JS profiling, geo performance | references/web-performance-analysis.md |
| Visibility changes — tab switching, background time, engagement quality | references/visibility-changes.md |
| Slow page load — backend vs render vs network vs JS triage | references/slow-page-load-playbook.md |
| Diagnosing zero results, anomalies, ambiguous data | references/troubleshooting.md |
Performance Thresholds (quick reference)
- LCP: Good < 2.5 s | Poor > 4.0 s
- INP: Good < 200 ms | Poor > 500 ms
- CLS: Good < 0.1 | Poor > 0.25
- Mobile cold start: Good < 3 s | Poor > 5 s
- Long tasks: > 50 ms problematic, > 250 ms severe
When to Use
Use this skill for real-user web, mobile, or hybrid frontend telemetry — Core Web Vitals, sessions, clicks, errors, crashes, request latency from the browser/app, and frontend↔backend trace correlation.
Use a different skill for:
- Synthetic monitors / availability checks →
dt-obs-synthetic - Backend services, traces, spans →
dt-obs-services,dt-obs-tracing - Infrastructure, hosts →
dt-obs-hosts - Logs →
dt-obs-logs - Problems and incidents →
dt-obs-problems
Content Security Policy (CSP) Violations
Monitor and analyze Content Security Policy violations for security and compliance.
Data Source: fetch user.events with characteristics.has_csp_violation
Key Fields:
csp.violated_directive- Original policy directive violatedcsp.effective_directive- Actual enforced directivecsp.blocked_uri.*- Blocked resource detailscsp.document_uri.*- Page where violation occurredcsp.disposition-enforce(blocked) orreport(logged only)
Contents
- All CSP Violations
- Violations by Blocked Resource
- Third-Party Violations
- Inline Script/Style Violations
- Violations by Page
- Enforced vs Report-Only
- CSP Violation Trends
- Directive-Specific Analysis
- Violation Details
- User Impact
All CSP Violations
Query all CSP violations:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| summarize
violation_count = count(),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, csp.violated_directive}
| sort violation_count desc
Use Case: Overview of CSP violation patterns.
Violations by Blocked Resource
Identify blocked resources:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| summarize
violation_count = count(),
by: {frontend.name, csp.blocked_uri.full, csp.effective_directive}
| sort violation_count desc
| limit 30
Use Case: Find resources to allow or fix.
Third-Party Violations
Analyze third-party resource blocks:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| filter csp.blocked_uri.provider == "third_party"
| summarize
violation_count = count(),
by: {frontend.name, csp.blocked_uri.domain, csp.effective_directive}
| sort violation_count desc
| limit 20
Use Case: Review third-party integrations causing violations.
Inline Script/Style Violations
Find inline content blocks:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| filter csp.blocked_uri.full == "inline"
| summarize
violation_count = count(),
by: {frontend.name, csp.effective_directive, csp.document_uri.path}
| sort violation_count desc
| limit 20
Use Case: Identify pages needing CSP nonces or hashes.
Violations by Page
Map violations to pages:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| summarize
violation_count = count(),
unique_blocked = countDistinct(csp.blocked_uri.full),
by: {frontend.name, csp.document_uri.path}
| sort violation_count desc
| limit 20
Use Case: Prioritize pages for CSP policy updates.
Enforced vs Report-Only
Compare enforcement modes:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| summarize
enforced_count = countIf(csp.disposition == "enforce"),
reported_count = countIf(csp.disposition == "report"),
by: {frontend.name}
Use Case: Plan CSP enforcement rollout.
CSP Violation Trends
Track violations over time:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| summarize
violation_count = count(),
by: {frontend.name, time_bucket = bin(start_time, 1d)}
| sort time_bucket asc
Use Case: Monitor CSP policy effectiveness.
Directive-Specific Analysis
Deep dive into specific directives:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| filter csp.effective_directive == "script-src"
| summarize
violation_count = count(),
by: {frontend.name, csp.blocked_uri.domain, csp.document_uri.path}
| sort violation_count desc
| limit 20
Use Case: Focus on high-risk directive violations.
Violation Details
Get detailed violation context:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| fields
start_time,
csp.violated_directive,
csp.effective_directive,
csp.blocked_uri.full,
csp.document_uri.path,
csp.line_number,
csp.column_number,
csp.sample
| sort start_time desc
| limit 50
Use Case: Debug specific CSP issues with code context.
User Impact
Measure user impact of violations:
fetch user.events, from: now() - 2h
| filter characteristics.has_csp_violation == true
| filter csp.disposition == "enforce"
| summarize
affected_users = countDistinct(dt.rum.instance.id, precision: 9),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, csp.effective_directive}
| sort affected_users desc
Use Case: Prioritize fixes by user impact.
Frontend Error Tracking
Comprehensive error analysis using both event-based queries (detailed diagnostics) and metric-based queries (trends and alerting).
Data Sources:
- Metric:
dt.frontend.error.count- Aggregated error counts for trends and alerting - Event:
fetch user.eventswitherror.type- Detailed error diagnostics with messages
Contents
- Metric-Based: Error Rate Monitoring
- Event-Based: JavaScript Exceptions
- Event-Based: Request Errors
- Metric-Based: Error Spike Detection
- Metric-Based: Browser-Specific Errors
- Event-Based: Errors by Device Type
- Metric-Based: Geographic Error Patterns
- Pages with Errors
- Users with Errors
- Frontend-Backend Trace Correlation
Metric-Based: Error Rate Monitoring
Track error rates across applications:
timeseries error_count = sum(dt.frontend.error.count, scalar: true),
request_count = sum(dt.frontend.request.count, scalar: true),
by: {frontend.name},
from: now() - 2h
| fieldsAdd
error_rate_percent = (error_count / request_count) * 100
| filter error_rate_percent > 1
| sort error_rate_percent desc
Use Case: Monitor application error rates and create alerts for threshold violations.
Event-Based: JavaScript Exceptions
Analyze specific JavaScript errors:
fetch user.events, from: now() - 2h
| filter error.type == "exception"
| summarize
exception_count = count(),
affected_users = countDistinct(dt.rum.instance.id, precision: 9),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, exception.message, exception.type}
| sort exception_count desc
| limit 20
Use Case: Debug specific JavaScript errors with detailed exception messages.
Event-Based: Request Errors
Analyze failed API requests:
fetch user.events, from: now() - 2h
| filter error.type == "request"
| summarize
error_count = count(),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, error.display_name}
| sort error_count desc
Use Case: Identify failing backend API calls from frontend applications.
Metric-Based: Error Spike Detection
Detect sudden increases in error rates:
timeseries {
error_count = sum(dt.frontend.error.count),
request_count = sum(dt.frontend.request.count)
},
by: {frontend.name},
from: now() - 24h,
interval: 1h
| fieldsAdd
error_rate_percent = (error_count[] / request_count[]) * 100
| join [
timeseries {
prev_error_count = sum(dt.frontend.error.count),
prev_request_count = sum(dt.frontend.request.count)
},
by: {frontend.name},
from: now() - 24h,
interval: 1h,
shift: 1h
| fieldsAdd prev_error_rate = (prev_error_count[] / prev_request_count[]) * 100
], on: { frontend.name }, fields: { prev_error_rate }
| fieldsAdd
error_rate_change = coalesce((error_rate_percent[] - prev_error_rate[]) / (prev_error_rate[]) * 100, 0)
| filter arrayAvg(error_rate_change) > 50
| sort error_rate_change desc
Use Case: Alert on error spikes indicating deployment issues.
Metric-Based: Browser-Specific Errors
Identify browser compatibility issues:
timeseries error_count = sum(dt.frontend.error.count, scalar: true),
request_count = sum(dt.frontend.request.count, scalar: true),
by: {frontend.name, browser.name},
from: now() - 4h
| fieldsAdd
error_rate_percent = (error_count / request_count) * 100
| filter request_count > 100
| sort error_rate_percent desc
Use Case: Prioritize browser-specific bug fixes based on error rates.
Event-Based: Errors by Device Type
Analyze errors by device:
fetch user.events, from: now() - 2h
| filter error.type == "exception"
| summarize
error_count = count(),
affected_users = countDistinct(dt.rum.instance.id, precision: 9),
by: {device.type}
| sort error_count desc
Use Case: Optimize error handling for specific device types.
Metric-Based: Geographic Error Patterns
Identify region-specific error issues:
timeseries error_count = sum(dt.frontend.error.count, scalar: true),
request_count = sum(dt.frontend.request.count, scalar: true),
by: {frontend.name, geo.country.iso_code},
from: now() - 6h
| fieldsAdd
error_rate_percent = (error_count / request_count) * 100
| filter request_count > 50 and error_rate_percent > 2
| sort error_rate_percent desc
Use Case: Detect regional infrastructure or connectivity issues.
Pages with Errors
Find error-prone pages:
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true
| filter error.exception_count > 0 or error.http_4xx_count > 0 or error.http_5xx_count > 0
| summarize
page_views = count(),
total_exceptions = sum(error.exception_count),
total_4xx = sum(error.http_4xx_count),
total_5xx = sum(error.http_5xx_count),
by: {frontend.name, page.url.path}
| sort total_exceptions desc
| limit 20
Use Case: Prioritize pages for error fixes.
Users with Errors
Find sessions with error patterns:
fetch user.events, from: now() - 2h
| filter characteristics.has_error == true
| summarize
error_count = count(),
error_types = collectDistinct(error.type),
by: {dt.rum.session.id, dt.rum.user_tag}
| filter error_count > 3
| sort error_count desc
Use Case: Identify frustrated users for follow-up.
Frontend-Backend Trace Correlation
Correlate frontend errors with backend traces for end-to-end diagnostics.
Key Fields:
trace.id- W3C trace ID linking frontend to backendspan.id- Frontend span IDrequest.trace_context_hint- Whether trace headers were setrequest.server_timing_hint- Whether backend trace info was received
Trace Context Coverage
Analyze trace correlation success rate:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
total_requests = count(),
traced_requests = countIf(isNotNull(trace.id)),
by: {request.trace_context_hint, request.server_timing_hint}
| fieldsAdd trace_rate = 100.0 * traced_requests / total_requests
| sort total_requests descUse Case: Identify gaps in end-to-end tracing coverage.
Slow Requests with Backend Traces
Find slow frontend requests and their backend traces:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter duration > 2s
| filter isNotNull(trace.id)
| fields
start_time,
url.path,
duration,
trace.id,
span.id,
http.response.status_code,
request.server_timing_hint
| sort duration desc
| limit 50Use Case: Get trace IDs for investigating slow requests in backend.
Backend Service Impact on Frontend
Join frontend requests with backend spans:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter isNotNull(trace.id)
| filter duration > 1s
| fields trace.id, frontend_duration = duration, url.path
| join [
fetch spans, from: now() - 2h
| summarize
backend_duration = sum(duration),
span_count = count(),
services = collectDistinct(dt.smartscape.service),
by: {trace.id}
], on:trace.id, fields:{backend_duration, span_count, services}
| fieldsAdd
backend_ratio = 100.0 * backend_duration / frontend_duration
| sort frontend_duration desc
| limit 30Use Case: Determine if slowness is frontend or backend.
Failed Requests with Traces
Correlate frontend errors with backend traces:
fetch user.events, from: now() - 2h
| filter characteristics.has_failed_request == true
| filter isNotNull(trace.id)
| fields
start_time,
url.full,
http.response.status_code,
trace.id,
error.type,
error.message
| sort start_time desc
| limit 50Use Case: Debug failed requests using backend trace data.
Cross-Origin Tracing Gaps
Identify requests missing traces due to CORS:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter request.trace_context_hint == "cross_origin"
| summarize
request_count = count(),
by: {url.domain, url.provider}
| sort request_count desc
| limit 20Use Case: Identify third-party domains needing CORS trace headers.
Mobile Application Monitoring
Track mobile app performance, startup times, crash analytics, and mobile-specific metrics.
Contents
Mobile App Start Performance
Analyze mobile application startup performance across cold, warm, and hot starts.
Data Source: fetch user.events with characteristics.has_app_start
Key Fields:
app_start.type- Start type:cold,warm,hotduration- Total startup duration (nanoseconds)- Platform-specific phase timings in
app_start.android.*,app_start.ios.*,app_start.flutter.*
Performance Thresholds:
- Cold start: Good < 3s | Acceptable < 5s | Poor > 5s
- Warm start: Good < 1.5s | Acceptable < 2s | Poor > 2s
- Hot start: Good < 500ms | Acceptable < 1s | Poor > 1s
App Start Overview
Query all app starts:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| summarize
start_count = count(),
avg_duration = avg(duration),
p50_duration = percentile(duration, 50),
p90_duration = percentile(duration, 90),
by: {frontend.name, app_start.type}
| sort app_start.type asc
Use Case: Baseline startup performance by type.
Cold Start Analysis
Analyze initial app launches:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter app_start.type == "cold"
| summarize
cold_starts = count(),
p50_duration = percentile(duration, 50),
p75_duration = percentile(duration, 75),
p95_duration = percentile(duration, 95),
by: {frontend.name}
Use Case: Optimize initial app launch experience.
Startup by App Version
Track startup improvements across releases:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter app_start.type == "cold"
| summarize
start_count = count(),
p50_duration = percentile(duration, 50),
by: {frontend.name, app.version}
| filter start_count > 10
| sort app.version desc
Use Case: Validate startup optimizations in releases.
Startup by Device
Identify slow devices:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter app_start.type == "cold"
| summarize
start_count = count(),
avg_duration = avg(duration),
p90_duration = percentile(duration, 90),
by: {device.manufacturer, device.model, os.version}
| filter start_count > 20
| sort p90_duration desc
| limit 20
Use Case: Target optimization for popular slow devices.
Android Startup Phases
Analyze Android-specific phases:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter os.name == "Android"
| summarize
avg_app_oncreate = avg(app_start.android.application.on_create.end_time - app_start.android.application.on_create.start_time),
avg_activity_oncreate = avg(app_start.android.activity.on_create.end_time - app_start.android.activity.on_create.start_time),
avg_activity_onstart = avg(app_start.android.activity.on_start.end_time - app_start.android.activity.on_start.start_time),
by: {frontend.name, app_start.type}
Use Case: Identify Android lifecycle bottlenecks.
iOS Startup Phases
Analyze iOS-specific phases:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter os.name == "iOS"
| summarize
avg_pre_runtime = avg(app_start.ios.pre_runtime_init.end_time - app_start.ios.pre_runtime_init.start_time),
avg_runtime_init = avg(app_start.ios.runtime_init.end_time - app_start.ios.runtime_init.start_time),
avg_uikit_init = avg(app_start.ios.uikit_init.end_time - app_start.ios.uikit_init.start_time),
avg_frame_render = avg(app_start.ios.initial_frame_render.end_time - app_start.ios.initial_frame_render.start_time),
by: {frontend.name, app_start.type}
Use Case: Identify iOS initialization bottlenecks.
Startup Trends
Track startup performance over time:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter app_start.type == "cold"
| summarize
p75_duration = percentile(duration, 75),
by: {frontend.name, time_bucket = bin(start_time, 1d)}
| sort time_bucket asc
Use Case: Monitor startup regressions over time.
Hot Start Analysis
Analyze background-to-foreground transitions:
fetch user.events, from: now() - 2h
| filter characteristics.has_app_start == true
| filter app_start.type == "hot"
| summarize
hot_starts = count(),
p50_duration = percentile(duration, 50),
p90_duration = percentile(duration, 90),
by: {frontend.name}
Use Case: Optimize app resume experience.
Mobile App Crashes & ANR Analysis
Analyze crashes and Application Not Responding (ANR) events for mobile applications.
Data Source: fetch user.events with characteristics.has_crash or characteristics.has_anr
Key Fields:
exception.type- Exception class (e.g.,java.net.ConnectException)exception.message- Error descriptionexception.stack_trace- Full stack traceexception.crash_signal_name- Signal for native crashes (e.g.,SIGSEGV)error.is_fatal- Whether error caused app termination
All Crashes
Query all mobile crashes:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| summarize
crash_count = count(),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, exception.type, error.display_name}
| sort crash_count desc
| limit 20
Use Case: Prioritize crash fixes by frequency and user impact.
ANR Events
Query Application Not Responding events (Android):
fetch user.events, from: now() - 2h
| filter characteristics.has_anr == true
| summarize
anr_count = count(),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, exception.message}
| sort anr_count desc
Use Case: Identify blocking operations causing ANRs.
Crash Rate by App Version
Track crash rate across versions:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| summarize
crash_count = count(),
by: {frontend.name, app.version}
| sort app.version desc
Use Case: Detect version-specific regressions after releases.
Crashes by Device Model
Identify device-specific issues:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| summarize
crash_count = count(),
affected_users = countDistinct(dt.rum.instance.id, precision: 9),
by: {device.model, device.manufacturer, os.name, os.version}
| sort crash_count desc
| limit 20
Use Case: Prioritize device-specific bug fixes.
Stack Trace Analysis
Get detailed crash information:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| fields
start_time,
app.version,
exception.type,
exception.message,
exception.stack_trace,
os.name,
device.model
| sort start_time desc
| limit 50
Use Case: Debug specific crash with full context.
Native Crash Signals
Analyze native crashes by signal:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| filter isNotNull(exception.crash_signal_name)
| summarize
crash_count = count(),
by: {exception.crash_signal_name, exception.type}
| sort crash_count desc
Use Case: Identify memory issues (SIGSEGV), abort signals (SIGABRT).
Crash Trends Over Time
Track crash frequency:
fetch user.events, from: now() - 2h
| filter characteristics.has_crash == true
| summarize
crash_count = count(),
by: {frontend.name, time_bucket = bin(start_time, 1d)}
| sort time_bucket asc
Use Case: Correlate crash spikes with releases or events.
Fatal vs Non-Fatal Errors
Compare error severity:
fetch user.events, from: now() - 2h
| filter characteristics.has_error == true
| summarize
total = count(),
fatal_count = countIf(error.is_fatal == true),
by: {frontend.name}
| fieldsAdd non_fatal_count = total - fatal_count
Use Case: Balance crash-free rate vs overall error handling.
Mobile View Summaries
Query mobile view engagement:
fetch user.events, from: now() - 2h
| filter characteristics.has_view_summary == true
| summarize
view_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
avg_foreground_time = avg(view.foreground_time),
by: {frontend.name, view.name}
| sort view_count desc
| limit 30
Use Case: Analyze mobile screen engagement.
Slow Page Load Playbook
Start by segmenting the problem by page, browser, geo location, and dt.rum.user_type.
Heuristics:
- High TTFB -> slow backend
- High LCP with normal TTFB -> render bottleneck
- High CLS -> layout shifts (late-loading content, ads, fonts)
- Long tasks dominate -> JavaScript execution bottlenecks (heavy frameworks, large bundles)
Contents
- Backend latency (high TTFB)
- Heavy JavaScript execution (long tasks)
- Large JavaScript bundles
- Large resources
- Cache effectiveness
- Compression waste
- Network issues
- Third-party dependencies
Backend latency (high TTFB)
fetch user.events
| filter frontend.name == "my-frontend" and characteristics.has_request == true
| filter page.url.path == "/checkout"
| summarize avg_ttfb = avg(request.time_to_first_byte), avg_duration = avg(duration)If TTFB is high, analyze backend spans by correlating frontend events with backend traces using trace.id.
Heavy JavaScript execution (long tasks)
Long tasks by page:
fetch user.events, from: now() - 2h
| filter characteristics.has_long_task == true
| summarize
long_task_count = count(),
total_blocking_time = sum(duration),
by: {frontend.name, page.url.path}
| sort total_blocking_time desc
| limit 20Long tasks by script source:
fetch user.events, from: now() - 2h
| filter frontend.name == "my-frontend"
| filter characteristics.has_long_task == true
| summarize
long_task_count = count(),
total_blocking_time = sum(duration),
by: {long_task.attribution.container_src}
| sort total_blocking_time desc
| limit 20Large JavaScript bundles
fetch user.events
| filter frontend.name == "my-frontend"
| filter characteristics.has_request
| filter endsWith(url.full, ".js")
| summarize dls = max(performance.decoded_body_size), by: url.full
| sort dls desc
| limit 20Large resources
fetch user.events
| filter frontend.name == "my-frontend"
| filter characteristics.has_request
| summarize dls = max(performance.decoded_body_size), by: url.full
| sort dls desc
| limit 20Cache effectiveness
fetch user.events, from: now() - 2h
| filter frontend.name == "my-frontend"
| filter characteristics.has_request == true
| fieldsAdd cache_status = if(
performance.incomplete_reason == "local_cache" or performance.transfer_size == 0 and
(performance.encoded_body_size > 0 or performance.decoded_body_size > 0),
"cached",
else: if(performance.transfer_size > 0, "network", else: "uncached")
)
| summarize
request_count = count(),
avg_duration = avg(duration),
by: {url.domain, cache_status}Compression waste
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter isNotNull(performance.encoded_body_size) and isNotNull(performance.decoded_body_size)
| filter performance.encoded_body_size > 0
| fieldsAdd
expansion_ratio = performance.decoded_body_size / performance.encoded_body_size,
wasted_bytes = performance.decoded_body_size - performance.encoded_body_size
| summarize
requests = count(),
avg_expansion_ratio = avg(expansion_ratio),
total_wasted_bytes = sum(wasted_bytes),
by: {request.url.host, request.url.path}
| sort total_wasted_bytes desc
| limit 50Network issues
Compare by location and domain when TTFB is high but backend performance is good:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
request_count = count(),
avg_duration = avg(duration),
p75_duration = percentile(duration, 75),
p95_duration = percentile(duration, 95),
by: {geo.country.iso_code, request.url.domain}
| sort p95_duration desc
| limit 50Analyze DNS time:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter isNotNull(performance.domain_lookup_start) and isNotNull(performance.domain_lookup_end)
| fieldsAdd dns_ms = performance.domain_lookup_end - performance.domain_lookup_start
| summarize
request_count = count(),
avg_dns_ms = avg(dns_ms),
p75_dns_ms = percentile(dns_ms, 75),
p95_dns_ms = percentile(dns_ms, 95),
by: {request.url.domain}
| sort p95_dns_ms desc
| limit 50Analyze by protocol (http/1.1, h2, h3):
fetch user.events
| filter characteristics.has_request
| summarize cnt = count(), by: {url.domain, performance.next_hop_protocol}
| sort cnt desc
| limit 50Third-party dependencies
Analyze request performance by domain:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
request_count = count(),
avg_duration = avg(duration),
p75_duration = percentile(duration, 75),
p95_duration = percentile(duration, 95),
by: {request.url.domain}
| sort p95_duration desc
| limit 50Troubleshooting
Contents
- Handling Zero Results
- Handling Anomalous Results
- Decision Tree: Ask vs. Investigate
- Common Investigation Steps
- Red Flags: When to Stop and Ask
Handling Zero Results
When queries return no data, follow this diagnostic workflow:
1. Validate Timeframe
- Check if timeframe is appropriate for the data type
- RUM data may have delay (1-2 minutes for recent events)
- Verify timeframe syntax:
-1h to now()or similar - Try expanding timeframe:
now()-24hfor initial exploration
2. Verify frontend Configuration
- Confirm frontend is instrumented and sending RUM data
- Check
frontend.namefilter is correct - Test without frontend filter to see if any RUM data exists
- Verify frontend name matches the environment
3. Check Data Availability
- Run basic query:
fetch user.events | limit 1 - If no events exist, RUM may not be configured
- Check if timeframe predates frontend deployment
- Verify user has access to the environment
4. Review Query Syntax
- Validate filters aren't too restrictive
- Check for typos in field names or metric names
- Test query incrementally: start simple, add filters gradually
- Verify characteristics filters match event types
When to Ask User for Clarification:
- No RUM data exists in environment → "Is RUM configured for this frontend?"
- Timeframe unclear → "What time period should I analyze?"
- Expected data missing → "Has this frontend sent data recently?"
Handling Anomalous Results
When query results seem unexpected or suspicious:
Unexpected High Values:
- Metric spikes: Verify interval aggregation (avg vs. max vs. sum)
- Session counts: Check for bot traffic or synthetic monitoring
- Error rates: Confirm error definition matches expectations
- Performance degradation: Look for deployment or infrastructure changes
Unexpected Low Values:
- Missing sessions: Verify
dt.rum.user_typefilter isn't excluding real users - Low request counts: Check if frontend filter is too narrow
- Few errors: Confirm error characteristics filter is correct
- Missing mobile data: Verify platform-specific fields exist
Inconsistent Data:
- Metrics vs. Events mismatch: Different aggregation methods are expected
- Geographic anomalies: Check timezone assumptions
- Device distribution skew: May reflect actual user base
- Version mismatches: Verify app version filtering logic
Decision Tree: Ask vs. Investigate
Query returns unexpected results
│
├─ Is this a zero-result scenario?
│ ├─ YES → Follow "Handling Zero Results" workflow
│ └─ NO → Continue
│
├─ Can I validate the result independently?
│ ├─ YES → Run validation query
│ │ ├─ Validation confirms result → Report findings
│ │ └─ Validation contradicts → Investigate further
│ └─ NO → Continue
│
├─ Is the anomaly clearly explained by data?
│ ├─ YES → Report with explanation
│ └─ NO → Continue
│
├─ Do I need domain knowledge to interpret?
│ ├─ YES → Ask user for context
│ │ Example: "The error rate is 15%. Is this expected for your frontend?"
│ └─ NO → Continue
│
└─ Is the issue ambiguous or requires clarification?
├─ YES → Ask specific question with data context
│ Example: "I see two frontends named 'web-app'. Which frontend name should I use?"
└─ NO → Investigate and report findings with caveatsCommon Investigation Steps
For Performance Issues: 1. Compare to baseline: Query same metric for previous week 2. Segment by dimension: Break down by device, browser, geography 3. Check for outliers: Use percentiles (p50, p95, p99) vs. averages 4. Correlate with deployments: Filter by app version or time windows
For Data Availability Issues: 1. Start broad: Query all RUM data without filters 2. Add filters incrementally: Isolate which filter eliminates data 3. Check related metrics: If events missing, try timeseries 4. Validate entity relationships: Confirm frontend-to-service links
For Unexpected Patterns: 1. Expand timeframe: Look for historical context 2. Cross-reference data sources: Compare events and metrics 3. Check sampling: Verify no sampling is affecting results 4. Consider external factors: Holidays, outages, traffic changes
Red Flags: When to Stop and Ask
Always ask the user when:
- No RUM data exists anywhere in the environment
- Multiple frontends match the user's description
- Results contradict user's stated expectations explicitly
- Data suggests monitoring is misconfigured
- Query requires business context (e.g., "acceptable error rate")
- Timeframe is ambiguous and affects interpretation significantly
Example clarifying questions:
- "I found two frontends named 'checkout'. Which one:
checkout-weborcheckout-mobile?" - "The query returns 0 results for the past hour. Should I expand the timeframe, or do you expect real-time data?"
- "The average LCP is 8 seconds, which exceeds the 4-second threshold. Is this frontend known to have performance issues?"
- "I see only synthetic traffic. Should I include
dt.rum.user_type=='REAL_USER'to focus on real users?"
User Actions
Analyze user actions that capture interaction lifecycles, resource loading, and DOM mutations.
Data Source: fetch user.events with characteristics.has_user_action == true
Key Fields:
user_action.instance_id- Unique user action IDuser_action.type- Action type:api,soft_navigation,xhruser_action.name- Action name (preferred;user_action.custom_nameis deprecated)user_action.complete_reason- How the action ended (for example:completed,timeout)user_action.mutation_count- DOM mutations during the actionuser_action.requests.count- Requests during the actionuser_action.requests.pending_request_count- Requests still pending at completionuser_action.resources.count- Resources loaded during the actionuser_action.resources.<initiator>.count- Resources by initiator type (for example:xmlhttprequest)interaction.name- Interaction type (for example:click)ui_element.name- Resolved UI element nameui_element.tag_name- UI element typecharacteristics.is_api_reported- True when reported via API
Contents
- User Action Overview
- Completion Reasons
- Action Type Distribution
- Actions by Interaction
- Resource-Heavy Actions
- Actions with Pending Requests
- DOM Mutation Analysis
- Timed-Out Actions
- Interrupted Actions
- User Action Web Vitals
- API-Reported Actions
User Action Overview
Query all user actions:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| summarize
action_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
avg_duration = avg(duration),
by: {frontend.name}Use Case: Baseline user action volume and duration.
Completion Reasons
Analyze how user actions end:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| summarize
action_count = count(),
by: {frontend.name, user_action.complete_reason}
| sort action_count descUse Case: Identify timeouts and interruptions.
Action Type Distribution
Break down user actions by type:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| summarize
action_count = count(),
avg_duration = avg(duration),
by: {frontend.name, user_action.type}
| sort action_count descUse Case: Compare API-reported, soft navigation, and XHR-driven actions.
Actions by Interaction
Map actions to interaction types:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| summarize
action_count = count(),
avg_duration = avg(duration),
avg_requests = avg(user_action.requests.count),
by: {frontend.name, interaction.name}
| sort action_count descUse Case: Understand which interactions trigger the most actions.
Resource-Heavy Actions
Find actions loading many resources:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter user_action.resources.count > 10
| fieldsAdd action_name = coalesce(user_action.name, user_action.custom_name, interaction.name)
| summarize
action_count = count(),
avg_resources = avg(user_action.resources.count),
avg_duration = avg(duration),
by: {frontend.name, action_name}
| sort avg_resources desc
| limit 20Use Case: Optimize actions that load too many resources.
Actions with Pending Requests
Find actions that complete with in-flight requests:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter user_action.requests.pending_request_count > 0
| summarize
action_count = count(),
avg_pending = avg(user_action.requests.pending_request_count),
by: {frontend.name, user_action.complete_reason}
| sort avg_pending descUse Case: Identify actions completing before requests finish.
DOM Mutation Analysis
Analyze DOM change patterns:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter user_action.mutation_count > 0
| summarize
action_count = count(),
avg_mutations = avg(user_action.mutation_count),
max_mutations = max(user_action.mutation_count),
by: {frontend.name}Use Case: Detect excessive DOM manipulation.
Timed-Out Actions
Analyze actions that timed out:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter user_action.complete_reason == "timeout"
| fieldsAdd action_name = coalesce(user_action.name, user_action.custom_name, interaction.name)
| summarize
timeout_count = count(),
avg_duration = avg(duration),
avg_pending = avg(user_action.requests.pending_request_count),
by: {frontend.name, action_name}
| sort timeout_count desc
| limit 20Use Case: Fix slow actions that time out.
Interrupted Actions
Analyze interrupted user flows:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter in(user_action.complete_reason, "interrupted_by_navigation", "interrupted_by_request", "interrupted_by_api")
| summarize
interrupted_count = count(),
by: {frontend.name, user_action.complete_reason, interaction.name}
| sort interrupted_count descUse Case: Understand action interruption patterns.
User Action Web Vitals
Get Web Vitals captured during user actions:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter isNotNull(web_vitals.largest_contentful_paint)
| summarize
action_count = count(),
p75_lcp = percentile(web_vitals.largest_contentful_paint, 75),
p75_cls = percentile(web_vitals.cumulative_layout_shift, 75),
by: {frontend.name}Use Case: Correlate user actions with Core Web Vitals.
API-Reported Actions
Analyze actions reported via the API:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_action == true
| filter characteristics.is_api_reported == true
| fieldsAdd action_name = coalesce(user_action.name, user_action.custom_name)
| summarize
action_count = count(),
avg_duration = avg(duration),
by: {frontend.name, action_name}
| sort action_count descUse Case: Track custom business actions reported by API.
User Sessions & Analytics
Track active user sessions, unique users, engagement patterns, and leverage custom properties for business insights.
Contents
- Schema Reference: `user.sessions`
- Core Session Metrics
- Active User Session Monitoring
- Unique Active User Tracking
- Geographic User Distribution
- Device Type Usage Patterns
- Browser Adoption Tracking
- Sessions with User Tags
- Sessions by Custom Property
- Session Duration Analysis
- Session Journey Overview
- New vs Returning Users
- User Interactions
- Page & View Analysis
Schema Reference: 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 (NOTdt.rum.session_id)dt.rum.instance.id— Instance IDfrontend.name— array of frontends involved in sessiondt.rum.application.type—webormobiledt.rum.user_type—real_user,synthetic, orrobot
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_counterror.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 flagcharacteristics.has_replay— Session replay available
User identity:
dt.rum.user_tag— User identifier (typically email, username or customerId), set viadtrum.identifyUser()API call in the instrumented frontend. Not always populated — only present when the frontend explicitly callsidentifyUser().- When
dt.rum.user_tagis empty,dt.rum.instance.idis 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 whenuser_tagis 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, notuser.events(where it may be empty even if the session has one).
Client/device context:
browser.name,browser.version,device.type,os.namegeo.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: Yonly 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.eventstouser.sessionsby session ID) — a narrowuser.sessionswindow 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.sessionsrecord. - 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.eventswithuser.sessions, exclude recent data (e.g., useto: now() - 1h) to avoid counting in-progress sessions as orphans.
Zombie sessions (events without a `user.sessions` record):
- Not every
dt.rum.session.idinuser.eventswill have a correspondinguser.sessionsrecord. 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.eventswithuser.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:
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 > 0Example — bounce rate and session quality:
fetch user.sessions, from: now() - 24h
| filter dt.rum.user_type == "real_user"
| summarize
total_sessions = count(),
bounces = countIf(characteristics.is_bounce == true),
zero_activity = countIf(toLong(navigation_count) == 0 and toLong(user_interaction_count) == 0),
avg_duration_s = avg(toLong(duration)) / 1000000000
| fieldsAdd bounce_rate_pct = round((bounces * 100.0) / total_sessions, decimals: 1)Core Session Metrics
Key Metrics:
dt.frontend.session.active.estimated_count- Active user sessionsdt.frontend.user.active.estimated_count- Unique active users
Key Fields (Event-Based):
dt.rum.session.id- Unique session identifierdt.rum.instance.id- Unique user/device instancedt.rum.user_tag- Custom user identifiersession_properties.__property_name__- Custom session propertiesevent_properties.__property_name__- Custom event properties
Alerting Thresholds:
- Critical: Active sessions dropping > 50%
- Track user growth trends for capacity planning
Active User Session Monitoring
Track active user sessions by application:
timeseries active_sessions = countDistinct(dt.frontend.session.active.estimated_count),
by: {frontend.name},
from: now() - 4h
| sort active_sessions desc
Use Case: Monitor real-time user engagement and capacity planning.
Unique Active User Tracking
Monitor unique active users:
timeseries unique_users = countDistinct(dt.frontend.user.active.estimated_count),
active_sessions = countDistinct(dt.frontend.session.active.estimated_count),
by: {frontend.name},
from: now() - 2h
| fieldsAdd
sessions_per_user = active_sessions[] / unique_users[],
avg_unique_users = arrayAvg(unique_users)
| sort avg_unique_users desc
Use Case: Understand user engagement patterns and identify power users.
Geographic User Distribution
Analyze user distribution across regions:
timeseries unique_users = countDistinct(dt.frontend.user.active.estimated_count),
active_sessions = countDistinct(dt.frontend.session.active.estimated_count),
by: {frontend.name, geo.country.iso_code},
from: now() - 6h
| fieldsAdd
avg_sessions_per_user = active_sessions[] / unique_users[],
peak_unique_users = arrayMax(unique_users)
| filter arrayMax(unique_users) > 5
| sort peak_unique_users desc
Use Case: Identify key geographic markets and plan regional infrastructure investments.
Device Type Usage Patterns
Compare user activity across device types:
timeseries unique_users = countDistinct(dt.frontend.user.active.estimated_count),
active_sessions = countDistinct(dt.frontend.session.active.estimated_count),
by: {frontend.name, device.type},
from: now() - 4h
| fieldsAdd
session_ratio = active_sessions[] / unique_users[],
peak_unique_users = arrayMax(unique_users)
| sort device.type, peak_unique_users desc
Use Case: Optimize mobile-first or desktop-first strategies based on device usage.
Browser Adoption Tracking
Track browser distribution among active users:
timeseries unique_users = countDistinct(dt.frontend.user.active.estimated_count),
active_sessions = countDistinct(dt.frontend.session.active.estimated_count),
by: {frontend.name, browser.name},
from: now() - 24h,
interval: 1h
| filter arrayMax(unique_users) > 1
| sort browser.name, timeframe desc
Use Case: Prioritize browser compatibility testing based on actual user distribution.
Sessions with User Tags
Query tagged user sessions:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_tag == true
| summarize
session_count = countDistinct(dt.rum.session.id),
event_count = count(),
by: {frontend.name, dt.rum.user_tag}
| sort session_count desc
| limit 50
Use Case: Analyze behavior of specific identified users.
Sessions by Custom Property
Aggregate by session property (e.g., subscription tier):
fetch user.events, from: now() - 2h
| filter characteristics.has_session_properties == true
| summarize
session_count = countDistinct(dt.rum.session.id),
user_count = countDistinct(dt.rum.instance.id, precision: 9),
by: {frontend.name}
Use Case: Segment users by custom business attributes.
Session Duration Analysis
Analyze session lengths:
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true or characteristics.has_view_summary == true
| summarize
session_duration = sum(duration),
by: {frontend.name, dt.rum.session.id}
| summarize
avg_duration = avg(session_duration),
p50_duration = percentile(session_duration, 50),
p90_duration = percentile(session_duration, 90),
by: {frontend.name}
Use Case: Understand user engagement depth.
Session Journey Overview
Trace user journey through an app:
fetch user.events, from: now() - 2h
| filter dt.rum.session.id == "<session_id>"
| fields
start_time,
characteristics.classifier,
view.name,
page.url.path,
interaction.name,
error.type
| sort start_time asc
Use Case: Debug specific user session issues.
New vs Returning Users
Analyze user retention patterns:
fetch user.events, from: now() - 2h
| filter characteristics.has_navigation == true
| summarize
sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, dt.rum.instance.id}
| summarize
single_session_users = countIf(sessions == 1),
returning_users = countIf(sessions > 1),
by: {frontend.name}
Use Case: Measure user retention and engagement.
User Interactions
Analyze user clicks, form inputs, scrolls, and other interactions for UX insights.
Data Source: fetch user.events with characteristics.has_user_interaction
Key Fields:
interaction.name- Type: click, change, blur, scroll, touch, etc.ui_element.name- Element identifier (aria-label, title, name, etc.)ui_element.custom_name- Custom name viadata-dt-nameattributeui_element.tag_name- HTML tag or mobile component typeui_element.features- Feature grouping viadata-dt-features
All User Interactions
Query all interaction types:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_interaction == true
| summarize
interaction_count = count(),
session_count = countDistinct(dt.rum.session.id),
by: {frontend.name, interaction.name}
| sort interaction_count desc
Click Analysis
Analyze button/link clicks:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_interaction == true
| filter interaction.name == "click"
| summarize
click_count = count(),
unique_users = countDistinct(dt.rum.instance.id, precision: 9),
by: {frontend.name, ui_element.resolved_name, ui_element.tag_name}
| sort click_count desc
| limit 30
Use Case: Identify most-clicked UI elements.
Feature Usage Analysis
Analyze custom feature areas:
fetch user.events, from: now() - 2h
| filter characteristics.has_user_interaction == true
| filter isNotNull(ui_element.features)
| summarize
interaction_count = count(),
unique_users = countDistinct(dt.rum.instance.id, precision: 9),
by: {ui_element.features}
| sort interaction_count desc
Use Case: Measure feature adoption using data-dt-features.
Page & View Analysis
Analyze page summaries (web) and view summaries (mobile) for engagement metrics.
Key Fields:
page.url.path/view.name- Page/view identifierpage.foreground_time/view.foreground_time- Active timepage.background_time/view.background_time- Hidden timeview.sequence_number- View position in sessionnavigation.type- How user arrived:navigate,reload,back_forward
Page Views Overview
Query all page views (web):
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true
| summarize
page_views = count(),
unique_sessions = countDistinct(dt.rum.session.id),
unique_users = countDistinct(dt.rum.instance.id, precision: 9),
by: {frontend.name, page.url.path}
| sort page_views desc
| limit 30
Use Case: Identify most visited pages.
Entry Pages
Analyze landing pages:
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true
| filter view.sequence_number == 1
| summarize
entry_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, page.url.path}
| sort entry_count desc
| limit 20
Use Case: Optimize landing page performance.
Views per Session
Analyze session depth:
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true or characteristics.has_view_summary == true
| summarize max_sequence = max(view.sequence_number), by: {frontend.name, dt.rum.session.id}
| summarize
sessions = count(),
avg_views = avg(max_sequence),
p50_views = percentile(max_sequence, 50),
p90_views = percentile(max_sequence, 90),
by: {frontend.name}
Use Case: Measure user journey depth.
Visibility Changes & Tab Activity
Monitor when users switch browser tabs or apps to understand engagement patterns.
Data Source: fetch user.events with characteristics.has_visibility_change
Key Fields:
visibility.state- Current state: foreground, background, prerender, unknownduration- Time in the visibility state
Contents
- Visibility State Distribution
- Tab Switch Frequency
- Time in Background
- Visibility by Page
- Prerender Activity
- Visibility Patterns Over Time
- Session Engagement Quality
- Device Type Comparison
- Unknown Visibility States
Visibility State Distribution
Analyze visibility patterns:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| summarize
change_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, visibility.state}
| sort change_count desc
Use Case: Understand foreground vs background activity.
Tab Switch Frequency
Count visibility changes per session:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| summarize change_count = count(), by: {frontend.name, dt.rum.session.id}
| summarize
sessions = count(),
avg_switches = avg(change_count),
p50_switches = percentile(change_count, 50),
p90_switches = percentile(change_count, 90),
by: {frontend.name}
Use Case: Measure user attention patterns.
Time in Background
Calculate background duration:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| filter visibility.state == "background"
| summarize
background_events = count(),
total_background_time = sum(duration),
avg_background_duration = avg(duration),
by: {frontend.name}
Use Case: Understand how long users leave tabs inactive.
Visibility by Page
Analyze which pages users leave:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| filter visibility.state == "background"
| summarize
background_count = count(),
avg_background_time = avg(duration),
by: {frontend.name, page.url.path}
| sort background_count desc
| limit 20
Use Case: Identify pages with engagement issues.
Prerender Activity
Track prerendered pages:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| filter visibility.state == "prerender"
| summarize
prerender_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name}
Use Case: Monitor speculation rules effectiveness.
Visibility Patterns Over Time
Track engagement trends:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| summarize
foreground = countIf(visibility.state == "foreground"),
background = countIf(visibility.state == "background"),
by: {frontend.name, time_bucket = bin(start_time, 1h)}
| sort time_bucket asc
Use Case: Correlate engagement with time of day.
Session Engagement Quality
Classify session engagement:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| summarize
foreground_count = countIf(visibility.state == "foreground"),
background_count = countIf(visibility.state == "background"),
by: {frontend.name, dt.rum.session.id}
| fieldsAdd engagement_ratio = foreground_count / (foreground_count + background_count + 1.0)
| summarize
high_engagement = countIf(engagement_ratio > 0.7),
medium_engagement = countIf(engagement_ratio >= 0.3 and engagement_ratio <= 0.7),
low_engagement = countIf(engagement_ratio < 0.3),
by: {frontend.name}
Use Case: Segment users by attention level.
Device Type Comparison
Compare visibility patterns across devices:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| summarize
visibility_changes = count(),
background_count = countIf(visibility.state == "background"),
by: {frontend.name, device.type}
| fieldsAdd background_ratio = background_count / visibility_changes
Use Case: Compare mobile vs desktop engagement.
Unknown Visibility States
Monitor edge cases:
fetch user.events, from: now() - 2h
| filter characteristics.has_visibility_change == true
| filter visibility.state == "unknown"
| summarize
unknown_count = count(),
by: {frontend.name, browser.name, browser.version}
Use Case: Identify browser compatibility issues.
Performance Analysis & Diagnostics
Advanced performance analysis including request timing, navigation patterns, geographic distribution, long tasks, and regression detection.
Contents
- Request Performance Metrics
- Request Timing Analysis
- Navigation Patterns
- Long Tasks & JavaScript Performance
- Advanced Performance Analysis
Request Performance Metrics
Monitor frontend request performance, response times, and throughput using aggregated RUM metrics.
Data Source: timeseries with dt.frontend.request.* metrics
Key Metrics:
dt.frontend.request.count- Total frontend requestsdt.frontend.request.duration- Request response times in milliseconds
Alerting Thresholds:
- Critical: p95 duration > SLA threshold (typically 2-3s)
- Warning: p95 duration approaching SLA threshold
- Track performance degradation > 20% hour-over-hour
Request Throughput Analysis
Monitor frontend request volume and patterns:
timeseries request_count = sum(dt.frontend.request.count),
by: {frontend.name, device.type},
from: now() - 2h
| fieldsAdd
requests_per_minute = request_count[] / 60
| filter arrayAvg(requests_per_minute) > 100
| sort request_count desc
Use Case: Track frontend request volume by application and device type.
Request Duration Performance
Analyze frontend request latency:
timeseries avg_duration = avg(dt.frontend.request.duration),
p75_duration = percentile(dt.frontend.request.duration, 75),
p95_duration = percentile(dt.frontend.request.duration, 95),
by: {frontend.name},
from: now() - 1h
| filter arrayAvg(p95_duration) > 3000
| sort p95_duration desc
Use Case: Identify applications with slow request response times.
Request Performance SLA Monitoring
Track adherence to performance SLAs:
timeseries request_count = sum(dt.frontend.request.count),
avg_duration = avg(dt.frontend.request.duration),
p95_duration = percentile(dt.frontend.request.duration, 95),
by: {frontend.name},
from: now() - 1h
| fieldsAdd
sla_threshold_ms = 2000
| fieldsAdd
sla_compliance_ratio = arrayAvg(iCollectArray(if(p95_duration[] < sla_threshold_ms, 1, else: 0))),
sla_buffer = sla_threshold_ms - arrayAvg(p95_duration)
| sort sla_buffer asc
Use Case: Monitor and report on performance SLA compliance.
Request Timing Analysis
Analyze W3C Resource Timing data to diagnose WHERE latency occurs in frontend requests.
Data Source: fetch user.events with characteristics.has_request == true
Key Timing Phases (all in nanoseconds):
performance.domain_lookup_start/end- DNS resolutionperformance.connect_start/end- TCP connectionperformance.secure_connection_start- TLS handshake startperformance.request_start- Request sent to serverperformance.response_start- First byte received (TTFB)performance.response_end- Download complete
Request Timing Breakdown
Analyze timing phases for slow requests:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter duration > 1s
| fieldsAdd
dns_time = performance.domain_lookup_end - performance.domain_lookup_start,
connect_time = performance.connect_end - performance.connect_start,
tls_time = performance.connect_end - performance.secure_connection_start,
server_time = performance.response_start - performance.request_start,
download_time = performance.response_end - performance.response_start
| summarize
avg_dns = avg(dns_time),
avg_connect = avg(connect_time),
avg_tls = avg(tls_time),
avg_server = avg(server_time),
avg_download = avg(download_time),
request_count = count(),
by: {url.domain}
| sort avg_server desc
| limit 20Use Case: Identify bottleneck phase (DNS, connection, server, download).
Third-Party Resource Performance
Compare first-party vs third-party resource timing:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
avg_duration = avg(duration),
p75_duration = percentile(duration, 75),
request_count = count(),
by: {url.provider, url.domain}
| filter request_count > 50
| sort p75_duration desc
| limit 30Use Case: Identify slow third-party resources impacting page performance.
HTTP Protocol Performance
Analyze performance by HTTP protocol version:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
avg_duration = avg(duration),
p90_duration = percentile(duration, 90),
request_count = count(),
by: {performance.next_hop_protocol}
| sort request_count descUse Case: Compare HTTP/1.1 vs HTTP/2 vs HTTP/3 performance.
Response Size Analysis
Identify oversized responses:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter performance.transfer_size > 100000
| fieldsAdd
compression_ratio = if(performance.encoded_body_size > 0,
toDouble(performance.decoded_body_size) / toDouble(performance.encoded_body_size),
else: 1.0)
| summarize
avg_transfer_size = avg(performance.transfer_size),
avg_compression_ratio = avg(compression_ratio),
request_count = count(),
by: {url.domain, url.path}
| sort avg_transfer_size desc
| limit 20Use Case: Find large payloads and compression opportunities.
Render-Blocking Resources
Identify resources blocking page render:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| filter performance.render_blocking_status == "blocking"
| summarize
avg_duration = avg(duration),
request_count = count(),
by: {url.domain, url.path, performance.initiator_type}
| sort avg_duration desc
| limit 20Use Case: Optimize critical rendering path.
Navigation Patterns
Analyze user navigation flows, referrers, and navigation types in web applications.
Data Source: fetch user.events with characteristics.has_navigation
Key Fields:
navigation.type- navigate, reload, back_forward, soft_navigation, prerendernavigation.tab_state- new, existing, duplicatedview.source.*- Previous view informationpage.source.url.*- Referrer URL components
External Referrers
Analyze traffic sources:
fetch user.events, from: now() - 2h
| filter characteristics.has_navigation == true
| filter navigation.type == "navigate"
| filter isNotNull(page.source.url.domain)
| summarize
referral_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, page.source.url.domain}
| sort referral_count desc
| limit 20
Use Case: Identify top traffic sources.
Internal Navigation Flows
Track page-to-page navigation:
fetch user.events, from: now() - 2h
| filter characteristics.has_navigation == true
| filter isNotNull(view.source.url.path)
| summarize
flow_count = count(),
by: {frontend.name, view.source.url.path, page.url.path}
| sort flow_count desc
| limit 30
Use Case: Visualize common user journeys.
Page Reload Analysis
Monitor page reloads:
fetch user.events, from: now() - 2h
| filter characteristics.has_navigation == true
| filter navigation.type == "reload"
| summarize
reload_count = count(),
unique_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, page.url.path}
| sort reload_count desc
| limit 20
Use Case: Identify pages with high reload rates (potential UX issues).
Long Tasks & JavaScript Performance
Analyze long-running JavaScript tasks that block the main thread and affect interactivity.
Data Source: fetch user.events with characteristics.has_long_task
Key Fields:
duration- Task duration (nanoseconds)long_task.name- Context attribution: self, same-origin, cross-origin, etc.long_task.attribution.container_*- Container details for iframe tasks
Performance Thresholds:
- Long task: > 50ms (blocks interaction)
- Critical: > 100ms (noticeable lag)
- Severe: > 250ms (frustrating UX)
Long Tasks Overview
Query all long tasks:
fetch user.events, from: now() - 2h
| filter characteristics.has_long_task == true
| summarize
task_count = count(),
avg_duration = avg(duration),
p75_duration = percentile(duration, 75),
p95_duration = percentile(duration, 95),
max_duration = max(duration),
by: {frontend.name}
Use Case: Baseline long task frequency and severity.
Long Tasks by Page
Identify pages with performance issues:
fetch user.events, from: now() - 2h
| filter characteristics.has_long_task == true
| summarize
task_count = count(),
avg_duration = avg(duration),
p90_duration = percentile(duration, 90),
by: {frontend.name, page.url.path}
| sort p90_duration desc
| limit 20
Use Case: Prioritize pages for JavaScript optimization.
Third-Party Long Tasks
Find blocking third-party scripts:
fetch user.events, from: now() - 2h
| filter characteristics.has_long_task == true
| filter in(long_task.name, "cross-origin-ancestor", "cross-origin-descendant", "cross-origin-unreachable")
| summarize
task_count = count(),
total_blocking_time = sum(duration),
by: {frontend.name, long_task.attribution.container_src}
| sort total_blocking_time desc
| limit 15
Use Case: Evaluate third-party script impact.
Critical Long Tasks
Find severely blocking tasks:
fetch user.events, from: now() - 2h
| filter characteristics.has_long_task == true
| filter duration > 250000000
| summarize
critical_tasks = count(),
avg_duration = avg(duration),
affected_sessions = countDistinct(dt.rum.session.id),
by: {frontend.name, page.url.path}
| sort critical_tasks desc
| limit 20
Use Case: Address worst performance offenders.
Advanced Performance Analysis
Advanced performance analysis including geographic distribution, correlations, and regression detection.
Geographic Performance Distribution
Compare request performance across regions:
timeseries request_count = sum(dt.frontend.request.count),
avg_duration = avg(dt.frontend.request.duration),
by: {frontend.name, geo.country.iso_code},
from: now() - 4h
| fieldsAdd
avg_duration_sec = arrayAvg(avg_duration) / 1000
| filter arraySum(request_count) > 50
| sort avg_duration_sec desc
Use Case: Identify geographic regions experiencing poor performance for CDN optimization.
Performance vs Error Rate Correlation
Correlate slow requests with error occurrences:
timeseries request_count = sum(dt.frontend.request.count),
error_count = sum(dt.frontend.error.count),
avg_duration = avg(dt.frontend.request.duration),
by: {frontend.name},
from: now() - 1h
| fieldsAdd
error_rate_percent = error_count[] * 100.0 / request_count[],
avg_duration_sec = avg_duration[] / 1000
| fieldsAdd avg_error_rate_percent = arrayAvg(error_rate_percent)
| filter avg_error_rate_percent > 5
| sort avg_error_rate_percent desc
Use Case: Identify if slow requests correlate with increased error rates.
Request Performance Degradation Detection
Monitor request duration trends for regressions:
timeseries {
avg_duration = avg(dt.frontend.request.duration),
p95_duration = percentile(dt.frontend.request.duration, 95),
request_count = sum(dt.frontend.request.count)
},
by: {frontend.name},
from: now() - 24h,
interval: 1h
| join [
timeseries {
prev_avg_duration = avg(dt.frontend.request.duration)
},
by: {frontend.name},
from: now() - 24h,
interval: 1h,
shift: 1h
], on: { frontend.name }, fields: { prev_avg_duration }
| fieldsAdd
duration_change_percent = coalesce((avg_duration[] - prev_avg_duration[]) * 100.0 / (prev_avg_duration[]), 0)
| filter arrayAvg(duration_change_percent) > 20
| sort duration_change_percent desc
Use Case: Detect performance regressions after deployments or infrastructure changes.
Time on Page
Analyze engagement depth:
fetch user.events, from: now() - 2h
| filter characteristics.has_page_summary == true
| summarize
page_views = count(),
avg_foreground = avg(page.foreground_time),
p50_foreground = percentile(page.foreground_time, 50),
p90_foreground = percentile(page.foreground_time, 90),
by: {frontend.name, page.url.path}
| filter page_views > 50
| sort avg_foreground desc
| limit 20
Use Case: Identify high-engagement pages.
ISP Performance Analysis
Identify network provider issues:
fetch user.events, from: now() - 2h
| filter characteristics.has_request == true
| summarize
avg_duration = avg(duration),
p90_duration = percentile(duration, 90),
request_count = count(),
by: {client.isp, geo.country.iso_code}
| filter request_count > 100
| sort p90_duration desc
| limit 20Use Case: Detect ISP-specific performance degradation.
Core Web Vitals
Monitor Core Web Vitals and frontend performance metrics. These metrics directly impact SEO rankings and user experience.
Data Source: fetch user.events with web_vitals.* fields
Google's Core Web Vitals Thresholds:
- LCP (Largest Contentful Paint): Good < 2.5s | Needs Improvement 2.5-4.0s | Poor > 4.0s (field:
web_vitals.largest_contentful_paint, duration in nanoseconds) - INP (Interaction to Next Paint): Good < 200ms | Needs Improvement 200-500ms | Poor > 500ms (field:
web_vitals.interaction_to_next_paint, duration in nanoseconds) - CLS (Cumulative Layout Shift): Good < 0.1 | Needs Improvement 0.1-0.25 | Poor > 0.25 (field:
web_vitals.cumulative_layout_shift, double) - FID (First Input Delay) (deprecated, replaced by INP): Good < 100ms | Needs Improvement 100-300ms | Poor > 300ms (field:
web_vitals.first_input_delay, duration in nanoseconds)
Metric-Based Queries: For timeseries dashboards and alerting, use dt.frontend.web.page.* metrics:
dt.frontend.web.page.largest_contentful_paintdt.frontend.web.page.interaction_to_next_paintdt.frontend.web.page.cumulative_layout_shiftdt.frontend.web.page.first_input_delay
Alerting: Critical if p75 values fall into "poor" range for more than 1 hour
Contents
- All Core Web Vitals
- Largest Contentful Paint (LCP)
- First Input Delay (FID)
- Interaction to Next Paint (INP)
- Cumulative Layout Shift (CLS)
- Web Vitals Trends
- Web Vitals by Page
- Web Vitals by Device
All Core Web Vitals
Query all web vitals (including INP which replaces FID):
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.largest_contentful_paint) or isNotNull(web_vitals.interaction_to_next_paint) or isNotNull(web_vitals.cumulative_layout_shift)
| summarize
lcp_p75 = percentile(web_vitals.largest_contentful_paint, 75),
inp_p75 = percentile(web_vitals.interaction_to_next_paint, 75),
cls_p75 = percentile(web_vitals.cumulative_layout_shift, 75),
by: {frontend.name}
Largest Contentful Paint (LCP)
Monitor LCP performance:
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.largest_contentful_paint)
| summarize
p50_lcp = percentile(web_vitals.largest_contentful_paint, 50),
p75_lcp = percentile(web_vitals.largest_contentful_paint, 75),
p90_lcp = percentile(web_vitals.largest_contentful_paint, 90),
by: {frontend.name}
| fieldsAdd
lcp_rating = if(
p75_lcp < 2500ms,
"good",
else: if(
p75_lcp < 4s,
"needs_improvement",
else: "poor"
)
)
First Input Delay (FID)
Note: FID is deprecated and replaced by INP (Interaction to Next Paint) as of March 2024. Consider using INP for new implementations.
Monitor interactivity (legacy):
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.first_input_delay)
| summarize
p75_fid = percentile(web_vitals.first_input_delay, 75),
p95_fid = percentile(web_vitals.first_input_delay, 95),
by: {frontend.name}
| fieldsAdd
fid_rating = if(
p75_fid < 1s,
"good",
else: if(
p75_fid < 3s,
"needs_improvement",
else: "poor"
)
)
Interaction to Next Paint (INP)
Monitor responsiveness (replaces FID):
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.interaction_to_next_paint)
| summarize
p75_inp = percentile(web_vitals.interaction_to_next_paint, 75),
p95_inp = percentile(web_vitals.interaction_to_next_paint, 95),
by: {frontend.name}
| fieldsAdd
inp_rating = if(
p75_inp < 2s,
"good",
else: if(
p75_inp < 5s,
"needs_improvement",
else: "poor"
)
)
Cumulative Layout Shift (CLS)
Monitor visual stability:
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.cumulative_layout_shift)
| summarize
p75_cls = percentile(web_vitals.cumulative_layout_shift, 75),
by: {frontend.name}
| fieldsAdd
cls_rating = if(
p75_cls < 0.1,
"good",
else: if(
p75_cls < 0.25,
"needs_improvement",
else: "poor"
)
)
Web Vitals Trends
Monitor trends over time:
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.largest_contentful_paint)
| summarize
p75_lcp = percentile(web_vitals.largest_contentful_paint, 75),
by: {time_bucket = bin(start_time, 1h)}
| sort time_bucket asc
Web Vitals by Page
Analyze per-page performance:
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.largest_contentful_paint)
| summarize
page_views = count(),
p75_lcp = percentile(web_vitals.largest_contentful_paint, 75),
by: {page.url}
| filter page_views > 100
| sort p75_lcp desc
| limit 20
Web Vitals by Device
Compare performance across devices:
fetch user.events, from: now() - 2h
| filter isNotNull(web_vitals.largest_contentful_paint)
| summarize
p75_lcp = percentile(web_vitals.largest_contentful_paint, 75),
p75_inp = percentile(web_vitals.interaction_to_next_paint, 75),
p75_cls = percentile(web_vitals.cumulative_layout_shift, 75),
by: {device.type}
Related skills
How it compares
Pick dt-obs-frontends over generic APM skills when you need Dynatrace-specific DQL for frontend RUM, geo breakdowns, and deployment regression checks.
FAQ
What does dt-obs-frontends do?
Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors, and trace correlation. Query via `user.events`, `user.sessio
When should I use dt-obs-frontends?
Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends. Core Web Vitals, user sessions, page performance, mobile crashes, frontend errors, and trace correlation. Query via `user.events`, `user.sessio
What are common prerequisites?
--- name: dt-obs-frontends description: Real User Monitoring (RUM) on Dynatrace - web, mobile, and hybrid frontends.
Is Dt Obs Frontends safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.