CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-fluent-logger

A structured logger for Fluentd (Node.js implementation)

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error handling with specific error types for different failure scenarios and event-based error reporting.

Capabilities

Event-Based Error Handling

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);
});

Error Types

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 error

Callback-Based Error Handling

Handle 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));

Reconnection Handling

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

Graceful Shutdown

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 Context Information

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);
});

docs

advanced-configuration.md

core-logging.md

error-handling.md

event-time.md

index.md

stream-integration.md

winston-integration.md

tile.json