or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-configuration.mdcore-logging.mderror-handling.mdevent-time.mdindex.mdstream-integration.mdwinston-integration.md
tile.json

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