Winston: La transmission de plusieurs paramètres aux fonctions de journalisation ne se comporte pas comme prévu

Créé le 6 août 2018  ·  43Commentaires  ·  Source: winstonjs/winston

Parlez-nous de votre environnement :

  • _ winston version ?_

    • [ ] winston@2

    • [x] winston@3

  • _ node -v sorties :_ v8.11.3
  • _Système d'exploitation ?_ macOS
  • _Langue ?_ Flux ES6/7

Quel est le problème?


Avec 3.xx :

const winston = require('winston')

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

logger.info('test log', 'with a second parameter')

Les sorties:

{"level":"info","message":"test log"}

Avec 2.xx :

const winston = require('winston')

const logger = new winston.Logger({
    transports: [new winston.transports.Console()]
})

logger.info('test log', 'with a second parameter')

Les sorties:

info: test log with a second parameter

Qu'espérez-vous qu'il se passe à la place ?


Il devrait sortir :

{"level":"info","message":"test log with a second parameter"}
important

Commentaire le plus utile

C'est un énorme changement pour nous. Si c'est intentionnel, cela doit être détaillé dans https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Tous les 43 commentaires

J'ai rencontré le même problème aujourd'hui. Je pense qu'il s'agit d'un bug car la documentation de mise à jour contient également un exemple comme celui-ci ici :

logger.info('transaction ok', { creditCard: 123456789012345 });

Actuellement, pour contourner ce problème, vous pouvez créer une fonction qui analyse les arguments avant de les transmettre à winston

@mulligan121 D'accord, mais je ne veux pas remplacer chaque instruction de journal dans ma base de code...

@indexzero Est-ce sous votre radar ? Je pense que c'est un changement majeur. Cela empêche en fait de passer de 2.x à 3.x car cela nous oblige à modifier chaque entrée de journal

Solution de contournement temporaire :

const wrapper = ( original ) => {
    return (...args) => original(args.join(" "));
};

winstonLogger.error = wrapper(winstonLogger.error);
winstonLogger.warn = wrapper(winstonLogger.warn);
winstonLogger.info = wrapper(winstonLogger.info);
winstonLogger.verbose = wrapper(winstonLogger.verbose);
winstonLogger.debug = wrapper(winstonLogger.debug);
winstonLogger.silly = wrapper(winstonLogger.silly);

C'est un énorme changement pour nous. Si c'est intentionnel, cela doit être détaillé dans https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Ainsi, après quelques recherches supplémentaires, j'ai découvert que vous aviez besoin du formateur splat pour que plusieurs arguments soient imprimés. Je pensais que c'était juste pour l'interpolation d'arguments (c'est-à-dire des trucs avec %s etc. dedans), mais il semble que vous en ayez besoin juste pour faire logger.info("something", value) .

Mais cela reste un peu bizarre pour moi - j'obtiens quelque chose qui ressemble à JSON dans la sortie avec une clé "méta", même si je n'utilise pas de format JSON :

logger.info('Test: %s', 1, 2, 3, 4);

Produit :

info: Test: 1 {"meta":[2,3,4]}

Même l'exemple dans les exemples ne produit pas ce qu'il dit :

https://github.com/winstonjs/winston/blob/master/examples/interpolation.js#L20 -L21

info: test message first, second {"meta":{"number":123}}

Je l'ai résolu en utilisant quelque chose du genre:

const { version } = require('../package');

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, colorize, label, printf, align } = format;
const { SPLAT } = require('triple-beam');
const { isObject } = require('lodash');

function formatObject(param) {
  if (isObject(param)) {
    return JSON.stringify(param);
  }
  return param;
}

// Ignore log messages if they have { private: true }
const all = format((info) => {
  const splat = info[SPLAT] || [];
  const message = formatObject(info.message);
  const rest = splat.map(formatObject).join(' ');
  info.message = `${message} ${rest}`;
  return info;
});

const customLogger = createLogger({
  format: combine(
    all(),
    label({ label: version }),
    timestamp(),
    colorize(),
    align(),
    printf(info => `${info.timestamp} [${info.label}] ${info.level}: ${formatObject(info.message)}`)
  ),
  transports: [new transports.Console()]
});

Voici une solution de contournement courante pour autoriser plusieurs paramètres :
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

Relative à:
https://github.com/winstonjs/winston/issues/1377

Il y a juste trop de choses qui ne fonctionnent pas de la même manière.

Salut, des mises à jour ici? Merci.

En attente également d'un moyen approprié d'émuler console.log(...args) dans winston ...

Nous avons corrigé un certain nombre de cas de bord / coin autour de splat et meta dans 3.2.0 , mais il semble que ce soit toujours un problème (voir : https://github.com /winstonjs/winston/pull/1576 pour le CHANGELOG.md ).

Veiller à ce que cela soit géré dans 3.3.0 . Merci pour votre patience les gars.

C'était une solution de contournement pour moi:

'use strict';

const { createLogger, format, transports } = require('winston');
const { SPLAT } = require('triple-beam');

const { combine, timestamp, label, printf, colorize } = format;

const formatObject = (param) => {
  if (typeof param === 'string') {
    return param;
  }

  if (param instanceof Error) {
    return param.stack ? param.stack : JSON.stringify(param, null, 2);
  }

  return JSON.stringify(param, null, 2);
};

const logFormat = printf((info) => {
  const { timestamp: ts, level, message } = info;
  const rest = info[SPLAT] || [];
  const msg = info.stack ? formatObject(info.stack) : formatObject(message);
  let result = `${ts} ${level}: ${msg}`;

  if (rest.length) {
    result += `\n${rest.map(formatObject).join('\n')}`;
  }

  return result;
});

const logger = createLogger({
  format: combine(
    label({ label: 'app' }),
    timestamp(),
    logFormat,
  ),
  transports: [
    new transports.Console({
      format: combine(colorize()),
      handleExceptions: true,
    }),
  ],
  exceptionHandlers: [
    new transports.File({ filename: 'exceptions.log' }),
  ],
});

Lorsque j'essaie de me connecter avec des arguments, j'obtiens une sortie étrange
var s = "Hello" log.info("asdasda", s)

Obtenir la sortie

{"0":"H","1":"e","2":"l","3":"l","4":"o","service":"XXXX","level":"info","message":"asdasda","timestamp":"2019-04-15 13:58:51"}

J'ai utilisé le code de démarrage rapide normal

Une chose que j'ai remarquée est la suivante :

  • obtenu un logger avec 2 transports (console et fichier rotatif)
  • Les deux utilisent le format splat
  • L'appel à la fonction de transformation de splat pour le premier transport efface le tableau stocké sur info sous le symbole SPLAT
  • Lors de l'appel à la fonction de transformation de splat pour le deuxième transport, le tableau SPLAT est vide et donc les arguments supplémentaires ne sont plus enregistrés

J'espère que cela pourra/sera corrigé

Remarque: testé avec la dernière version publiée de Winston, un rapide coup d'œil au code semble suggérer que c'est toujours comme ça dans master (qui a d'autres correctifs, semble-t-il)

Quelques améliorations par rapport à la solution de @luislobo

  • Ignorer le message s'il est de style splat, c'est-à-dire qu'il contient %s %d or %j pour des messages comme logger.info(`hello %s`,'world')
  • formateurs réorganisés pour que la colorisation passe en premier https://github.com/winstonjs/winston#colorizing -standard-logging-levels
  • Suppression de formateObject dans printf
  • trimEnd si les arguments supplémentaires ne sont pas fournis dans le looger
const { version } = require('../package');

const { createLogger, format, transports } = require('winston');
const { combine, timestamp, colorize, label, printf, align } = format;
const { SPLAT } = require('triple-beam');
const { isObject,trimEnd } = require('lodash');

function formatObject(param) {
  if (isObject(param)) {
    return JSON.stringify(param);
  }
  return param;
}

const all = format((info) => {
  const splat = info[SPLAT] || []

    const isSplatTypeMessage =
        typeof info.message === 'string' &&
        (info.message.includes('%s') || info.message.includes('%d') || info.message.includes('%j'))
    if (isSplatTypeMessage) {
        return info
    }
    const message = formatObject(info.message)
    const rest = splat
        .map(formatObject)
        .join(' ')
    info.message = trimEnd(`${message} ${rest}`)
    return info
});

const customLogger = createLogger({
  format: combine(
    colorize(),
    all(),
    label({ label: version }),
    timestamp(),
    align(),
    printf(info => `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`)
  ),
  transports: [new transports.Console()]
});

Une autre mise à jour à ce sujet qui me manque peut-être ? Il semble qu'il y ait un soutien assez fort pour que cette fonctionnalité soit à nouveau ajoutée, mais le dernier engagement à cet égard remonte à près de 6 mois.

Salut, énorme Bumper et show stopper, essayant de migrer vers le 3.X, gardera probablement le 2.x pendant un certain temps.

Un peu déçu :(

C'est ce qui a fonctionné pour moi:

const { format, createLogger, transports } = require('winston');
const jsonStringify = require('fast-safe-stringify');

const logLikeFormat = {
  transform(info) {
    const { timestamp, label, message } = info;
    const level = info[Symbol.for('level')];
    const args = info[Symbol.for('splat')];
    const strArgs = args.map(jsonStringify).join(' ');
    info[Symbol.for('message')] = `${timestamp} [${label}] ${level}: ${message} ${strArgs}`;
    return info;
  }
};

const debugFormat = {
  transform(info) {
    console.log(info);
    return info;
  }
};

const logger = createLogger({
  format: format.combine(
    // debugFormat, // uncomment to see the internal log structure
    format.timestamp(),
    format.label({ label: 'myLabel' }),
    logLikeFormat,
    // debugFormat, // uncomment to see the internal log structure
  ),
  transports: [
    new transports.Console()
  ]
});


logger.info('foo', 'bar', 1, [2, 3], true, { name: 'John' });

ce qui donne : 2019-07-04T21:30:08.455Z [myLabel] info: foo "bar" 1 [2,3] true {"name":"John"}

Fondamentalement, format.combine définit un pipeline pour un objet info . Pour chaque fonction de formatage, transform est appelé et le message de journal final doit être écrit dans info[Symbol.for('message')]
J'espère que cela t'aides

Je voulais une solution qui prend en charge %d , %o etc, mais qui inclut toujours par défaut tous les arguments de repos, de sorte que logger.info('hello %d %j', 42, {a:3}, 'some', 'more', 'arguments') soit rendu comme ceci :

hello 42 {"a": 3} some more arguments

Ma solution pour cela a fini par utiliser le symbole splat mais en invoquant manuellement util.format() directement à partir de printf , qui par défaut inclut tous les arguments de repos :

const {format} = require('util');
const winston = require('winston');
const {combine, timestamp, printf} = winston.format;
const SPLAT = Symbol.for('splat');

const transport = new winston.transports.Console({
    format: combine(
        timestamp(),
        printf(({timestamp, level, message, [SPLAT]: args = []}) =>
            `${timestamp} - ${level}: ${format(message, ...args)}`)
    )
})

Si vous ne voulez pas printf vous pouvez bien sûr ajouter une transformation qui développe simplement les arguments en info.message , puis formater le résultat final comme vous le souhaitez :

format: combine(
  {
    transform(info) {
      const {[SPLAT]: args = [], message} = info;

      info.message = format(message, ...args);

      return info;
    }
  },
  simple()
)  

Le problème avec l'utilisation de format.splat() est qu'il consomme les arguments correspondants mais semble jeter le reste. En invoquant moi-même util.format() je peux outrepasser ce comportement.

Même problème, une mise à jour concernant celui-ci? J'adore Winston, mais cela me rend fou quand je ne peux pas imprimer tous les arguments passés au logger, ce que je peux faire avec console.log() .

@fr1sk Avez-vous essayé ma mise en forme ci-dessus ? Cela donne console.log() comportement similaire à console.log() implémentation du nœud util.format() ma connaissance).

Il est décevant que ce problème n'ait toujours pas été résolu et que mon problème connexe ait été verrouillé.
Je travaille avec de nombreuses équipes différentes et elles partagent toutes la frustration de perdre le DX précédent lors de la migration vers la v3.

J'ai passé une journée entière à essayer des choses.
Les solutions ci-dessus étaient proches, mais il me manquait la colorisation et le saut de ligne des arguments supplémentaires.

OMI, la connexion à la console devrait être agréable.
Voici ma tentative pour y arriver...

configuration v2 pour comparaison

const winston = require('winston');
const chalk = require('chalk');

const logger = new winston.Logger({
  transports: [
    new winston.transports.Console({
      level: 'info',
      colorize: true,
      prettyPrint: true,
      timestamp: true
    })
  ]
});

logger.info({ one: 1, two: 2, three: 3 });
logger.info(chalk.blue('[TEST]:'), { one: 1, two: 2, three: 3 }, [4, 5, 6]);
logger.info(chalk.blue('[TEST]:'), null, undefined, 'one', 2, { 3: 3, 4: '4' });
logger.info(chalk.blue('[TEST]:'), chalk.yellow('Bombastic'), () => {}, /foo/);
logger.error(chalk.blue('[ERR]:'), new Error('Error number 1'));
logger.error(new Error('Error number 2'));

Winston2

configuration v3

const { createLogger, format, transports } = require('winston');
const { inspect } = require('util');
const chalk = require('chalk');
const hasAnsi = require('has-ansi');

function isPrimitive(val) {
  return val === null || (typeof val !== 'object' && typeof val !== 'function');
}
function formatWithInspect(val) {
  const prefix = isPrimitive(val) ? '' : '\n';
  const shouldFormat = typeof val !== 'string' || !hasAnsi(val);

  return prefix + (shouldFormat ? inspect(val, { depth: null, colors: true }) : val);
}

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.errors({ stack: true }),
    format.colorize(),
    format.printf(info => {
      const msg = formatWithInspect(info.message);
      const splatArgs = info[Symbol.for('splat')] || [];
      const rest = splatArgs.map(data => formatWithInspect(data)).join(' ');

      return `${info.timestamp} - ${info.level}: ${msg} ${rest}`;
    })
  ),
  transports: [new transports.Console()]
});

logger.info({ one: 1, two: 2, three: 3 });
logger.info(chalk.blue('[TEST]:'), { one: 1, two: 2, three: 3 }, [4, 5, 6]);
logger.info(chalk.blue('[TEST]:'), null, undefined, 'one', 2, { 3: 3, 4: '4' });
logger.info(chalk.blue('[TEST]:'), chalk.yellow('Bombastic'), () => {}, /foo/);
logger.error(chalk.blue('[ERR]:'), new Error('Error number 1'));
logger.error(new Error('Error number 2'));

Winston3

Il y a beaucoup de choses à gérer, dont certaines

  • Une valeur de type objet comme premier argument
  • Plusieurs arguments de valeurs mixtes
  • Chaînes avec des codes d'échappement ANSI, par exemple en utilisant chalk
  • Valeurs ésotériques comme Function et RegEx
  • Une erreur comme premier argument ou n'importe où après cela
  • Devrait toujours fonctionner correctement avec d'autres formateurs

Bugs actuels avec cette solution

  • Passer une erreur comme 1er argument n'imprime pas la trace de la pile

    • C'est parce que info.message n'est qu'une chaîne et que le segment de pile est dans la propriété stack

  • Passer un objet avec une propriété message comme premier argument n'affiche que message , en supprimant toutes les autres propriétés

    • Même raison que ci-dessus

  • Passer une erreur en tant que 2ème argument concatène en quelque sorte le message d'erreur au-dessus de info.message (qui est généralement la valeur du 1er argument), ce qui entraîne l'affichage du message d'erreur deux fois

Jouer avec le formateur errors n'a pas aidé.

Améliorations positives

  • La journalisation des valeurs primitives est maintenant embellie
  • L'enregistrement de plusieurs valeurs de type objet ajoute désormais une nouvelle ligne entre elles
  • L'enregistrement d'un tableau est maintenant plus joli au lieu de ressembler à un objet avec des clés d'index

Éditer:

On peut gérer les erreurs de formatage mais c'est bidon :

function formatWithInspect(val) {
+ if (val instanceof Error) {
+   return '';
+ }

  const prefix = isPrimitive(val) ? '' : '\n';
  const shouldFormat = typeof val !== 'string' || !hasAnsi(val);

  return prefix + (shouldFormat ? inspect(val, { depth: null, colors: true }) : val);
}

...
    format.printf((info) => {
      const msg = formatWithInspect(info.message);
      const splatArgs = info[Symbol.for('splat')] || [];
      const rest = splatArgs.map((data) => formatWithInspect(data)).join(' ');
+    const stackTrace = info.stack ? `\n${info.stack}` : '';

      return `${info.timestamp} - ${info.level}: ${msg} ${rest}${stackTrace}`;
    })
...

cela fonctionne pour moi en utilisant info[Symbol.for('splat')]

const logger = winston.createLogger({
    level: 'debug',
    transports: [
        ...
    ],
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.printf((info: any) => {
            const timestamp = info.timestamp.trim();
            const level = info.level;
            const message = (info.message || '').trim();
            const args = info[Symbol.for('splat')];
            const strArgs = (args || []).map((arg: any) => {
                return util.inspect(arg, {
                    colors: true
                });
            }).join(' ');
            return `[${timestamp}] ${level} ${message} ${strArgs}`;
        })
    )
});

Je comprends que les versions majeures introduisent des changements de rupture, mais mon Dieu...

Aucune des solutions ne m'a donné le même comportement que winston v2.
Les solutions de @henhal et @yamadashy ont toutes les deux le même problème : la valeur du message est affichée sous forme de chaîne.
logger.debug(err) créer un message de journal :

debug:  Error: ETIMEDOUT

alors que logger.debug('anystringhere', err) crée :

debug:  anystringhereError: ETIMEDOUT { RequestError: Error: ETIMEDOUT
    at new RequestError (/path/to/node_modules/request-promise-core/lib/errors.js:14:15)
 <the rest of full error stack here>

Le deuxième problème est que les arguments supplémentaires sont supprimés lors de l'utilisation avec le niveau d'information - winston v3 semble gérer cela dans l'autre sens avant le formatage.

Le troisième problème est l'espace manquant entre 2 messages (notez "anystringhereError").

Ma configuration actuelle de l'enregistreur v3 :

const { format } = require('util');
const winston = require("winston");

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      level: 'debug'
      format: winston.format.combine(
          winston.format.colorize(),
          winston.format.align(),
          winston.format.printf(
              ({level, message, [Symbol.for('splat')]: args = []}) => `${level}: ${format(message, ...args)}`
          )
      )
    }),
  ]
});
module.exports = logger;

J'en ai assez et je vais revenir à winston v2 avec juste ça :

const Winston = require("winston");

const logger = new Winston.Logger({
  transports: [
    new Winston.transports.Console({
      level: 'debug',
      handleExceptions: true,
      json: false,
      colorize: true,
    })
  ],
});
module.exports = logger;

Cette configuration v2 n'a pas les problèmes mentionnés ci-dessus.

Je veux partager ma configuration winston :

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

const myFormat = printf(({ level, message, label, timestamp, ...rest }) => {
    const splat = rest[Symbol.for('splat')];
    const strArgs = splat ? splat.map((s) => util.formatWithOptions({ colors: true, depth: 10 }, s)).join(' ') : '';
    return `${timestamp}  ${level}  ${util.formatWithOptions({ colors: true, depth: 10}, message)} ${strArgs}`;
});

const logger = createLogger({
    level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
    format: combine(
        colorize(),
        timestamp({
            format: 'YYYY-M-DD HH:mm:ss',
        }),
        padLevels(),
        myFormat
    ),
    transports: [new transports.Console()],
});

logger.info('test info');
logger.error('test error');
logger.debug('test debug');

image

Voici une solution de contournement courante pour autoriser plusieurs paramètres :
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

voici le lien mis à jour vers la solution proposée
https://github.com/rooseveltframework/roosevelt/blob/0.13.0/lib/tools/logger.js

La mienne est une variante des excellentes suggestions de @alexilyaev :

const { omit } = require('lodash');
const hasAnsi = require('has-ansi');

function isPrimitive(val) {
  return val === null || (typeof val !== 'object' && typeof val !== 'function');
}
function formatWithInspect(val) {
  if (val instanceof Error) {
    return '';
  }

  const shouldFormat = typeof val !== 'string' && !hasAnsi(val);
  const formattedVal = shouldFormat
    ? inspect(val, { depth: null, colors: true })
    : val;

  return isPrimitive(val) ? formattedVal : `\n${formattedVal}`;
}

// Handles all the different log formats for console
function getDomainWinstonLoggerFormat(format) {
  return format.combine(
    format.timestamp(),
    format.errors({ stack: true }),
    format.colorize(),
    format.printf((info) => {
      const stackTrace = info.stack ? `\n${info.stack}` : '';

      // handle single object
      if (!info.message) {
        const obj = omit(info, ['level', 'timestamp', Symbol.for('level')]);
        return `${info.timestamp} - ${info.level}: ${formatWithInspect(obj)}${stackTrace}`;
      }

      const splatArgs = info[Symbol.for('splat')] || [];
      const rest = splatArgs.map(data => formatWithInspect(data)).join('');
      const msg = formatWithInspect(info.message);

      return `${info.timestamp} - ${info.level}: ${msg}${rest}${stackTrace}`;
    }),
  );
}

pour les journaux comme :

logger.info({
    joe: 'blow',
  });
  logger.info('Single String');
  logger.info('With Func ', () => {}, /foo/);
  logger.info('String One ', 'String Two');
  logger.info('String One ', 'String Two ', 'String Three');
  logger.info('Single Object ', {
    test: 123,
  });
  logger.info(
    'Multiple Objects ',
    {
      test: 123,
    },
    {
      martin: 5555,
    },
  );
  logger.error('Error: ', new Error('Boom!'));

Il sort comme ceci - ce qui est bon pour tous mes scénarios :

Screen Shot 2020-02-06 at 10 54 31 pm

Hey je commence avec Winston, jamais utilisé v2, donc je suis sur v3.2.1

J'essayais de faire simplement :

import winston, { format } from 'winston';
winston.format(format.combine(format.splat(), format.simple()));

winston.info('buildFastify dataPath %s', opts.dataPath);

Et en espérant que l'interpolation de chaîne fonctionne ; mais non.

{"level":"info","message":"buildFastify dataPath %s"}

Quand j'attendais

{"level":"info","message":"buildFastify dataPath /My/Data/Path"}

Cette question est la même chose en quelque sorte? Ou je suis obligé d'utiliser la fonction logger.log('info', .....) place ?

Éditer

L'événement essayant cela ne fonctionne pas.

  winston.log('info', 'buildFastify dataPath %s', opts.dataPath);

sortir:

{"level":"info","message":"buildFastify dataPath %s"}

Merci à ceux qui ont posté les suggestions ci-dessus.
Putain j'ai jeté un jour juste pour intégrer Winston 3 sur mon projet :/

Celui-ci m'a frappé aujourd'hui - j'ai réalisé que mon nouvel enregistreur de fantaisie lançait tous mes arguments ...rest . Cela peut ne pas fonctionner pour tout le monde ou pour tous les cas d'utilisation, mais dans mon cas, j'ai trouvé acceptable de simplement envelopper tout ce que je voulais journalisé en tant que tableau. Ce n'est pas le plus joli, mais c'est plus léger que certaines des solutions de contournement [très intelligentes] qui nécessitent plus de code. J'espère que ceci aide quelqu'un d'autre!

logger.info(["Something happened!", foo, bar]);

Vraiment bouleversant qu'ils aient décidé de faire un changement aussi important et qu'ils ne l'aient TOUJOURS pas mentionné dans le guide de migration ou la documentation.

Quoi qu'il en soit, je voulais partager ma solution que j'ai trouvée assez compacte et qui suit comment node js implémente console.log en utilisant util.format

const winstonLogger= createLogger(...);

const writeLogType = (logLevel) => {
    return function () {
        const args = Array.from(arguments);
        winstonLogger[logLevel](util.format(...args));
    };
};

const logger = {
    silly: writeLogType('silly'),
    debug: writeLogType('debug'),
    verbose: writeLogType('verbose'),
    info: writeLogType('info'),
    warn: writeLogType('warn'),
    error: writeLogType('error'),
};

Je voudrais intervenir et demander que cette fonctionnalité soit ajoutée au noyau Winston. Actuellement, j'utilise le formateur personnalisé avec splat pour obtenir cette fonctionnalité, ce qui est honnêtement très compliqué. Ce serait bien d'avoir des fonctionnalités qui correspondent à console.log

Les mises à jour?

Outre les comportements ci-dessus pour la v3, je souhaite également ajouter ceci :

logger.info('param 1', { propInJson1: 'propInJson 1', propInJson2: 'propInJson 2' });

produira ceci

{"propInJson1":"propInJson 1","propInJson2":"propInJson 2","level":"info","message":"param 1"}

la version que j'utilise : (v3.2.1)
Les configs :

winstonLogger.add(new winston.transports.Console());

Je ne comprends toujours pas comment générer plusieurs valeurs dans Winston.

Je voulais juste remplacer console.log('Hello', var1, '!') par logger.log('Hello', var1, '!') .

Pour être honnête, essayer d'utiliser Winston conduit toujours à beaucoup de temps perdu et à des problèmes surprenants avec la journalisation.

Vraiment bouleversant qu'ils aient décidé de faire un changement aussi important et qu'ils ne l'aient TOUJOURS pas mentionné dans le guide de migration ou la documentation.

Quoi qu'il en soit, je voulais partager ma solution que j'ai trouvée assez compacte et qui suit comment node js implémente console.log en utilisant util.format

const winstonLogger= createLogger(...);

const writeLogType = (logLevel) => {
    return function () {
        const args = Array.from(arguments);
        winstonLogger[logLevel](util.format(...args));
    };
};

const logger = {
    silly: writeLogType('silly'),
    debug: writeLogType('debug'),
    verbose: writeLogType('verbose'),
    info: writeLogType('info'),
    warn: writeLogType('warn'),
    error: writeLogType('error'),
};

De plus, utilisez util.formatWithOptions({ colors: true }, ...args); pour obtenir une sortie d'impression en couleur comme avec un console.log normal

Cela fonctionne pour moi. combineMessageAndSplat qui combine message et splat en utilisant util.format

const winston = require("winston");
const util    = require('util');

const combineMessageAndSplat = () => {
    return {transform: (info, opts) => {
      //combine message and args if any
      info.message =  util.format(info.message, ...info[Symbol.for('splat')]  ||  [] )
      return info;
    }
  }
}

const logger = winston.createLogger({
  format:
    winston.format.combine(
      combineMessageAndSplat(),
      winston.format.simple()
    )
});

logger.add(new winston.transports.Console({
    level: 'info'
  })
);

logger.info("string");          // info: string
logger.info({a:1,b:[{c:[1]}]}); // info: { a: 1, b: [ { c: [Array] } ] }
logger.info("1","2",{a:1});     // info: 1 2 { a: 1 }
logger.info([{},""]);           // info: [ {}, '' ]
logger.error(new Error());      // error: Error ...    at Object.<anonymous> 

J'ai dû me conformer aux méthodes console.*, en m'orientant sur tout ce qui précède, et ...

  • méthodes de console par défaut (elles se colorent par défaut) pour chrome-dev-tools
  • sortie console terminal électrons (également colorée par défaut)
  • écriture dans un fichier

Mais : La sortie du fichier doit afficher les détails de l'objet.

J'ai donc fini avec :

// make File and FileList parseable // from: https://stackoverflow.com/a/51939522/1644202
File.prototype.toObject = function () {
    return Object({
        name: String(this.name),
        path: String(this.path),
        lastModified: parseInt(this.lastModified),
        lastModifiedDate: String(this.lastModifiedDate),
        size: parseInt(this.size),
        type: String(this.type)
    });
};

FileList.prototype.toArray = function () {
    return Array.from(this).map(function (file) {
        return file.toObject()
    });
};

// this fixes: winston console transport to use the original console functions and all the colorization/folding/etc that comes with it
const Transport = require('winston-transport');
class WebDeveloperConsole extends Transport {
    constructor(opts) {
        super(opts);
    }

    log(info, callback) {
        (window.console[info.level] || window.console.log).apply(window.console, [info.timestamp, ...info.message]);
        callback();
    }
};

// Electron app console output
class AppConsole extends Transport {
    constructor(opts) {
        super(opts);

        const { remote } = require('electron');
        this.electronConsole = remote.getGlobal('console');
    }

    log(info, callback) {
        (this.electronConsole[info.level] || this.electronConsole.log).apply(this.electronConsole, [info.timestamp, ...info.message]);
        callback();
    }
};

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

let logger = createLogger({
    level: 'trace',
    levels: {
        error: 0,
        warn: 1,
        info: 2,
        //http: 3,   no console.* methods
        //verbose: 4,
        debug: 3,
        trace: 4
    },

    format: format.combine(
        format.prettyPrint(),
        format.timestamp({
            format: 'DD-MM-YYYY hh:mm:ss A'
        }),

        {
            transform(info) {
                const { timestamp, message } = info;
                const level = info[Symbol.for('level')];
                const args = [message, ...(info[Symbol.for('splat')] || [])];  // join the args back into one arr
                info.message = args;  // for all custom transports (mainly the console ones)

                let msg = args.map(e => {
                    if (e.toString() == '[object FileList]')
                        return util.inspect(e.toArray(), true, 10);
                    else if (e.toString() == '[object File]')
                        return util.inspect(e.toObject(), true, 10);
                    else if (e.toString() == '[object Object]') {
                        return util.inspect(e, true, 5);
                    }
                    else if (e instanceof Error)
                        return e.stack
                    else
                        return e;
                }).join(' ');

                info[Symbol.for('message')] = `${timestamp} - ${level}: ${msg}`; // for inbuild transport / file-transport

                return info;
            }
        },
    ),
    transports: [
        //new transports.Console(),
        new WebDeveloperConsole(),
        new AppConsole(),
        ...
    ],
    ...
});

@indexzero Vous vous prévoyiez toujours de résoudre ce problème à un moment donné ?

J'explore Winston à utiliser dans mon projet, je me demande maintenant si cela vaut la peine de prendre le risque car ce problème est ouvert depuis plus de 2 ans. pour moi, cela semble être une fonction de base pour tout framework de journalisation

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