api-reporter
v1.1.7
Published
Client-side API error reporter with support for Sumo Logic or custom services.
Maintainers
Readme
API Reporter
A comprehensive JavaScript library for monitoring, reporting, and debugging API calls, network requests, and application performance in web applications. Features intelligent error detection, P1 incident monitoring with visual context capture, performance tracking, and real-time alerting.
🚀 Features
Core Monitoring
- HTTP Request Intercepting: Automatic monitoring of
fetch()andXMLHttpRequestcalls - Resource Error Detection: Monitor failed CSS, JS, image, and other resource loads
- PostMessage Monitoring: Track cross-origin and iframe communications
- Performance Monitoring: Detect slow requests and performance degradation
- User Context Tracking: Automatically extract and maintain user profile data
Advanced Capabilities
- P1 Incident Detection: Intelligent critical failure detection with visual context
- Screenshot Capture: Automatic screenshot capture during P1 incidents
- DOM Snapshot: Capture page state for debugging
- Console Error Tracking: Monitor and correlate console errors with incidents
- Real-time Alerting: Immediate notifications for critical issues
Data Management
- Event Deduplication: Prevent duplicate error reports
- Local Storage Persistence: Offline-capable event queuing
- Batch Processing: Efficient event transmission
- Retry Logic: Automatic retry with exponential backoff
📁 Project Structure
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
npm install api-reporterOr include via CDN:
<script src="https://unpkg.com/api-reporter/dist/api-reporter.min.js"></script>🏗️ Quick Start
Basic Setup
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
});🆕 Dynamic Configuration (NEW!)
Fetch configuration dynamically from your backend while maintaining local fallbacks:
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:
// 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
Dynamic Domain Tracking
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:
// 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 configuredSupported 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
Advanced Configuration
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
Event Logging
// 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
}
}
}));User Profile Management
// Set user profile
apiReporter.setUserProfile({
userId: '12345',
email: '[email protected]',
role: 'premium',
segment: 'enterprise'
});
// Get current profile
const profile = apiReporter.getUserProfile();
console.log('Current user:', profile);Performance Monitoring
// 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);Queue Management
// 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.
Flow Configuration
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'
};P1 Event Structure
When a P1 incident is detected, the following data is captured:
{
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: '[email protected]'
},
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
Core Configuration
| 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 |
Performance Monitoring
| 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) |
Event Management
| 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 |
Resource Monitoring
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| logResourceErrors | boolean | false | Monitor resource load failures |
| logPreflight | boolean | false | Log OPTIONS requests |
PostMessage Monitoring
interface PostMessageConfig {
postMessageOriginWhitelist?: string[];
excludePostMessagesEvents?: string[];
monitorSpecificPostMessagesEvents?: string[];
}Field Extraction
interface FieldExtractionRule {
api: string; // URL pattern or '*'
source: 'request' | 'response' | '*';
fields: string[] | Record<string, string> | RegExp[];
}📡 Service Integrations
SumoLogic
apiReporter.init({
service: 'sumologic',
serviceOptions: {
endpoint: 'https://collectors.sumologic.com/receiver/v1/http/YOUR_COLLECTOR_CODE'
}
});Custom Service
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:
HTTP Errors
fetch_http_error- HTTP error from fetch requestsxhr_http_error- HTTP error from XMLHttpRequestfetch_network_error- Network failure in fetchxhr_network_error- Network failure in XHRxhr_timeout_error- Request timeoutxhr_aborted_error- Request aborted
Performance Events
fetch_slow_request- Slow fetch requestxhr_slow_request- Slow XHR requestperformance_degradation_alert- Overall performance issues
Resource Events
resource_load_error- Failed CSS/JS/image loads
PostMessage Events
postmessage_communication- PostMessage activitypostmessage_cross_origin- Cross-origin communication
P1 Events
p1_incident_alert- Critical flow failure
Custom Events
custom_api_error- Manual error logging- Any custom type you define
🎨 Event Structure
Each event contains:
{
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: "[email protected]",
// ... 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
Custom User Data Extraction
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()
})
});Sensitive Data Masking
P1 screenshots automatically mask sensitive data:
<!-- These will be automatically blurred in screenshots -->
<input type="password" />
<input type="credit-card" />
<div data-sensitive="true">Secret info</div>
<div data-mask="true">PII data</div>
<div class="credit-card-number">4111-1111-1111-1111</div>Manual P1 Monitoring
const p1Monitor = apiReporter.getP1FlowMonitor();
// Track custom events
p1Monitor.trackRequest({
url: '/api/critical-operation',
method: 'POST',
status: 500
});
// Update flow configuration
p1Monitor.updateConfig({
flows: [...newFlows]
});Performance Optimization
// 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
Data Protection
- 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
GDPR Compliance
// Clear user data
apiReporter.setUserProfile({});
apiReporter.clearEvents();
// Disable tracking
apiReporter.destroy();🚀 Production Deployment
Performance Considerations
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
}
});Error Handling
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
Development Mode
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'
});Disable in Tests
if (process.env.NODE_ENV === 'test') {
// Don't initialize in test environment
} else {
apiReporter.init(config);
}📚 API Reference
Main Methods
init(config)- Initialize the reporterdestroy()- Clean up and stop monitoringlogEvent(data)- Log custom eventssetUserProfile(profile)- Set user contextgetUserProfile()- Get current user contextsendEvents()- Force send queued eventsclearEvents()- Clear event queuedebugMode(enabled)- Toggle debug logginggetQueueStatus()- Get queue informationgetPerformanceMetrics()- Get performance datagetP1FlowMonitor()- Get P1 monitor instance
Configuration Validation
The library validates all configuration options and provides helpful error messages for invalid configurations.
🤝 Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
📄 License
MIT License - see LICENSE file for details.
🆘 Support
- Documentation: GitHub Wiki
- Issues: GitHub Issues
- Discussions: GitHub Discussions
