Comprehensive error handling with specific error types for different failure scenarios and event-based error reporting.
Handle errors through event listeners on FluentSender instances.
/**
* Error event - emitted when logging or connection errors occur
* @param error - Error object with details about the failure
*/
sender.on('error', (error) => { });
/**
* Connect event - emitted when connection to Fluentd is established
*/
sender.on('connect', () => { });Usage Examples:
const logger = require('fluent-logger');
// Instance-based error handling
const sender = logger.createFluentSender('app', {
host: 'log-server.example.com',
port: 24224,
enableReconnect: true,
reconnectInterval: 30000
});
// Handle connection errors
sender.on('error', (error) => {
console.error('Fluent Logger error:', error.name, error.message);
// Log to alternative destination
console.error('Failed log data:', error.options);
// Implement fallback logging
if (error.name === 'ResponseTimeout') {
console.log('Timeout occurred, data may have been sent');
} else if (error.name === 'DataTypeError') {
console.error('Invalid data format:', error.options.record);
}
});
// Handle successful connections
sender.on('connect', () => {
console.log('Successfully connected to Fluentd');
});
// Use sender with error handling
sender.emit('user_login', { user_id: 12345, method: 'oauth' });
// Singleton error handling
logger.configure('app', {
host: 'localhost',
port: 24224
});
// Note: For singleton, you need to access the internal sender
logger.on('error', (error) => {
console.error('Singleton logger error:', error);
});Specific error classes for different failure scenarios.
// Base error class
class BaseError extends Error {
name: string;
message: string;
options?: object; // Additional error context
}
// Configuration errors
class ConfigError extends BaseError {
// Thrown for invalid configuration options
}
// Missing tag errors
class MissingTag extends BaseError {
// Thrown when required tag is missing
}
// Response validation errors
class ResponseError extends BaseError {
// Thrown when server response is invalid
}
// Response timeout errors
class ResponseTimeout extends BaseError {
// Thrown when acknowledgment timeout occurs
}
// Data type errors
class DataTypeError extends BaseError {
// Thrown when log data is not an object
}
// Authentication handshake errors
class HandshakeError extends BaseError {
// Thrown during security handshake failures
}Usage Examples:
const logger = require('fluent-logger');
try {
// This will throw ConfigError
const sender = logger.createFluentSender('test', {
eventMode: 'InvalidMode'
});
} catch (error) {
if (error.name === 'ConfigError') {
console.error('Configuration error:', error.message);
}
}
// Handle different error types in event handler
const sender = logger.createFluentSender('app', {
host: 'localhost',
port: 24224,
requireAckResponse: true,
ackResponseTimeout: 5000
});
sender.on('error', (error) => {
switch (error.name) {
case 'ConfigError':
console.error('Configuration problem:', error.message);
break;
case 'MissingTag':
console.error('Missing tag:', error.message);
console.error('Context:', error.options);
break;
case 'DataTypeError':
console.error('Invalid data type:', error.message);
console.error('Received:', typeof error.options.record);
break;
case 'ResponseTimeout':
console.error('Response timeout:', error.message);
console.log('Log may have been sent despite timeout');
break;
case 'ResponseError':
console.error('Server response error:', error.message);
console.error('Details:', error.options);
break;
case 'HandshakeError':
console.error('Authentication failed:', error.message);
console.log('Check shared key and hostname configuration');
break;
default:
console.error('Unknown error:', error.name, error.message);
}
});
// Examples that trigger different errors
// DataTypeError - data must be object
sender.emit('test', 'not an object'); // Triggers DataTypeError
// MissingTag - no tag when sender has no tag_prefix
const noTagSender = logger.createFluentSender(null);
noTagSender.emit(null, { data: 'test' }); // Triggers MissingTag errorHandle errors through callbacks on individual emit operations.
/**
* Emit with error callback
* @param label - Event label
* @param data - Log data object
* @param timestamp - Optional timestamp
* @param callback - Callback function (error) => void
*/
sender.emit(label, data, timestamp?, callback);
sender.emit(label, data, callback);
sender.emit(data, callback);Usage Examples:
const logger = require('fluent-logger');
const sender = logger.createFluentSender('app');
// Handle individual emit errors
sender.emit('user_action', {
action: 'login',
user_id: 12345
}, (error) => {
if (error) {
console.error('Failed to send log:', error.message);
// Implement retry logic
setTimeout(() => {
sender.emit('user_action', {
action: 'login',
user_id: 12345,
retry: true
}, (retryError) => {
if (retryError) {
console.error('Retry also failed:', retryError.message);
} else {
console.log('Retry successful');
}
});
}, 1000);
} else {
console.log('Log sent successfully');
}
});
// Callback with timestamp
sender.emit('timed_event', {
event: 'scheduled_task'
}, new Date(), (error) => {
if (error) {
console.error('Scheduled log failed:', error);
}
});
// Promise wrapper for callback-based API
function emitAsync(sender, label, data, timestamp) {
return new Promise((resolve, reject) => {
const args = [label, data];
if (timestamp) args.push(timestamp);
args.push((error) => {
if (error) reject(error);
else resolve();
});
sender.emit.apply(sender, args);
});
}
// Use async/await with error handling
async function logWithRetry(sender, label, data, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await emitAsync(sender, label, data);
console.log(`Log sent successfully on attempt ${attempt}`);
return;
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error.message);
if (attempt === maxRetries) {
console.error('All retry attempts failed');
throw error;
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
// Usage
logWithRetry(sender, 'critical_event', { alert: 'system_overload' })
.catch(error => console.error('Failed to send critical log:', error));Handle automatic reconnection events and failures.
// Reconnection is handled automatically when enableReconnect: true
// Monitor reconnection through error and connect events
interface ReconnectionOptions {
enableReconnect?: boolean; // Enable automatic reconnection (default: true)
reconnectInterval?: number; // Reconnect interval in ms (default: 600000)
}Usage Examples:
const logger = require('fluent-logger');
const sender = logger.createFluentSender('app', {
host: 'unreliable-server.example.com',
port: 24224,
enableReconnect: true,
reconnectInterval: 10000 // Reconnect every 10 seconds
});
let isConnected = false;
let connectionAttempts = 0;
sender.on('connect', () => {
isConnected = true;
connectionAttempts = 0;
console.log('Connected to Fluentd');
});
sender.on('error', (error) => {
isConnected = false;
connectionAttempts++;
console.error(`Connection error (attempt ${connectionAttempts}):`, error.message);
// Implement circuit breaker pattern
if (connectionAttempts > 5) {
console.warn('Too many connection failures, disabling reconnect temporarily');
sender.enableReconnect = false;
// Re-enable reconnection after 5 minutes
setTimeout(() => {
console.log('Re-enabling reconnection');
sender.enableReconnect = true;
connectionAttempts = 0;
}, 300000);
}
});
// Queue logs when disconnected
const logQueue = [];
function safeEmit(label, data) {
if (isConnected) {
sender.emit(label, data, (error) => {
if (error) {
// Re-queue on error
logQueue.push({ label, data });
}
});
} else {
// Queue for later
logQueue.push({ label, data });
}
}
// Flush queue when connection is restored
sender.on('connect', () => {
console.log(`Flushing ${logQueue.length} queued logs`);
while (logQueue.length > 0) {
const { label, data } = logQueue.shift();
sender.emit(label, data);
}
});
// Usage
safeEmit('user_event', { action: 'click', element: 'button' });Handle graceful shutdown with error handling.
/**
* End connection with optional final emit
* @param label - Final event label (optional)
* @param data - Final event data (optional)
* @param callback - Completion callback (optional)
*/
sender.end(label?, data?, callback?);Usage Examples:
const logger = require('fluent-logger');
const sender = logger.createFluentSender('app');
// Graceful shutdown handler
process.on('SIGINT', () => {
console.log('Shutting down gracefully...');
sender.end('shutdown', {
reason: 'SIGINT',
uptime: process.uptime(),
timestamp: Date.now()
}, (error) => {
if (error) {
console.error('Error during shutdown:', error.message);
process.exit(1);
} else {
console.log('Shutdown log sent successfully');
process.exit(0);
}
});
// Force exit after timeout
setTimeout(() => {
console.error('Shutdown timeout, forcing exit');
process.exit(1);
}, 5000);
});
// Multiple senders shutdown
const senders = [
logger.createFluentSender('app'),
logger.createFluentSender('metrics'),
logger.createFluentSender('audit')
];
async function gracefulShutdown() {
console.log('Shutting down all senders...');
const shutdownPromises = senders.map(sender => {
return new Promise((resolve, reject) => {
sender.end('multi_shutdown', { sender: sender.tag_prefix }, (error) => {
if (error) reject(error);
else resolve();
});
});
});
try {
await Promise.all(shutdownPromises);
console.log('All senders shut down successfully');
} catch (error) {
console.error('Error during multi-sender shutdown:', error);
}
}
process.on('SIGTERM', gracefulShutdown);Error objects include contextual information to help with debugging.
interface ErrorOptions {
tag_prefix?: string; // Sender tag prefix
label?: string; // Event label
record?: any; // Log data that caused error
ack?: string; // Expected acknowledgment ID
chunk?: string; // Received acknowledgment ID
}Usage Examples:
const sender = logger.createFluentSender('debug');
sender.on('error', (error) => {
console.error('Error Details:');
console.error(' Type:', error.name);
console.error(' Message:', error.message);
if (error.options) {
console.error(' Context:', JSON.stringify(error.options, null, 2));
// Access specific context fields
if (error.options.tag_prefix) {
console.error(' Tag Prefix:', error.options.tag_prefix);
}
if (error.options.record) {
console.error(' Failed Record:', error.options.record);
}
if (error.options.ack && error.options.chunk) {
console.error(' ACK Mismatch - Expected:', error.options.ack);
console.error(' ACK Mismatch - Received:', error.options.chunk);
}
}
console.error(' Stack:', error.stack);
});