Custom console log function, a console.log wrapper

Learn custom console log function, a console.log wrapper with practical examples, diagrams, and best practices. Covers javascript, debug-backtrace development techniques with visual explanations.

Crafting a Custom Console Log Function for Enhanced Debugging

Hero image for Custom console log function, a console.log wrapper

Learn how to create a custom console.log wrapper in JavaScript to add features like timestamps, log levels, and debug backtraces, significantly improving your debugging workflow.

The console.log function is an indispensable tool for JavaScript developers, offering a quick way to inspect variables and track program flow. However, its basic functionality can sometimes fall short in complex applications. This article will guide you through creating a custom console.log wrapper, allowing you to extend its capabilities with features like timestamps, log levels, and automatic debug backtraces. This approach not only centralizes your logging logic but also provides a more robust and informative debugging experience.

Why Wrap console.log?

Wrapping console.log offers several advantages over using it directly. Firstly, it provides a single point of control for all logging output. This means you can easily enable or disable logging, filter messages by severity, or even send logs to a remote server without modifying every console.log call in your codebase. Secondly, it allows you to enrich log messages with additional context, such as the exact time of the log, the file and line number where the log originated, or even the current user's session ID. This extra information is invaluable when diagnosing issues, especially in production environments.

flowchart TD
    A[Application Code] --> B{Custom Log Function}
    B --> C{Add Timestamp}
    B --> D{Add Backtrace}
    B --> E{Filter by Level}
    C --> F[Format Message]
    D --> F
    E --> F
    F --> G[console.log()]
    G --> H[Browser Console]

Flowchart of a custom console log function's processing steps.

Implementing a Basic Wrapper

Let's start with a simple wrapper that adds a timestamp to each log message. This foundational step demonstrates the core concept of intercepting and enhancing console.log calls. We'll create a global object or a module that exposes our custom logging functions, ensuring they can be easily accessed throughout your application. This basic structure will serve as the canvas for more advanced features.

const customLogger = {
  log: (...args) => {
    const timestamp = new Date().toISOString();
    console.log(`[${timestamp}]`, ...args);
  },
  warn: (...args) => {
    const timestamp = new Date().toISOString();
    console.warn(`[${timestamp}]`, ...args);
  },
  error: (...args) => {
    const timestamp = new Date().toISOString();
    console.error(`[${timestamp}]`, ...args);
  }
};

// Usage:
customLogger.log('This is a regular message.');
customLogger.warn('Something might be wrong.');
customLogger.error('An error occurred!');

A basic custom logger adding timestamps to console messages.

Adding Debug Backtraces

One of the most powerful features you can add to a custom logger is the ability to automatically include a debug backtrace. A backtrace (or stack trace) shows the sequence of function calls that led to the current point in the code, making it incredibly easy to pinpoint the exact location of a log message. This is particularly useful when dealing with deeply nested function calls or asynchronous operations. We can achieve this by creating an Error object and inspecting its stack property.

const customLoggerWithTrace = {
  log: (...args) => {
    const timestamp = new Date().toISOString();
    const error = new Error();
    const stack = error.stack.split('\n');
    // The first two lines are usually 'Error' and the logger function itself
    const callerLine = stack[2] ? stack[2].trim() : 'unknown caller';
    console.log(`[${timestamp}] [${callerLine}]`, ...args);
  },
  // ... similar for warn and error
};

function firstFunction() {
  secondFunction();
}

function secondFunction() {
  customLoggerWithTrace.log('Message from second function.');
}

firstFunction();

Custom logger with automatic debug backtrace inclusion.

Advanced Features: Log Levels and Environment Control

To make your custom logger truly versatile, you can introduce log levels (e.g., debug, info, warn, error) and control logging based on the environment. This allows you to show verbose debug messages only during development, while only warn and error messages are visible in production. This approach significantly reduces console clutter and prevents sensitive information from being exposed in live environments.

const LOG_LEVELS = {
  DEBUG: 0,
  INFO: 1,
  WARN: 2,
  ERROR: 3,
  NONE: 4
};

// Set the minimum log level for the current environment
// In a real app, this might come from an environment variable
const currentLogLevel = LOG_LEVELS.DEBUG; // Example: LOG_LEVELS.ERROR for production

const advancedLogger = {
  _log: (level, ...args) => {
    if (level >= currentLogLevel) {
      const timestamp = new Date().toISOString();
      const error = new Error();
      const stack = error.stack.split('\n');
      const callerLine = stack[3] ? stack[3].trim() : 'unknown caller'; // Adjust stack index for _log wrapper
      const prefix = `[${timestamp}] [${Object.keys(LOG_LEVELS).find(key => LOG_LEVELS[key] === level)}] [${callerLine}]`;

      switch (level) {
        case LOG_LEVELS.DEBUG:
        case LOG_LEVELS.INFO:
          console.log(prefix, ...args);
          break;
        case LOG_LEVELS.WARN:
          console.warn(prefix, ...args);
          break;
        case LOG_LEVELS.ERROR:
          console.error(prefix, ...args);
          break;
      }
    }
  },
  debug: (...args) => advancedLogger._log(LOG_LEVELS.DEBUG, ...args),
  info: (...args) => advancedLogger._log(LOG_LEVELS.INFO, ...args),
  warn: (...args) => advancedLogger._log(LOG_LEVELS.WARN, ...args),
  error: (...args) => advancedLogger._log(LOG_LEVELS.ERROR, ...args)
};

// Usage:
advancedLogger.debug('This is a debug message.');
advancedLogger.info('User logged in.');
advancedLogger.warn('Deprecated API usage.');
advancedLogger.error('Failed to fetch data.');

Custom logger with log levels and environment-based filtering.