Winston: [3.0.0] El objeto de error no se analiza ni se imprime

Creado en 29 may. 2018  ·  68Comentarios  ·  Fuente: winstonjs/winston

Háblenos de su entorno:

  • _ winston versión? _
  • _ node -v salidas: _ v8.11.1
  • _¿Sistema operativo? _ (Windows, macOS o Linux) macOS
  • _Idioma? _ (Todos | TypeScript XX | ES6 / 7 | ES5 | Dart) Todos

¿Cuál es el problema?

Registrar un objeto Error nodo da como resultado un mensaje vacío:

Ejemplo:

const winston = require('winston');
const { createLogger, format, transports } = winston;

const logger = createLogger({
  transports: [
    new transports.Console()
  ]
});

let err = new Error('this is a test');
logger.log({level: 'error', message: err});

Salida resultante:

% node test.js
{"level":"error","message":{}}

También:

logger.error(new Error('hello'))

Resultados en:

{"level":"error"}

¿Qué esperas que suceda en su lugar?

Espero que la clave del mensaje tenga al menos el mensaje de error incluido. Si intento un formateador personalizado, info tampoco tiene el objeto de error, por lo que debe eliminarse en alguna parte.

Otra información

Hágame saber cómo puedo ayudar - feliz de lanzar un PR, pero todavía no sé lo suficiente [email protected] para encontrarlo

bug important

Comentario más útil

No, realmente, esto es inaceptable para una biblioteca de registro.
El encargado de mantenimiento debería simplemente poner un ejemplo bien resaltado en los documentos donde se muestra cómo registrar un error, incluso definiendo el formato printf personalizado y el formato no json y donde puede registrar el error con algo como logger.error ("algo", err) y logger .error (err)
Winston parecía estar genial, pero este problema es increíblemente inaceptable.

Todos 68 comentarios

Tenemos algo de cobertura de prueba para esto, pero claramente necesitamos más. ¿Qué está pasando debajo de las sábanas?

  1. Su instancia Error se pasa a lo largo de la cadena de tuberías de flujo objectMode
  2. El formato predeterminado para Logger es json (ver: json código de formato en logform )
  3. message y stack propiedades en Error no son enumerables, lo que hace que JSON.stringify genere algo que no se espera.
console.log(JSON.stringify(new Error('lol nothing here')));
// '{}'

Desde una perspectiva de diseño winston@3 introdujo formats exactamente para este tipo de problema para aumentar el rendimiento. Hablando de rendimiento, curiosamente pino hace algo interesante aquí . Quizás la solución sea implementar algo similar a asJson en el formato predeterminado json .

Si alguien está buscando una solución rápida, puede incluir enumerateErrorFormat en el formato de su registrador por ahora. Con suerte, tendremos una solución para esto antes del 3.0.0 próxima semana (o poco después en 3.0.1 )

const winston = require('../');
const { createLogger, format, transports } = winston;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

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

// Error as message
console.log('Run FIRST test...');
logger.log({ level: 'error', message: new Error('FIRST test error') });

// Error as info (one argument)
console.log('\nRun SECOND test...');
const err = new Error('SECOND test error');
err.level = 'info';
logger.info(err);

// Error as info (two arguments);
console.log('\nRun THIRD test...');
logger.log('info', new Error('THIRD test error'));

@indexzero , intenté seguir tu solución, pero no funciona. ¿Sabes por qué?

Formateador:
`` ` javascript const level = settings.debug ? 'debug' : 'info'; const printFormat = winston.format.printf(info => $ {info.timestamp} - $ {info.level}: $ {info.message}`);
const enumerateErrorFormat = winston.format (info => {
if (info.message instanceof Error) {
info.message = Object.assign ({
mensaje: info.message.message,
pila: info.message.stack,
}, info.mensaje);
}
if (instancia de información de Error) {
return Object.assign ({
mensaje: info.message,
pila: info.stack,
}, info);
}
devolver información;
});

const consoleLogger = winston.createLogger ({
nivel,
formato: winston.format.timestamp (),
transportes: [
new winston.transports.Console ({
formato: winston.format.combine (
winston.format.colorize (),
enumerateErrorFormat (),
printFormat,
),
}),
],
});
Code: javascript
intentar {
// Algún error de lanzamiento de código
} atrapar (err) {
logger.error (err);
}
Output:
2018-06-28T21: 17: 25.140Z - error: indefinido
Info object: javascript
{nivel: '\ u001b [31merror \ u001b [39m', marca de tiempo: '2018-06-28T21: 17: 25.140Z', [Símbolo (nivel)]: 'error'}
`` ``
¿Dónde está el atributo de mensaje en el registro de errores?

@sandrocsimas Me di cuenta de que es necesario

Formateador

const consoleLogger = winston.createLogger({
  level,
  format: winston.format.combine(
    winston.format.timestamp(),
    enumerateErrorFormat()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        printFormat,
      ),
    }),
  ],
});

Todavía no entiendo por qué aunque

Creo que estoy experimentando el mismo error que @sandrocsimas.

Aquí está mi configuración de registrador:

logger.js

const winston = require('winston');
const {configure, format} = winston;
const {combine, colorize, timestamp, printf} = format;

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const myConsoleFormat = printf(info => {
  console.log('** Info Object: **');
  console.log(info);
  console.log('** Winston Output: **');
  return `${info.level}: ${info.message}`;
});

winston.configure({
  transports: [
    new winston.transports.Console({
      format: combine(
        colorize(),
        enumerateErrorFormat(),
        myConsoleFormat
      ),
    })
  ]
});

Si lo pruebo con este bloque de código:

Prueba A

const logger = require('winston');
try {
  throw(new Error());
} catch (err) {
  logger.error(err);
}

donde new Error() no contiene un valor de mensaje, obtengo este resultado:

Salida A

** Info Object: **
{ message: 
   { message: '',
     stack: 'Error\n    at Object.<anonymous> (app.js:21:9)\n    at Module._compile (module.js:652:30)\n    at Object.Module._extensions..js (module.js:663:10)\n    at Module.load (module.js:565:32)\n    at tryModuleLoad (module.js:505:12)\n    at Function.Module._load (module.js:497:3)\n    at Module.require (module.js:596:17)\n    at require (internal/module.js:11:18)\n    at Object.<anonymous> (server.js:11:13)\n    at Module._compile (module.js:652:30)' },
  level: '\u001b[31merror\u001b[39m',
  [Symbol(level)]: 'error',
  [Symbol(message)]: '{"message":{},"level":"error"}' }
** Winston Output: **
error: [object Object]

Donde error: [object Object] es exactamente lo que esperaba

Sin embargo, si lo pruebo con este bloque de código:

Prueba B

const logger = require('winston');
try {
  throw(new Error('This causes error: undefined'));
} catch (err) {
  logger.error(err);
}

Donde new Error() contiene un valor de mensaje, obtengo este resultado:

Salida B

** Info Object: **
{ level: '\u001b[31merror\u001b[39m',
  [Symbol(level)]: 'error',
  [Symbol(message)]: '{"level":"error"}' }
** Winston Output: **
error: undefined

Como puede ver, obtengo el mismo error: undefined que obtiene @sandrocsimas . Esperaba obtener error: [object Object]

Tenga en cuenta, si pruebo este bloque de código:

Prueba C

const logger = require('winston');
try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Donde uso logger.log lugar de logger.error obtengo la misma salida que la Salida A anterior

Tengo el mismo problema. Soy nuevo en Winston. Probé la solución funcionó . ¿Tienes alguna solución?

@ nvtuan305 , ¿ probaste la solución de editaste un poco? Si es así, ¿podría proporcionar un código de muestra? Su código debería funcionar si está usando logger.log({level: ____, message: err}); No funcionará si está haciendo logger.info , logger.error , o cualquier otro sabor de logger.<level> . Estoy casi seguro de que es un error como especifiqué anteriormente y debería corregirse en una versión posterior.

¿Me estoy perdiendo algo, o es un completo dolor de cabeza (o incluso imposible?) Obtener el mismo resultado que se obtiene fácilmente de console.log / error / warn?

try {
   // ...
  throw new Error('foo');
} catch (e) {
  console.error('Caught error:', e);  // convenient, informative
  logger.error('Caught error:', e);  // nope, the second parameter is something else (couldn't find docs)
  logger.error(`Caught error: ${e}`);  // stack lost
  logger.error(`Caught error: ${JSON.stringify(e)}`);  // Caught error: {}
}

¿Cuál es el código winston equivalente para obtener el mismo resultado que
console.error('Caught error:', error); ?

¿Y dónde está la documentación para los parámetros tomados por los métodos de conveniencia en el objeto del registrador?

@dandv

logger.error('Caught error:', e);

Esto no funciona porque, a diferencia de console.log() , el logger.<level>(message) Winston solo toma un parámetro llamado mensaje. Ese parámetro de mensaje es un objeto o una cadena (alguien me corrija si me equivoco, pero eso es lo que tengo entendido).

Tenga en cuenta que también puede usar logger.log({level: <level>, message: <message>}) . Para obtener más información sobre estas dos funciones, recomiendo leer esta parte de los documentos: Winston Docs on Log Levels . Asegúrese de leer el Uso de niveles de registro

logger.error(`Caught error: ${e}`);

No puedo decir definitivamente por qué esto no genera la pila, pero sé que esto no es un problema con winston. Si prueba console.log(`Caught error: ${e}`) , tampoco incluye la pila. No he trabajado mucho con literales de plantilla, por lo que los literales de plantilla no funcionan bien con objetos o el archivo console.log de javascript reconoce el objeto como un objeto de error y, por lo tanto, solo genera la propiedad del mensaje. Esa es mi mejor suposición.

logger.error(`Caught error: ${JSON.stringify(e)}`)

Este llega al corazón de lo que trata este hilo de errores. Primero debe comprender algunos detalles técnicos sobre javascript. Tenga en cuenta que si prueba console.log(`Caught error: ${JSON.stringify(e)}`) también obtendrá el mismo resultado Caught error: {} . Como explicó @indexzero :

message y stack propiedades en Error no son enumerables, lo que hace que JSON.stringify genere algo que no se espera.

Básicamente, debido a que las propiedades message y stack no son enumerables, JSON.stringify omite esas propiedades, que es la forma en que termina con un objeto vacío {} . Para comprender mejor la enumerabilidad, recomiendo leer esta enumerabilidad y propiedad de las propiedades .

Afortunadamente, debido a la forma en que se diseñó winston 3.0 (accesorios para el equipo de winston), tenemos una solución para esto que dio @indexzero . Ayudaré a explicarlo. Primero creas esta función:

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

De los objetos docs info.level y info.message . Esa propiedad info.message ES el objeto de error si eso es todo lo que pasó. Así que creamos un nuevo objeto donde message.stack y message.message (Piense en ello como Error.stack y Error.message ) ahora son enumerables, e incluimos cualquier otra propiedad que también pueda estar asociada a ese objeto de error.

A continuación, creará este registrador que utiliza la función enumerateErrorFormat() anterior:

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

Esto tomará cualquier message que pase y verificará si es un objeto de error. Si es así, solucionará el problema de enumeración. Luego, pasa el mensaje a format.json que identificará cualquier objeto (error o no). Si no es un objeto, entonces es una cadena y format.json effectivley no hace nada, ¡y estás en casa gratis!

Aún así, sería bueno si no tuviéramos que crear ese enumerateErrorFormat ya que los objetos de error se registran comúnmente. Según tengo entendido, el equipo de Winston está trabajando en una solución que se lanzará en una versión posterior.

Algunas notas finales. Esto solo funciona si usa logger.log({level: <level>, message: <message>}) donde mensaje es el objeto de error. Ejemplo:

try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Hay otro error en winston donde este código no funciona, como expliqué en mi otra publicación anterior:

try {
  throw(new Error('This will not work'));
} catch (err) {
  logger.error(err);
}

Por alguna razón, la propiedad info.message no está definida cuando usamos logger.error(err) . Con suerte, @indexzero puede resolver esto.

@ SamuelMaddox17 @indexzero ¡ Gracias! Intenté usar logger.log({level: 'error', message: err}); y funciona

¿Se puede arreglar esto para logger.error, etc.?

Es engorroso y detallado usar logger.log , especialmente porque con logger.error puede agregar fácilmente múltiples argumentos.

Hola a todos, estoy investigando esto. @indexzero : ¿todavía cree que la mejor idea es esencialmente agregar la funcionalidad enumerateErrorFormat al formateador json de forma predeterminada? ¿Tenemos que preocuparnos por separado si meta es un Error no solo un object (supongo que la gente se quejará si no manejamos ese caso también?) ? Además, estoy usando master , pero parece que logger.error me funciona con la solución de @indexzero / @ SamuelMaddox17 anterior:

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

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

logger.error(new Error('whatever'));

Tras una mayor investigación, parece que el problema logger.error que expliqué anteriormente es solo un problema cuando se usa el registrador predeterminado. @DABH , probé su código y funciona para mí, pero cuando lo cambio al registrador predeterminado no funciona:

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

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

winston.configure({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    })
  ]
});

winston.error(new Error('whatever'));

En segundo lugar, estoy de acuerdo en que se debe agregar enumerateErrorFormat al formato json; y probablemente también tengas razón sobre meta .

Finalmente, me gustaría notar que el ejemplo de código dado por @DABH hace que la pila no "imprima de forma bonita" por así

{"message":"whatever","stack":"Error: whatever\n    at Object.<anonymous> (/Users/samuelmaddox/Desktop/winston-test/index.js:33:14)\n    at Module._compile (internal/modules/cjs/loader.js:689:30)\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)\n    at Module.load (internal/modules/cjs/loader.js:599:32)\n    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)\n    at Function.Module._load (internal/modules/cjs/loader.js:530:3)\n    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)\n    at startup (internal/bootstrap/node.js:266:19)\n    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)","level":"error"}

Como puede ver, al generar el error con una función a JSON, los caracteres de nueva línea \n no crean nuevas líneas reales. Este es el comportamiento esperado al tomar un objeto y convertirlo a JSON, pero probablemente no sea el comportamiento que realmente quisiéramos de un registrador, al menos al iniciar sesión en la consola.

Gracias por investigar más en este @DABH

Para su información, aquí es donde llegué después de jugar un poco con esto:

import winston from 'winston';
const format = winston.format;

const printNice = format.printf(info => {
    const {level, message} = info;
    return `Logging Level: ${level} - Logging Message: ${message}`;
});

const enumerateErrorFormat = format(info => {
    if (info.message instanceof Error) {
        info.message = Object.assign({
            message: `${info.message.message}\n============\n${info.message.stack}`
        }, info.message);
    }

    if (info instanceof Error) {
        return Object.assign({
            message: `${info.message}\n============\n${info.stack}`
        }, info);
    }

    return info;
});

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

export default logger;

El problema es causado por este error: https://github.com/winstonjs/winston-transport/issues/31

Definitivamente usamos este formulario en winston2.x sin problemas. winston.err('some message', err); junto con winston.error(err) lo anterior enumerateErrorFormat corrige winston.error(err) pero no el caso de uso con err como segundo parámetro.

@SamuelMaddox17

logger.log ({nivel: ____, mensaje: err});

funciona gracias

De acuerdo, descubrí algo. Mi comentario del 3 de septiembre es incorrecto. Este no es un problema con el registrador predeterminado. Este es un problema con el lugar donde define level y / o format . @DABH aquí está su código anterior:

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

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

logger.error(new Error('whatever'));

Si elimina esto:

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    enumerateErrorFormat(),
    format.json()
  ),
  transports: [
    new winston.transports.Console(),
  ],
});

Y reemplázalo con esto:

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    }),
  ],
});

Entonces aparece el problema info.message === undefined . Creo que debería estar bien especificar el nivel y el formato de cada transporte; y estoy casi seguro de que esto estaba permitido en Winston 2.0.

Aquí está su ejemplo de código con mi cambio de código para que pueda ejecutarlo y probarlo fácilmente:

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

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug',
      format: format.combine(
        enumerateErrorFormat(),
        format.json()
      ),
    }),
  ],
});

logger.error(new Error('whatever'));

Es de esperar que esto ayude a llegar a la raíz del problema.

Creé https://github.com/winstonjs/winston/pull/1527

Esto cubre todas las opciones. Sin embargo, algunas pruebas fallan, así que lo he cerrado por ahora. Se esperan las fallas dada la solución, pero no creo que esté en posición de hacer la llamada para enmendar / eliminar las pruebas.

La compilación fallida está aquí https://travis-ci.org/winstonjs/winston/jobs/453012141 y es obvio por qué las pruebas ahora fallan cuando lee el código de prueba:
https://github.com/winstonjs/winston/blob/c42ab7fdc51b88db180a7dd90c52ce04ddd4e054/test/logger.test.js#L668

¿Pensamientos?

Creo que el problema esta en esta linea
const info = (msg && !(msg instanceof Error) && msg.message && msg) || {
mensaje: msg
};
agregar una verificación de instancia de Error parece resolver el problema, como señala @crowleym

Para cualquiera que todavía esté lidiando con esto, este es el formateador de solución que se me ocurrió (fragmento de mi módulo logger.js):

const { format, transports, createLogger }     = require("winston");
const { combine, timestamp, colorize, printf } = format;

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

    return combine(timestamp(), printf(format));
}

Por alguna razón, logger.error(new Error("hello")) solo funciona si define el formateador globalmente en winston.createLogger 🤔 y luego obtiene el objeto Error en info en el formateador.

Si define un formateador por transporte, entonces debe usar logger.log({level: "error", message: new Error("FAILED")}) y manejar el objeto Error a través de info.message lugar de info para acceder al objeto Error.

¿Supongo que hay un error al configurar el campo de formato en las opciones de transporte?

Esos son solo mis 2 centavos y lo que funcionó para mí, soy nuevo en Winston y no tengo experiencia en JavaScript, así que no me cites en nada.

Mi enfoque fue un intento de solucionar la causa raíz. Pero no está recibiendo mucha tracción de los propietarios de repositorios ...

Sí, lo entiendo. Literalmente pasé mucho tiempo averiguando esto porque soy nuevo en Winston y pensé que quizás era yo quien lo estaba utilizando incorrectamente o no había comprendido los conceptos detrás de él lo suficientemente bien todavía.

Pero afortunadamente me topé con algunos hilos (este incluido) que me mostraron lo contrario. Con suerte, solucionarán esto para que no tenga que seguir usando la solución alternativa.

Esto podría deberse a wintson-transport , consulte https://github.com/winstonjs/winston-transport/issues/31 para conocer el problema y https://github.com/winstonjs/winston-transport/pull/ 34 para un PR.

El registro directo de objetos de error siempre es un desastre debido a sus propiedades no enumerables. Personalmente, considero que esta es una mala práctica, pero suficientes personas en la comunidad se muestran inflexibles al respecto como un requisito de que debemos apoyarla.

Considerando adoptar https://github.com/winstonjs/logform/pull/59 como formato para admitir comportamientos como este. En el lado positivo, encapsula todos los casos extremos que son tan comunes cuando se tratan los errores como mensajes de registro. En el lado negativo, sería otro formato al que la gente debería optar (similar a .splat() )

@indexzero Tiendo a estar de acuerdo, pero el registro de errores directo puede ser útil cuando se combina con el formato de registro personalizado / printf si es necesario mostrar varios tipos Error diferente, y no recuerdo que Winston 2.x lo haya intentado luchar contra esta práctica ya que se permitió fuera de la caja.

Entonces, la solución propuesta para enumerateErrorFormat funciona pero no es compatible con el formato logger.error('some message', err) . Porque ni info.message ni info son instanceof Error . También quiero señalar otro problema con esta solución. Actualmente estoy registrando un error que se devuelve desde superagent

    try {
      const response = await request
        .get('https://some/endpoint')
        .set('Authorization', bearerToken);
      logger.info('successfully received response');
      return response.body;
    } catch (e) {
      logger.error('An error was caught while getting programs');
      logger.error(e); // <<< THE ERROR LOG  
    }

Si simplemente usamos Object.assign, ¡enfríe la pila y se establecerá el mensaje! PERO, cualquier otra información que fuera parte del error también se registrará. Esto puede ser muy peligroso en los casos en que los errores tienen datos confidenciales como Authorization Headers (que en este caso se incluye como parte del objeto de error).

Pero entonces podrías decir. Esto no es culpa de winston, no es culpa de winston que superagent agregue estos datos al error. ¡ESTOY DE ACUERDO! SIN EMBARGO, debido a que todo se almacena plano en el objeto info , resulta muy difícil mantener la información anterior y no anular el resto.

Casi sería bueno si, al usar logger.error. Asumió que el segundo parámetro era un error y lo puso en el objeto info como info.error y luego, si se registraba de otra manera, la interfaz sería { level: "error", error: YOUR ERROR OBJECT}

Realmente estoy escupiendo aquí, pero la nueva interfaz definitivamente ha sido un poco frustrante (todo en información).

Solo para desarrollar el punto que estaba haciendo, supongamos que logger.error( e ) donde e es de tipo error.

Luego, en su código, ha configurado winston de la siguiente manera:

winston.configure({
    format: combine(
      timestamp(),
      enumerateErrorFormat(),
      ...
    ),
  });

la marca de tiempo se inserta en el objeto de error 😱. ¿Eso realmente tiene sentido? Piense en eso ... el objeto de error que envió está recibiendo un nuevo prop dinámicamente ... marca de tiempo.

Creo que la mejor solución general a este problema sería admitir la siguiente sintaxis

logger.error('An error occurred when doing something important', { error: e } );

y luego internamente puede crear un formateador de errores que busque un campo de error.

Actualización sobre esta gente:

Espero poder coserlo y enviarlo en los próximos días. Es el penúltimo número de nuestro rastreador [email protected]

Hola amigos, consulte https://github.com/winstonjs/winston/pull/1562. Este fue el último elemento en nuestra lista de verificación de liberación de 3.2.0 por lo que una vez que el PR esté cosido, podremos liberarlo.

Hasta que se publique una solución, utilizo la siguiente solución:

logger.error = item => {
  logger.log({ level: 'error', message: item instanceof Error ? item.stack : item });
};

Estoy en la última versión de Winston (3.2.1) y todavía obtengo undefined al pasar un error a logger.error

@ezze tuvo una gran solución, pero el seguimiento de la pila se fuerza a una nueva línea. aquí hay una versión ligeramente modificada que lo pone en una línea (por lo que se captura mediante un simple grep de archivos de registro)

logger.error = item => {
  const message = item instanceof Error
    ? item.stack.replace('\n', '').replace('    ', ' - trace: ')
    : item;
  logger.log({ level: 'error', message });
};

con salida <Error message> - trace: <stack trace>

Si hay una manera más fácil de hacer esto con la última versión de Winston, hágamelo saber @indexzero. Soy nuevo en la biblioteca y estaba siguiendo los documentos.

Acabo de ver el enlace que publicaste en el PR. ¿Significa esto que para pasar un error a logger.error requiere una cadena de mensaje, luego el error?

try {
  someThing();
} catch(error) {
  logger.error(error); // what I would like to do
  logger.error('special message', error); // what I believe is required?
}

@ the-vampiire Terminó habiendo 2 problemas de los que habló este hilo. El primero es lo que mencionó el póster original y el segundo es el problema que mencioné, que es el mismo que el suyo. Creo que solo solucionaron el problema de los carteles originales. He tenido la intención de verificar más para asegurarme y luego abrir un nuevo problema si ese es el caso. Lamentablemente, no he tenido tiempo de sumergirme más profundo. Mientras tanto, si usa logger.log({level: 'error', message: err}); donde err es un objeto de error, entonces funcionará.

Aún teniendo este problema, perdí mucho tiempo para resolverlo, la solución de @ the-vampiire funciona bien.

¿Por qué está cerrado este boleto?

Anular logger.error es hasta ahora la mejor solución porque no agrega una propiedad de marca de tiempo a un objeto Error que se pasa como un único argumento a error (). Es probable que la mayoría de la gente espere que los objetos Error sean inmutables. Si no anula también logger.info y cualquier otro método relacionado con el nivel, es una sorpresa cuando las cosas no funcionan como se esperaba. Nuevamente, a menos que desee que se modifique su objeto, sin importar su tipo, no lo envíe directamente a un método de registro de Winston.

La función es compatible desde [email protected]

Uso de ejemplo:

const winston = require('winston');
const { transports, format } = winston;

const print = format.printf((info) => {
  const log = `${info.level}: ${info.message}`;

  return info.stack
    ? `${log}\n${info.stack}`
    : log;
});

const logger = winston.createLogger({
  level: 'debug',
  format: format.combine(
    format.errors({ stack: true }),
    print,
  ),
  transports: [new transports.Console()],
});

const error = new Error('Ooops');

logger.error(error);
logger.error('An error occurred:', error);

cc @ HRK44 @ el-vampiro

También sigo encontrándome con el problema.
Cuando llamo al error como logger.error(error); obtengo solo undefined .
Solo si lo llamo como logger.error('Something went wrong', error) obtengo el error completo y puedo analizarlo.

También sigo encontrándome con el problema.
Cuando llamo al error como logger.error(error); obtengo solo undefined .
Solo si lo llamo como logger.error('Something went wrong', error) obtengo el error completo y puedo analizarlo.
¿Agregaste esto?

format.errors({ stack: true })

Sí, sigue siendo el mismo problema. Intento reproducirlo en una esencia.

@ OBrown92 Hoy me he enfrentado al mismo problema. Puedo confirmar que funciona, si format.errors({ stack: true }) , se aplica al registrador, no al transporte. En este caso, es posible utilizar tanto logger.error(error); como logger.error('Something went wrong', error) . Sin embargo, hay un problema cuando trato de aplicar format.errors({ stack: true }) al transporte elegido. En este caso, obtengo undefined por logger.error(error); , pero logger.error('Something went wrong', error) funciona correctamente.

No estoy seguro de si se trata de un comportamiento esperado o de un error, pero dediqué mucho tiempo a encontrar la causa, así que corríjalo o menciónelo en algún lugar de su documentación. Sería realmente útil.

De todos modos, estoy muy agradecido por su trabajo en este gran proyecto.

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

format.errors is not a function ... bueno, eso es una sorpresa.

@holmberd
¿Importó los formatos del paquete winston?
Uso de ejemplo:
const { format } = require('winston')
O
const winston = require('winston'); const { format } = winston;

@aybhalala yepp, aunque no importa, la pila de errores se pasa a printf sin ella.

Actualización: debido a que todavía hay problemas con esto, he estado haciendo lo siguiente por un tiempo y ha estado funcionando muy bien

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

^^ Ese es el uso esperado. Dado que los simples mortales nunca se darán cuenta de esto, debe documentarse.

Dado que los simples mortales nunca se darán cuenta de esto, debe documentarse

😂 +1 a ese compañero

Utilizo winston con un transporte de terceros ( @google-cloud/logging-winston ), por lo que tengo un poco menos de control sobre la sintaxis. Además, encuentro esto más intuitivo:

const error = new Error('something bad happened');
logger.error('was doing this and', error);

Al iniciar sesión en la consola, concateno la pila en el mensaje. Pero el resultado es algo como esto:

ERROR was doing this andsomething bad happened Error: something bad happened <rest of the stack.>

Dado que winston concatena meta.message en el mensaje original, existe el extraño andsomething y el mensaje duplicado que también se imprime en la pila. Esto se describe en el n. ° 1660.

Parece que # 1664 está intentando solucionar este problema. Mientras tanto, escribí un formateador que "deshace" esa concatenación: https://github.com/winstonjs/winston/issues/1660#issuecomment -512226578

@dandv

logger.error('Caught error:', e);

Esto no funciona porque, a diferencia de console.log() , el logger.<level>(message) Winston solo toma un parámetro llamado mensaje. Ese parámetro de mensaje es un objeto o una cadena (alguien me corrija si me equivoco, pero eso es lo que tengo entendido).

Tenga en cuenta que también puede usar logger.log({level: <level>, message: <message>}) . Para obtener más información sobre estas dos funciones, recomiendo leer esta parte de los documentos: Winston Docs on Log Levels . Asegúrese de leer el Uso de niveles de registro

logger.error(`Caught error: ${e}`);

No puedo decir definitivamente por qué esto no genera la pila, pero sé que esto no es un problema con winston. Si prueba console.log(`Caught error: ${e}`) , tampoco incluye la pila. No he trabajado mucho con literales de plantilla, por lo que los literales de plantilla no funcionan bien con objetos o el archivo console.log de javascript reconoce el objeto como un objeto de error y, por lo tanto, solo genera la propiedad del mensaje. Esa es mi mejor suposición.

logger.error(`Caught error: ${JSON.stringify(e)}`)

Este llega al corazón de lo que trata este hilo de errores. Primero debe comprender algunos detalles técnicos sobre javascript. Tenga en cuenta que si prueba console.log(`Caught error: ${JSON.stringify(e)}`) también obtendrá el mismo resultado Caught error: {} . Como explicó @indexzero :

message y stack propiedades en Error no son enumerables, lo que hace que JSON.stringify genere algo que no se espera.

Básicamente, debido a que las propiedades message y stack no son enumerables, JSON.stringify omite esas propiedades, que es la forma en que termina con un objeto vacío {} . Para comprender mejor la enumerabilidad, recomiendo leer esta enumerabilidad y propiedad de las propiedades .

Afortunadamente, debido a la forma en que se diseñó winston 3.0 (accesorios para el equipo de winston), tenemos una solución para esto que dio @indexzero . Ayudaré a explicarlo. Primero creas esta función:

const enumerateErrorFormat = format(info => {
  if (info.message instanceof Error) {
    info.message = Object.assign({
      message: info.message.message,
      stack: info.message.stack
    }, info.message);
  }

  if (info instanceof Error) {
    return Object.assign({
      message: info.message,
      stack: info.stack
    }, info);
  }

  return info;
});

De los objetos docs info.level y info.message . Esa propiedad info.message ES el objeto de error si eso es todo lo que pasó. Así que creamos un nuevo objeto donde message.stack y message.message (Piense en ello como Error.stack y Error.message ) ahora son enumerables, e incluimos cualquier otra propiedad que también pueda estar asociada a ese objeto de error.

A continuación, creará este registrador que utiliza la función enumerateErrorFormat() anterior:

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

Esto tomará cualquier message que pase y verificará si es un objeto de error. Si es así, solucionará el problema de enumeración. Luego pasa el mensaje a format.json cual identificará cualquier objeto (error o no). Si no es un objeto, entonces es una cadena y format.json effectivley no hace nada, ¡y estás en casa gratis!

Aún así, sería bueno si no tuviéramos que crear ese enumerateErrorFormat ya que los objetos de error se registran comúnmente. Según tengo entendido, el equipo de Winston está trabajando en una solución que se lanzará en una versión posterior.

Algunas notas finales. Esto solo funciona si usa logger.log({level: <level>, message: <message>}) donde mensaje es el objeto de error. Ejemplo:

try {
  throw(new Error('This should work'));
} catch (err) {
  logger.log({level: 'error', message: err});
}

Hay otro error en winston donde este código no funciona, como expliqué en mi otra publicación anterior:

try {
  throw(new Error('This will not work'));
} catch (err) {
  logger.error(err);
}

Por alguna razón, la propiedad info.message no está definida cuando usamos logger.error(err) . Con suerte, @indexzero puede resolver esto.

Muy buena explicación, solo quiero agregar eso con logger.error( Error capturado: $ {e} ); pierdes la pila debido a la forma en que funciona el literal de cadena en javascript, `${e}` es exactamente lo mismo que e.toString() , por lo que imprimir solo el mensaje de error es el comportamiento esperado.

¿Esto sigue siendo un problema? Sigo teniendo problemas con esto:

const { createLogger, format, transports } = require('winston')

const { combine } = format

const errorFormatter = format((info) => {
  console.log(info)
  return info
})


const consoleTransport = new transports.Console({
  format: combine(errorFormatter()),
})

const logger = createLogger({
  transports: [
    consoleTransport,
  ],
})

try {
  throw new Error('Error message')
} catch(err) {
  logger.error(err) // info doesnt have the error object
  logger.error('', err) // info have the error object
}

¿Esto sigue siendo un problema? Sigo teniendo problemas con esto:

const { createLogger, format, transports } = require('winston')

const { combine } = format

const errorFormatter = format((info) => {
  console.log(info)
  return info
})


const consoleTransport = new transports.Console({
  format: combine(errorFormatter()),
})

const logger = createLogger({
  transports: [
    consoleTransport,
  ],
})

try {
  throw new Error('Error message')
} catch(err) {
  logger.error(err) // info doesnt have the error object
  logger.error('', err) // info have the error object
}

el mismo problema aqui...

después de mirar el código fuente, puedo ver qué parámetros acepta:

interface LeveledLogMethod {
(mensaje: cadena, devolución de llamada: LogCallback): Logger;
(mensaje: cadena, meta: cualquiera, devolución de llamada: LogCallback): Logger;
(mensaje: cadena, ... meta: cualquiera []): Logger;
(infoObjeto: objeto): Logger;
}

por lo tanto, si pasa el objeto de error como primer parámetro, solo tomará el mensaje del error porque solo comprende cadenas, y si pasa el error en el segundo parámetro, puede acceder al seguimiento de la pila en info.stack

por cierto, no pude encontrar esto en ninguna parte de los documentos

Encontré dos soluciones, la primera es usar format.errors , mencionado en logform en el registrador principal , luego crear un messageFormatter usando format.printf y agregar condicionalmente un campo stack extraído de info ( format.errors({ stack: true}) agregará eso).

La otra solución que preferí fue piratear los registradores de nivel de Winston:

const addArgs = format((info) => {
  const args: any[] = info[Symbol.for('splat')]
  info.args = args ? [...args] : []
  return info
})

const messageFormatter = format.printf(info => {
  const { timestamp: timeString = '', message, args = [] } = info
  const formattedMsgWithArgs = util.formatWithOptions({ colors: true }, message, ...args)
  const msg = `${timeString} - ${info.level}: ${formattedMsgWithArgs}`
  return msg
})

const logger = createLogger({
  format: format.combine(
    addArgs(),
    format.timestamp({ format: 'HH:mm:ss.SSS' })
  ),

  transports: [
    new transports.Console({
      format: format.combine(format.colorize(), messageFormatter),
    }),
  ],
})

const levels = ['debug', 'info', 'error']
levels.forEach((level) => {
  logger[level] = (msg: any, ...remains: any) => {
    if(typeof msg != "string") {
      return logger.log(level, '', msg, ...remains)
    }

    logger.log(level, msg, ...remains)
  }  
})

Parece que de esta manera puedo obtener un registro de errores similar a console.log

Puedo verificar que el comentario de @tiagonapoli sobre cómo format.errors tiene que estar en el registrador _parent_ es correcto. Cuando hago algo como esto:

winston.loggers.add("default");
const log = winston.loggers.get("default");
/* get a `transportOptions` object and a `transportType` */
transportOptions.format = logform.format.combine(
  logform.format.errors({ stack: true }),
  logform.format.timestamp(),
  logform.format.printf(myFormatter)
);
log.add(new winston.transports[transportType](transportOptions);

El manejo del objeto Error se hace como si fuera una cadena. Sin embargo, si hago algo como esto:

winston.loggers.add("default");
const log = winston.loggers.get("default");
log.format = logform.format.errors({ stack: true });
/* get a `transportOptions` object and a `transportType` */
transportOptions.format = logform.format.combine(
  logform.format.timestamp(),
  logform.format.printf(myFormatter)
);
log.add(new winston.transports[transportType](transportOptions);

El manejo del objeto Error funciona correctamente.

Me parece que el error aquí es que no debería haber diferencia en el comportamiento.

Entonces, ¿esto todavía no se ha solucionado después de 1 año? ¿Tengo que hackear el código del registrador de Winston para que funcione?

Sí, esto me dio suficientes dolores de cabeza que Winston comenzó a parecer mucho más problemático de lo que valía para mi caso de uso relativamente simple ... Terminé escribiendo mi propia pequeña clase de registrador, y recomendaría a otros que hicieran lo mismo a menos que Winston proporciona algo que REALMENTE necesita.

Chicos, ¿en serio? esto es frustrante...

No soy un comprometidor, pero probablemente tenga razón al decir que esto no se va a "arreglar" porque no está roto. Vale la pena usar Winston. Solo necesita configurarlo; el mejor consejo para mí está arriba en https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691

Aún no ?

Actualización: debido a que todavía hay problemas con esto, he estado haciendo lo siguiente por un tiempo y ha estado funcionando muy bien

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

de donde viene el rastro?

No, realmente, esto es inaceptable para una biblioteca de registro.
El encargado de mantenimiento debería simplemente poner un ejemplo bien resaltado en los documentos donde se muestra cómo registrar un error, incluso definiendo el formato printf personalizado y el formato no json y donde puede registrar el error con algo como logger.error ("algo", err) y logger .error (err)
Winston parecía estar genial, pero este problema es increíblemente inaceptable.

Esta es mi opinión sobre cómo registrar errores usando Winston. No es único, muchos de los anteriores también tienen soluciones de trabajo basadas en los mismos conceptos.

Fondo
Estoy usando @jsdevtools/ono para envolver tipos de objetos arbitrarios en errores personalizados, pero independientemente, esta solución todavía parece funcionar bien en errores de nodo nativo (por ejemplo, errores fs eperm) y clases de error personalizadas.

Explicación
Básicamente, confío en format.errors({stack:true}) y format.metadata() . Como se menciona en https://github.com/winstonjs/winston/issues/1338#issuecomment -532327143, esto debe estar en el formateador principal .

Los metadatos ayudan a cambiar todas las propiedades personalizadas del objeto de error a info.metadata .

Quería imprimir 3 tipos de información: el mensaje de error, el seguimiento de pila y las propiedades del objeto de error. El mensaje de error ya era texto sin formato. Imprimí bastante la pila info.metadata.stack usando el módulo pretty-error . Para las propiedades del objeto de error, no quería que el stacktrace volviera a aparecer, así que cloné el objeto y eliminé la propiedad stacktrace. Luego imprimí bastante el objeto de error usando fast-safe-stringify , que es el mismo módulo de cadena en el que se basa Winston.

    const lodash = require("lodash");
    const path = require("path");
    const winston = require("winston");
    const { default: stringify } = require("fast-safe-stringify");
    const logDir = "D:/temp/logs";

    // pretty formatting
    const PrettyError = require("pretty-error");
    const pe = new PrettyError();
    pe.withoutColors()
        .appendStyle({
            'pretty-error > trace':
            {
                display: 'inline'
            },
            'pretty-error > trace > item':
            {
                marginBottom: 0,
                bullet: '"*"'
            }
        })
        // @ts-ignore
        .alias(/.*[\\\/]CelebrityQuery/i, "<project>")
        .alias(/\[CelebrityQuery\][\\\/]?/i, "")
        .skip(/** <strong i="23">@type</strong> {(_:any) => boolean} */ (traceline => {
            if (traceline && traceline.dir) {
                return traceline.dir.toString().startsWith("internal");
            }
            return false;
        }))
        .skipNodeFiles();

    const consoleFormat = winston.format.combine(
        winston.format.colorize(),
        winston.format.timestamp({
            format: 'DD MMM HH:mm:ss'
        }),
        winston.format.printf(info => {
            if (!lodash.isEmpty(info.metadata) && info.metadata.hasOwnProperty("stack")) {
                let dup = lodash.clone(info.metadata);
                delete dup.stack;
                const errBody = stringify(dup, undefined, 4);
                const stack = pe.render({ stack: info.metadata.stack });
                return `${info.timestamp} ${info.level} ${info.message}${errBody}\n${stack}`;
            } else if (lodash.isString(info.message)) {
                return `${info.timestamp} ${info.level} ${info.message}`;
            } else {
                return `${info.timestamp} ${info.level} ${stringify(info.message, undefined, 4)}`;
            }
        })
    );
    const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.json());
    return winston.createLogger({
        level: 'debug',
        format: winston.format.combine(
            winston.format.errors({ stack: true }),
            winston.format.metadata()
        ),
        transports: [
            new winston.transports.Console({
                format: consoleFormat,
                level: 'info',
            }),
            new winston.transports.File({
                filename: path.join(logDir, "stdout.json"),
                format: logFormat,
                level: 'debug',
                maxsize: 1000000,
                tailable: true
            })
        ]
    });

Log Screenshot

PD: También encuentro que la solución mencionada en https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691 es una buena alternativa. Es decir, usando logger.warn("Oh no", { error: new Error() }) , luego haciendo referencia a info.error en su formateador personalizado.

@tiagonapoli, su solución sobre el uso de format.errors en el registrador principal funcionó para mí:

const logger = createLogger({
  transports: loggerTransports,
});

logger.format = format.errors({ stack: true });

Es bastante doloroso configurar este registrador ... ¿No podría simplemente comportarse como console.log fuera de la caja?

@ will093 lo mismo aquí. He estado en ese tema de nuevo y no entiendo por qué mi console.log es agradable y limpio y el formato Winston es una mierda.

Mi 2 ¢

// Enable console logging when not in production
if (process.env.NODE_ENV !== "production") {
    logger.add(new transports.Console({
        format: format.combine(
            format.colorize(),
            format.simple(),
            format.printf(info => {
                const { level, ...rest } = info;
                let rtn = "";
                // rtn += info.timestamp;
                rtn += "[" + info.level + "] ";
                if (rest.stack) {
                    rtn += rest.message.replace(rest.stack.split("\n")[0].substr(7),"");
                    rtn += "\n";
                    rtn += "[" + level + "] ";
                    rtn += rest.stack.replace(/\n/g, `\n[${level}]\t`);
                } else {
                    rtn += rest.message;
                }
                return rtn;
            }),
        ),
    }));
}

Ejemplo de logger.error("Error during schema stitching", e);

image

usar @tiagonapoli y la solución de @ will093 de agregarlo solo al padre parece ser la forma más fácil de admitir errores de registro directo y aún registrar mensajes; aquí hay un ejemplo completo de una configuración mínima con marcas de tiempo:

const createLogger = () => {
  const logFormatter = winston.format.printf(info => {
    let { timestamp, level, code, stack, message } = info;

    // print out http error code w/ a space if we have one
    code = code ? ` ${code}` : '';
    // print the stack if we have it, message otherwise.
    message = stack || message;

    return `${timestamp} ${level}${code}: ${message}`;
  });

  return winston.createLogger({
    level: 'info',
    // put the errors formatter in the parent for some reason, only needed there:
    format: winston.format.errors({ stack: true }),
    transports: new winston.transports.Console({
      format: winston.format.combine(
        winston.format.timestamp(),
        logFormatter
      ),
  });
};

funciona con una pila cuando se llama con un error como: logger.error(error) , funciona con una cadena cuando se llama como logger.error('a regular message') , se ve así en mis registros:

2020-09-23T20:05:30.30Z info: Feathers application started on http://localhost:3030
2020-09-23T20:05:35.40Z info: job queue - redis ready, registering queues...
2020-09-23T20:05:40.25Z error 401: NotAuthenticated: invalid authorization header
    at new NotAuthenticated (/path/to/server/node_modules/@feathersjs/errors/lib/index.js:94:17)
    at Object.<anonymous> (/path/to/server/src/hooks/authentication.js:123:456)
    at /path/to/server/node_modules/@feathersjs/commons/lib/hooks.js:116:46

esto no intenta resolver la incompatibilidad logger.error('message here', error) winston con console.log , que parece hacer la solución más complicada de @tiagonapoli .

Además, si le gustan los registros json, puede colocar logFormatter aquí y usar winston.format.json() en su lugar, que aún incluirá la pila, pero no es bonito.

Actualización: debido a que todavía hay problemas con esto, he estado haciendo lo siguiente por un tiempo y ha estado funcionando muy bien

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

¿Dónde está la definición de trace ()?

Actualización: debido a que todavía hay problemas con esto, he estado haciendo lo siguiente por un tiempo y ha estado funcionando muy bien

// Grab the default winston logger
const winston = require('winston');

const { format } = winston;
const { combine, timestamp } = format;

// Custom format function that will look for an error object and log out the stack and if 
// its not production, the error itself
const myFormat = format.printf((info) => {
  const { timestamp: tmsmp, level, message, error, ...rest } = info;
  let log = `${tmsmp} - ${level}:\t${message}`;
  // Only if there is an error
  if ( error ) {
    if ( error.stack) log = `${log}\n${error.stack}`;
    if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
  }
  // Check if rest is object
  if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
    log = `${log}\n${JSON.stringify(rest, null, 2)}`;
  }
  return log;
});

 winston.configure({
    transports: [
      new winston.transports.Console({
        level: 'debug',
        timestamp: true,
        handleExceptions: true
    }),
  ];
    format: combine(
      trace(),
      timestamp(),
      myFormat
    ),
  });


// Finally you log like this
logger.error('An error occurred!!!', { error } );

de donde viene el rastro?

alguna respuesta sobre esto?

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