Skip to main content
Proliferate’s structured logging goes beyond console.log to provide searchable, correlatable, and analyzable logs.

Why Structured Logging?

console.log('User logged in: [email protected] at ' + new Date());
// Output: "User logged in: [email protected] at Mon Dec 25 2023 14:30:00"
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:
LevelPriorityUse Case
trace1Finest-grained debugging, function entry/exit
debug5Detailed debugging information
info9Normal application events
warn13Potential issues that aren’t errors
error17Error conditions
fatal21Critical 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

EnvironmentRecommended minLevel
Developmentdebug or trace
Stagingdebug
Productioninfo or warn

Automatic Metadata

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');
// 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');