How to Build a Global Color-Coded Logger in JavaScript and TypeScript (with Examples)


Hey there! Logging is super important when it comes to building any application. It allows you to keep an eye on what's going on inside your program at different points, making it a breeze to debug, maintain, and monitor. In this blog post, we’re excited to show you how to create a global logger in both JavaScript and TypeScript. We’ll make sure it logs execution steps with fun, color-coded outputs for various log levels like info, debug, error, and warn. Let’s dive in!

We'll start with JavaScript and then move on to TypeScript for those who want stronger type safety and scalability in their projects.

Why Build a Global Logger?

A global logger is a centralized logging mechanism that allows you to log important messages from anywhere in your application. It helps:

  • Track function execution and errors.
  • Monitor the application behavior.
  • Debug issues by logging detailed information.
  • Separate different types of logs visually with color-coding (e.g., errors in red, warnings in orange).

Requirements for Our Logger:

  1. Color-Coded Output: To easily identify different log levels.
  2. Global Access: Should be accessible from anywhere in the program.
  3. Timestamps: Each log should include a timestamp to track when an event occurred.
  4. Function Name Logging: Optionally, log the function name where the logger is called.

JavaScript Implementation

We’ll start by building a color-coded global logger in plain JavaScript using the browser’s console.log​ method and CSS-like formatting.

Step-by-Step Implementation:

const Logger = (() => {
  const logLevels = {
    INFO: 'INFO',
    DEBUG: 'DEBUG',
    ERROR: 'ERROR',
    WARN: 'WARN',
  };

  const colors = {
    INFO: 'color: green',    // Green for info
    DEBUG: 'color: blue',    // Blue for debug
    ERROR: 'color: red',     // Red for error
    WARN: 'color: orange',   // Orange for warning
  };

  // Helper function to get the current timestamp
  const getCurrentTimestamp = () => {
    return new Date().toISOString();
  };

  // Logger function
  const log = (level, message, functionName) => {
    const color = colors[level] || 'color: black';
    console.log(`%c[${getCurrentTimestamp()}] [${level}] ${functionName ? '[' + functionName + ']' : ''} - ${message}`, color);
  };

  return {
    info: (message, functionName = '') => log(logLevels.INFO, message, functionName),
    debug: (message, functionName = '') => log(logLevels.DEBUG, message, functionName),
    error: (message, functionName = '') => log(logLevels.ERROR, message, functionName),
    warn: (message, functionName = '') => log(logLevels.WARN, message, functionName),
  };
})();

// Example usage of the global logger

function someFunction() {
  Logger.info('Starting someFunction', 'someFunction');
  Logger.debug('Processing data', 'someFunction');

  try {
    // Simulating execution step
    let data = [1, 2, 3];
    Logger.info('Data initialized', 'someFunction');
    
    // Simulate a step that throws an error
    throw new Error('Something went wrong');
  } catch (error) {
    Logger.error(`Error occurred: ${error.message}`, 'someFunction');
  }

  Logger.warn('Potential issue with someFunction', 'someFunction');
  Logger.info('Ending someFunction', 'someFunction');
}

function anotherFunction() {
  Logger.info('Starting anotherFunction', 'anotherFunction');
  // Other execution steps...
  Logger.info('Ending anotherFunction', 'anotherFunction');
}

// Call the functions to demonstrate the logger
someFunction();
anotherFunction();


How It Works:

  • Color Coding: We use the %c syntax in console.log to apply CSS styles to log messages. For example, error messages are in red, info messages in green, and warn messages in orange.
  • Global Access: The logger is encapsulated in an Immediately Invoked Function Expression (IIFE), creating a singleton that can be accessed anywhere in the program.
  • Timestamps: Each log message includes a timestamp using the new Date().toISOString() method.
  • Function Name: Optionally, you can pass the function name where the logger is called, making the logs more informative.

Example Output:

bithost - ramkrishna

This approach is simple and effective, especially for browser-based JavaScript applications. However, if you're working with TypeScript, you’ll want a more type-safe version.

TypeScript Implementation

TypeScript brings the added benefit of type safety, which makes it easier to manage large-scale applications. We can improve the JavaScript logger by adding type annotations and stricter rules with TypeScript.

TypeScript Version:

type LogLevel = 'INFO' | 'DEBUG' | 'ERROR' | 'WARN';

class Logger {
  private static logLevels: Record<LogLevel, string> = {
    INFO: 'INFO',
    DEBUG: 'DEBUG',
    ERROR: 'ERROR',
    WARN: 'WARN',
  };

  private static colors: Record<LogLevel, string> = {
    INFO: 'color: green',    // Green for info
    DEBUG: 'color: blue',    // Blue for debug
    ERROR: 'color: red',     // Red for error
    WARN: 'color: orange',   // Orange for warning
  };

  // Helper function to get the current timestamp
  private static getCurrentTimestamp(): string {
    return new Date().toISOString();
  }

  // Main logging function
  private static log(level: LogLevel, message: string, functionName?: string): void {
    const color = Logger.colors[level] || 'color: black';
    console.log(`%c[${Logger.getCurrentTimestamp()}] [${level}] ${functionName ? '[' + functionName + ']' : ''} - ${message}`, color);
  }

  public static info(message: string, functionName?: string): void {
    Logger.log(Logger.logLevels.INFO, message, functionName);
  }

  public static debug(message: string, functionName?: string): void {
    Logger.log(Logger.logLevels.DEBUG, message, functionName);
  }

  public static error(message: string, functionName?: string): void {
    Logger.log(Logger.logLevels.ERROR, message, functionName);
  }

  public static warn(message: string, functionName?: string): void {
    Logger.log(Logger.logLevels.WARN, message, functionName);
  }
}

// Example usage

function someFunction(): void {
  Logger.info('Starting someFunction', 'someFunction');
  Logger.debug('Processing data', 'someFunction');

  try {
    let data = [1, 2, 3];
    Logger.info('Data initialized', 'someFunction');

    // Simulating an error
    throw new Error('Something went wrong');
  } catch (error) {
    Logger.error(`Error occurred: ${(error as Error).message}`, 'someFunction');
  }

  Logger.warn('Potential issue with someFunction', 'someFunction');
  Logger.info('Ending someFunction', 'someFunction');
}

function anotherFunction(): void {
  Logger.info('Starting anotherFunction', 'anotherFunction');
  Logger.info('Ending anotherFunction', 'anotherFunction');
}

someFunction();
anotherFunction();


Key Differences in TypeScript:

  • Type Safety: The LogLevel type is a union type, restricting log levels to specific values. This prevents mistakes like passing invalid log levels.
  • Function Overloading: The log() method is private, while info(), debug(), error(), and warn() are public methods with strict typing for both message and functionName.
  • Error Handling: TypeScript ensures we explicitly cast the error as Error when logging error messages.


Example Output:

Just like the JavaScript example, the output will be color-coded and include function names and timestamps.

Conclusion

By building a global logger in both JavaScript and TypeScript, we can easily track our application's behavior while making debugging more efficient. Whether you're working with a smaller script or a large enterprise application, having a consistent and color-coded logging system ensures that your logs are both readable and informative.

  • JavaScript: Perfect for smaller applications or browser-based projects.
  • TypeScript: Ideal for larger, more complex applications where type safety and scalability are essential.

By following these steps, you can integrate color-coded logging into your projects today and enjoy better insight into your application’s inner workings!

Hope you find this helpful !!!

How to Build a Global Color-Coded Logger in JavaScript and TypeScript (with Examples)
Ram Krishna September 26, 2024
Share this post
Sign in to leave a comment
Secure Coding in Node.js: Input Sanitization Using Middleware
Malicious Input Middleware