Proliferate’s structured logging goes beyond console.log to provide searchable, correlatable, and analyzable logs.
Why Structured Logging?
Proliferate . logger . info ( 'User logged in' , {
email: '[email protected] ' ,
timestamp: Date . now (),
method: 'oauth' ,
});
// Output: { level: "info", message: "User logged in", attributes: { ... }}
Benefits:
Searchable: Find all logs where method = 'oauth'
Filterable: Show only error level logs
Analyzable: Count logins by method
Correlated: Link logs to errors via trace ID
Enabling Logging
Logging is opt-in. Enable it during initialization:
import Proliferate from '@proliferate/sdk' ;
Proliferate . init ({
endpoint: 'https://api.proliferate.com/api/v1/errors' ,
apiKey: 'pk_your_api_key' ,
logs: {
enabled: true ,
minLevel: 'info' , // Filter out trace and debug in production
},
});
Log Levels
Proliferate supports six log levels:
Level Priority Use Case trace1 Finest-grained debugging, function entry/exit debug5 Detailed debugging information info9 Normal application events warn13 Potential issues that aren’t errors error17 Error conditions fatal21 Critical failures requiring immediate attention
Using the Logger
Basic Usage
const logger = Proliferate . logger ;
logger . trace ( 'Entering calculateTotal' );
logger . debug ( 'Cart items loaded' , { count: items . length });
logger . info ( 'Order placed' , { orderId: order . id , amount: order . total });
logger . warn ( 'Rate limit approaching' , { remaining: 10 , limit: 100 });
logger . error ( 'Payment failed' , { error: err . message , orderId: order . id });
logger . fatal ( 'Database connection lost' , { host: db . host });
Adding Attributes
Attributes are key-value pairs that provide context:
logger . info ( 'User action' , {
action: 'checkout' ,
cart_items: 3 ,
total_amount: 99.99 ,
currency: 'USD' ,
is_returning_customer: true ,
});
Supported attribute types: string, number, boolean, null. Complex values are automatically stringified.
Filtering by Level
The minLevel option filters out lower-priority logs:
Proliferate . init ({
logs: {
enabled: true ,
minLevel: 'info' , // trace and debug are ignored
},
});
logger . debug ( 'This is ignored' );
logger . info ( 'This is captured' );
logger . error ( 'This is captured' );
Level Recommendations
Environment Recommended minLevel Development debug or traceStaging debugProduction info or warn
Every log entry automatically includes:
{
timestamp : 1703500000.123 , // Unix seconds with milliseconds
level : 'info' ,
message : 'User logged in' ,
attributes : { ... },
trace_id : 'a1b2c3d4...' , // For error correlation
user : { id : 'user_123' , email : '[email protected] ' }, // If set
account : { id : 'acct_456' , name : 'Acme Corp' }, // If set
}
Buffer and Flush
Logs are buffered and sent in batches for efficiency:
Proliferate . init ({
logs: {
enabled: true ,
maxBufferSize: 50 , // Flush after 50 logs (default)
flushIntervalMs: 5000 , // Or every 5 seconds (default)
},
});
Automatic Flushing
Logs are automatically flushed:
When the buffer reaches maxBufferSize
Every flushIntervalMs milliseconds
When an error is captured (immediate flush)
When error or fatal level logs are created
When the page becomes hidden (visibility change)
Manual Flushing
// Async flush
await Proliferate . logger . flush ();
// Sync flush (uses sendBeacon, for unload handlers)
Proliferate . logger . flushSync ();
Console Capture
Optionally capture console.* calls as structured logs:
Proliferate . init ({
logs: {
enabled: true ,
captureConsole: true ,
consoleLevels: [ 'log' , 'info' , 'warn' , 'error' ],
},
});
// These now create structured logs
console . log ( 'User clicked button' ); // Captured as 'info' level
console . warn ( 'Deprecated feature used' ); // Captured as 'warn' level
console . error ( 'Failed to load image' ); // Captured as 'error' level
Best Practices
// Good
logger . info ( 'Order completed' , { orderId });
logger . error ( 'Payment failed' , { error: err . message });
// Bad - using wrong levels
logger . error ( 'Order completed' , { orderId }); // Not an error
logger . info ( 'Payment failed' ); // This is an error
// Good - actionable context
logger . error ( 'API request failed' , {
endpoint: '/api/users' ,
status: 500 ,
duration_ms: 1234 ,
request_id: 'req_abc123' ,
});
// Bad - too vague
logger . error ( 'Request failed' );
Avoid Logging Sensitive Data
// Bad - logging sensitive data
logger . info ( 'User logged in' , { password: user . password });
// Good - log only what's needed
logger . info ( 'User logged in' , { userId: user . id , method: 'password' });
// Good - consistent attribute names
logger . info ( 'Order created' , { order_id: '123' , user_id: 'abc' });
logger . info ( 'Order shipped' , { order_id: '123' , carrier: 'ups' });
// Bad - inconsistent naming
logger . info ( 'Order created' , { orderId: '123' , userId: 'abc' });
logger . info ( 'Order shipped' , { order: '123' , shippingCarrier: 'ups' });
// Bad - logging inside hot loops
items . forEach ( item => {
logger . debug ( 'Processing item' , { id: item . id });
});
// Good - log summary
logger . info ( 'Processing items' , { count: items . length });
No-Op Logger
If logging isn’t enabled, the logger methods are no-ops (no-operation), so you can safely call them without checking:
// Safe even if logging is disabled
Proliferate . logger . info ( 'This does nothing if logging is disabled' );