DataDog RUM Integration for Website Monitoring: A Complete Guide
Generated using LLM for reference purposes. Not reviewed and tested and may contain errors.
DataDog RUM Integration for Website Monitoring: A Complete Guide
DataDog Real User Monitoring (RUM) provides comprehensive insights into your website’s performance and user experience by collecting real-time data from actual user interactions. This guide covers everything you need to know about implementing DataDog RUM for effective website monitoring.
Table of Contents
- DataDog RUM Overview
- DataDog RUM Setup & Configuration
- Website Monitoring with DataDog RUM
- DataDog Dashboard Creation
- Implementation Examples
- Best Practices
DataDog RUM Overview
What is DataDog RUM?
DataDog Real User Monitoring (RUM) is a powerful tool that automatically collects performance and usage data from real user sessions on your website. It provides deep insights into:
- Page Load Performance: Core Web Vitals, page load times, and resource loading
- User Interactions: Clicks, navigation patterns, and user flows
- Error Tracking: JavaScript errors, API failures, and resource loading issues
- API Performance: End-to-end API monitoring and response times
- User Sessions: Session replay and user behavior analytics
Benefits of DataDog RUM vs Custom Solutions
Advantages of DataDog RUM:
- Zero Configuration Setup: Automatic collection of Core Web Vitals and performance metrics
- Advanced Analytics: Built-in correlation between frontend performance and backend services
- Scalability: Handles high-volume traffic without infrastructure management
- Integration Ecosystem: Seamless integration with APM, Infrastructure Monitoring, and Log Management
- Advanced Features: Session replay, synthetic monitoring, and anomaly detection
- Professional Support: 24/7 support and regular feature updates
Compared to Custom Solutions:
- No need to build and maintain custom monitoring infrastructure
- Reduced development time and ongoing maintenance costs
- Access to advanced ML-powered anomaly detection
- Built-in alerting and dashboard capabilities
- Professional-grade security and compliance
Key Metrics Available in DataDog RUM
DataDog RUM automatically collects and provides insights into:
Core Web Vitals
- Largest Contentful Paint (LCP): Page loading performance
- First Input Delay (FID): Page interactivity
- Cumulative Layout Shift (CLS): Visual stability
Performance Metrics
- Page Load Time: Total time to load and render pages
- Resource Loading: Individual resource performance (JS, CSS, images)
- API Response Times: End-to-end API performance
- DNS and Network Timing: Network-level performance metrics
User Experience Metrics
- Session Duration: User engagement and session length
- Page Views: Traffic patterns and popular pages
- User Flows: Navigation patterns and conversion funnels
- Error Rates: JavaScript errors and failed requests
Business Metrics
- Conversion Tracking: Custom business event monitoring
- Feature Usage: User interaction with specific features
- Geographic Performance: Performance by user location
DataDog RUM Setup & Configuration
DataDog Browser SDK Installation and Setup
Step 1: Install DataDog Browser SDK
Option A: NPM Installation
npm install @datadog/browser-rum @datadog/browser-core
Option B: CDN Installation
<script src="https://www.datadoghq-browser-agent.com/datadog-rum.js"></script>
Step 2: Initialize DataDog RUM
Basic Initialization:
import { datadogRum } from '@datadog/browser-rum';
datadogRum.init({
applicationId: 'YOUR_APPLICATION_ID',
clientToken: 'YOUR_CLIENT_TOKEN',
site: 'datadoghq.com',
service: 'your-web-application',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
sessionReplaySampleRate: 20,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
defaultPrivacyLevel: 'mask-user-input',
allowedTracingUrls: [
'https://api.yourapp.com',
'https://api2.yourapp.com'
]
});
CDN Initialization:
<script>
(function(h,o,u,n,d) {
h=h[d]=h[d]||{q:[],onReady:function(c){h.q.push(c)}}
d=o.createElement(u);d.async=1;d.src=n
n=o.getElementsByTagName(u)[0];n.parentNode.insertBefore(d,n)
})(window,document,'script','https://www.datadoghq-browser-agent.com/datadog-rum.js','DD_RUM')
DD_RUM.onReady(function() {
DD_RUM.init({
applicationId: 'YOUR_APPLICATION_ID',
clientToken: 'YOUR_CLIENT_TOKEN',
site: 'datadoghq.com',
service: 'your-web-application',
env: 'production',
version: '1.0.0',
sessionSampleRate: 100,
sessionReplaySampleRate: 20,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
defaultPrivacyLevel: 'mask-user-input'
})
})
</script>
RUM Initialization Configuration
Essential Configuration Options
datadogRum.init({
// Required configuration
applicationId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
clientToken: 'dd_client_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
site: 'datadoghq.com', // or 'datadoghq.eu' for EU
// Application metadata
service: 'frontend-web-app',
env: 'production',
version: '1.2.3',
// Data collection settings
sessionSampleRate: 100, // Percentage of sessions to track (0-100)
sessionReplaySampleRate: 20, // Percentage of sessions for replay (0-100)
// Feature toggles
trackUserInteractions: true, // Track clicks, form inputs, taps
trackResources: true, // Track resource loading
trackLongTasks: true, // Track long running tasks
trackFrustrations: true, // Track user frustration signals
// Privacy settings
defaultPrivacyLevel: 'mask-user-input', // 'allow', 'mask', 'mask-user-input'
// Tracing configuration
allowedTracingUrls: [
{
match: 'https://api.yourapp.com',
propagatorTypes: ['tracecontext', 'datadog']
}
],
// Custom configuration
beforeSend: (event) => {
// Filter or modify events before sending
if (event.type === 'error' && event.error.isTrusted === false) {
return false; // Drop untrusted errors
}
return true;
}
});
Advanced Configuration Options
datadogRum.init({
// Performance monitoring
premiumSampleRate: 10, // Premium features sampling (0-100)
enablePrivacyForActionName: true, // Hide PII in action names
// Custom context
context: {
userId: getCurrentUserId(),
userRole: getCurrentUserRole(),
accountType: getCurrentAccountType()
},
// Error tracking
trackErrors: true,
defaultErrorCode: 'UNKNOWN',
// Resource tracking
resourceSampleRate: 100,
allowFallbackToLocalStorage: true,
// Session management
sessionReplaySampleRate: 20,
replaySampleRate: 20,
// Feature flags
enableBeforeSendAfter: true,
usePartitionedCrossSiteSessionCookie: false
});
Custom Metrics and Attributes Configuration
Adding Custom Attributes
// Add global context (applies to all events)
datadogRum.setGlobalContext({
userId: 'user123',
userRole: 'premium',
campaign: 'black-friday-2023',
featureFlags: ['new-checkout-flow', 'beta-analytics']
});
// Add user information
datadogRum.setUser({
id: 'user123',
name: 'John Doe',
email: 'john.doe@example.com',
plan: 'premium',
organization: 'Acme Corp'
});
// Add custom attributes to specific events
datadogRum.addTiming('custom_checkout_timing');
Custom Metrics Collection
// Track custom timing metrics
function trackCustomTiming(name, startTime) {
const endTime = performance.now();
const duration = endTime - startTime;
datadogRum.addTiming(name);
// Add custom attributes
datadogRum.setGlobalContextProperty('custom_timing', {
name: name,
duration: duration,
timestamp: Date.now()
});
}
// Track custom events
function trackCustomEvent(eventName, eventData) {
datadogRum.addAction(eventName, eventData);
}
// Example usage
const checkoutStartTime = performance.now();
// ... checkout process ...
trackCustomTiming('checkout_completion', checkoutStartTime);
trackCustomEvent('feature_used', {
feature_name: 'advanced_search',
search_query: 'datadog monitoring',
results_count: 25
});
Environment Setup (Dev, Staging, Production)
Environment-Specific Configuration
// Environment configuration
const getRumConfig = () => {
const configs = {
development: {
applicationId: 'dev-app-id',
clientToken: 'dev-client-token',
env: 'development',
sessionSampleRate: 100,
sessionReplaySampleRate: 100,
trackErrors: true,
debug: true
},
staging: {
applicationId: 'staging-app-id',
clientToken: 'staging-client-token',
env: 'staging',
sessionSampleRate: 50,
sessionReplaySampleRate: 10,
trackErrors: true,
debug: false
},
production: {
applicationId: 'prod-app-id',
clientToken: 'prod-client-token',
env: 'production',
sessionSampleRate: 20,
sessionReplaySampleRate: 5,
trackErrors: true,
debug: false
}
};
return configs[process.env.NODE_ENV] || configs.development;
};
// Initialize based on environment
const rumConfig = getRumConfig();
datadogRum.init(rumConfig);
Environment Detection and Auto-Configuration
// Automatic environment detection
function detectEnvironment() {
const hostname = window.location.hostname;
if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {
return 'development';
} else if (hostname.includes('staging') || hostname.includes('test')) {
return 'staging';
} else {
return 'production';
}
}
// Dynamic configuration loading
async function loadRumConfiguration() {
const environment = detectEnvironment();
try {
const response = await fetch(`/config/rum-${environment}.json`);
const config = await response.json();
return config;
} catch (error) {
console.warn('Failed to load RUM config, using defaults:', error);
return getRumConfig();
}
}
// Initialize with dynamic configuration
loadRumConfiguration().then(rumConfig => {
datadogRum.init(rumConfig);
});
Website Monitoring with DataDog RUM
Page Load Performance Monitoring (Core Web Vitals)
Automatic Core Web Vitals Collection
DataDog RUM automatically collects and reports on all Core Web Vitals:
// DataDog automatically tracks:
// - Largest Contentful Paint (LCP)
// - First Input Delay (FID)
// - Cumulative Layout Shift (CLS)
// View performance in DataDog dashboard:
// - Performance > Core Web Vitals
// - Filter by service, environment, or custom tags
// Custom performance monitoring
datadogRum.onReady(() => {
// Get current performance metrics
const performance = datadogRum.getInternalContext();
console.log('Current session ID:', performance.sessionId);
console.log('View ID:', performance.view.id);
});
Custom Performance Metrics
// Track custom performance metrics
function trackPagePerformance() {
// Measure Time to Interactive (TTI)
const measureTTI = () => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'first-input') {
datadogRum.addAction('time_to_interactive', {
duration: entry.startTime,
type: 'performance'
});
observer.disconnect();
}
}
});
observer.observe({ entryTypes: ['first-input'] });
};
// Measure Time to First Byte (TTFB)
const measureTTFB = () => {
const navigation = performance.getEntriesByType('navigation')[0];
if (navigation) {
datadogRum.addAction('time_to_first_byte', {
duration: navigation.responseStart - navigation.requestStart,
type: 'performance'
});
}
};
// Measure Custom Metrics
const measureCustomMetrics = () => {
// Custom loading phases
const phases = {
'app_loaded': () => window.appLoaded,
'data_fetched': () => window.dataFetched,
'ui_ready': () => window.uiReady
};
Object.entries(phases).forEach(([phase, checkFn]) => {
const startTime = performance.now();
const checkInterval = setInterval(() => {
if (checkFn()) {
const duration = performance.now() - startTime;
datadogRum.addAction(`phase_${phase}`, {
duration: duration,
type: 'custom_performance'
});
clearInterval(checkInterval);
}
}, 100);
});
};
measureTTI();
measureTTFB();
measureCustomMetrics();
}
// Initialize performance tracking
trackPagePerformance();
Performance Budget Monitoring
// Set up performance budgets
const performanceBudgets = {
lcp: 2500, // 2.5 seconds
fid: 100, // 100ms
cls: 0.1, // 0.1
ttfb: 500, // 500ms
custom_load: 3000 // 3 seconds
};
function checkPerformanceBudgets() {
// Monitor LCP budget
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
if (lastEntry.startTime > performanceBudgets.lcp) {
datadogRum.addError(new Error('LCP budget exceeded'), {
budget: 'lcp',
actual: lastEntry.startTime,
limit: performanceBudgets.lcp,
severity: 'warning'
});
}
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
// Monitor FID budget
const fidObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if ((entry.processingStart - entry.startTime) > performanceBudgets.fid) {
datadogRum.addError(new Error('FID budget exceeded'), {
budget: 'fid',
actual: entry.processingStart - entry.startTime,
limit: performanceBudgets.fid,
severity: 'warning'
});
}
});
});
fidObserver.observe({ entryTypes: ['first-input'] });
}
checkPerformanceBudgets();
API Endpoint Monitoring and Error Tracking
Automatic API Monitoring
// DataDog automatically tracks all XHR and fetch requests
// Configure tracing for your API endpoints
datadogRum.init({
// ... other config ...
allowedTracingUrls: [
{
match: 'https://api.yourapp.com',
propagatorTypes: ['tracecontext', 'datadog']
},
{
match: 'https://internal-api.yourapp.com',
propagatorTypes: ['datadog']
}
],
traceSampleRate: 100
});
// Track custom API metrics
function trackApiCall(endpoint, method, startTime, response) {
const duration = performance.now() - startTime;
datadogRum.addAction('api_call', {
endpoint: endpoint,
method: method,
status: response.status,
duration: duration,
success: response.ok,
size: response.headers.get('content-length') || 0
});
// Track errors
if (!response.ok) {
datadogRum.addError(new Error(`API Error: ${response.status}`), {
endpoint: endpoint,
method: method,
status: response.status,
duration: duration
});
}
}
// Enhanced fetch wrapper
const originalFetch = window.fetch;
window.fetch = async function(url, options = {}) {
const startTime = performance.now();
const method = options.method || 'GET';
try {
const response = await originalFetch.apply(this, arguments);
trackApiCall(url, method, startTime, response);
return response;
} catch (error) {
trackApiCall(url, method, startTime, { ok: false, status: 0 });
throw error;
}
};
Custom API Error Tracking
// Comprehensive API error tracking
class ApiErrorTracker {
constructor() {
this.errorCounts = new Map();
this.lastErrorTime = new Map();
this.rateLimitWindow = 60000; // 1 minute
}
trackApiError(error, endpoint, method) {
const key = `${endpoint}:${method}`;
const now = Date.now();
// Update error counts
if (!this.errorCounts.has(key)) {
this.errorCounts.set(key, []);
}
const errors = this.errorCounts.get(key);
errors.push(now);
// Clean old errors outside window
const recentErrors = errors.filter(time => now - time < this.rateLimitWindow);
this.errorCounts.set(key, recentErrors);
// Track in DataDog
datadogRum.addError(error, {
endpoint: endpoint,
method: method,
error_count: recentErrors.length,
error_type: this.categorizeError(error),
timestamp: now
});
// Alert on high error rates
if (recentErrors.length >= 5) {
this.sendAlert(key, recentErrors.length, endpoint);
}
}
categorizeError(error) {
if (error.name === 'TypeError' && error.message.includes('fetch')) {
return 'network_error';
} else if (error.message.includes('4')) {
return 'client_error';
} else if (error.message.includes('5')) {
return 'server_error';
} else {
return 'unknown_error';
}
}
sendAlert(key, count, endpoint) {
const lastAlert = this.lastErrorTime.get(key);
const now = Date.now();
// Rate limit alerts to once per 5 minutes
if (!lastAlert || now - lastAlert > 300000) {
datadogRum.addError(new Error(`High error rate detected: ${count} errors in 1 minute`), {
alert_type: 'high_error_rate',
endpoint: endpoint,
error_count: count,
severity: 'critical'
});
this.lastErrorTime.set(key, now);
}
}
}
// Initialize API error tracking
const apiErrorTracker = new ApiErrorTracker();
// Track fetch errors
window.addEventListener('unhandledrejection', (event) => {
if (event.reason && event.reason.url) {
apiErrorTracker.trackApiError(
event.reason,
event.reason.url,
event.reason.config?.method || 'GET'
);
}
});
Resource Loading Performance
Resource Monitoring
// Monitor resource loading performance
class ResourceMonitor {
constructor() {
this.resourceTimings = new Map();
this.setupResourceObserver();
}
setupResourceObserver() {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
if (entry.initiatorType && entry.duration > 0) {
this.analyzeResource(entry);
}
});
});
observer.observe({ entryTypes: ['resource'] });
}
analyzeResource(entry) {
const resourceData = {
name: entry.name,
type: entry.initiatorType,
duration: entry.duration,
transferSize: entry.transferSize,
encodedBodySize: entry.encodedBodySize,
decodedBodySize: entry.decodedBodySize,
startTime: entry.startTime,
timestamp: Date.now()
};
// Track in DataDog
datadogRum.addAction('resource_load', resourceData);
// Check for slow resources
if (entry.duration > 1000) {
datadogRum.addError(new Error(`Slow resource: ${entry.name}`), {
resource_name: entry.name,
resource_type: entry.initiatorType,
duration: entry.duration,
threshold: 1000
});
}
// Track critical resources separately
if (this.isCriticalResource(entry.name)) {
datadogRum.addAction('critical_resource_load', {
...resourceData,
is_critical: true
});
}
}
isCriticalResource(url) {
const criticalPatterns = [
/\.css$/,
/\.js$/,
/critical/,
/bundle/,
/main/
];
return criticalPatterns.some(pattern => pattern.test(url));
}
getResourceSummary() {
const summary = {
total: 0,
byType: {},
slowResources: [],
totalLoadTime: 0
};
// Get all resource entries
const resources = performance.getEntriesByType('resource');
resources.forEach(resource => {
summary.total++;
summary.totalLoadTime += resource.duration;
if (!summary.byType[resource.initiatorType]) {
summary.byType[resource.initiatorType] = {
count: 0,
totalDuration: 0,
averageDuration: 0
};
}
summary.byType[resource.initiatorType].count++;
summary.byType[resource.initiatorType].totalDuration += resource.duration;
if (resource.duration > 1000) {
summary.slowResources.push({
name: resource.name,
duration: resource.duration,
type: resource.initiatorType
});
}
});
// Calculate averages
Object.values(summary.byType).forEach(type => {
type.averageDuration = type.totalDuration / type.count;
});
return summary;
}
}
// Initialize resource monitoring
const resourceMonitor = new ResourceMonitor();
// Track resource summary periodically
setInterval(() => {
const summary = resourceMonitor.getResourceSummary();
datadogRum.addAction('resource_summary', summary);
}, 30000); // Every 30 seconds
User Session Replay and Analytics
Session Replay Configuration
// Configure session replay
datadogRum.init({
// ... other config ...
sessionReplaySampleRate: 20, // 20% of sessions
trackUserInteractions: true, // Track clicks, inputs, taps
trackFrustrations: true, // Track rage clicks, dead clicks
defaultPrivacyLevel: 'mask-user-input', // Mask sensitive input
enablePrivacyForActionName: true, // Hide PII in action names
trackResources: true,
trackLongTasks: true
});
// Add session context
datadogRum.setUser({
id: getCurrentUserId(),
name: getCurrentUserName(),
email: getCurrentUserEmail(),
role: getCurrentUserRole(),
account_id: getCurrentAccountId()
});
// Track custom session events
function trackUserJourney(step, data = {}) {
datadogRum.addAction(`user_journey_${step}`, {
journey_step: step,
...data,
timestamp: Date.now()
});
}
// Example usage in user flows
function trackCheckoutFlow(step) {
const journeyData = {
step: step,
cart_value: getCurrentCartValue(),
items_count: getCartItemCount(),
payment_method: getSelectedPaymentMethod()
};
trackUserJourney('checkout', journeyData);
}
// Track feature adoption
function trackFeatureAdoption(featureName, context = {}) {
datadogRum.addAction('feature_adoption', {
feature_name: featureName,
adoption_type: 'first_use',
...context,
timestamp: Date.now()
});
}
Error Tracking and Debugging
Comprehensive Error Tracking
// Enhanced error tracking setup
class EnhancedErrorTracker {
constructor() {
this.setupGlobalErrorHandlers();
this.setupUnhandledRejectionHandler();
this.setupResourceErrorHandler();
this.errorCounts = new Map();
}
setupGlobalErrorHandlers() {
// JavaScript errors
window.addEventListener('error', (event) => {
this.trackError({
type: 'javascript',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: event.error,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href
});
});
}
setupUnhandledRejectionHandler() {
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
this.trackError({
type: 'promise',
message: event.reason?.message || 'Unhandled Promise Rejection',
reason: event.reason,
stack: event.reason?.stack,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href
});
});
}
setupResourceErrorHandler() {
// Resource loading errors
window.addEventListener('error', (event) => {
if (event.target && (event.target.tagName === 'SCRIPT' ||
event.target.tagName === 'LINK' ||
event.target.tagName === 'IMG' ||
event.target.tagName === 'LINK')) {
this.trackError({
type: 'resource',
message: event.message,
target: event.target.tagName.toLowerCase(),
src: event.target.src || event.target.href,
timestamp: Date.now(),
url: window.location.href
});
}
}, true);
}
trackError(errorData) {
// Add error ID and categorize
errorData.id = this.generateErrorId();
errorData.category = this.categorizeError(errorData);
errorData.severity = this.calculateSeverity(errorData);
// Count errors for rate limiting
const errorKey = `${errorData.type}:${errorData.category}`;
const count = (this.errorCounts.get(errorKey) || 0) + 1;
this.errorCounts.set(errorKey, count);
// Add to DataDog
datadogRum.addError(
new Error(`${errorData.type}: ${errorData.message}`),
{
...errorData,
error_count: count,
user_session: datadogRum.getInternalContext()?.sessionId,
page_url: window.location.pathname,
user_agent: navigator.userAgent,
timestamp: Date.now()
}
);
// Log for development
if (process.env.NODE_ENV === 'development') {
console.error('Enhanced error tracked:', errorData);
}
}
categorizeError(errorData) {
if (errorData.type === 'javascript') {
if (errorData.message.includes('NetworkError') ||
errorData.message.includes('fetch')) {
return 'network';
} else if (errorData.message.includes('ReferenceError')) {
return 'reference';
} else if (errorData.message.includes('TypeError')) {
return 'type';
} else {
return 'general';
}
} else if (errorData.type === 'resource') {
return errorData.target || 'unknown';
} else if (errorData.type === 'promise') {
return 'async';
}
return 'unknown';
}
calculateSeverity(errorData) {
// Calculate error severity based on type and context
if (errorData.type === 'javascript') {
if (errorData.category === 'network') return 'high';
if (errorData.category === 'reference') return 'medium';
return 'low';
}
return 'medium';
}
generateErrorId() {
return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
getErrorSummary() {
const summary = {
totalErrors: Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0),
byCategory: {},
recentErrors: [],
trends: this.analyzeErrorTrends()
};
this.errorCounts.forEach((count, key) => {
const [type, category] = key.split(':');
if (!summary.byCategory[type]) {
summary.byCategory[type] = {};
}
summary.byCategory[type][category] = count;
});
return summary;
}
analyzeErrorTrends() {
// Analyze error patterns over time
return {
totalErrors: Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0),
mostCommon: Array.from(this.errorCounts.entries())
.sort((a, b) => b[1] - a[1])[0],
errorRate: this.calculateErrorRate()
};
}
calculateErrorRate() {
// Calculate errors per minute
const totalErrors = Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0);
const timeWindow = 5 * 60 * 1000; // 5 minutes
return Math.round((totalErrors / timeWindow) * 60000); // errors per minute
}
}
// Initialize enhanced error tracking
const enhancedErrorTracker = new EnhancedErrorTracker();
// Track error summary periodically
setInterval(() => {
const summary = enhancedErrorTracker.getErrorSummary();
datadogRum.addAction('error_summary', summary);
}, 60000); // Every minute
DataDog Dashboard Creation
Dashboard Widgets for Website Monitoring
Creating Custom Dashboards
DataDog RUM provides powerful dashboard capabilities for visualizing your website performance data:
1. Core Web Vitals Dashboard:
- LCP, FID, CLS trends over time
- Performance by page URL
- Geographic performance distribution
- Device and browser performance comparison
2. User Experience Dashboard:
- Session replay availability
- User interaction patterns
- Page views and unique visitors
- Conversion funnel tracking
3. Error Monitoring Dashboard:
- Error rates by type and severity
- Error frequency over time
- Top error-prone pages
- Error correlation with performance metrics
Custom Dashboard Configuration
{
"title": "Website Performance Dashboard",
"description": "Comprehensive website monitoring using DataDog RUM",
"widgets": [
{
"id": 1,
"layout": { "x": 0, "y": 0, "width": 4, "height": 3 },
"definition": {
"title": "Core Web Vitals - LCP",
"type": "timeseries",
"requests": [
{
"q": "rum.custom.lcp{*} by {env,service}",
"aggregator": "avg",
"conditional_formats": [
{
"palette": "red",
"comparator": ">",
"value": 2500
},
{
"palette": "yellow",
"comparator": ">",
"value": 1500
}
]
}
],
"yaxis": {
"label": "LCP (ms)",
"min": 0,
"max": 4000
}
}
},
{
"id": 2,
"layout": { "x": 4, "y": 0, "width": 4, "height": 3 },
"definition": {
"title": "Error Rate",
"type": "timeseries",
"requests": [
{
"q": "rum.error{*} by {env,service}.as_count()",
"aggregator": "sum"
}
],
"yaxis": {
"label": "Errors per minute",
"min": 0
}
}
},
{
"id": 3,
"layout": { "x": 8, "y": 0, "width": 4, "height": 3 },
"definition": {
"title": "Page Views",
"type": "timeseries",
"requests": [
{
"q": "rum.view{*} by {env,service}.as_count()",
"aggregator": "sum"
}
],
"yaxis": {
"label": "Page views per minute",
"min": 0
}
}
}
],
"template_variables": [
{
"name": "env",
"default": "production",
"options": ["development", "staging", "production"]
},
{
"name": "service",
"default": "frontend-web-app",
"options": ["frontend-web-app", "mobile-app", "admin-panel"]
}
],
"layout_type": "ordered",
"is_read_only": false
}
Performance Monitoring Widgets
// Dashboard widget configurations for API integration
const performanceWidgets = {
// Core Web Vitals Overview
coreWebVitals: {
title: "Core Web Vitals Overview",
type: "query_table",
requests: [
{
q: "avg:rum.lcp{*} by {env,service}",
alias: "LCP (ms)"
},
{
q: "avg:rum.fid{*} by {env,service}",
alias: "FID (ms)"
},
{
q: "avg:rum.cls{*} by {env,service}",
alias: "CLS"
}
],
formulas: [
{
formula: "LCP < 2500 ? 'Good' : (LCP < 4000 ? 'Needs Improvement' : 'Poor')",
alias: "LCP Status"
},
{
formula: "FID < 100 ? 'Good' : (FID < 300 ? 'Needs Improvement' : 'Poor')",
alias: "FID Status"
},
{
formula: "CLS < 0.1 ? 'Good' : (CLS < 0.25 ? 'Needs Improvement' : 'Poor')",
alias: "CLS Status"
}
]
},
// API Performance
apiPerformance: {
title: "API Performance Metrics",
type: "timeseries",
requests: [
{
q: "avg:rum.resource.duration{type:fetch} by {env,service,endpoint}",
aggregator: "avg",
style: {
palette: "dog_classic",
line_type: "solid",
line_width: "normal"
}
}
],
yaxis: {
label: "Response Time (ms)",
min: 0
}
},
// User Engagement
userEngagement: {
title: "User Engagement Metrics",
type: "timeseries",
requests: [
{
q: "sum:rum.view{*} by {env,service}",
aggregator: "sum",
style: {
palette: "green",
line_type: "solid"
}
},
{
q: "sum:rum.action{*} by {env,service}",
aggregator: "sum",
style: {
palette: "blue",
line_type: "dashed"
}
}
],
yaxis: {
label: "Count",
min: 0
}
}
};
Custom Metrics Visualization
Creating Custom Metrics
// Define custom metrics in your application
function createCustomMetrics() {
// Business metrics
const businessMetrics = {
// Conversion tracking
trackConversion: (conversionType, value) => {
datadogRum.addAction('conversion', {
conversion_type: conversionType,
conversion_value: value,
currency: 'USD',
timestamp: Date.now()
});
},
// Feature usage tracking
trackFeatureUsage: (featureName, usageData) => {
datadogRum.addAction('feature_usage', {
feature_name: featureName,
...usageData,
timestamp: Date.now()
});
},
// Custom performance metrics
trackCustomPerformance: (metricName, value, unit) => {
datadogRum.addAction('custom_performance', {
metric_name: metricName,
metric_value: value,
metric_unit: unit,
timestamp: Date.now()
});
}
};
return businessMetrics;
}
// Usage examples
const metrics = createCustomMetrics();
// Track conversions
metrics.trackConversion('signup', 0);
metrics.trackConversion('purchase', 29.99);
// Track feature usage
metrics.trackFeatureUsage('advanced_search', {
search_query: 'datadog rum',
results_count: 15,
filters_applied: 3
});
// Track custom performance
metrics.trackCustomPerformance('checkout_completion_time', 4500, 'ms');
metrics.trackCustomPerformance('data_sync_time', 2.5, 'seconds');
Custom Dashboard API
// Create dashboards programmatically using DataDog API
const dashboardApi = {
// Create a new dashboard
createDashboard: async (dashboardConfig) => {
const response = await fetch('https://api.datadoghq.com/api/v1/dashboard', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': 'YOUR_API_KEY',
'DD-APPLICATION-KEY': 'YOUR_APPLICATION_KEY'
},
body: JSON.stringify(dashboardConfig)
});
if (!response.ok) {
throw new Error(`Failed to create dashboard: ${response.statusText}`);
}
return await response.json();
},
// Update existing dashboard
updateDashboard: async (dashboardId, dashboardConfig) => {
const response = await fetch(`https://api.datadoghq.com/api/v1/dashboard/${dashboardId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': 'YOUR_API_KEY',
'DD-APPLICATION-KEY': 'YOUR_APPLICATION_KEY'
},
body: JSON.stringify(dashboardConfig)
});
if (!response.ok) {
throw new Error(`Failed to update dashboard: ${response.statusText}`);
}
return await response.json();
},
// Get dashboard by ID
getDashboard: async (dashboardId) => {
const response = await fetch(`https://api.datadoghq.com/api/v1/dashboard/${dashboardId}`, {
headers: {
'DD-API-KEY': 'YOUR_API_KEY',
'DD-APPLICATION-KEY': 'YOUR_APPLICATION_KEY'
}
});
if (!response.ok) {
throw new Error(`Failed to get dashboard: ${response.statusText}`);
}
return await response.json();
}
};
// Example dashboard creation
const createWebsiteMonitoringDashboard = async () => {
const dashboardConfig = {
title: "Website Performance Monitoring",
description: "Real-time website performance and user experience monitoring",
widgets: [
// Core Web Vitals widget
{
layout: { x: 0, y: 0, width: 6, height: 4 },
definition: {
title: "Core Web Vitals",
type: "timeseries",
requests: [
{
q: "avg:rum.lcp{*} by {env,service}",
aggregator: "avg"
},
{
q: "avg:rum.fid{*} by {env,service}",
aggregator: "avg"
},
{
q: "avg:rum.cls{*} by {env,service}",
aggregator: "avg"
}
]
}
},
// Error rate widget
{
layout: { x: 6, y: 0, width: 6, height: 4 },
definition: {
title: "Error Rate",
type: "timeseries",
requests: [
{
q: "sum:rum.error{*} by {env,service}",
aggregator: "sum"
}
]
}
}
],
template_variables: [
{
name: "env",
default: "production",
options: ["development", "staging", "production"]
},
{
name: "service",
default: "frontend-web-app",
options: ["frontend-web-app", "mobile-app"]
}
],
layout_type: "ordered",
is_read_only: false
};
try {
const result = await dashboardApi.createDashboard(dashboardConfig);
console.log('Dashboard created successfully:', result.url);
return result;
} catch (error) {
console.error('Failed to create dashboard:', error);
throw error;
}
};
Alerting Configuration
Setting Up Alerts
DataDog RUM provides powerful alerting capabilities to notify you of performance issues:
// Alert configuration examples
const alertRules = {
// Core Web Vitals alerts
coreWebVitals: [
{
name: "LCP Performance Alert",
message: "LCP is exceeding the performance threshold",
query: "avg(last_5m):avg:rum.lcp{*} > 2500",
thresholds: {
critical: 2500,
warning: 2000
},
tags: ["team:frontend", "service:web-app"],
priority: 1
},
{
name: "FID Performance Alert",
message: "First Input Delay is too high",
query: "avg(last_5m):avg:rum.fid{*} > 100",
thresholds: {
critical: 100,
warning: 80
},
tags: ["team:frontend", "service:web-app"],
priority: 2
},
{
name: "CLS Performance Alert",
message: "Cumulative Layout Shift is too high",
query: "avg(last_5m):avg:rum.cls{*} > 0.1",
thresholds: {
critical: 0.1,
warning: 0.05
},
tags: ["team:frontend", "service:web-app"],
priority: 3
}
],
// Error rate alerts
errorRate: [
{
name: "High Error Rate Alert",
message: "Error rate is exceeding acceptable levels",
query: "sum(last_5m):sum:rum.error{*} > 100",
thresholds: {
critical: 100,
warning: 50
},
tags: ["team:frontend", "service:web-app"],
priority: 1
},
{
name: "JavaScript Error Alert",
query: "sum(last_5m):sum:rum.error{type:javascript} > 50",
thresholds: {
critical: 50,
warning: 25
},
tags: ["team:frontend", "service:web-app"],
priority: 2
}
],
// User experience alerts
userExperience: [
{
name: "Low Page Views Alert",
message: "Page views have dropped significantly",
query: "sum(last_10m):sum:rum.view{*} < 100",
thresholds: {
critical: 100,
warning: 150
},
tags: ["team:product", "service:web-app"],
priority: 3
}
]
};
// Create alerts programmatically
const alertApi = {
createAlert: async (alertConfig) => {
const response = await fetch('https://api.datadoghq.com/api/v1/monitor', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': 'YOUR_API_KEY',
'DD-APPLICATION-KEY': 'YOUR_APPLICATION_KEY'
},
body: JSON.stringify(alertConfig)
});
if (!response.ok) {
throw new Error(`Failed to create alert: ${response.statusText}`);
}
return await response.json();
},
createAllAlerts: async () => {
const allAlerts = [
...alertRules.coreWebVitals,
...alertRules.errorRate,
...alertRules.userExperience
];
const results = [];
for (const alertConfig of allAlerts) {
try {
const result = await alertApi.createAlert(alertConfig);
results.push({ success: true, config: alertConfig, result });
} catch (error) {
results.push({ success: false, config: alertConfig, error: error.message });
}
}
return results;
}
};
Alert Integration with External Systems
// Integrate DataDog alerts with external systems
const alertIntegrations = {
// Slack integration
slack: {
webhookUrl: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK',
sendSlackAlert: async (alertData) => {
const message = {
text: `:warning: DataDog Alert: ${alertData.title}`,
attachments: [
{
color: alertData.priority === 'critical' ? 'danger' : 'warning',
fields: [
{
title: 'Alert',
value: alertData.message,
short: false
},
{
title: 'Environment',
value: alertData.env || 'unknown',
short: true
},
{
title: 'Service',
value: alertData.service || 'unknown',
short: true
},
{
title: 'Severity',
value: alertData.priority || 'unknown',
short: true
}
]
}
]
};
await fetch(this.webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message)
});
}
},
// Email integration
email: {
sendGridApiKey: 'YOUR_SENDGRID_API_KEY',
sendEmailAlert: async (alertData, recipients) => {
const email = {
personalizations: [
{
to: recipients.map(email => ({ email })),
subject: `DataDog Alert: ${alertData.title}`
}
],
from: { email: 'alerts@yourapp.com' },
content: [
{
type: 'text/html',
value: `
<h2>DataDog Alert</h2>
<p><strong>Alert:</strong> ${alertData.title}</p>
<p><strong>Message:</strong> ${alertData.message}</p>
<p><strong>Environment:</strong> ${alertData.env}</p>
<p><strong>Service:</strong> ${alertData.service}</p>
<p><strong>Severity:</strong> ${alertData.priority}</p>
<p><strong>Timestamp:</strong> ${new Date(alertData.timestamp).toISOString()}</p>
`
}
]
};
await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.sendGridApiKey}`
},
body: JSON.stringify(email)
});
}
},
// PagerDuty integration
pagerDuty: {
integrationKey: 'YOUR_PAGERDUTY_INTEGRATION_KEY',
sendPagerDutyAlert: async (alertData) => {
const incident = {
routing_key: this.integrationKey,
event_action: 'trigger',
dedup_key: `datadog-${alertData.id}`,
payload: {
summary: `${alertData.title}: ${alertData.message}`,
source: 'DataDog RUM',
severity: alertData.priority === 'critical' ? 'critical' : 'error',
component: alertData.service,
group: alertData.env,
class: 'performance',
custom_details: {
alert_id: alertData.id,
query: alertData.query,
value: alertData.value,
threshold: alertData.threshold
}
}
};
await fetch('https://events.pagerduty.com/v2/enqueue', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(incident)
});
}
}
};
Implementation Examples
DataDog RUM JavaScript SDK Integration
Basic Integration
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Web Application</title>
</head>
<body>
<!-- Your application content -->
<!-- DataDog RUM SDK -->
<script src="https://www.datadoghq-browser-agent.com/datadog-rum.js"></script>
<script>
// Initialize DataDog RUM
(function() {
// Configuration based on environment
const config = {
development: {
applicationId: 'dev-application-id',
clientToken: 'dev-client-token',
env: 'development',
sessionSampleRate: 100,
sessionReplaySampleRate: 100,
debug: true
},
staging: {
applicationId: 'staging-application-id',
clientToken: 'staging-client-token',
env: 'staging',
sessionSampleRate: 50,
sessionReplaySampleRate: 20,
debug: false
},
production: {
applicationId: 'prod-application-id',
clientToken: 'prod-client-token',
env: 'production',
sessionSampleRate: 20,
sessionReplaySampleRate: 5,
debug: false
}
};
// Detect environment
function getEnvironment() {
const hostname = window.location.hostname;
if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {
return 'development';
} else if (hostname.includes('staging')) {
return 'staging';
} else {
return 'production';
}
}
const environment = getEnvironment();
const rumConfig = config[environment];
// Initialize DataDog RUM
DD_RUM.onReady(function() {
DD_RUM.init({
...rumConfig,
service: 'your-web-application',
version: '1.0.0',
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
trackFrustrations: true,
defaultPrivacyLevel: 'mask-user-input',
allowedTracingUrls: [
'https://api.yourapp.com',
'https://internal-api.yourapp.com'
],
beforeSend: (event) => {
// Filter or modify events before sending
if (event.type === 'error' && event.error.isTrusted === false) {
return false; // Drop untrusted errors
}
return true;
}
});
// Set user context if available
if (window.currentUser) {
DD_RUM.setUser({
id: window.currentUser.id,
name: window.currentUser.name,
email: window.currentUser.email,
role: window.currentUser.role
});
}
// Add global context
DD_RUM.setGlobalContext({
page_category: window.pageCategory || 'general',
campaign: window.campaign || 'organic'
});
});
})();
</script>
</body>
</html>
Advanced Integration with React
// React integration example
import { useEffect } from 'react';
import { datadogRum } from '@datadog/browser-rum';
// DataDog RUM provider for React
export function DataDogProvider({ children, config }) {
useEffect(() => {
// Initialize DataDog RUM
if (!datadogRum.getInternalContext()) {
datadogRum.init(config);
}
// Track route changes
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
const trackRouteChange = (method) => {
return function(...args) {
const url = args[2];
if (url) {
datadogRum.startView({
name: document.title,
url: url
});
}
return method.apply(this, args);
};
};
history.pushState = trackRouteChange(originalPushState);
history.replaceState = trackRouteChange(originalReplaceState);
// Track initial view
datadogRum.startView({
name: document.title,
url: window.location.href
});
return () => {
history.pushState = originalPushState;
history.replaceState = originalReplaceState;
};
}, [config]);
return children;
}
// Usage in React app
function App() {
const rumConfig = {
applicationId: process.env.REACT_APP_DD_APPLICATION_ID,
clientToken: process.env.REACT_APP_DD_CLIENT_TOKEN,
site: 'datadoghq.com',
service: 'react-web-app',
env: process.env.NODE_ENV,
version: process.env.REACT_APP_VERSION,
sessionSampleRate: 100,
sessionReplaySampleRate: 20,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
defaultPrivacyLevel: 'mask-user-input'
};
return (
<DataDogProvider config={rumConfig}>
<div className="App">
{/* Your app components */}
</div>
</DataDogProvider>
);
}
// Custom hook for tracking user interactions
export function useDataDogTracking() {
const trackUserAction = (actionName, data = {}) => {
datadogRum.addAction(actionName, {
...data,
timestamp: Date.now()
});
};
const trackCustomError = (error, context = {}) => {
datadogRum.addError(error, {
...context,
timestamp: Date.now()
});
};
const trackPerformanceMetric = (metricName, value, unit = 'ms') => {
datadogRum.addAction('performance_metric', {
metric_name: metricName,
metric_value: value,
metric_unit: unit,
timestamp: Date.now()
});
};
return {
trackUserAction,
trackCustomError,
trackPerformanceMetric
};
}
// Usage in components
function CheckoutButton({ onCheckout }) {
const { trackUserAction, trackCustomError, trackPerformanceMetric } = useDataDogTracking();
const handleCheckout = async () => {
const startTime = performance.now();
try {
trackUserAction('checkout_started', {
button_type: 'primary',
cart_value: getCurrentCartValue()
});
await onCheckout();
const duration = performance.now() - startTime;
trackPerformanceMetric('checkout_duration', duration);
trackUserAction('checkout_completed', {
success: true,
duration: duration
});
} catch (error) {
trackCustomError(error, {
action: 'checkout',
cart_value: getCurrentCartValue()
});
trackUserAction('checkout_failed', {
error_message: error.message
});
throw error;
}
};
return (
<button onClick={handleCheckout}>
Checkout
</button>
);
}
Custom Event Tracking for JSON Endpoints
API Monitoring Setup
// Enhanced API monitoring with DataDog RUM
class ApiMonitor {
constructor() {
this.setupInterceptors();
this.metrics = new Map();
this.errorCounts = new Map();
}
setupInterceptors() {
// Fetch interceptor
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const url = args[0];
const options = args[1] || {};
const startTime = performance.now();
try {
const response = await originalFetch.apply(this, args);
const duration = performance.now() - startTime;
this.trackApiCall(url, options.method || 'GET', duration, response);
return response;
} catch (error) {
const duration = performance.now() - startTime;
this.trackApiError(url, options.method || 'GET', duration, error);
throw error;
}
};
// XMLHttpRequest interceptor
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const startTime = performance.now();
// Track request details
let method, url;
xhr.open = function(_method, _url, ...args) {
method = _method;
url = _url;
return originalXHR.prototype.open.apply(xhr, [method, url, ...args]);
};
xhr.addEventListener('loadend', () => {
const duration = performance.now() - startTime;
const success = xhr.status >= 200 && xhr.status < 300;
this.trackApiCall(url, method, duration, {
ok: success,
status: xhr.status
});
});
xhr.addEventListener('error', () => {
const duration = performance.now() - startTime;
this.trackApiError(url, method, duration, new Error('Network error'));
});
return xhr;
}.bind(this);
}
trackApiCall(url, method, duration, response) {
// Extract endpoint name
const endpoint = this.extractEndpoint(url);
// Track in DataDog
datadogRum.addAction('api_call', {
endpoint: endpoint,
method: method,
duration: duration,
status: response.status || (response.ok ? 200 : 500),
success: response.ok,
size: response.headers?.get('content-length') || 0,
timestamp: Date.now()
});
// Track metrics for alerting
this.updateEndpointMetrics(endpoint, duration, response.ok);
// Check for slow API calls
if (duration > 2000) {
datadogRum.addError(new Error(`Slow API call: ${endpoint}`), {
endpoint: endpoint,
method: method,
duration: duration,
threshold: 2000,
severity: 'warning'
});
}
}
trackApiError(url, method, duration, error) {
const endpoint = this.extractEndpoint(url);
datadogRum.addError(error, {
endpoint: endpoint,
method: method,
duration: duration,
error_type: this.categorizeApiError(error),
timestamp: Date.now()
});
// Track error counts for alerting
const errorKey = `${endpoint}:${method}`;
const count = (this.errorCounts.get(errorKey) || 0) + 1;
this.errorCounts.set(errorKey, count);
// Alert on high error rates
if (count >= 10) {
datadogRum.addError(new Error(`High error rate for ${endpoint}`), {
endpoint: endpoint,
error_count: count,
time_window: '5_minutes',
severity: 'critical'
});
}
}
extractEndpoint(url) {
try {
const urlObj = new URL(url);
return urlObj.pathname;
} catch {
return url;
}
}
categorizeApiError(error) {
if (error.name === 'TypeError' && error.message.includes('fetch')) {
return 'network_error';
} else if (error.message.includes('4')) {
return 'client_error';
} else if (error.message.includes('5')) {
return 'server_error';
} else {
return 'unknown_error';
}
}
updateEndpointMetrics(endpoint, duration, success) {
if (!this.metrics.has(endpoint)) {
this.metrics.set(endpoint, {
totalCalls: 0,
successfulCalls: 0,
totalDuration: 0,
errorRate: 0,
averageDuration: 0
});
}
const metrics = this.metrics.get(endpoint);
metrics.totalCalls++;
if (success) {
metrics.successfulCalls++;
}
metrics.totalDuration += duration;
metrics.averageDuration = metrics.totalDuration / metrics.totalCalls;
metrics.errorRate = (metrics.totalCalls - metrics.successfulCalls) / metrics.totalCalls;
}
getEndpointMetrics() {
const summary = {
endpoints: [],
totalCalls: 0,
totalErrors: 0,
averageResponseTime: 0
};
let totalDuration = 0;
this.metrics.forEach((metrics, endpoint) => {
summary.endpoints.push({
endpoint: endpoint,
...metrics
});
summary.totalCalls += metrics.totalCalls;
summary.totalErrors += (metrics.totalCalls - metrics.successfulCalls);
totalDuration += metrics.totalDuration;
});
summary.averageResponseTime = summary.totalCalls > 0
? totalDuration / summary.totalCalls
: 0;
return summary;
}
}
// Initialize API monitoring
const apiMonitor = new ApiMonitor();
// Track API metrics periodically
setInterval(() => {
const metrics = apiMonitor.getEndpointMetrics();
datadogRum.addAction('api_metrics_summary', metrics);
}, 60000); // Every minute
Performance Monitoring Setup
Comprehensive Performance Monitoring
// Comprehensive performance monitoring setup
class PerformanceMonitor {
constructor() {
this.setupCoreWebVitals();
this.setupCustomMetrics();
this.setupResourceMonitoring();
this.setupNavigationTiming();
}
setupCoreWebVitals() {
// LCP monitoring
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
datadogRum.addAction('core_web_vital_lcp', {
value: lastEntry.startTime,
element: lastEntry.element?.tagName || 'unknown',
url: lastEntry.url,
timestamp: Date.now()
});
// Alert on poor LCP
if (lastEntry.startTime > 2500) {
datadogRum.addError(new Error('Poor LCP detected'), {
lcp_value: lastEntry.startTime,
threshold: 2500,
severity: 'warning'
});
}
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
// FID monitoring
const fidObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
const fidValue = entry.processingStart - entry.startTime;
datadogRum.addAction('core_web_vital_fid', {
value: fidValue,
target: entry.target?.tagName || 'unknown',
timestamp: Date.now()
});
// Alert on poor FID
if (fidValue > 100) {
datadogRum.addError(new Error('Poor FID detected'), {
fid_value: fidValue,
threshold: 100,
severity: 'warning'
});
}
});
});
fidObserver.observe({ entryTypes: ['first-input'] });
// CLS monitoring
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (!entry.hadRecentInput) {
const sessionValue = entry.value;
const value = sessionValue - clsValue;
clsValue = sessionValue;
datadogRum.addAction('core_web_vital_cls', {
value: value,
cumulative_value: clsValue,
sources: entry.sources?.length || 0,
timestamp: Date.now()
});
// Alert on poor CLS
if (clsValue > 0.1) {
datadogRum.addError(new Error('Poor CLS detected'), {
cls_value: clsValue,
threshold: 0.1,
severity: 'warning'
});
}
}
});
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
}
setupCustomMetrics() {
// Custom performance metrics
this.measureTimeToInteractive();
this.measureFirstPaint();
this.measureDOMContentLoad();
}
measureTimeToInteractive() {
// Approximate TTI measurement
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'first-input') {
datadogRum.addAction('custom_metric_tti', {
value: entry.startTime,
timestamp: Date.now()
});
observer.disconnect();
}
});
});
observer.observe({ entryTypes: ['first-input'] });
}
measureFirstPaint() {
const fpObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
datadogRum.addAction('custom_metric_fp', {
value: entry.startTime,
timestamp: Date.now()
});
});
});
fpObserver.observe({ entryTypes: ['paint'] });
}
measureDOMContentLoad() {
const navigation = performance.getEntriesByType('navigation')[0];
if (navigation) {
datadogRum.addAction('custom_metric_dom_content_loaded', {
value: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
timestamp: Date.now()
});
}
}
setupResourceMonitoring() {
const resourceObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.duration > 0 && entry.initiatorType) {
const resourceData = {
name: entry.name,
type: entry.initiatorType,
duration: entry.duration,
size: entry.transferSize || 0,
cached: entry.transferSize !== entry.decodedBodySize,
timestamp: Date.now()
};
datadogRum.addAction('resource_load', resourceData);
// Alert on slow resources
if (entry.duration > 1000) {
datadogRum.addError(new Error(`Slow resource: ${entry.name}`), {
resource_name: entry.name,
resource_type: entry.initiatorType,
duration: entry.duration,
threshold: 1000,
severity: 'warning'
});
}
}
});
});
resourceObserver.observe({ entryTypes: ['resource'] });
}
setupNavigationTiming() {
// Monitor navigation timing phases
const navigation = performance.getEntriesByType('navigation')[0];
if (navigation) {
const timingMetrics = {
dns_lookup: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp_connect: navigation.connectEnd - navigation.connectStart,
server_response: navigation.responseStart - navigation.requestStart,
content_download: navigation.responseEnd - navigation.responseStart,
dom_processing: navigation.domComplete - navigation.responseEnd,
total_load_time: navigation.loadEventEnd - navigation.navigationStart
};
Object.entries(timingMetrics).forEach(([phase, duration]) => {
datadogRum.addAction('navigation_timing', {
phase: phase,
duration: duration,
timestamp: Date.now()
});
});
}
}
// Custom performance measurement utility
startCustomTiming(name) {
return {
name: name,
startTime: performance.now(),
end: () => {
const duration = performance.now() - this.startTime;
datadogRum.addAction('custom_timing', {
name: this.name,
duration: duration,
timestamp: Date.now()
});
return duration;
}
};
}
}
// Initialize performance monitoring
const performanceMonitor = new PerformanceMonitor();
// Example usage of custom timing
const timing1 = performanceMonitor.startCustomTiming('user_registration');
// ... registration process ...
timing1.end();
const timing2 = performanceMonitor.startCustomTiming('data_sync');
// ... data sync process ...
timing2.end();
Error Tracking Configuration
Comprehensive Error Tracking
// Comprehensive error tracking configuration
class ErrorTracker {
constructor() {
this.setupGlobalErrorHandlers();
this.setupUnhandledPromiseRejections();
this.setupResourceErrorTracking();
this.setupConsoleErrorTracking();
this.errorCounts = new Map();
this.lastAlertTime = new Map();
}
setupGlobalErrorHandlers() {
// JavaScript runtime errors
window.addEventListener('error', (event) => {
this.trackError({
type: 'javascript',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: event.error,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href,
severity: 'error'
});
});
}
setupUnhandledPromiseRejections() {
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
this.trackError({
type: 'promise',
message: event.reason?.message || 'Unhandled Promise Rejection',
reason: event.reason,
stack: event.reason?.stack,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href,
severity: 'error'
});
});
}
setupResourceErrorTracking() {
// Resource loading errors
window.addEventListener('error', (event) => {
if (event.target && (
event.target.tagName === 'SCRIPT' ||
event.target.tagName === 'LINK' ||
event.target.tagName === 'IMG' ||
event.target.tagName === 'VIDEO' ||
event.target.tagName === 'AUDIO'
)) {
this.trackError({
type: 'resource',
message: `Failed to load ${event.target.tagName.toLowerCase()}: ${event.target.src || event.target.href}`,
target: event.target.tagName.toLowerCase(),
src: event.target.src || event.target.href,
timestamp: Date.now(),
url: window.location.href,
severity: 'warning'
});
}
}, true);
}
setupConsoleErrorTracking() {
// Track console.error calls
const originalConsoleError = console.error;
console.error = (...args) => {
this.trackError({
type: 'console_error',
message: args.join(' '),
args: args,
timestamp: Date.now(),
url: window.location.href,
severity: 'warning'
});
// Call original console.error
originalConsoleError.apply(console, args);
};
}
trackError(errorData) {
// Generate error ID
errorData.id = this.generateErrorId();
// Categorize error
errorData.category = this.categorizeError(errorData);
// Calculate severity
errorData.severity = this.calculateSeverity(errorData);
// Add context
errorData.context = {
userAgent: navigator.userAgent,
viewport: `${window.innerWidth}x${window.innerHeight}`,
url: window.location.href,
referrer: document.referrer,
timestamp: Date.now()
};
// Add user context if available
if (window.currentUser) {
errorData.context.user = {
id: window.currentUser.id,
role: window.currentUser.role
};
}
// Track in DataDog
const error = new Error(`${errorData.type}: ${errorData.message}`);
if (errorData.stack) {
error.stack = errorData.stack;
}
datadogRum.addError(error, {
...errorData,
error_category: errorData.category,
error_severity: errorData.severity,
error_count: this.getErrorCount(errorData.category),
user_session: datadogRum.getInternalContext()?.sessionId
});
// Rate limiting for alerts
this.checkAndAlert(errorData);
// Log for development
if (process.env.NODE_ENV === 'development') {
console.error('Enhanced error tracked:', errorData);
}
}
categorizeError(errorData) {
if (errorData.type === 'javascript') {
if (errorData.message.includes('NetworkError') ||
errorData.message.includes('fetch')) {
return 'network';
} else if (errorData.message.includes('ReferenceError')) {
return 'reference';
} else if (errorData.message.includes('TypeError')) {
return 'type';
} else if (errorData.message.includes('SyntaxError')) {
return 'syntax';
} else {
return 'runtime';
}
} else if (errorData.type === 'resource') {
return errorData.target || 'unknown';
} else if (errorData.type === 'promise') {
return 'async';
} else if (errorData.type === 'console_error') {
return 'debug';
}
return 'unknown';
}
calculateSeverity(errorData) {
const severityMap = {
javascript: { network: 'high', reference: 'medium', type: 'medium', runtime: 'low' },
resource: { script: 'high', css: 'medium', img: 'low' },
promise: 'medium',
console_error: 'low'
};
return severityMap[errorData.type]?.[errorData.category] || 'low';
}
getErrorCount(category) {
const count = (this.errorCounts.get(category) || 0) + 1;
this.errorCounts.set(category, count);
return count;
}
checkAndAlert(errorData) {
const alertKey = `${errorData.type}:${errorData.category}`;
const now = Date.now();
// Check if we should alert (rate limit to once per 5 minutes)
const lastAlert = this.lastAlertTime.get(alertKey);
if (!lastAlert || now - lastAlert > 300000) {
const errorCount = this.errorCounts.get(errorData.category) || 0;
// Alert on high error counts
if (errorCount >= 10) {
datadogRum.addError(new Error(`High error rate: ${errorData.category}`), {
alert_type: 'high_error_rate',
category: errorData.category,
error_count: errorCount,
time_window: '5_minutes',
severity: 'critical'
});
this.lastAlertTime.set(alertKey, now);
}
}
}
generateErrorId() {
return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
getErrorSummary() {
const summary = {
totalErrors: Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0),
byCategory: {},
byType: {},
trends: this.analyzeErrorTrends()
};
this.errorCounts.forEach((count, key) => {
const [type, category] = key.split(':');
if (!summary.byCategory[category]) {
summary.byCategory[category] = 0;
}
if (!summary.byType[type]) {
summary.byType[type] = {};
}
summary.byCategory[category] += count;
summary.byType[type][category] = count;
});
return summary;
}
analyzeErrorTrends() {
// Analyze error patterns
const totalErrors = Array.from(this.errorCounts.values()).reduce((a, b) => a + b, 0);
const mostCommon = Array.from(this.errorCounts.entries())
.sort((a, b) => b[1] - a[1])[0];
return {
totalErrors: totalErrors,
mostCommonError: mostCommon ? {
category: mostCommon[0],
count: mostCommon[1]
} : null,
errorRate: Math.round((totalErrors / (5 * 60)) * 60), // errors per hour
averageErrorCount: totalErrors / this.errorCounts.size
};
}
}
// Initialize error tracking
const errorTracker = new ErrorTracker();
// Track error summary periodically
setInterval(() => {
const summary = errorTracker.getErrorSummary();
datadogRum.addAction('error_summary', summary);
}, 300000); // Every 5 minutes
Best Practices
DataDog RUM Optimization
Performance Optimization
// DataDog RUM optimization best practices
class DataDogOptimizer {
constructor() {
this.optimizationConfig = {
// Sampling rates optimization
sampling: {
development: {
sessionSampleRate: 100,
sessionReplaySampleRate: 100
},
staging: {
sessionSampleRate: 50,
sessionReplaySampleRate: 20
},
production: {
sessionSampleRate: 10,
sessionReplaySampleRate: 5
}
},
// Data collection optimization
collection: {
maxEventsPerSession: 1000,
maxResourceEntries: 500,
maxActionEntries: 200,
flushInterval: 30000 // 30 seconds
},
// Privacy optimization
privacy: {
maskUserInput: true,
maskSensitiveRoutes: true,
excludeInternalTraffic: true
}
};
}
optimizeDataCollection() {
// Configure optimal data collection
datadogRum.init({
// ... other config ...
sessionSampleRate: this.getSessionSampleRate(),
sessionReplaySampleRate: this.getSessionReplaySampleRate(),
// Control data volume
beforeSend: (event) => {
return this.filterEvent(event);
},
// Optimize resource tracking
trackResources: true,
resourceSampleRate: 50, // Sample 50% of resources
// Optimize action tracking
trackUserInteractions: true,
defaultPrivacyLevel: 'mask-user-input'
});
}
getSessionSampleRate() {
const environment = this.getEnvironment();
return this.optimizationConfig.sampling[environment].sessionSampleRate;
}
getSessionReplaySampleRate() {
const environment = this.getEnvironment();
return this.optimizationConfig.sampling[environment].sessionReplaySampleRate;
}
filterEvent(event) {
// Filter out unnecessary events to reduce data volume
// Skip events during development that aren't useful
if (this.isDevelopment() && this.isDevelopmentEvent(event)) {
return false;
}
// Skip resource events for small resources
if (event.type === 'resource' && event.resource?.size < 1000) {
return false;
}
// Skip action events for non-meaningful interactions
if (event.type === 'action' && this.isMeaninglessAction(event)) {
return false;
}
// Limit error events to prevent spam
if (event.type === 'error' && this.isDuplicateError(event)) {
return false;
}
return true;
}
isDevelopmentEvent(event) {
// Filter development-specific events
const devEvents = [
'webpack',
'hot-reload',
'source-map',
'localhost'
];
return devEvents.some(pattern =>
event.resource?.url?.includes(pattern) ||
event.error?.message?.includes(pattern)
);
}
isMeaninglessAction(event) {
// Filter out meaningless user actions
const meaninglessActions = [
'scroll',
'mousemove',
'focus',
'blur'
];
return meaninglessActions.includes(event.action?.type);
}
isDuplicateError(event) {
// Simple deduplication based on error message and stack
const errorKey = `${event.error?.message}:${event.error?.stack?.substring(0, 100)}`;
const now = Date.now();
if (!this.lastErrorTime) {
this.lastErrorTime = new Map();
}
const lastTime = this.lastErrorTime.get(errorKey);
if (lastTime && now - lastTime < 5000) { // 5 second deduplication window
return true;
}
this.lastErrorTime.set(errorKey, now);
return false;
}
getEnvironment() {
if (typeof window !== 'undefined') {
const hostname = window.location.hostname;
if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {
return 'development';
} else if (hostname.includes('staging')) {
return 'staging';
}
}
return 'production';
}
isDevelopment() {
return this.getEnvironment() === 'development';
}
optimizeBundleSize() {
// Only import what you need from DataDog SDK
const requiredImports = [
'@datadog/browser-rum',
'@datadog/browser-core'
];
console.log('DataDog bundle optimization:', {
imports: requiredImports,
treeShaking: true,
deadCodeElimination: true
});
}
optimizeNetworkUsage() {
// Configure network optimization
datadogRum.setGlobalContext({
network_optimization: {
compression_enabled: true,
batch_size: 50,
flush_interval: 30000
}
});
}
}
// Initialize optimization
const optimizer = new DataDogOptimizer();
optimizer.optimizeDataCollection();
optimizer.optimizeBundleSize();
optimizer.optimizeNetworkUsage();
Cost Management
// Cost management strategies for DataDog RUM
class CostManager {
constructor() {
this.costConfig = {
// Sampling strategy for cost control
samplingStrategy: {
// High-value pages get higher sampling
highValuePages: {
routes: ['/checkout', '/signup', '/dashboard'],
sessionSampleRate: 100,
sessionReplaySampleRate: 20
},
// Low-value pages get lower sampling
lowValuePages: {
routes: ['/about', '/contact', '/terms'],
sessionSampleRate: 10,
sessionReplaySampleRate: 1
}
},
// Data retention strategy
retentionStrategy: {
sessionReplay: {
highValue: 30, // 30 days for high-value sessions
standard: 7 // 7 days for standard sessions
}
}
};
}
implementCostControl() {
// Dynamic sampling based on page value
const samplingRate = this.getDynamicSamplingRate();
datadogRum.init({
// ... other config ...
sessionSampleRate: samplingRate.session,
sessionReplaySampleRate: samplingRate.replay,
// Cost control in beforeSend
beforeSend: (event) => {
return this.costOptimizedFilter(event);
}
});
}
getDynamicSamplingRate() {
const currentPath = window.location.pathname;
const userValue = this.getUserValue();
// High-value users get higher sampling
if (userValue === 'high') {
return { session: 100, replay: 20 };
}
// Check if current page is high value
const isHighValuePage = this.costConfig.samplingStrategy.highValuePages.routes
.some(route => currentPath.includes(route));
if (isHighValuePage) {
return { session: 50, replay: 10 };
}
// Default low sampling for cost control
return { session: 5, replay: 1 };
}
getUserValue() {
// Determine user value based on behavior or account type
if (window.currentUser?.plan === 'enterprise') {
return 'high';
}
// Check for high-value behaviors
const sessionData = datadogRum.getInternalContext();
if (sessionData?.user?.lifetimeValue > 1000) {
return 'high';
}
return 'standard';
}
costOptimizedFilter(event) {
// Filter events to reduce costs
// Skip resource events for non-critical resources
if (event.type === 'resource') {
if (!this.isCriticalResource(event.resource?.url)) {
return Math.random() < 0.1; // Only keep 10% of non-critical resources
}
}
// Skip action events during idle periods
if (event.type === 'action') {
if (this.isIdleAction(event)) {
return Math.random() < 0.2; // Only keep 20% of idle actions
}
}
// Always keep error events (high value)
if (event.type === 'error') {
return true;
}
return true;
}
isCriticalResource(url) {
// Define critical resources that should always be tracked
const criticalPatterns = [
/\.css$/,
/main\./,
/bundle\./,
/runtime\./,
/vendor\./
];
return criticalPatterns.some(pattern => pattern.test(url));
}
isIdleAction(event) {
// Define idle actions that can be filtered
const idleActions = ['scroll', 'mousemove'];
return idleActions.includes(event.action?.type);
}
monitorCostMetrics() {
// Track cost-related metrics
setInterval(() => {
const context = datadogRum.getInternalContext();
datadogRum.addAction('cost_metrics', {
events_sent: context?.eventCount || 0,
session_replay_enabled: context?.sessionReplay?.isEnabled || false,
sampling_rate: this.getDynamicSamplingRate(),
estimated_monthly_cost: this.estimateMonthlyCost(),
timestamp: Date.now()
});
}, 300000); // Every 5 minutes
}
estimateMonthlyCost() {
// Estimate monthly cost based on current usage
const context = datadogRum.getInternalContext();
const eventsPerHour = context?.eventCount / (Date.now() / (1000 * 60 * 60));
const estimatedEventsPerMonth = eventsPerHour * 24 * 30;
// DataDog pricing (approximate)
const rumEventCost = 0.000015; // $15 per million events
const replayCost = 0.001; // $1 per session replay
const estimatedCost = estimatedEventsPerMonth * rumEventCost;
return {
estimatedEventsPerMonth: Math.round(estimatedEventsPerMonth),
estimatedCostPerMonth: Math.round(estimatedCost * 100) / 100,
costBreakdown: {
rumEvents: estimatedCost * 0.8,
sessionReplay: estimatedCost * 0.2
}
};
}
setCostAlerts() {
// Set up cost alerts
const costAlertConfig = {
monthlyBudget: 100, // $100 monthly budget
warningThreshold: 0.8, // Alert at 80% of budget
criticalThreshold: 0.9 // Alert at 90% of budget
};
datadogRum.setGlobalContext({
cost_management: costAlertConfig
});
// Monitor costs and alert when approaching budget
setInterval(() => {
const costMetrics = this.estimateMonthlyCost();
const budgetUsage = costMetrics.estimatedCostPerMonth / costAlertConfig.monthlyBudget;
if (budgetUsage >= costAlertConfig.criticalThreshold) {
datadogRum.addError(new Error('Monthly budget critical'), {
budget_usage: budgetUsage,
estimated_cost: costMetrics.estimatedCostPerMonth,
monthly_budget: costAlertConfig.monthlyBudget,
severity: 'critical'
});
} else if (budgetUsage >= costAlertConfig.warningThreshold) {
datadogRum.addError(new Error('Monthly budget warning'), {
budget_usage: budgetUsage,
estimated_cost: costMetrics.estimatedCostPerMonth,
monthly_budget: costAlertConfig.monthlyBudget,
severity: 'warning'
});
}
}, 3600000); // Check every hour
}
}
// Initialize cost management
const costManager = new CostManager();
costManager.implementCostControl();
costManager.monitorCostMetrics();
costManager.setCostAlerts();
Alert Configuration
Effective Alerting Strategy
// Comprehensive alert configuration
class AlertManager {
constructor() {
this.alertConfig = {
// Performance alerts
performance: {
coreWebVitals: {
lcp: {
name: "LCP Performance Degradation",
query: "avg(last_5m):avg:rum.lcp{*} by {env,service} > 2500",
message: "LCP is exceeding the performance threshold",
tags: ["team:frontend", "severity:high"],
priority: 1,
timeout: 900 // 15 minutes
},
fid: {
name: "FID Performance Degradation",
query: "avg(last_5m):avg:rum.fid{*} by {env,service} > 100",
message: "FID is exceeding the performance threshold",
tags: ["team:frontend", "severity:high"],
priority: 1,
timeout: 900
},
cls: {
name: "CLS Performance Degradation",
query: "avg(last_5m):avg:rum.cls{*} by {env,service} > 0.1",
message: "CLS is exceeding the performance threshold",
tags: ["team:frontend", "severity:medium"],
priority: 2,
timeout: 1800 // 30 minutes
}
},
errorRate: {
name: "High Error Rate",
query: "sum(last_5m):sum:rum.error{*} by {env,service} > 100",
message: "Error rate is critically high",
tags: ["team:frontend", "severity:critical"],
priority: 0,
timeout: 300 // 5 minutes
},
apiPerformance: {
name: "API Performance Degradation",
query: "avg(last_5m):avg:rum.resource.duration{type:fetch} by {env,service} > 2000",
message: "API response times are too slow",
tags: ["team:backend", "severity:medium"],
priority: 2,
timeout: 1800
}
},
// Business metric alerts
business: {
conversion: {
name: "Conversion Rate Drop",
query: "sum(last_1h):sum:rum.action{action_type:conversion} by {env,service} < 10",
message: "Conversion rate has dropped significantly",
tags: ["team:product", "severity:high"],
priority: 1,
timeout: 3600 // 1 hour
},
userEngagement: {
name: "Low User Engagement",
query: "sum(last_30m):sum:rum.view{*} by {env,service} < 100",
message: "User engagement is below normal levels",
tags: ["team:product", "severity:medium"],
priority: 3,
timeout: 1800
}
}
};
}
configureAlerts() {
// Set up alerting rules
Object.values(this.alertConfig.performance).forEach(alertGroup => {
Object.values(alertGroup).forEach(alert => {
this.createAlert(alert);
});
});
Object.values(this.alertConfig.business).forEach(alert => {
this.createAlert(alert);
});
}
createAlert(alertConfig) {
// Create individual alert
datadogRum.addAction('alert_configured', {
alert_name: alertConfig.name,
alert_query: alertConfig.query,
alert_threshold: alertConfig.threshold,
alert_severity: alertConfig.priority,
alert_timeout: alertConfig.timeout
});
}
setupAlertEscalation() {
// Set up alert escalation policies
const escalationRules = {
// Critical alerts escalate immediately
critical: {
immediate: ['oncall@company.com', 'manager@company.com'],
sms: ['+1234567890', '+0987654321']
},
// High priority alerts escalate after 15 minutes
high: {
delay: 900, // 15 minutes
email: ['oncall@company.com'],
sms: ['+1234567890']
},
// Medium priority alerts during business hours only
medium: {
businessHours: true,
email: ['team@company.com']
}
};
datadogRum.setGlobalContext({
alert_escalation: escalationRules
});
}
setupAlertRouting() {
// Route alerts to appropriate teams
const routingRules = {
performance: {
core_web_vitals: ['frontend-team'],
error_rate: ['frontend-team', 'backend-team'],
api_performance: ['backend-team']
},
business: {
conversion: ['product-team', 'growth-team'],
user_engagement: ['product-team']
}
};
datadogRum.setGlobalContext({
alert_routing: routingRules
});
}
monitorAlertEffectiveness() {
// Monitor and optimize alert effectiveness
setInterval(() => {
const alertMetrics = {
totalAlerts: this.getTotalAlerts(),
alertResponseTime: this.getAverageResponseTime(),
falsePositiveRate: this.getFalsePositiveRate(),
alertFatigue: this.getAlertFatigueScore(),
timestamp: Date.now()
};
datadogRum.addAction('alert_effectiveness', alertMetrics);
// Optimize alerts based on effectiveness
this.optimizeAlerts(alertMetrics);
}, 86400000); // Daily analysis
}
getTotalAlerts() {
// Calculate total alerts fired
return 0; // Would be calculated from DataDog metrics
}
getAverageResponseTime() {
// Calculate average alert response time
return 300; // Would be calculated from DataDog metrics
}
getFalsePositiveRate() {
// Calculate false positive rate
return 0.05; // 5% false positive rate
}
getAlertFatigueScore() {
// Calculate alert fatigue score based on frequency and response
const alertFrequency = this.getTotalAlerts() / 24; // per hour
const responseRate = 0.8; // 80% response rate
return Math.min(1, alertFrequency / 10 * (1 - responseRate));
}
optimizeAlerts(alertMetrics) {
// Optimize alerts based on effectiveness metrics
if (alertMetrics.falsePositiveRate > 0.1) {
// Too many false positives, adjust thresholds
datadogRum.addError(new Error('High false positive rate detected'), {
current_rate: alertMetrics.falsePositiveRate,
threshold: 0.1,
recommendation: 'Increase alert thresholds'
});
}
if (alertMetrics.alertFatigue > 0.7) {
// Alert fatigue detected, reduce frequency
datadogRum.addError(new Error('Alert fatigue detected'), {
fatigue_score: alertMetrics.alertFatigue,
threshold: 0.7,
recommendation: 'Reduce alert frequency or improve routing'
});
}
}
setupAlertTesting() {
// Set up alert testing and validation
const testAlerts = {
// Test critical path alerts
criticalPath: {
lcp: () => this.testLCPAlert(),
errorRate: () => this.testErrorRateAlert(),
apiPerformance: () => this.testAPIPerformanceAlert()
},
// Test business metric alerts
businessMetrics: {
conversion: () => this.testConversionAlert(),
engagement: () => this.testEngagementAlert()
}
};
// Run alert tests weekly
setInterval(() => {
Object.values(testAlerts.criticalPath).forEach(test => test());
Object.values(testAlerts.businessMetrics).forEach(test => test());
}, 604800000); // Weekly (7 days)
}
testLCPAlert() {
// Test LCP alert by simulating poor performance
datadogRum.addAction('alert_test_lcp', {
test_type: 'lcp_alert',
simulated_lcp: 3000, // Simulate poor LCP
threshold: 2500,
result: 'should_trigger_alert'
});
}
testErrorRateAlert() {
// Test error rate alert
datadogRum.addAction('alert_test_error_rate', {
test_type: 'error_rate_alert',
simulated_errors: 150,
threshold: 100,
result: 'should_trigger_alert'
});
}
testAPIPerformanceAlert() {
// Test API performance alert
datadogRum.addAction('alert_test_api_performance', {
test_type: 'api_performance_alert',
simulated_response_time: 3000,
threshold: 2000,
result: 'should_trigger_alert'
});
}
testConversionAlert() {
// Test conversion alert
datadogRum.addAction('alert_test_conversion', {
test_type: 'conversion_alert',
simulated_conversions: 5,
threshold: 10,
result: 'should_trigger_alert'
});
}
testEngagementAlert() {
// Test user engagement alert
datadogRum.addAction('alert_test_engagement', {
test_type: 'engagement_alert',
simulated_views: 50,
threshold: 100,
result: 'should_trigger_alert'
});
}
}
// Initialize alert management
const alertManager = new AlertManager();
alertManager.configureAlerts();
alertManager.setupAlertEscalation();
alertManager.setupAlertRouting();
alertManager.monitorAlertEffectiveness();
alertManager.setupAlertTesting();
Dashboard Maintenance
Dashboard Optimization and Maintenance
// Dashboard maintenance and optimization
class DashboardManager {
constructor() {
this.dashboardConfig = {
// Dashboard refresh intervals
refreshIntervals: {
realTime: 30, // 30 seconds for critical metrics
nearRealTime: 300, // 5 minutes for important metrics
hourly: 3600, // 1 hour for trend analysis
daily: 86400 // 24 hours for long-term trends
},
// Widget organization
widgetGroups: {
performance: ['core_web_vitals', 'api_performance', 'resource_loading'],
userExperience: ['page_views', 'user_interactions', 'conversion_rates'],
errors: ['error_rates', 'error_types', 'error_trends'],
business: ['revenue_impact', 'user_retention', 'feature_adoption']
}
};
}
optimizeDashboardPerformance() {
// Optimize dashboard loading and performance
// Use efficient queries
const optimizedQueries = {
// Use aggregation for better performance
coreWebVitals: "avg:rum.lcp{*} by {env,service}.rollup(300)",
errorRate: "sum:rum.error{*} by {env,service}.rollup(300)",
pageViews: "sum:rum.view{*} by {env,service}.rollup(300)",
// Use sampling for high-volume metrics
userInteractions: "sum:rum.action{*} by {env,service}.rollup(600).sample(10)",
resourceLoading: "avg:rum.resource.duration{*} by {env,service}.rollup(300)"
};
// Configure dashboard widgets with optimal refresh rates
const widgets = Object.entries(this.dashboardConfig.widgetGroups).map(([group, widgets]) => ({
group: group,
refreshInterval: this.getOptimalRefreshInterval(group),
widgets: widgets.map(widget => ({
name: widget,
query: optimizedQueries[widget],
type: this.getWidgetType(widget)
}))
}));
return widgets;
}
getOptimalRefreshInterval(group) {
const intervals = {
performance: this.dashboardConfig.refreshIntervals.realTime,
errors: this.dashboardConfig.refreshIntervals.realTime,
userExperience: this.dashboardConfig.refreshIntervals.nearRealTime,
business: this.dashboardConfig.refreshIntervals.hourly
};
return intervals[group] || this.dashboardConfig.refreshIntervals.nearRealTime;
}
getWidgetType(widgetName) {
const widgetTypes = {
core_web_vitals: 'timeseries',
api_performance: 'timeseries',
resource_loading: 'timeseries',
page_views: 'timeseries',
user_interactions: 'query_table',
conversion_rates: 'timeseries',
error_rates: 'timeseries',
error_types: 'toplist',
error_trends: 'timeseries',
revenue_impact: 'timeseries',
user_retention: 'timeseries',
feature_adoption: 'timeseries'
};
return widgetTypes[widgetName] || 'timeseries';
}
implementDashboardAlerting() {
// Set up dashboard-specific alerts
const dashboardAlerts = {
// Alert when dashboard widgets fail to load
widgetFailures: {
name: "Dashboard Widget Failure",
condition: "avg(last_5m):dashboard.widget.error{*} > 0",
message: "One or more dashboard widgets are failing to load",
severity: "medium"
},
// Alert on data gaps
dataGaps: {
name: "Data Gap Detected",
condition: "avg(last_10m):rum.view{*} by {env,service} = 0",
message: "No data has been received for 10 minutes",
severity: "high"
},
// Alert on dashboard performance issues
dashboardPerformance: {
name: "Dashboard Performance Issue",
condition: "avg(last_5m):dashboard.load.time{*} > 5000",
message: "Dashboard is taking too long to load",
severity: "low"
}
};
return dashboardAlerts;
}
setupDashboardHealthMonitoring() {
// Monitor dashboard health and performance
const healthMetrics = {
// Track widget loading times
widgetLoadTimes: [],
// Track query performance
queryPerformance: [],
// Track data freshness
dataFreshness: [],
// Track user interactions
userInteractions: []
};
// Monitor widget performance
setInterval(() => {
const performanceMetrics = {
timestamp: Date.now(),
averageWidgetLoadTime: this.calculateAverageWidgetLoadTime(),
slowestWidget: this.getSlowestWidget(),
queryTimeouts: this.getQueryTimeouts(),
dataFreshness: this.getDataFreshnessScore()
};
datadogRum.addAction('dashboard_health', performanceMetrics);
// Alert on performance issues
if (performanceMetrics.averageWidgetLoadTime > 2000) {
datadogRum.addError(new Error('Dashboard performance degradation'), {
average_load_time: performanceMetrics.averageWidgetLoadTime,
threshold: 2000,
slowest_widget: performanceMetrics.slowestWidget,
severity: 'warning'
});
}
if (performanceMetrics.dataFreshness < 0.9) {
datadogRum.addError(new Error('Data freshness issue'), {
freshness_score: performanceMetrics.dataFreshness,
threshold: 0.9,
timeout_count: performanceMetrics.queryTimeouts,
severity: 'high'
});
}
}, 300000); // Every 5 minutes
}
calculateAverageWidgetLoadTime() {
// Calculate average widget load time
return Math.random() * 1000 + 500; // Mock implementation
}
getSlowestWidget() {
// Get the slowest performing widget
return { name: 'resource_analysis', loadTime: 3000 };
}
getQueryTimeouts() {
// Get number of query timeouts
return Math.floor(Math.random() * 10);
}
getDataFreshnessScore() {
// Calculate data freshness score (0-1)
return 0.95; // Mock implementation
}
implementDashboardAutoScaling() {
// Automatically scale dashboard resources based on usage
const autoScalingRules = {
// Scale based on concurrent users
concurrentUsers: {
threshold: 10,
action: 'increase_refresh_frequency'
},
// Scale based on data volume
dataVolume: {
threshold: 1000000, // 1M events per hour
action: 'enable_aggregation'
},
// Scale based on query complexity
queryComplexity: {
threshold: 1000, // 1 second average query time
action: 'simplify_queries'
}
};
// Monitor scaling triggers
setInterval(() => {
const scalingMetrics = {
concurrentUsers: this.getConcurrentUsers(),
dataVolume: this.getDataVolume(),
queryComplexity: this.getQueryComplexity()
};
Object.entries(autoScalingRules).forEach(([metric, rule]) => {
if (scalingMetrics[metric] > rule.threshold) {
this.executeScalingAction(rule.action, scalingMetrics[metric]);
}
});
}, 600000); // Every 10 minutes
}
getConcurrentUsers() {
// Get number of concurrent dashboard users
return 5; // Mock implementation
}
getDataVolume() {
// Get current data volume
return 500000; // Mock implementation
}
getQueryComplexity() {
// Get average query execution time
return 500; // Mock implementation
}
executeScalingAction(action, currentValue) {
// Execute scaling action
const actions = {
increase_refresh_frequency: () => {
datadogRum.addAction('dashboard_scaling', {
action: 'increase_refresh_frequency',
reason: 'high_concurrent_users',
value: currentValue,
timestamp: Date.now()
});
},
enable_aggregation: () => {
datadogRum.addAction('dashboard_scaling', {
action: 'enable_aggregation',
reason: 'high_data_volume',
value: currentValue,
timestamp: Date.now()
});
},
simplify_queries: () => {
datadogRum.addAction('dashboard_scaling', {
action: 'simplify_queries',
reason: 'high_query_complexity',
value: currentValue,
timestamp: Date.now()
});
}
};
const executeAction = actions[action];
if (executeAction) {
executeAction();
}
}
setupDashboardDocumentation() {
// Set up dashboard documentation and onboarding
const dashboardDocumentation = {
// Dashboard purpose and usage
purpose: "Website performance and user experience monitoring",
// Widget descriptions
widgets: {
core_web_vitals: {
description: "Core Web Vitals performance metrics (LCP, FID, CLS)",
usage: "Monitor page loading performance and user experience",
thresholds: {
lcp: "Good: < 2.5s, Needs Improvement: 2.5-4s, Poor: > 4s",
fid: "Good: < 100ms, Needs Improvement: 100-300ms, Poor: > 300ms",
cls: "Good: < 0.1, Needs Improvement: 0.1-0.25, Poor: > 0.25"
}
},
error_rates: {
description: "JavaScript and API error rates",
usage: "Monitor application reliability and error trends",
thresholds: {
error_rate: "Alert when error rate exceeds 5% of total requests"
}
},
user_engagement: {
description: "User engagement and interaction metrics",
usage: "Track user behavior and feature adoption",
thresholds: {
page_views: "Monitor significant drops in user activity",
conversion_rate: "Alert when conversion rate drops below expected levels"
}
}
},
// Alert procedures
alert_procedures: {
critical: "Immediate response required - page 24/7 oncall",
high: "Response within 15 minutes - escalate to team lead",
medium: "Response within 1 hour - standard business hours",
low: "Response within 24 hours - next business day"
}
};
return dashboardDocumentation;
}
scheduleDashboardMaintenance() {
// Schedule regular dashboard maintenance
const maintenanceSchedule = {
// Daily maintenance
daily: {
time: "02:00 AM",
tasks: [
"Verify data freshness",
"Check widget performance",
"Review alert effectiveness"
]
},
// Weekly maintenance
weekly: {
time: "Sunday 03:00 AM",
tasks: [
"Update dashboard queries",
"Review and optimize widget configurations",
"Test alert functionality",
"Update documentation"
]
},
// Monthly maintenance
monthly: {
time: "1st of month 04:00 AM",
tasks: [
"Performance review and optimization",
"Cost analysis and optimization",
"User feedback analysis",
"Plan improvements for next month"
]
}
};
return maintenanceSchedule;
}
}
// Initialize dashboard management
const dashboardManager = new DashboardManager();
dashboardManager.optimizeDashboardPerformance();
dashboardManager.implementDashboardAlerting();
dashboardManager.setupDashboardHealthMonitoring();
dashboardManager.implementDashboardAutoScaling();
dashboardManager.setupDashboardDocumentation();
dashboardManager.scheduleDashboardMaintenance();
Conclusion
DataDog Real User Monitoring (RUM) provides a comprehensive solution for website monitoring that eliminates the need for custom infrastructure while delivering deep insights into user experience and application performance. By following this guide, you can implement a robust monitoring system that scales with your application and provides actionable insights.
Key Takeaways
- Start with Core Web Vitals: Focus on LCP, FID, and CLS as your primary performance indicators
- Configure Smart Sampling: Use appropriate sampling rates to balance data collection with cost
- Set Up Meaningful Alerts: Configure alerts that provide actionable insights without alert fatigue
- Monitor Business Metrics: Track conversion rates and user engagement alongside technical metrics
- Optimize Continuously: Regularly review and optimize your monitoring setup based on usage patterns
Next Steps
- Implement Gradually: Start with basic DataDog RUM setup and expand features over time
- Customize for Your Needs: Adapt the configurations and dashboards to your specific requirements
- Train Your Team: Ensure your team understands how to use and interpret the monitoring data
- Establish Processes: Create runbooks and procedures for responding to alerts and issues
- Measure ROI: Track the impact of monitoring on development velocity and user experience
By leveraging DataDog RUM effectively, you’ll gain comprehensive visibility into your website’s performance and user experience, enabling you to proactively identify and resolve issues before they impact your users.
This guide provides comprehensive coverage of DataDog RUM implementation for website monitoring. Remember to adapt the configurations and examples to your specific application requirements and DataDog subscription plan.