Winston: [3.0.0] L'objet d'erreur n'est ni analysé ni imprimé

Créé le 29 mai 2018  ·  68Commentaires  ·  Source: winstonjs/winston

Veuillez nous parler de votre environnement:

  • _ winston version? _
  • _ node -v sorties: _ v8.11.1
  • _Système d'exploitation? _ (Windows, macOS ou Linux) macOS
  • _Language? _ (Tous | TypeScript XX | ES6 / 7 | ES5 | Dart) Tous

Quel est le problème?

La journalisation d'un objet noeud Error entraîne un message vide:

Exemple:

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});

Sortie résultante:

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

Également:

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

Résulte en:

{"level":"error"}

Que pensez-vous qu'il se passe à la place?

Je m'attends à ce que la clé de message contienne au moins le message d'erreur. Si j'essaie un formateur personnalisé, info ne contient pas non plus l'objet d'erreur, il doit donc être supprimé quelque part?

Les autres informations

Dites-moi comment je peux vous aider - heureux de feuilleter un PR mais je ne connais pas encore assez pour le trouver [email protected]

bug important

Commentaire le plus utile

Non vraiment, c'est inacceptable pour une bibliothèque de journalisation.
Le responsable de la maintenance doit simplement mettre un exemple bien mis en évidence sur les documents où il est montré comment enregistrer une erreur, même en définissant un format printf personnalisé et un format non json et où vous pouvez enregistrer une erreur avec quelque chose comme logger.error ("quelque chose", err) et un enregistreur .error (err)
Winston semblait génial mais ce problème est incroyablement inacceptable

Tous les 68 commentaires

Nous avons une couverture de test pour cela, mais nous avons clairement besoin de plus. Que se passe-t-il sous les couvertures:

  1. Votre instance Error est transmise le long de la chaîne de canaux objectMode stream
  2. Le format par défaut pour Logger est json (voir: json format code dans logform )
  3. message propriétés stack sur Error sont pas énumérables, ce qui fait que JSON.stringify génère quelque chose auquel on ne s'attend pas.
console.log(JSON.stringify(new Error('lol nothing here')));
// '{}'

Du point winston@3 vue de la conception, formats pour exactement ce genre de problème afin d'augmenter les performances. En parlant de performance, il est intéressant de noter que pino fait quelque chose d'intéressant ici . Peut-être que la solution implémente quelque chose de similaire à asJson dans le format par défaut json .

Si quelqu'un cherche une solution rapide, vous pouvez inclure enumerateErrorFormat dans le format de votre enregistreur pour le moment. Nous espérons avoir une solution à ce problème avant 3.0.0 la semaine prochaine (ou peu de temps après dans 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 , j'ai essayé de suivre votre solution de contournement, mais cela ne fonctionne pas. Est-ce que tu sais pourquoi?

Formateur:
`` ` 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 ({
message: info.message.message,
pile: info.message.stack,
}, info.message);
}
if (info instanceof Error) {
renvoie Object.assign ({
message: info.message,
pile: info.stack,
}, Info);
}
retour d'information;
});

const consoleLogger = winston.createLogger ({
niveau,
format: winston.format.timestamp (),
les transports: [
nouveau winston.transports.Console ({
format: winston.format.combine (
winston.format.colorize (),
enumerateErrorFormat (),
printFormat,
),
}),
],
});
Code: javascript
essayer {
// Une erreur de lancement de code
} catch (err) {
logger.error (err);
}
Output:
2018-06-28T21: 17: 25.140Z - erreur: indéfini
Info object: javascript
{level: '\ u001b [31merror \ u001b [39m' ', timestamp:' 2018-06-28T21: 17: 25.140Z ', [Symbol (level)]:' error '}
`` ``
Où se trouve l'attribut de message dans le journal des erreurs?

@sandrocsimas J'ai remarqué que vous devez donner la fonction enumerateErrorFormat au formateur par défaut de l'enregistreur afin de le faire fonctionner.

Formateur

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,
      ),
    }),
  ],
});

Je ne comprends toujours pas pourquoi tho

Je pense que je rencontre le même bug que @sandrocsimas.

Voici ma configuration de logger:

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 je le teste avec ce bloc de code:

Test A

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

new Error() ne contient pas de valeur de message, j'obtiens cette sortie:

Sortie 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]

error: [object Object] est exactement ce à quoi je m'attendais

Cependant, si je le teste avec ce bloc de code:

Test B

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

new Error() contient une valeur de message, j'obtiens cette sortie:

Sortie B

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

Comme vous pouvez le voir, j'obtiens le même error: undefined que @sandrocsimas obtient. Je m'attendais à recevoir error: [object Object]

Remarque, si j'essaye ce bloc de code:

Test C

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

Où j'utilise logger.log au lieu de logger.error j'obtiens la même sortie que la sortie A ci-dessus

J'ai le même problème. Je suis nouveau dans winston. J'ai essayé la solution @indexzero mais ne fonctionne pas. Avez-vous une solution?

@ nvtuan305 , avez-vous essayé la solution de @indexzero exactement ou l'avez-vous un peu logger.log({level: ____, message: err}); Cela ne fonctionnera pas si vous faites logger.info , logger.error , ou toute autre saveur de logger.<level> . Je suis presque certain que c'est un bogue comme je l'ai spécifié ci-dessus et il devrait être corrigé dans une version ultérieure.

Est-ce que je manque quelque chose, ou est-ce un casse-tête complet (ou même impossible?) D'obtenir le même résultat que celui obtenu facilement à partir 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: {}
}

Quel est le code winston équivalent pour obtenir la même sortie que
console.error('Caught error:', error); ?

Et où est la documentation des paramètres pris par les méthodes pratiques sur l'objet logger?

@dandv

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

Cela ne fonctionne pas car, contrairement à console.log() , le logger.<level>(message) winston ne prend qu'un seul paramètre appelé message. Ce paramètre de message est soit un objet, soit une chaîne (quelqu'un me corrige si je me trompe mais c'est ce que je comprends).

Notez que vous pouvez également utiliser logger.log({level: <level>, message: <message>}) . Pour en savoir plus sur ces deux fonctions, je vous recommande de lire cette partie de la documentation: Winston Docs on Log Levels . Assurez-vous de lire Utilisation des niveaux de journalisation

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

Je ne peux pas dire avec certitude pourquoi cela ne produit pas la pile, mais je sais que ce n'est pas un problème avec winston. Si vous essayez console.log(`Caught error: ${e}`) cela n'inclut pas non plus la pile. Je n'ai pas beaucoup travaillé avec les littéraux de modèle, donc soit les littéraux de modèle ne fonctionnent pas bien avec les objets, soit le console.log de javascript reconnaît l'objet comme un objet d'erreur et ne produit donc que la propriété message. C'est ma meilleure supposition.

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

Celui-ci va au cœur de ce dont parle ce fil de bogue. Vous devez d'abord comprendre quelques détails techniques sur javascript. Notez que si vous essayez console.log(`Caught error: ${JSON.stringify(e)}`) vous obtenez également la même sortie Caught error: {} . Comme l'explique @indexzero :

message propriétés stack sur Error sont pas énumérables, ce qui fait que JSON.stringify génère quelque chose auquel on ne s'attend pas.

Fondamentalement, comme les propriétés message et stack sont pas énumérables, JSON.stringify ignore ces propriétés, ce qui vous permet de vous retrouver avec un objet vide {} . Pour mieux comprendre l' énumération, je recommande de lire cet

Heureusement, en raison de la façon dont winston 3.0 a été conçu (accessoires pour l'équipe winston), nous avons une solution de contournement pour cela que @indexzero a donné. Je vais t'aider à l'expliquer. Commencez par créer cette fonction:

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;
});

À partir des objets Docs info.level et info.message . Cette propriété info.message EST l'objet d'erreur si c'est tout ce que vous avez passé. Nous créons donc un nouvel objet où message.stack et message.message (Considérez-le comme Error.stack et Error.message ) sont maintenant énumérables, et nous incluons toutes les autres propriétés qui peuvent également être attachées à cet objet d'erreur.

Ensuite, vous allez créer cet enregistreur qui utilise la fonction enumerateErrorFormat() ci-dessus:

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

Cela prendra tout message vous passez et vérifiera s'il s'agit d'un objet d'erreur. Si c'est le cas, cela résoudra le problème d'énumération. Ensuite, il passe le message à format.json qui va stringifier n'importe quel objet (erreur ou non). Si ce n'est pas un objet, c'est une chaîne et format.json effectivley ne fait rien, et vous êtes libre!

Néanmoins, ce serait bien si nous n'avions pas à créer ce enumerateErrorFormat car les objets d'erreur sont généralement enregistrés. Si je comprends bien, l'équipe winston travaille sur un correctif qui sera publié dans une version ultérieure.

Quelques notes finales. Cela ne fonctionne que si vous utilisez logger.log({level: <level>, message: <message>}) où message est l'objet d'erreur. Exemple:

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

Il y a un autre bogue dans winston où ce code ne fonctionne pas, comme je l'ai expliqué dans mon autre article ci-dessus:

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

Pour une raison quelconque, la propriété info.message n'est pas définie lorsque nous utilisons logger.error(err) . Espérons que @indexzero pourra comprendre celui-ci.

@ SamuelMaddox17 @indexzero Merci! J'ai essayé d'utiliser logger.log({level: 'error', message: err}); et ça marche

Cela peut-il être corrigé pour logger.error, etc.?

Il est fastidieux et verbeux d'utiliser logger.log , d'autant plus qu'avec logger.error vous pouvez facilement ajouter plusieurs arguments.

Salut à tous, je regarde ça. @indexzero : vous pensez toujours que la meilleure idée est d'ajouter essentiellement la fonctionnalité enumerateErrorFormat au formateur json par défaut? Devons-nous nous inquiéter séparément de savoir si meta est un Error pas seulement un object (je suppose que les gens se plaindront si nous ne traitons pas également ce cas?) ? De plus, j'utilise master , mais il semble que logger.error fonctionne pour moi avec la solution de @indexzero / @ SamuelMaddox17 ci-dessus:

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'));

Après une enquête plus approfondie, il semble que le problème logger.error j'ai expliqué ci-dessus n'est qu'un problème lors de l'utilisation de l'enregistreur par défaut. @DABH , j'ai essayé votre code et cela fonctionne pour moi, mais quand je le change sur l'enregistreur par défaut, cela ne fonctionne pas:

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'));

Deuxièmement, je suis d'accord que enumerateErrorFormat devrait être ajouté au format json; et vous avez probablement raison sur meta aussi.

Enfin, j'aimerais noter que l'exemple de code donné par @DABH fait que la pile ne "s'imprime pas assez" si vous voulez; au moins sur ma machine exécutant macOS High Sierra. Voici à quoi cela ressemble pour moi:

{"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"}

Comme vous pouvez le voir, lors de la sortie de l'erreur avec une fonction vers JSON, les caractères de nouvelle ligne \n ne créent pas de nouvelles lignes réelles. Il s'agit d'un comportement attendu lors de la prise d'un objet et de sa conversion en JSON, mais ce n'est probablement pas le comportement que nous souhaitons réellement d'un enregistreur, du moins lors de la connexion à la console.

Merci de regarder plus dans ce @DABH

Pour info, c'est là que je suis arrivé après avoir joué un peu avec ça:

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;

Le problème est causé par ce bogue: https://github.com/winstonjs/winston-transport/issues/31

Nous avons définitivement utilisé ce formulaire dans winston2.x sans aucun problème. winston.err('some message', err); avec winston.error(err) ci-dessus enumerateErrorFormat corrige winston.error(err) mais pas le cas d'utilisation avec err comme deuxième paramètre.

@ SamuelMaddox17

logger.log ({niveau: ____, message: err});

ça marche thx

D'accord, j'ai découvert quelque chose. Mon commentaire du 3 septembre est faux. Ce n'est pas un problème avec l'enregistreur par défaut. C'est un problème avec la définition de level et / ou format . @DABH voici votre ancien code:

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 vous supprimez ceci:

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

Et remplacez-le par ceci:

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

Ensuite, le problème info.message === undefined apparaît. Je pense qu'il devrait être correct de spécifier le niveau et le format de chaque transport; et je suis presque certain que cela a été autorisé dans Winston 2.0.

Voici votre exemple de code avec mon changement de code afin que vous puissiez facilement exécuter et tester:

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'));

Espérons que cela aide à aller à la racine du problème.

J'ai créé https://github.com/winstonjs/winston/pull/1527

Cela couvre toutes les options. Cependant, certains tests échouent donc je l'ai fermé pour le moment. Les échecs sont attendus étant donné le correctif, mais je ne pense pas être en mesure de faire l'appel pour modifier / supprimer les tests.

La version défaillante est ici https://travis-ci.org/winstonjs/winston/jobs/453012141 et il est évident pourquoi les tests échouent maintenant lorsque vous lisez le code de test:
https://github.com/winstonjs/winston/blob/c42ab7fdc51b88db180a7dd90c52ce04ddd4e054/test/logger.test.js#L668

Pensées?

Je pense que le problème est dans cette ligne
const info = (msg && !(msg instanceof Error) && msg.message && msg) || {
message: msg
};
l'ajout d'une vérification de instanceof Error semble résoudre le problème comme le souligne @crowleym

Pour tous ceux qui sont encore confrontés à cela, voici le formateur de solution de contournement que j'ai réussi à proposer (extrait de mon module 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));
}

Pour une raison quelconque, logger.error(new Error("hello")) ne fonctionne que si vous définissez le formateur globalement dans winston.createLogger 🤔 et que vous obtenez ensuite l'objet Error dans info dans le formateur.

Si vous définissez un formateur par transport, vous devez utiliser logger.log({level: "error", message: new Error("FAILED")}) et gérer l'objet Error via info.message au lieu de info pour accéder à l'objet Error.

Je suppose qu'il y a un bug lors de la définition du champ de format dans les options de transport?

Ce ne sont que mes 2 cents et ce qui a fonctionné pour moi, je suis nouveau sur Winston et je ne suis pas expérimenté en JavaScript, alors ne me citez rien.

Mon approche était une tentative de corriger la cause profonde. Mais ce n'est pas beaucoup de traction de la part des propriétaires de repo ...

Ouais, je comprends ça. J'ai littéralement passé beaucoup de temps à comprendre cela parce que je suis nouveau sur Winston et que je pensais que c'était peut-être moi qui l'utilisais de manière incorrecte ou que je n'avais pas encore suffisamment saisi les concepts sous-jacents.

Mais heureusement, je suis tombé sur quelques fils (celui-ci inclus) qui m'ont montré le contraire. J'espère qu'ils vont résoudre ce problème afin que je n'ai pas à continuer à utiliser la solution de contournement.

Cela peut être causé par wintson-transport , voir https://github.com/winstonjs/winston-transport/issues/31 pour le problème et https://github.com/winstonjs/winston-transport/pull/ 34 pour un PR.

La journalisation directe des objets d'erreur est toujours un gâchis en raison de leurs propriétés non énumérables. Personnellement, je considère que c'est une mauvaise pratique, mais suffisamment de gens dans la communauté sont catégoriques à ce sujet, car nous devons la soutenir.

Envisager d'adopter https://github.com/winstonjs/logform/pull/59 comme format pour prendre en charge des comportements comme celui-ci. Sur le plan positif, il encapsule tous les cas extrêmes qui sont si courants lors du traitement des erreurs comme des messages de journal. En revanche, ce serait encore un autre format auquel les gens devraient adhérer (similaire à .splat() )

@indexzero J'ai tendance à être d'accord, mais la journalisation directe des erreurs peut être utile lorsqu'elle est associée au format de journalisation personnalisé / printf s'il est nécessaire d'afficher plusieurs types Error différemment, et je ne me souviens pas que Winston 2.x a essayé de lutter contre cette pratique car elle était autorisée hors de la boîte.

Ainsi, la solution proposée pour enumerateErrorFormat fonctionne mais ne prend pas en charge le format logger.error('some message', err) . Parce que ni info.message ou info n'est instanceof Error . Je tiens également à souligner un autre problème avec cette solution. Je consigne actuellement une erreur renvoyée par 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 nous utilisons simplement Object.assign, refroidissez la pile et le message sera défini! MAIS, toute autre information faisant partie de l'erreur sera également enregistrée. Cela peut être très dangereux dans les cas où les erreurs ont des données sensibles telles que Authorization Headers (qui dans ce cas sont incluses dans l'objet d'erreur).

Mais alors vous pourriez dire. Ce n'est pas une faute de winston, ce n'est pas la faute de winston si le superagent ajoute ces données à l'erreur. JE SUIS D'ACCORD! CEPENDANT, comme tout est stocké à plat sur l'objet info , il devient très difficile de conserver les anciennes informations et de ne pas remplacer les autres.

Ce serait presque bien si, lors de l'utilisation de logger.error. Il a supposé que le deuxième paramètre était et error et l'a placé sur l'objet info sous la forme info.error , puis en cas de journalisation dans l'autre sens, l'interface serait { level: "error", error: YOUR ERROR OBJECT}

Je suis vraiment en train de cracher ici mais la nouvelle interface a définitivement été un peu frustrante (tout sur info).

Juste pour expliquer ce que je voulais dire, supposons que vous logger.error( e ) où e est de type error.

Ensuite, dans votre code, vous avez configuré winston comme suit:

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

l'horodatage est poussé sur l'objet d'erreur 😱. Cela a-t-il vraiment un sens? Pensez-y… l'objet d'erreur que vous avez envoyé reçoit un nouvel accessoire dynamiquement .. horodatage.

Je pense que la meilleure solution globale à ce problème serait de prendre en charge la syntaxe suivante

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

puis en interne, vous pouvez créer un formateur d'erreur qui recherche le champ d'erreur!

Mise à jour sur ces gens:

En espérant que cela soit cousu et expédié dans les prochains jours. C'est l'avant-dernier numéro de notre outil [email protected] suivi

Salut les gens - veuillez consulter https://github.com/winstonjs/winston/pull/1562. C'était le dernier élément de notre liste de contrôle des versions de 3.2.0 donc une fois que le PR sera cousu, nous pourrons le publier.

Jusqu'à ce qu'un correctif soit publié, j'utilise la solution de contournement suivante:

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

Je suis sur le dernier winston (3.2.1) et je reçois toujours undefined en passant une erreur à logger.error

@ezze avait une excellente solution mais la trace de la pile est forcée sur une nouvelle ligne. voici une version légèrement modifiée qui le met sur une seule ligne (donc il est capturé par un simple grep de fichiers journaux)

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

avec sortie <Error message> - trace: <stack trace>

s'il existe un moyen plus simple de le faire avec la dernière version de winston, veuillez me le faire savoir @indexzero. je suis nouveau dans la bibliothèque et suivais les documents

Je viens de voir le lien que vous avez publié sur le PR. cela signifie-t-il que pour passer une erreur à logger.error nécessite une chaîne de message, puis l'erreur?

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 Il a fini par y avoir 2 problèmes dont ce fil a parlé. Le premier étant ce que l'affiche originale a soulevé, et le second étant le problème que j'ai soulevé qui est le même que votre problème. Je pense qu'ils n'ont résolu que le problème des affiches d'origine. Je voulais vérifier plus avant pour m'en assurer, puis ouvrir un nouveau problème si tel est le cas. Je n'ai malheureusement pas eu le temps de plonger plus profondément. En attendant, si vous utilisez logger.log({level: 'error', message: err});err est un objet d'erreur, cela fonctionnera.

Toujours avec ce problème, perdu beaucoup de temps pour le résoudre, la solution de @ the-vampiire fonctionne bien.

Pourquoi ce ticket est-il fermé?

La substitution de logger.error est jusqu'à présent la meilleure solution car elle n'ajoute pas de propriété d'horodatage à un objet Error qui est passé en tant qu'argument unique à error (). La plupart des gens s'attendent probablement à ce que les objets Error soient immuables. Si vous ne remplacez pas également logger.info et toutes les autres méthodes liées au niveau, il est surprenant que les choses ne fonctionnent pas comme prévu. Encore une fois, à moins que vous ne souhaitiez que votre objet soit modifié, quel que soit son type, ne l'envoyez pas directement à une méthode de journalisation Winston.

La fonctionnalité est prise en charge depuis [email protected]

Exemple d'utilisation:

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 @ le-vampiire

Je rencontre également toujours le problème.
Quand j'appelle l'erreur comme logger.error(error); je n'obtiens que undefined .
Seulement si je l'appelle comme logger.error('Something went wrong', error) j'obtiens l'erreur complète et je peux l'analyser.

Je rencontre également toujours le problème.
Quand j'appelle l'erreur comme logger.error(error); je n'obtiens que undefined .
Seulement si je l'appelle comme logger.error('Something went wrong', error) j'obtiens l'erreur complète et je peux l'analyser.
Avez-vous ajouté ceci?

format.errors({ stack: true })

Oui, toujours le même problème. J'essaye de le reproduire en un mot.

@ OBrown92 J'ai rencontré le même problème aujourd'hui. Je peux confirmer que cela fonctionne, si format.errors({ stack: true }) , est appliqué à l'enregistreur, pas au transport. Dans ce cas, il est possible d'utiliser à la fois logger.error(error); et logger.error('Something went wrong', error) . Cependant, il y a un problème lorsque j'essaie d'appliquer format.errors({ stack: true }) au transport choisi. Dans ce cas, j'obtiens undefined pour logger.error(error); , mais logger.error('Something went wrong', error) fonctionne correctement.

Je ne sais pas si c'est un comportement attendu ou s'il s'agit d'un bogue, mais j'ai passé beaucoup de temps à en trouver la cause, veuillez donc le corriger ou le mentionner quelque part dans votre documentation. Ce serait vraiment utile.

Quoi qu'il en soit, je suis très reconnaissant pour votre travail sur ce grand projet.

J'étais confronté au même problème, alors j'ai écrit ce paquet, utils-deep-clone . Vérifiez-le.

format.errors is not a function ... c'est une surprise.

@holmberd
Avez-vous importé les formats du package winston?
Exemple d'utilisation:
const { format } = require('winston')
Ou alors
const winston = require('winston'); const { format } = winston;

@aybhalala yepp, même si cela n'a pas d'importance, la pile d'erreurs est passée à printf sans elle.

Mise à jour: comme il y a encore des problèmes avec cela, je fais ce qui suit depuis un certain temps et cela fonctionne très 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 } );

^^ C'est l'usage prévu. Puisque de simples mortels ne comprendront jamais cela, cela devrait être documenté.

Puisque de simples mortels ne le comprendront jamais, cela devrait être

😂 +1 à ce compagnon

J'utilise winston avec un transport tiers ( @google-cloud/logging-winston ) donc j'ai un peu moins de contrôle sur la syntaxe. De plus, je trouve cela juste plus intuitif:

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

Lors de la connexion à la console, je concatène la pile sur le message. Mais le résultat est quelque chose comme ceci:

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

Puisque winston concatène meta.message sur le message d'origine, il y a l'étrange andsomething et le message en double qui est également imprimé dans la pile. Ceci est décrit dans # 1660.

Il semble que # 1664 essaie de résoudre ce problème. En attendant, j'ai écrit un formateur qui "annule" cette concaténation: https://github.com/winstonjs/winston/issues/1660#issuecomment -512226578

@dandv

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

Cela ne fonctionne pas car, contrairement à console.log() , le logger.<level>(message) winston ne prend qu'un seul paramètre appelé message. Ce paramètre de message est soit un objet, soit une chaîne (quelqu'un me corrige si je me trompe mais c'est ce que je comprends).

Notez que vous pouvez également utiliser logger.log({level: <level>, message: <message>}) . Pour en savoir plus sur ces deux fonctions, je vous recommande de lire cette partie de la documentation: Winston Docs on Log Levels . Assurez-vous de lire Utilisation des niveaux de journalisation

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

Je ne peux pas dire avec certitude pourquoi cela ne produit pas la pile, mais je sais que ce n'est pas un problème avec winston. Si vous essayez console.log(`Caught error: ${e}`) cela n'inclut pas non plus la pile. Je n'ai pas beaucoup travaillé avec les littéraux de modèle, donc soit les littéraux de modèle ne fonctionnent pas bien avec les objets, soit le console.log de javascript reconnaît l'objet comme un objet d'erreur et ne produit donc que la propriété message. C'est ma meilleure supposition.

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

Celui-ci va au cœur de ce dont parle ce fil de bogue. Vous devez d'abord comprendre quelques détails techniques sur javascript. Notez que si vous essayez console.log(`Caught error: ${JSON.stringify(e)}`) vous obtenez également la même sortie Caught error: {} . Comme l'explique @indexzero :

message propriétés stack sur Error sont pas énumérables, ce qui fait que JSON.stringify génère quelque chose auquel on ne s'attend pas.

Fondamentalement, comme les propriétés message et stack sont pas énumérables, JSON.stringify ignore ces propriétés, ce qui vous permet de vous retrouver avec un objet vide {} . Pour mieux comprendre l' énumération, je recommande de lire cet

Heureusement, en raison de la façon dont winston 3.0 a été conçu (accessoires pour l'équipe winston), nous avons une solution de contournement pour cela que @indexzero a donné. Je vais t'aider à l'expliquer. Commencez par créer cette fonction:

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;
});

À partir des objets Docs info.level et info.message . Cette propriété info.message EST l'objet d'erreur si c'est tout ce que vous avez passé. Nous créons donc un nouvel objet où message.stack et message.message (Considérez-le comme Error.stack et Error.message ) sont maintenant énumérables, et nous incluons toutes les autres propriétés qui peuvent également être attachées à cet objet d'erreur.

Ensuite, vous allez créer cet enregistreur qui utilise la fonction enumerateErrorFormat() ci-dessus:

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

Cela prendra tout ce que vous passerez message et vérifiera s'il s'agit d'un objet d'erreur. Si c'est le cas, cela résoudra le problème d'énumération. Ensuite, il passe le message à format.json qui va stringifier tout objet (erreur ou non). Si ce n'est pas un objet, c'est une chaîne et format.json effectivley ne fait rien, et vous êtes libre!

Néanmoins, ce serait bien si nous n'avions pas à créer ce enumerateErrorFormat car les objets d'erreur sont généralement enregistrés. Si je comprends bien, l'équipe winston travaille sur un correctif qui sera publié dans une version ultérieure.

Quelques notes finales. Cela ne fonctionne que si vous utilisez logger.log({level: <level>, message: <message>}) où message est l'objet d'erreur. Exemple:

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

Il y a un autre bogue dans winston où ce code ne fonctionne pas, comme je l'ai expliqué dans mon autre article ci-dessus:

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

Pour une raison quelconque, la propriété info.message n'est pas définie lorsque nous utilisons logger.error(err) . Espérons que @indexzero pourra comprendre celui-ci.

Très bonne explication, je veux juste ajouter qu'avec logger.error( Caught error: $ {e} ); vous perdez la pile à cause de la façon dont ce littéral de chaîne fonctionne en javascript, `${e}` est exactement le même que e.toString() , donc n'imprimer que le message d'erreur est le comportement attendu.

C'est toujours un problème? J'ai toujours des problèmes avec ceci:

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
}

C'est toujours un problème? J'ai toujours des problèmes avec ceci:

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
}

Même problème ici...

après avoir regardé le code source, je peux voir quels paramètres il accepte:

interface LeveledLogMethod {
(message: chaîne, rappel: LogCallback): Logger;
(message: string, meta: any, callback: LogCallback): Logger;
(message: string, ... meta: any []): Logger;
(infoObject: objet): Logger;
}

donc si vous passez l'objet d'erreur en tant que premier paramètre, il ne prendra que le message de l'erreur car il ne comprend que les chaînes, et si vous passez l'erreur dans le deuxième paramètre, vous pouvez accéder à la trace de la pile dans info.stack

btw je n'ai trouvé cela nulle part dans la documentation

J'ai trouvé deux solutions, la première est d'utiliser format.errors , mentionné sur le logform dans le logger parent , puis de créer un messageFormatter en utilisant format.printf et d'ajouter conditionnellement un champ stack extrait de info ( format.errors({ stack: true}) l'ajoutera).

L'autre solution, que j'ai préférée, était de pirater les enregistreurs de niveau 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)
  }  
})

Il semble que je puisse obtenir une journalisation des erreurs similaire à console.log

Je peux vérifier que le commentaire de @tiagonapoli sur la façon dont format.errors doit être sur l'enregistreur _parent_ est exact. Quand je fais quelque chose comme ça:

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);

La manipulation d'un objet Error se fait comme s'il s'agissait d'une chaîne. Cependant, si je fais quelque chose comme ça:

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);

La gestion de l'objet Error fonctionne correctement.

Il me semble que le bogue ici est qu'il ne devrait y avoir aucune différence de comportement.

Donc, ce n'est toujours pas résolu après 1 an? Dois-je pirater le code Winston Logger pour le faire fonctionner?

Oui, cela m'a donné assez de maux de tête pour que Winston ait commencé à sembler beaucoup plus de problèmes que cela n'en valait la peine pour mon cas d'utilisation relativement simple ... J'ai fini par écrire ma propre petite classe de bûcherons, et je recommanderais aux autres de faire de même à moins que Winston fournit quelque chose dont vous avez VRAIMENT besoin.

Les gars, vraiment? C'est frustrant...

Je ne suis pas un commetteur, mais j'ai probablement raison de dire que cela ne va pas être «corrigé» parce que ce n'est pas cassé. Winston vaut la peine d'être utilisé. Il vous suffit de le configurer - le meilleur conseil pour moi est ci-dessus à https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691

Pas encore ?

Mise à jour: comme il y a encore des problèmes avec cela, je fais ce qui suit depuis un certain temps et cela fonctionne très 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'où vient la trace?

Non vraiment, c'est inacceptable pour une bibliothèque de journalisation.
Le responsable de la maintenance doit simplement mettre un exemple bien mis en évidence sur les documents où il est montré comment enregistrer une erreur, même en définissant un format printf personnalisé et un format non json et où vous pouvez enregistrer une erreur avec quelque chose comme logger.error ("quelque chose", err) et un enregistreur .error (err)
Winston semblait génial mais ce problème est incroyablement inacceptable

C'est mon point de vue sur la façon de consigner les erreurs à l'aide de Winston. Ce n'est pas unique, beaucoup de gens ci-dessus ont des solutions de travail basées sur les mêmes concepts.

Arrière-plan
J'utilise @jsdevtools/ono pour envelopper des types d'objets arbitraires dans des erreurs personnalisées, mais quoi qu'il en soit, cette solution semble toujours fonctionner correctement sur les erreurs de nœud natif (par exemple, les erreurs fs eperm) et les classes d'erreur personnalisées.

Explication
Fondamentalement, je compte sur format.errors({stack:true}) et format.metadata() . Comme mentionné par https://github.com/winstonjs/winston/issues/1338#issuecomment -532327143, cela doit être dans le formateur parent .

Les métadonnées aident à déplacer toutes les propriétés personnalisées de l'objet d'erreur vers info.metadata .

Je voulais imprimer 3 types d'informations: le message d'erreur, le stacktrace et les propriétés de l'objet d'erreur. Le message d'erreur était déjà en texte brut. J'ai joliment imprimé la pile info.metadata.stack utilisant le module pretty-error . Pour les propriétés de l'objet d'erreur, je ne voulais pas que le stacktrace réapparaisse, j'ai donc cloné l'objet et supprimé la propriété stacktrace. J'ai ensuite joliment imprimé l'objet d'erreur en utilisant fast-safe-stringify , qui est le même module stringify sur lequel winston s'appuie.

    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

PS: Je trouve également que la solution mentionnée dans https://github.com/winstonjs/winston/issues/1338#issuecomment -506354691 est une bonne alternative. Ie en utilisant logger.warn("Oh no", { error: new Error() }) , puis en référençant info.error dans votre formateur personnalisé.

@tiagonapoli votre solution sur l'utilisation de format.errors sur l'enregistreur parent a fonctionné pour moi:

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

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

C'est assez pénible de configurer cet enregistreur ... Ne pourrait-il pas simplement se comporter comme console.log hors de la boîte?

@ will093 même chose ici. Je suis à nouveau sur ce problème et je ne comprends pas pourquoi mon console.log est beau et propre et le format winston est de la merde.

Mon 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;
            }),
        ),
    }));
}

Exemple pour logger.error("Error during schema stitching", e);

image

L'utilisation de la solution de @tiagonapoli et @ will093 consistant à l'ajouter uniquement au parent semble être le moyen le plus simple de prendre en charge directement les erreurs de journalisation et de toujours journaliser les messages - voici un exemple complet de configuration minimale avec horodatage:

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
      ),
  });
};

fonctionne avec une pile lorsqu'elle est appelée avec une erreur comme: logger.error(error) , fonctionne avec une chaîne lorsqu'elle est appelée comme logger.error('a regular message') , ressemble à ceci dans mes journaux:

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

cela ne tente logger.error('message here', error) winston avec console.log , ce que semble faire la solution la plus complexe de

De plus, si vous aimez les journaux json, vous pouvez déposer le logFormatter ici et utiliser winston.format.json() à sa place, ce qui inclura toujours la pile - mais ce n'est pas joli.

Mise à jour: comme il y a encore des problèmes avec cela, je fais ce qui suit depuis un certain temps et cela fonctionne très 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 } );

où est la définition de trace ()?

Mise à jour: comme il y a encore des problèmes avec cela, je fais ce qui suit depuis un certain temps et cela fonctionne très 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'où vient la trace?

une réponse à ce sujet?

Cette page vous a été utile?
0 / 5 - 0 notes