Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a more verbose example of customizing log output #365

Open
MikeRatcliffe opened this issue Aug 15, 2023 · 2 comments
Open

Add a more verbose example of customizing log output #365

MikeRatcliffe opened this issue Aug 15, 2023 · 2 comments

Comments

@MikeRatcliffe
Copy link

MikeRatcliffe commented Aug 15, 2023

I've been playing around with 5.0.0-beta.25 and found very little about customizing the output in this version. After going through the source, I have created a more verbose example to help others.

The only strange thing is that your toJSON() method in electron-log/src/main/transforms/object.js returns an object, rather than JSON. It removes cyclic references so that JSON.stringify() can safely handle them, but the method name should probably be changed.

Renderer

This is my logger.js file. It initializes the log and keeps the format simple. Most importantly with this setup, the output from console.log(), console.error() etc. in the renderer process appear in both DevTools and the terminal output:

import log from 'electron-log/renderer';

log.transports.console.format = '{text}';

// Echo console.log() etc. to the terminal using electron logger.
Object.assign(console, log.functions);

export { log };

Main Process

The log customization in the main process is more verbose, but simple to customize.

global.logHome and global.logLevel need to be set first:

const chalk = require('chalk');
const log = require('electron-log/main');
const { maxDepth, toJSON } = require('electron-log/src/main/transforms/object');

async function initLogs() {
  const color = {
    error: chalk.bgRed.white.bold,
    warn: chalk.yellow.bold,
    info: chalk.blue.bold,
    verbose: (t) => t,
    debug: (t) => t,
    silly: (t) => t,
  };

  log.transports.file.resolvePathFn = () => path.join(global.logHome, 'debug_last.log');
  log.transports.file.level = global.logLevel;

  // Change the console output to just the text we create in our hook.
  log.transports.console.format = '{text}';

  log.hooks.push((message, transport) => {
    let text = null;

    if (transport !== log.transports.console) {
      return message;
    }

    // Clone message and data because they are shared by the different
    // transports.
    const newMessage = Object.assign({}, message);
    const { data, date, level } = newMessage;
    const dataClone = [...data];

    if (typeof dataClone[0] === 'string') {
      text = dataClone[0];
    } else {
      // Deal with objects, arrays etc.
      // Step 1: Ensure the object is not deeper the 6 levels.
      let safeObj = maxDepth({ data: dataClone[0] });

      // Step 2: This 'toJSON' method actually removes cyclic references so that
      // JSON.stringify() can safely handle them.
      safeObj = toJSON({ data: safeObj });

      // Step 3: JSON.stringify() the safe object
      text = JSON.stringify(toJSON({ data: safeObj }));
    }

    // Personal tweak to highlight messages starting with 'XXXXX'
    if (text.startsWith('XXXXX')) {
      text = chalk.bold(text);
    }

    // Build strings ready for output
    const colorize = color[level];
    const lvl = ('[' + level + ']').padStart(9, ' ');
    const formattedTime = date.toTimeString().substring(0, 8);

    // Tag entries with their process type:
    //   - M: main
    //   - R: renderer
    const processType = newMessage.variables.processType === 'main' ? 'M' : 'R';

    // Colorize the beginning of the output
    const prefix = colorize(`${formattedTime} ${processType} ${lvl}`);

    // Add the final string back to the clone of the data array and save it to
    // newMessage.data
    dataClone[0] = `${prefix} ${text}`;
    newMessage.data = dataClone;

    // Return the newly constructed message
    return newMessage;
  });

  log.initialize({ preload: true });

  Object.assign(console, log.functions);
}
@megahertz
Copy link
Owner

I agree, it makes sense to make an example of output processing. In v5 a new property transport.transforms was introduced for that. I have added it to type definitions. But I want to play a bit more with it and add the corresponding documentation when I'm sure the public interface is good enough.

As for toJSON function, it just follows the standard convention when it returns a JSON representation of some object, it doesn't have to be a string.

@frankz61
Copy link

frankz61 commented Nov 2, 2023

It really helped me. Sometimes, it may be necessary to add chalk.level = 3 to make colorization work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants