Winston: Parece que 3.0 está registrando {} objetos de error

Creado en 19 mar. 2018  ·  12Comentarios  ·  Fuente: winstonjs/winston

Hola a todos
Tengo algunos problemas con winston 3.0 que registra objetos de error vacíos cuando usa un formato json.

module.exports = winston.createLogger({
  format: format.combine(
    format.label({ label: 'my label'}),
    format.timestamp(),
    format.prettyPrint()
  ),
  transports: [
    new (winston.transports.Console)({
      level: error
    })
  ]
}); 

registra lo esperado

 {error: [stack trace here] }
 ```

 while 

module.exports = winston.createLogger ({
formato: format.combine (
format.label ({label: 'my label'}),
format.timestamp (),
format.json ()
),
transportes: [
nuevo (winston.transports.Console) ({
nivel: error
})
]
});

logs the erroneous

{error: {} }

when it is passed 

logger.log ({
error: nuevo Error ('mi mensaje de error')
});

Manually creating a format with 

winston.format.printf
`` ``
también devolvió el resultado erróneo.

¿Alguien sabe de una solución para esto?

Comentario más útil

Versión más concisa:

function format() {
  const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
  const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
  return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}

const logger = createLogger({
  format: format(),
});

Pero esto es lo que realmente hago para tener registros bonitos localmente y errores JSON en producción:

function prodFormat() {
  const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
  const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
  return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}

function devFormat() {
  const formatMessage = info => `${info.level} ${info.message}`;
  const formatError = info => `${info.level} ${info.message}\n\n${info.stack}\n`;
  const format = info => info instanceof Error ? formatError(info) : formatMessage(info);
  return combine(colorize(), printf(format))
}

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  exitOnError: false,
  transports: [new transports.Console()],
  format: isProd ? prodFormat() : devFormat(),
});

que me da esto en produccion

bildschirmfoto 2018-09-21 um 16 46 35

y esto en desarrollo

bildschirmfoto 2018-09-21 um 16 47 04

Todos 12 comentarios

Una solución alternativa:

function errorReplacer(key, value) {
  if (value instanceof Error) {
    return { message: value.message, stack: value.stack };
  }
  return value;
}

const logFormat = winston.format.printf((info) => {
  return `${JSON.stringify(info, errorReplacer)}`;
});

module.exports = winston.createLogger({
  format: format.combine(
    format.label({ label: 'my label' }),
    format.timestamp(),
    logFormat
  ), ...

Espero eso ayude.

La solución no funcionó para mí (el mensaje de información se pierde en alguna parte)

Solución alternativa:

const winston = require('winston')
const MESSAGE = Symbol.for('message')

/*
 * function replacer (key, value)
 * Handles proper stringification of Buffer output.
 */
const replacer = function (key, value) {
  return value instanceof Buffer
    ? value.toString('base64')
    : value
}

var json_log = winston.format(function (info, opts) {
  var display = info
  if (info._error) {
    display = Object.assign({}, info, { message: info._error })
    delete display._error
  }
  info[MESSAGE] = JSON.stringify(display, opts.replacer || replacer, opts.space)
  return info
})

var preserve_error_message = format(function (info, opts) {
  if (_.isError(info)) {
    info._error = info.message
  }
  return info
})

const logger = winston.createLogger({
  levels: ...,
  format: format.combine(format.timestamp(), preserve_error_message()),
  transports: [
    new transports.Console({
    level: this.config.console.level,
    format: json_log({ space: 2 })
  })
]

Eso no parece particularmente elegante, por lo que un funcionario para preservar info.message cuando info es un Error sería ideal (funciona cuando info es un Objeto)

Me estoy encontrando con esto también. ¿No debería winston manejar esto desde el primer momento? @indexzero ¿qué opinas de esto?

Después de mirar la implementación de logform json, ahora veo cómo se supone que se debe usar opts.replacer y se me ocurrió otra solución al combinar esta publicación de stackoverflow con la cadena de búfer existente:

'use strict';

const winston = require('../');

const logger = winston.createLogger({
    format: winston.format.combine(
        winston.format.json({ replacer: replaceErrors })
    ),
    transports: [
        new winston.transports.Console(),
    ]
});

function replaceErrors(key, value) {
    if (value instanceof Buffer) {
        return value.toString('base64');
    } else if (value instanceof Error) {
        var error = {};

        Object.getOwnPropertyNames(value).forEach(function (key) {
            error[key] = value[key];
        });

        return error;
    }

    return value;
}


var error = new Error('foooo');
error.my_custom_stuff = 'bar';

logger.log('error', 'Hello, this is a raw logging event', { error });

que imprime:

{"error":{"stack":"Error: foooo\n    at Object.<anonymous> (/home/dino/work/winston/examples/json.js:31:13)\n    at Module._compile (module.js:662:30)\n    at Object.Module._extensions..js (module.js:673:10)\n    at Module.load (module.js:575:32)\n    at tryModuleLoad (module.js:515:12)\n    at Function.Module._load (module.js:507:3)\n    at Function.Module.runMain (module.js:703:10)\n    at startup (bootstrap_node.js:193:16)\n    at bootstrap_node.js:660:3","message":"foooo","my_custom_stuff":"bar"},"level":"error","message":"Hello, this is a raw logging event"}

Ya no creo que esto deba manejarse fuera de la caja, porque creo que una biblioteca de registro no debe determinar cómo se debe serializar su error en un mensaje de registro, usted mismo lo define.

No estoy seguro de su caso de uso exacto, pero ¿por qué no está registrando errores directamente?

logger.log(new Error('my error message'));

(Eso funciona a partir de https://github.com/winstonjs/winston/pull/1234).

Si necesita el objeto de error dentro de un objeto más grande que está registrando, entonces vale la pena señalar que Node.js Errors no son objetos JSON, pero hay cosas como https://www.npmjs.com/package / utils-error-to-json o quizás haciendo un parse(stringify(error)) que haría que esto funcione con el formateador JSON. Parece que eso es lo que hace la solución de

Si va a seguir adelante y marcar esto como cerrado, no dude en volver a abrir o hacer un nuevo número si surgen nuevos problemas / preguntas. ¡Gracias!

Ni siquiera parse(stringify(error)) funciona, ya que aún pierde el seguimiento de la pila.
Me las arreglé para hacer que esto funcione creando un formateador personalizado como este:

const formatErrorConverter = winston.format(
    info =>
        info instanceof Error
            ? Object.assign({ level: info.level, message: info.message, stack: info.stack }, info)
            : info,
);

¡Y funciona bastante bien!

instance of Error funciona solo para formateadores de nivel superior. NO funciona para formateadores de nivel de transporte. ¿Alguien puede explicar por qué?

Versión más concisa:

function format() {
  const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
  const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
  return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}

const logger = createLogger({
  format: format(),
});

Pero esto es lo que realmente hago para tener registros bonitos localmente y errores JSON en producción:

function prodFormat() {
  const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
  const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
  return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}

function devFormat() {
  const formatMessage = info => `${info.level} ${info.message}`;
  const formatError = info => `${info.level} ${info.message}\n\n${info.stack}\n`;
  const format = info => info instanceof Error ? formatError(info) : formatMessage(info);
  return combine(colorize(), printf(format))
}

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  exitOnError: false,
  transports: [new transports.Console()],
  format: isProd ? prodFormat() : devFormat(),
});

que me da esto en produccion

bildschirmfoto 2018-09-21 um 16 46 35

y esto en desarrollo

bildschirmfoto 2018-09-21 um 16 47 04

Escribí esto para admitir clases de error personalizadas con propiedades desconocidas y también para limpiar la pila porque esas nuevas líneas son horribles en el registro basado en json.

const replacer = (key, value) => {
  if (value instanceof Error) {
    return Object.getOwnPropertyNames(value).reduce((all, valKey) => {
      if(valKey === 'stack') {
        return {
          ...all,
          at: value[valKey].split('\n').filter(va => va.trim().slice(0, 5) != 'Error').map((va, i) => `stack ${i} ${va.trim().slice(3).trim()}`)
        }
      } else {
        return {
          ...all,
          [valKey]: value[valKey]
        }
      }

    }, {});

  } else {
    return value;
  }
}

resultado:

{"at":["stack 0 /Users/sarahriehl/Documents/code/boilerplate/logging/routes/log.js:24:15","stack 1 Layer.handle [as handle_request] (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/layer.js:95:5)","stack 2 next (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/route.js:137:13)","stack 3 Route.dispatch (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/route.js:112:3)","stack 4 Layer.handle [as handle_request] (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/layer.js:95:5)","stack 5 /Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:281:22","stack 6 Function.process_params (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:335:12)","stack 7 next (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:275:10)","stack 8 Function.handle (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:174:3)","stack 9 router (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:47:12)"],"message":"argh!!!","level":"error","timestamp":"Thu 2019-02-14 01:58:58 -0600"}

Hice un ejemplo para el entorno de desarrollo local y GCP Stackdriver Logging .

https://github.com/mrdulin/blog/issues/75

Estaba enfrentando el mismo problema, así que escribí este paquete, utils-deep-clone . Echale un vistazo.

¿Fue útil esta página
0 / 5 - 0 calificaciones