CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-log

Universal pluggable logging utility with configurable levels and namespacing support

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

log-writer-system.mddocs/

Log Writer System

Abstract writer system for creating custom log output handlers. Writers subscribe to log events and handle actual output formatting and destination routing, enabling pluggable log output across different environments and platforms.

Capabilities

Abstract Writer Constructor

Base class for creating custom log writers that handle log output.

/**
 * Creates a log writer instance
 * @param {object} env - Environment variables object
 * @param {object} [options] - Optional configuration
 * @param {string} [options.defaultNamespace] - Default namespace for logs
 * @throws {Error} If called without 'new' keyword
 */
function LogWriter(env, options);

Usage Examples:

const LogWriter = require("log/lib/abstract-writer");

// Basic writer setup
const writer = new LogWriter(process.env);

// Writer with options
const writer = new LogWriter(process.env, {
  defaultNamespace: "myapp"
});

// Environment variables used:
// - LOG_LEVEL: threshold level (error, warning, notice, info, debug)
// - LOG_DEBUG: namespace-based debug filter
// - DEBUG: fallback debug filter  
// - LOG_TIME: timestamp format configuration

Static Properties

Class-level properties and methods for writer configuration.

/**
 * Maps log levels to display symbols/prefixes
 * @type {object}
 */
LogWriter.levelPrefixes;

/**
 * Resolves namespace message prefix for display
 * @param {Logger} logger - Logger instance
 * @returns {string|null} Namespace prefix or null
 */
LogWriter.resolveNamespaceMessagePrefix(logger);

Usage Examples:

const LogWriter = require("log/lib/abstract-writer");

// Level symbols/prefixes
console.log(LogWriter.levelPrefixes);
// { debug: "●", info: "ℹ", notice: "ℹ", warning: "⚠", error: "✖" }

// Namespace prefix resolution
const log = require("log");
const appLogger = log.get("myapp:service");

const prefix = LogWriter.resolveNamespaceMessagePrefix(appLogger);
console.log(prefix); // "myapp:service" or shortened version

Instance Methods

Methods available on writer instances for handling log events.

/**
 * Check if a logger is enabled for output
 * @param {Logger} logger - Logger instance to check
 * @returns {boolean} True if logger should output
 */
writer.isLoggerEnabled(logger);

/**
 * Set up level-specific properties on logger
 * @param {Logger} logger - Logger instance to configure
 */
writer.setupLevelLogger(logger);

/**
 * Set up level message prefix on logger
 * @param {Logger} logger - Logger instance to configure  
 */
writer.setupLevelMessagePrefix(logger);

/**
 * Abstract method - must be implemented by subclasses
 * @param {LogEvent} event - Log event to write
 */
writer.writeMessage(event);

/**
 * Resolve message tokens into formatted strings
 * @param {LogEvent} event - Log event to process
 */
writer.resolveMessageTokens(event);

/**
 * Resolve final message from tokens
 * @param {LogEvent} event - Log event to process
 */
writer.resolveMessage(event);

Master Writer Registration

Global writer registration system for managing the active log writer.

/**
 * Get the currently registered master writer
 * @returns {LogWriter|null} Active writer or null
 */
function getMasterWriter();

/**
 * Register a master log writer (only one allowed)
 * @param {LogWriter} writer - Writer instance to register
 * @returns {LogWriter} The registered writer
 * @throws {Error} If master writer already registered or invalid writer
 */
getMasterWriter.register(writer);

Usage Examples:

const getMasterWriter = require("log/lib/get-master-writer");

// Check current master writer
const currentWriter = getMasterWriter();
console.log(currentWriter); // null if none registered

// Register a new writer
const writer = new MyCustomWriter(process.env);
getMasterWriter.register(writer);

// Attempting to register another throws error
try {
  getMasterWriter.register(anotherWriter);
} catch (e) {
  console.log(e.message); // "Cannot register: Master log writer already registered"
}

Event System

Log Events

Writers receive log events through the global event emitter.

/**
 * Log event structure passed to writers
 */
interface LogEvent {
  /** Logger instance that generated the event */
  logger: Logger;
  /** Array of message arguments passed to logger */
  messageTokens: any[];
  /** Resolved message string (set by writer) */
  message?: string;
}

/**
 * Init event structure for new logger initialization
 */
interface InitEvent {
  /** Newly initialized logger instance */
  logger: Logger;
}

Event Emitter

Global event emitter for log system events.

/**
 * Global log event emitter
 * Events:
 * - 'log': Emitted when logger is called with message
 * - 'init': Emitted when new logger is initialized
 */
const emitter = require("log/lib/emitter");

Usage Examples:

const emitter = require("log/lib/emitter");

// Listen for log events
emitter.on("log", (event) => {
  console.log("Log event:", {
    level: event.logger.level,
    namespace: event.logger.namespace,
    tokens: event.messageTokens
  });
});

// Listen for logger initialization
emitter.on("init", (event) => {
  console.log("New logger:", {
    level: event.logger.level,
    namespace: event.logger.namespace
  });
});

Custom Writer Implementation

Basic Writer Example

const LogWriter = require("log/lib/abstract-writer");

class ConsoleWriter extends LogWriter {
  constructor(env, options) {
    super(env, options);
  }
  
  writeMessage(event) {
    const { logger, message } = event;
    
    // Get level prefix
    const levelPrefix = this.constructor.levelPrefixes[logger.level];
    
    // Get namespace prefix
    const namespacePrefix = this.constructor.resolveNamespaceMessagePrefix(logger);
    
    // Format output
    let output = `${levelPrefix} `;
    if (namespacePrefix) {
      output += `[${namespacePrefix}] `;
    }
    output += message;
    
    // Write to console
    console.log(output);
  }
}

// Initialize writer
new ConsoleWriter(process.env);

Advanced Writer Features

class AdvancedWriter extends LogWriter {
  constructor(env, options) {
    super(env, options);
    this.logFile = options?.logFile;
  }
  
  writeMessage(event) {
    const { logger } = event;
    
    // Custom filtering
    if (!this.shouldLog(logger)) {
      return;
    }
    
    // Custom formatting
    const formattedMessage = this.formatMessage(event);
    
    // Multiple outputs
    console.log(formattedMessage);
    
    if (this.logFile) {
      this.writeToFile(formattedMessage);
    }
  }
  
  shouldLog(logger) {
    // Custom logic for log filtering
    return logger.isEnabled && logger.levelIndex <= 2;
  }
  
  formatMessage(event) {
    const timestamp = new Date().toISOString();
    const { logger, message } = event;
    
    return `${timestamp} [${logger.level.toUpperCase()}] ${logger.namespace || 'ROOT'}: ${message}`;
  }
  
  writeToFile(message) {
    // File writing logic
    require('fs').appendFileSync(this.logFile, message + '\n');
  }
}

Environment Configuration

Environment Variables

Writers use environment variables for configuration:

// LOG_LEVEL: Sets visibility threshold
// Values: error, warning, notice, info, debug
// Default: notice
process.env.LOG_LEVEL = "info";

// LOG_DEBUG: Namespace-based debug filtering
// Format: comma-separated namespaces, "-" prefix to disable
// Examples: "myapp", "service:*", "db,-db:verbose"  
process.env.LOG_DEBUG = "myapp:service,database";

// DEBUG: Fallback debug filter (debug-lib compatible)
process.env.DEBUG = "myapp:*";

// LOG_TIME: Timestamp configuration
// Implementation-specific format
process.env.LOG_TIME = "iso";

Visibility Control

Writers automatically apply visibility control based on level thresholds and namespace filters:

const LogWriter = require("log/lib/abstract-writer");

// Environment setup
process.env.LOG_LEVEL = "notice";  // Show notice and above
process.env.LOG_DEBUG = "myapp";   // Also show debug logs for "myapp" namespace

const writer = new MyWriter(process.env);

// These will be visible (notice and above):
log.error("Error message");    // ✓ Visible (error >= notice)
log.warning("Warning");        // ✓ Visible (warning >= notice) 
log.notice("Notice");          // ✓ Visible (notice >= notice)

// These will be hidden (below notice threshold):
log.info("Info message");      // ✗ Hidden (info < notice)
log.debug("Debug message");    // ✗ Hidden (debug < notice)

// But debug namespace is enabled:
log.get("myapp").debug("Debug in myapp"); // ✓ Visible (namespace filter)

docs

core-logging.md

enable-disable-control.md

index.md

log-writer-system.md

namespace-management.md

utility-functions.md

tile.json