Client-side API error reporter with support for Sumo Logic or custom services.
npm install api-reporterfetch() and XMLHttpRequest calls
api-reporter/
โโโ src/
โ โโโ api-reporter.js # Main entry point and ApiReporter class
โ โโโ index.js # Public API exports
โ โโโ constants.js # Configuration constants and defaults
โ โโโ ConfigManager.js # Configuration validation and management
โ โโโ EventManager.js # Event queuing and deduplication
โ โโโ ServiceManager.js # Service abstraction layer
โ โโโ UserProfileManager.js # User context and profile management
โ โ
โ โโโ interceptors/ # Request/response interceptors
โ โ โโโ index.js # Interceptor exports and setup
โ โ โโโ BaseInterceptor.js # Base interceptor functionality
โ โ โโโ FetchInterceptor.js # Fetch API monitoring
โ โ โโโ XHRInterceptor.js # XMLHttpRequest monitoring
โ โ โโโ ResourceInterceptor.js # Resource load error detection
โ โ โโโ PostMessageInterceptor.js # PostMessage communication tracking
โ โ
โ โโโ services/ # External service integrations
โ โ โโโ index.js # Service exports
โ โ โโโ BaseService.js # Base service interface
โ โ โโโ SumoLogicService.js # SumoLogic integration
โ โ
โ โโโ monitors/ # Specialized monitoring components
โ โ โโโ P1FlowMonitor.js # P1 incident detection and visual capture
โ โ
โ โโโ utils/ # Utility functions and helpers
โ โโโ Logger.js # Centralized logging with debug modes
โ โโโ PerformanceMonitor.js # Performance tracking and alerting
โ โโโ request-utils.js # Request parsing utilities
โ โโโ validation-utils.js # Configuration validation helpers
โ
โโโ types/
โ โโโ index.d.ts # TypeScript definitions
โ
โโโ dist/ # Built distribution files
โ โโโ api-reporter.js # ES modules build
โ โโโ api-reporter.min.js # Minified build
โ โโโ api-reporter.umd.js # UMD build for browsers
โ
โโโ docs/ # Documentation
โ โโโ API.md # API reference
โ โโโ CONFIGURATION.md # Configuration guide
โ โโโ EXAMPLES.md # Usage examples
โ
โโโ tests/ # Test files
โ โโโ unit/ # Unit tests
โ โโโ integration/ # Integration tests
โ โโโ fixtures/ # Test data
โ
โโโ package.json # Package configuration
โโโ README.md # This file
โโโ LICENSE # License file
โโโ CHANGELOG.md # Version history
โโโ rollup.config.js # Build configuration
`
๐ฆ Installation
`bash
npm install api-reporter
`
Or include via CDN:
`html
`
๐๏ธ Quick Start
$3
`javascript
import apiReporter from 'api-reporter';
// Initialize with basic monitoring
await apiReporter.init({
// Required: Define which endpoints to monitor
endpoints: ['/api/', '/graphql'],
// Or monitor specific domains
domainsToTrack: ['api.yourapp.com'],
// Required: Service configuration
service: 'sumologic',
serviceOptions: {
endpoint: 'https://endpoint.sumologic.com/receiver/v1/http/YOUR_COLLECTOR_CODE'
},
// Optional: App identification
appName: 'MyApp',
environment: 'production',
// Enable debug logging
debug: true
});
`
$3
Fetch configuration dynamically from your backend while maintaining local fallbacks:
`javascript
await apiReporter.init({
// Backend configuration endpoint
remoteConfigUrl: '/api/reporter-config',
// Local configuration (fallback + local-only fields)
appName: 'MyApp',
environment: 'production',
service: 'sumologic',
serviceOptions: {
endpoint: 'https://collectors.sumologic.com/receiver/v1/http/YOUR_CODE'
},
// These will be merged with backend config
domainsToTrack: ['myapp.com'],
endpoints: ['/api/users']
});
// Check configuration source
console.log('Config source:', apiReporter.getQueueStatus().configSource);
// Outputs: 'backend', 'local', or 'backend+local'
`
Backend Endpoint Example:
`javascript
// Your backend should respond to GET /api/reporter-config
app.get('/api/reporter-config', (req, res) => {
res.json({
configVersion: '1.0.0',
configSource: 'backend',
domainsToTrack: ['api.myapp.com', 'cdn.myapp.com'],
endpoints: ['/api/critical/', '/api/payments/'],
monitorPerformance: true,
slowRequestThreshold: 2000,
// ... other configuration
});
});
`
Configuration Merging Rules:
- ๐ Local-only: appName, environment, service, addCustomUserData
- ๐ Arrays combined: domainsToTrack, endpoints (deduplicated)
- ๐ฏ Backend takes precedence: Performance settings, monitoring rules
- ๐ก๏ธ Graceful fallback: Uses local config if backend unavailable
$3
The API reporter supports dynamic domain expressions that are evaluated at runtime. This is particularly useful when your backend configuration needs to track the current hostname:
`javascript
// Backend returns this configuration:
{
domainsToTrack: [
'window.location.hostname', // Evaluated to actual hostname
'avatrade.com',
'avarnd.com',
'avatrade.io',
'avaapiweb.com',
'hqliveassets.com'
]
}
// The final domainsToTrack will include:
// - The actual hostname (e.g., 'myapp.com' if that's your current domain)
// - All the static domains from the backend
// - Any local domains you've configured
`
Supported Dynamic Expressions:
- 'window.location.hostname' - Evaluates to the current page's hostname
- 'location.hostname' - Alternative syntax for the same
Safety Features:
- โ
Safe evaluation - no arbitrary code execution
- โ
Fallback to original string if evaluation fails
- โ
Works in both browser and server environments
- โ
Automatic deduplication after evaluation
$3
`javascript
await apiReporter.init({
// Monitoring targets
endpoints: ['*'], // Monitor all endpoints
domainsToTrack: ['api.example.com', 'cdn.example.com'],
// Service configuration
service: 'sumologic',
serviceOptions: {
endpoint: 'https://collectors.sumologic.com/receiver/v1/http/YOUR_CODE'
},
// Performance monitoring
monitorPerformance: true,
slowRequestThreshold: 2000, // 2 seconds
averageAlertThreshold: 2500, // 2.5 seconds
// User data extraction
fieldsToExtract: [
{
api: '/api/login',
source: 'response',
fields: ['userId', 'email', 'role']
},
{
api: '*',
source: 'request',
fields: {
'customerId': 'customer_id', // Map field names
'sessionId': 'session_id'
}
}
],
// Resource monitoring
logResourceErrors: true,
// PostMessage monitoring
monitorPostMessages: {
postMessageOriginWhitelist: ['https://trusted-domain.com'],
excludePostMessagesEvents: ['heartbeat', 'ping'],
monitorSpecificPostMessagesEvents: ['payment', 'auth']
},
// P1 Flow Monitoring
enableP1FlowMonitoring: true,
p1Config: {
enabled: true,
captureScreenshot: true,
screenshotScale: 0.5,
screenshotQuality: 0.7,
captureDomSnapshot: true,
captureConsoleErrors: true,
flows: [
{
flow: 'checkout',
enabled: true,
failureThreshold: 2,
completionWindow: 30000,
criticalEndpoints: ['/api/payment', '/api/order'],
criticalPostMessageEvents: ['payment_failed', 'order_error'],
exclude: ['/api/analytics']
}
],
realTimeNotificationApi: 'https://alerts.yourcompany.com/p1',
apiKey: 'your-api-key'
},
// Event management
maxQueueSize: 1000,
batchSize: 10,
retryIntervalMs: 30000,
deduplicationWindowMs: 60000,
maxRetries: 3,
// Custom user data
addCustomUserData: () => ({
sessionId: getSessionId(),
experimentGroup: getUserExperiment(),
customerId: getCustomerId()
}),
// Local storage enrichment
localStorageEnrichmentKeys: ['userId', 'sessionData', 'preferences']
});
`
๐ Core APIs
$3
`javascript
// Manual event logging
apiReporter.logEvent({
type: 'custom_error',
message: 'Something went wrong',
context: {
component: 'checkout',
action: 'submit_payment'
},
extraData: {
orderId: '12345',
errorCode: 'PAYMENT_FAILED'
}
});
// Custom API error events
window.dispatchEvent(new CustomEvent('apiError', {
detail: {
type: 'business_logic_error',
message: 'Invalid coupon code',
request: {
url: '/api/apply-coupon',
method: 'POST',
status: 400
}
}
}));
`
$3
`javascript
// Set user profile
apiReporter.setUserProfile({
userId: '12345',
email: 'user@example.com',
role: 'premium',
segment: 'enterprise'
});
// Get current profile
const profile = apiReporter.getUserProfile();
console.log('Current user:', profile);
`
$3
`javascript
// Get performance metrics
const metrics = apiReporter.getPerformanceMetrics();
console.log('Average response time:', metrics.averageSeconds);
console.log('Slow request percentage:', metrics.slowRequestPercentage);
console.log('Slowest endpoints:', metrics.slowestEndpoints);
`
$3
`javascript
// Check queue status
const status = apiReporter.getQueueStatus();
console.log('Events pending:', status.queueSize);
console.log('Is sending:', status.isSending);
// Force send events
await apiReporter.sendEvents();
// Clear all queued events
apiReporter.clearEvents();
`
๐ฅ P1 Flow Monitoring
P1 Flow Monitoring provides intelligent critical failure detection with rich visual context for debugging.
$3
`javascript
const p1Config = {
enabled: true,
flows: [
{
flow: 'user-registration',
enabled: true,
failureThreshold: 1, // Any failure triggers P1
criticalEndpoints: ['/api/register', '/api/verify-email'],
criticalPostMessageEvents: ['registration_failed'],
exclude: ['/api/analytics', '/api/tracking']
},
{
flow: 'payment-processing',
enabled: true,
failureThreshold: 2, // 2 failures trigger P1
criticalEndpoints: ['/api/payment', '/api/billing'],
criticalPostMessageEvents: ['payment_declined', 'fraud_detected']
}
],
// Visual context capture
captureScreenshot: true,
screenshotScale: 0.5, // Reduce size
screenshotQuality: 0.7,
maxScreenshotSize: 500000, // 500KB limit
// DOM state capture
captureDomSnapshot: true,
// Console error tracking
captureConsoleErrors: true,
maxConsoleErrors: 50,
// Real-time alerting
realTimeNotificationApi: 'https://alerts.company.com/p1',
screenshotUploadEndpoint: 'https://uploads.company.com/screenshots',
apiKey: 'your-p1-api-key'
};
`
$3
When a P1 incident is detected, the following data is captured:
`javascript
{
type: 'p1_incident_alert',
severity: 'critical',
p1Data: {
flow: 'checkout',
reason: 'Critical endpoint failed: /api/payment',
failedEndpoints: [
{
url: '/api/payment',
status: 500,
method: 'POST',
type: 'http'
}
],
flowConfig: {
failureThreshold: 1,
criticalEndpoints: ['/api/payment']
},
userContext: {
userId: '12345',
email: 'user@example.com'
},
visualContext: {
hasScreenshot: true,
screenshotSize: 245,
hasDomSnapshot: true,
consoleErrorCount: 3,
viewport: { width: 1920, height: 1080 },
performanceMetrics: { ... }
}
},
metrics: {
totalRequests: 5,
failedRequests: 1,
successRate: '80.0'
}
}
`
๐๏ธ Configuration Options
$3
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| endpoints | string[] \| string | [] | URL patterns to monitor |
| domainsToTrack | string[] \| string | [] | Domains to monitor (supports dynamic expressions like 'window.location.hostname') |
| service | string \| object | - | Service type or custom service |
| serviceOptions | object | {} | Service-specific configuration |
| appName | string | 'unknown' | Application name |
| environment | string | 'unknown' | Environment (dev/staging/prod) |
| debug | boolean | false | Enable debug logging |
$3
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| monitorPerformance | boolean | false | Enable performance monitoring |
| slowRequestThreshold | number | 2000 | Slow request threshold (ms) |
| averageAlertThreshold | number | 2500 | Average response time alert (ms) |
$3
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| maxQueueSize | number | 1000 | Maximum events in queue |
| batchSize | number | 10 | Events per batch |
| retryIntervalMs | number | 30000 | Retry interval |
| deduplicationWindowMs | number | 60000 | Deduplication window |
| maxRetries | number | 3 | Maximum retry attempts |
$3
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| logResourceErrors | boolean | false | Monitor resource load failures |
| logPreflight | boolean | false | Log OPTIONS requests |
$3
`typescript
interface PostMessageConfig {
postMessageOriginWhitelist?: string[];
excludePostMessagesEvents?: string[];
monitorSpecificPostMessagesEvents?: string[];
}
`
$3
`typescript
interface FieldExtractionRule {
api: string; // URL pattern or '*'
source: 'request' | 'response' | '*';
fields: string[] | Record | RegExp[];
}
`
๐ก Service Integrations
$3
`javascript
apiReporter.init({
service: 'sumologic',
serviceOptions: {
endpoint: 'https://collectors.sumologic.com/receiver/v1/http/YOUR_COLLECTOR_CODE'
}
});
`
$3
`javascript
class CustomService {
async send(event) {
// Send event to your custom endpoint
const response = await fetch('/api/errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event)
});
if (!response.ok) {
throw new Error(Failed to send: ${response.status});
}
}
}
apiReporter.init({
service: new CustomService()
});
`
๐ Event Types
The library automatically captures various event types:
$3
- fetch_http_error - HTTP error from fetch requests
- xhr_http_error - HTTP error from XMLHttpRequest
- fetch_network_error - Network failure in fetch
- xhr_network_error - Network failure in XHR
- xhr_timeout_error - Request timeout
- xhr_aborted_error - Request aborted
$3
- fetch_slow_request - Slow fetch request
- xhr_slow_request - Slow XHR request
- performance_degradation_alert - Overall performance issues
$3
- resource_load_error - Failed CSS/JS/image loads
$3
- postmessage_communication - PostMessage activity
- postmessage_cross_origin - Cross-origin communication
$3
- p1_incident_alert - Critical flow failure
$3
- custom_api_error - Manual error logging
- Any custom type you define
๐จ Event Structure
Each event contains:
`javascript
{
id: "uuid-string",
type: "error_type",
timestamp: 1234567890,
appVersion: "1.0.0",
appName: "MyApp",
environment: "production",
interceptor: "fetch|xhr|resource|manual",
request: {
method: "POST",
url: "https://api.example.com/endpoint",
domain: "api.example.com",
path: "/endpoint",
status: 500,
statusText: "Internal Server Error",
response: {...},
headers: {...}
},
context: {
scriptOrigin: "https://myapp.com",
pageUrl: "https://myapp.com/checkout",
userAgent: "Mozilla/5.0...",
isMonitored: true,
user: {
userId: "12345",
email: "user@example.com",
// ... extracted fields
},
localStorage: {
// ... enrichment data
}
},
// Optional performance data
performance: {
duration: 3500,
durationSeconds: 3.5
},
// Optional P1 data
p1Data: {
flow: "checkout",
reason: "Critical failure",
visualContext: {...}
}
}
`
๐ง Advanced Usage
$3
`javascript
apiReporter.init({
// Extract specific fields from API responses
fieldsToExtract: [
{
api: '/api/login',
source: 'response',
fields: ['token', 'userId', 'permissions']
},
{
api: '/api/user/profile',
source: 'response',
fields: {
'customerId': 'customer_id', // Rename fields
'tier': 'subscription_tier'
}
},
{
api: '*',
source: 'request',
fields: [/^\w+@\w+\.\w+$/] // Extract emails with regex
}
],
// Add custom context to every event
addCustomUserData: () => ({
sessionId: getSessionId(),
experimentGroup: getABTestGroup(),
feature: getCurrentFeature()
})
});
`
$3
P1 screenshots automatically mask sensitive data:
`html
Secret info
PII data
4111-1111-1111-1111
`
$3
`javascript
const p1Monitor = apiReporter.getP1FlowMonitor();
// Track custom events
p1Monitor.trackRequest({
url: '/api/critical-operation',
method: 'POST',
status: 500
});
// Update flow configuration
p1Monitor.updateConfig({
flows: [...newFlows]
});
`
$3
`javascript
// Monitor specific performance metrics
const metrics = apiReporter.getPerformanceMetrics();
if (metrics.averageSeconds > 3.0) {
// Alert development team
apiReporter.logEvent({
type: 'performance_alert',
message: High average response time: ${metrics.averageSeconds}s,
context: {
slowestEndpoints: metrics.slowestEndpoints
}
});
}
`
๐ Privacy & Security
$3
- Automatic masking of sensitive form fields in screenshots
- Configurable field extraction with explicit rules
- No automatic collection of PII without configuration
- Local storage encryption options
$3
`javascript
// Clear user data
apiReporter.setUserProfile({});
apiReporter.clearEvents();
// Disable tracking
apiReporter.destroy();
`
๐ Production Deployment
$3
`javascript
apiReporter.init({
// Optimize for production
debug: false,
maxQueueSize: 500,
batchSize: 20,
retryIntervalMs: 60000,
// Reduce screenshot impact
p1Config: {
screenshotScale: 0.3,
screenshotQuality: 0.5,
maxScreenshotSize: 200000
}
});
`
$3
`javascript
try {
apiReporter.init(config);
} catch (error) {
console.error('Failed to initialize API Reporter:', error);
// Fallback logging
}
// Monitor initialization
setTimeout(() => {
const status = apiReporter.getQueueStatus();
if (!status.isInitialized) {
console.warn('API Reporter failed to initialize');
}
}, 5000);
`
๐งช Testing
$3
`javascript
apiReporter.init({
debug: true,
environment: 'development',
service: 'sumologic',
serviceOptions: {
endpoint: 'https://test-endpoint.sumologic.com/...'
}
});
// Test manual events
apiReporter.logEvent({
type: 'test_event',
message: 'Testing API Reporter'
});
`
$3
`javascript
if (process.env.NODE_ENV === 'test') {
// Don't initialize in test environment
} else {
apiReporter.init(config);
}
`
๐ API Reference
$3
- init(config) - Initialize the reporter
- destroy() - Clean up and stop monitoring
- logEvent(data) - Log custom events
- setUserProfile(profile) - Set user context
- getUserProfile() - Get current user context
- sendEvents() - Force send queued events
- clearEvents() - Clear event queue
- debugMode(enabled) - Toggle debug logging
- getQueueStatus() - Get queue information
- getPerformanceMetrics() - Get performance data
- getP1FlowMonitor()` - Get P1 monitor instance