Winston: Passar vários parâmetros para funções de registro não se comporta como esperado

Criado em 6 ago. 2018  ·  43Comentários  ·  Fonte: winstonjs/winston

Conte-nos sobre o seu ambiente:

  • _ winston versão? _

    • [] winston@2

    • [x] winston@3

  • _ node -v saídas: _ v8.11.3
  • _Sistema operacional? _ MacOS
  • _Idioma? _ Fluxo ES6 / 7

Qual é o problema?


Com 3.xx:

const winston = require('winston')

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

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

Saídas:

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

Com 2.xx:

const winston = require('winston')

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

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

Saídas:

info: test log with a second parameter

O que você espera que aconteça em vez disso?


Deve produzir:

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

Comentários muito úteis

Esta é uma mudança de última hora ENORME para nós. Se for intencional, deve ser detalhado em https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Todos 43 comentários

Corri para o mesmo problema hoje. Acredito que seja um bug, pois a documentação do upgrade também tem um exemplo como esse aqui :

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

Atualmente, para contornar isso, você pode criar uma função que analisa os argumentos antes de serem passados ​​para o winston

@ mulligan121 Ok, mas não quero substituir todas as instruções de log em minha base de código ...

@indexzero Isso está fora do seu radar? Eu acho que esta é uma grande mudança significativa. Na verdade, impede a mudança de 2.x para 3.x, pois exige que alteremos todas as entradas de registro

Solução temporária:

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

Esta é uma mudança de última hora ENORME para nós. Se for intencional, deve ser detalhado em https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Portanto, depois de mais alguma investigação, descobri que você precisa do formatador splat para que vários argumentos sejam impressos. Eu pensei que era apenas para interpolação de argumento (ou seja, coisas com% s etc. nele), mas parece que você precisa disso apenas para fazer logger.info("something", value) .

Mas isso ainda está sendo um pouco estranho para mim - estou recebendo algo que se parece com JSON na saída com uma chave "meta", embora não esteja usando um formato JSON:

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

Produz:

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

Mesmo o exemplo em exemplos não produz o que diz que irá:

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

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

Eu resolvi isso usando algo assim:

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

Esta é uma solução alternativa comum para permitir vários parâmetros:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

Relacionado a:
https://github.com/winstonjs/winston/issues/1377

Existem muitas coisas que não funcionam da mesma forma.

Oi, alguma atualização aqui? Obrigado.

Também aguardando a maneira adequada de emular console.log(...args) no winston ...

Corrigimos vários casos de borda / canto em torno de splat e meta em 3.2.0 , mas parece que isso ainda é um problema (consulte: https://github.com / winstonjs / winston / pull / 1576 para CHANGELOG.md ).

Garantirá que isso seja tratado em 3.3.0 . Obrigado por sua paciência pessoal.

Esta foi uma solução alternativa para mim:

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

Quando tento registrar com argumentos, recebo uma saída estranha
var s = "Hello" log.info("asdasda", s)

Obtendo a saída

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

Eu usei o código de início rápido normal

Uma coisa que notei é o seguinte:

  • obteve um logger com 2 transportes (console e arquivo rotativo)
  • Ambos usam o formato splat
  • A chamada para a função de transformação de splat para o primeiro transporte limpa a matriz armazenada em informações sob o símbolo SPLAT
  • Na chamada para a função de transformação de splat para o segundo transporte, a matriz SPLAT está vazia e, portanto, os argumentos extras não são mais registrados

Espero que isso possa / seja consertado

Nota: testei isso com a última versão lançada do Winston, uma rápida olhada no código parece sugerir que ainda é assim no master (que parece ter outras correções)

Algumas melhorias em relação à solução de @luislobo 👏

  • Ignore a mensagem se ela estiver no estilo splat, ou seja, contiver %s %d or %j para mensagens como logger.info(`hello %s`,'world')
  • reordene os formadores para tornar o colorido em primeiro lugar https://github.com/winstonjs/winston#colorizing -standard-logging-levels
  • Removido twise formatoteObject em printf
  • trimEnd se os argumentos de adição não forem fornecidos no 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()]
});

Alguma atualização adicional que eu possa estar perdendo? Parece que há um suporte muito forte para que esse recurso seja adicionado novamente, mas o último compromisso com isso foi há quase 6 meses.

Olá, Amortecedor enorme e bloqueador de show, tentando migrar para o 3.X, provavelmente vou manter o 2.x por um tempo.

Um pouco decepcionado :(

Isto é o que funcionou para mim:

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

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

Basicamente format.combine define um pipeline para um objeto info . Para cada função de formato, transform é chamado e a mensagem de log final deve ser gravada em info[Symbol.for('message')]
espero que isto ajude

Eu queria uma solução que suportasse %d , %o etc, mas ainda padrão para incluir quaisquer argumentos restantes, de modo que logger.info('hello %d %j', 42, {a:3}, 'some', 'more', 'arguments') seja renderizado assim:

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

Minha solução para isso acabou usando o símbolo splat mas invocando manualmente util.format() diretamente de printf , que por padrão inclui quaisquer argumentos restantes:

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

Se você não quiser printf você pode, é claro, adicionar uma transformação que simplesmente expande os argumentos em info.message e formatar o resultado final da maneira que desejar:

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

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

      return info;
    }
  },
  simple()
)  

O problema de usar format.splat() é que ele consome os argumentos correspondentes, mas parece jogar fora o resto. Invocando util.format() eu mesmo, posso substituir esse comportamento.

Mesmo problema, alguma atualização em relação a este? Eu amo Winston, mas fico louco quando não consigo imprimir todos os argumentos passados ​​para o logger, o que posso fazer com console.log() .

@ fr1sk Você tentou minha formatação acima? Dá console.log() como o comportamento (do nó console.log() implementação usa internamente util.format() afaik).

É decepcionante que esse problema ainda não tenha sido resolvido e que meu problema relacionado tenha sido bloqueado.
Estou trabalhando com muitas equipes diferentes e todas compartilham a frustração de perder o DX anterior ao migrar para a v3.

Passei um dia inteiro experimentando coisas.
As soluções acima foram próximas, mas estava faltando a colorização e a quebra de linha de argumentos extras.

IMO, o registro no Console deve ser delicioso.
Aqui está minha tentativa de fazer acontecer ...

configuração v2 para comparação

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

configuração 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

Há muitas coisas para lidar, algumas das quais

  • Um valor semelhante a um objeto como o primeiro argumento
  • Vários argumentos de valores mistos
  • Strings com códigos de escape ANSI, por exemplo, usando chalk
  • Valores esotéricos como Função e RegEx
  • Um erro como primeiro argumento ou em qualquer lugar depois disso
  • Ainda deve funcionar bem com outros formatadores

Bugs atuais com esta solução

  • Passar um erro como o primeiro argumento não imprime o Stack Trace

    • Isso ocorre porque info.message é apenas uma string e o trato da pilha está na propriedade stack

  • Passar um objeto com uma propriedade message como primeiro argumento imprime apenas message , descartando quaisquer outras propriedades

    • Mesmo motivo que o acima

  • Passar um erro como o segundo argumento de alguma forma concatena a mensagem de erro no topo de info.message (que geralmente é o valor do primeiro argumento), resultando em ver a mensagem de erro duas vezes

    • Rastreado em https://github.com/winstonjs/winston/issues/1660

    • Na verdade, isso faz parte do README:

      > NOTA: qualquer propriedade {message} em um meta objeto fornecido será automaticamente concatenada a qualquer msg já fornecida: Por exemplo, o seguinte concatenará 'mundo' em 'hello':

Brincar com o formatador errors não ajudou.

Melhorias positivas

  • O registro de valores primitivos agora fica embelezado
  • Registrar vários valores semelhantes a objetos agora adiciona uma nova linha entre eles
  • Registrar um Array agora é embelezado em vez de se parecer com um objeto com chaves de índices

Editar:

Podemos lidar com os erros de formatação, mas é hacky:

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

funciona para mim usando 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}`;
        })
    )
});

Eu entendo que as versões principais introduzem mudanças significativas, mas meu Deus ...

Nenhuma das soluções me deu o mesmo comportamento do winston v2.
As soluções de @henhal e @yamadashy têm o mesmo problema: o valor da mensagem é mostrado como a string.
logger.debug(err) criar mensagem de registro:

debug:  Error: ETIMEDOUT

enquanto logger.debug('anystringhere', err) cria:

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>

O segundo problema é que os argumentos adicionais são suprimidos ao usar com nível de informação - o winston v3 parece lidar com isso de outra maneira antes de formatar.

O terceiro problema é a falta de espaço entre 2 mensagens (observe "anystringhereError").

Minha configuração atual do logger 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;

Eu tive o suficiente disso e vou voltar para o winston v2 apenas com isto:

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;

Esta configuração v2 não tem os problemas mencionados acima.

Quero compartilhar minha configuração 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

Esta é uma solução alternativa comum para permitir vários parâmetros:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

aqui está o link atualizado para a solução proposta
https://github.com/rooseveltframework/roosevelt/blob/0.13.0/lib/tools/logger.js

A minha é uma variação das ótimas sugestões do @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}`;
    }),
  );
}

para registros como:

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

A saída é assim - o que é bom para todos os meus cenários:

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

Ei, estou começando com o Winston, nunca usei a v2, então estou na v3.2.1

Eu estava tentando simplesmente fazer:

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

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

E esperando que a interpolação de strings funcione; mas não.

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

Quando eu estava esperando

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

Esse problema é a mesma coisa de alguma forma? Ou sou forçado a usar a função logger.log('info', .....) ?

Editar

O evento tentando fazer isso não funciona.

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

saída:

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

Obrigado a quem postou as sugestões acima.
Droga, eu joguei fora um dia apenas para integrar o Winston 3 no meu projeto: /

Este me atingiu hoje - percebi que meu novo logger extravagante estava jogando fora todos os meus ...rest argumentos. Isso pode não funcionar para todos ou para todos os casos de uso, mas, no meu caso, achei aceitável apenas empacotar tudo o que queria registrado como um array. Não é o mais bonito, mas é mais leve do que algumas das soluções alternativas [muito inteligentes] que exigem mais código. Espero que isso ajude mais alguém!

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

É muito chato que eles tenham decidido fazer uma alteração significativa tão grande e AINDA não tenha mencionado isso no guia de migração ou na documentação.

Apesar de tudo, eu queria compartilhar minha solução que achei bastante compacta e segue como node js implementa console.log usando 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'),
};

Eu gostaria de entrar na conversa e solicitar que esse recurso seja adicionado ao Winston principal. Atualmente, estou usando o formatador personalizado com splat para obter essa funcionalidade, que honestamente parece muito hacky. Seria bom ter uma funcionalidade que corresponda a console.log

Alguma atualização?

Além dos comportamentos acima para a v3, quero adicionar isso também:

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

vai produzir isso

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

a versão que estou usando: (v3.2.1)
As configurações:

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

Ainda não entendo como gerar vários valores em Winston.

Eu só queria substituir console.log('Hello', var1, '!') por logger.log('Hello', var1, '!') .

Para ser honesto, tentar usar o Winston sempre leva a muito tempo perdido e a problemas surpreendentes com o registro.

É muito chato que eles tenham decidido fazer uma alteração significativa tão grande e AINDA não tenha mencionado isso no guia de migração ou na documentação.

Apesar de tudo, eu queria compartilhar minha solução que achei bastante compacta e segue como node js implementa console.log usando 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'),
};

Além disso, use util.formatWithOptions({ colors: true }, ...args); para obter saída de impressão colorida como com console.log normal

Isso funciona para mim. combineMessageAndSplat que combina mensagem e splat usando 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> 

Tive que obedecer aos métodos console. *, Orientando sobre todos os itens acima, e ...

  • métodos de console padrão (eles são coloridos por padrão) para chrome-dev-tools
  • saída do console do terminal de elétrons (também colorido por padrão)
  • gravando em um arquivo

Mas: A saída do arquivo deve mostrar detalhes do objeto.

Então acabei com:

// 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 Estava se perguntando se você ainda estava planejando resolver isso em algum momento?

Estou explorando o winston para usar em meu projeto, mas agora me pergunto se vale a pena correr o risco, pois essa edição está aberta há mais de 2 anos. para mim, isso parece uma função básica para qualquer estrutura de registro

Esta página foi útil?
0 / 5 - 0 avaliações