Enhance Console Logging with a Custom Global Log Wrapper in WebDriverIO

Add Color-Coded Logs, Handle Promises, and Improve Debugging in WebDriverIO

Enhance Console Logging with a Custom Global Log Wrapper in WebDriverIO

Step-by-Step Guide to Creating a Custom Log Wrapper in WebDriverIO

In this guide, we’ll enhance console logging in WebDriverIO by creating a global log wrapper with custom features like color-coding and promise handling. Logging is an essential part of debugging and understanding what your code is doing under the hood. While console.log() works well, enhancing it with custom formatting and color coding can make your logs more readable and informative. In this blog, we’ll walk through how to create a global wrapper around console.log() in your WebDriverIO configuration, adding flexibility and clarity to your log messages.


Why Use a Global Log Wrapper in WebDriverIO?

One of the key challenges in Automation Frameworks is distinguishing your custom log messages from framework or Node.js logs. A simple console output can easily get buried, making it harder to track essential messages. We need a way to make our logs stand out while adding some useful features like skipping empty logs, handling unresolved promises, and color-coding for different log levels.


Why global.d.ts file?

global.d.ts File
/**
 * Logs a message to the console
 * @param text - The text or object to log
 */
declare function log(text: any, level = 'info'): void;

This code is a TypeScript declaration for a global function named log .Let's examine its components:

  1. Function Declaration:

    • The declare keyword is used to tell TypeScript that this function exists somewhere in the global scope, but its implementation is not provided here.

    • This is commonly used in definition files (.d.ts) to describe the shape of existing JavaScript code for TypeScript.

  2. Function Name and Parameters:

    • The function is named log

    • It takes two parameters: text : of type any, meaning it can accept any type of input (string, number, object, etc.).

    • level : which has a default value of 'info'. This parameter is optional due to the default value.

  3. Return Type:

    • The function is declared to return void, meaning it doesn't return any value.
  4. JSDoc Comments:

    • The code includes a JSDoc comment block that provides a brief description of the function and its parameters.

    • This is useful for generating documentation and providing inline help in IDEs.

  5. Usage: This declaration allows you to use the log function anywhere in your TypeScript code without having to import it. For example:

     global.log("Hello, world!");
     global.log({ name: "John", age: 30 });
     global.log("Warning!", "warn");
    
  6. Purpose:

    • This function is likely used for logging messages or objects to the console with different severity levels.

    • The actual implementation (which is not shown here) would determine how these logs are formatted and displayed.

This declaration is particularly useful in a TypeScript project where you want to use a global logging function across different files without needing to import it everywhere. It provides type checking and autocompletion support for the log function in TypeScript-aware editors.


Step 1: Creating a Simple log() Wrapper

The console.log() method is great for outputting information to the console, but we can enhance and shorten it with a custom log() wrapper. Add this snippet at the end of your wdio.conf.ts file:

/**
 * log wrapper
 * @param text to be output to the console window
 */
global.log = (text) => {
    console.log(`---> ${text}`);
}

This new global.log() works like console.log(), but with a little extra formatting to help your logs stand out. Let’s see it in action:

Before:

console.log('Entering text');

Output:

[0-0] Entering text

After:

await global.log('Entering text');

Output:

[0-0] ---> Entering text

By adding this wrapper, your custom messages are clearly separated from the ones generated by the framework or Node.js.


Step 2: How to Handle Promises and Enhancing the log() Function

Let’s take this a step further. What if we want to skip empty strings and null values in our logs? Or detect when a Promise is passed to the log? Here’s an improved version:

global.log = (text) => {
    if (text) { // Truthy value check
        if (text === Promise) {
            console.log('---> WARN: Log was passed a Promise object');
            console.trace();
        } else {
            console.log(`---> ${text}`);
        }
    }
}

This version gives you more control. It:

  • Ignores empty and null values.

  • Warns when a Promise object is passed.

  • Prints a console trace showing where the unresolved Promise originated.


Step 3: How to Add Color-Coded Logs

To make your logs even more distinguishable, we can add color coding using ANSI escape codes. Here’s how:

global.log = (text, level = 'info') => {
    if (text === undefined || text === null) {
        console.log('\x1b[33m%s\x1b[0m', '---> No log content (undefined or null)'); // Yellow for empty values
        return;
    }

    let colorCode;
    switch (level.toLowerCase()) {
        case 'info':
            colorCode = '\x1b[32m'; // Green for info
            break;
        case 'warn':
            colorCode = '\x1b[33m'; // Yellow for warnings
            break;
        case 'error':
            colorCode = '\x1b[31m'; // Red for errors
            break;
        default:
            colorCode = '\x1b[37m'; // White for others
    }

    if (text instanceof Promise) {
        console.log('\x1b[33m%s\x1b[0m', '---> WARN: Log was passed a Promise object'); // Yellow warning
        console.trace();
    } else if (typeof text === 'object') {
        console.log(`${colorCode}%s\x1b[0m`, `---> ${level.toUpperCase()}: ${JSON.stringify(text, null, 2)}`);
    } else {
        console.log(`${colorCode}%s\x1b[0m`, `---> ${level.toUpperCase()}: ${text}`);
    }
};

Function Definition

Here's an updated version of global.log function with color codes:

  • Info logs will be green.

  • Warn logs will be yellow.

  • Error logs will be red.

The global.log function is defined globally, meaning it can be accessed from anywhere in the application.

It takes two parameters:

  • text: The message or object to be logged

  • level: The severity level of the log (default is 'info')

Handling Different Types of Input:

  • If the input is undefined or null, it logs a yellow message saying "No log content".

  • If the input is a Promise, it warns that a Promise was passed and shows a stack trace.

  • If the input is an object, it's converted to a formatted JSON string.

  • For other types (like strings), it's logged as-is.

Color Code Explanation:

  • \x1b[32m: Green color for info logs.

  • \x1b[33m: Yellow color for warn logs.

  • \x1b[31m: Red color for error logs.

  • \x1b[37m: Default white for other log levels.

  • \x1b[0m: Resets the color back to normal after printing the log.

Output Format:

  • Each log message starts with "--->" followed by the uppercase log level and then the message.

  • For objects, it uses JSON.stringify with indentation for better readability.

💡
Check out our guide on setting up WebDriverIO v9 for more details.


Example Usage

Now, you can use this function throughout your framework:

global.log('This is an info log');         // Green text
global.log('This is a warning', 'warn');   // Yellow text
global.log('This is an error!', 'error');  // Red text
global.log({ key: 'value' }, 'info');      // Green formatted object

Output:

[0-0] ---> INFO: This is an info log
[0-0] ---> WARN: This is a warning
[0-0] ---> ERROR: This is an error!
[0-0] ---> INFO: {
  "key": "value"
}

Conclusion

By creating a custom global.log() function, we’ve improved how logs are handled in our framework. From adding color-coding to ignoring empty logs, this wrapper makes debugging much easier, while ensuring the framework’s logs are separated from ours.

Custom log wrapper WebDriverIO with color-coded output Handling promises in global log wrapper WebDriverIO

Have you tried creating your own log wrapper in WebDriverIO? Share your experience in the comments below, and don't forget to subscribe for more WebDriverIO tips and tricks!

Happy logging!


Important Links:


Did you find this article valuable?

Support Hardik Chotaliya by becoming a sponsor. Any amount is appreciated!