Skip to content

Instantly share code, notes, and snippets.

@jasperkuperus
Last active May 20, 2021 14:26
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jasperkuperus/9df894041e3d5216ce25af03d38ec3f1 to your computer and use it in GitHub Desktop.
Save jasperkuperus/9df894041e3d5216ce25af03d38ec3f1 to your computer and use it in GitHub Desktop.
Winston / Stackdriver severity level
const winston = require('winston');
const { LEVEL } = require('triple-beam');
const SeverityLookup = {
'default': 'DEFAULT',
'silly': 'DEFAULT',
'verbose': 'DEBUG',
'debug': 'DEBUG',
'http': 'notice',
'info': 'info',
'warn': 'WARNING',
'error': 'ERROR',
}
const stackdriverSeverityFormat = winston.format((info) => ({
...info,
// Add severity to your log
severity: SeverityLookup[info[LEVEL]] || SeverityLookup['default'],
}));
const formatters = [
winston.format.timestamp(),
// Add the format that supplements the JSON with severity
stackdriverSeverityFormat(),
winston.format.json(),
];
const logger = winston.createLogger({
level: 'silly',
format: winston.format.combine(...formatters),
stderrLevels: ['error'],
transports: [
new winston.transports.Console(),
],
});
@mlarcher
Copy link

mlarcher commented May 5, 2020

Thank you @jasperkuperus this has been very helpful !
just in case someone wants to go a step further and preserve the errors in the logged output, here's how we went about it :

function preserveErrors(input) {
  const output = {};
  Object.entries(input).forEach(([key, value]) => {
    let outputValue = value;
    if (value instanceof Error) {
      outputValue = {};
      Object.getOwnPropertyNames(value).forEach((errorKey) => {
        outputValue[errorKey] = value[errorKey];
      });
    }
    if (Object.prototype.toString.call(value) === '[object Object]') {
      outputValue = preserveErrors(value);
    }

    output[key] = outputValue;
  });
  return output;
}
const SeverityLookup = {
    default: 'DEFAULT',
    silly: 'DEFAULT',
    verbose: 'DEBUG',
    debug: 'DEBUG',
    http: 'notice',
    info: 'info',
    warn: 'WARNING',
    error: 'ERROR',
  };

  // add severity level for GCP stackdriver
  // cf https://github.com/googleapis/nodejs-logging-winston/issues/386#issuecomment-602195498
  const stackdriverSeverityFormat = format((info) => ({
    ...info,
    severity: SeverityLookup[info[LEVEL]] || SeverityLookup.default,
  }));

  const errorsPreserverFormat = format((info) => {
    // we spread the original `info` too in order to preserve the keys behind Symbols (i.e. splat/level)
    return {
      ...info,
      ...preserveErrors(info),
    };
  });

  const formatters = [format.timestamp(), stackdriverSeverityFormat(), errorsPreserverFormat(), format.json()];

  const loggingWinston = new transports.Console({
    format: format.combine(...formatters),
    stderrLevels: ['error'],
  });

It's probably not bulletproof, but it can help too.
We get decent output in GCP with severity and errors, and that is what we wanted from the nodejs-logging-winston lib. It probably offers more features for error handling on the GCP side, but we don't miss it for now
At least now the logs are found when clicking on "instance logs" of a deployment in GCP, which is a breeze :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment