Winston: Pasar varios parámetros a las funciones de registro no se comporta como se esperaba

Creado en 6 ago. 2018  ·  43Comentarios  ·  Fuente: winstonjs/winston

Háblenos de su entorno:

  • _ winston versión? _

    • [] winston@2

    • [x] winston@3

  • _ node -v salidas: _ v8.11.3
  • _Sistema operativo? _ MacOS
  • _Idioma? _ Flow ES6 / 7

¿Cuál es el problema?


Con 3.xx:

const winston = require('winston')

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

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

Salidas:

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

Con 2.xx:

const winston = require('winston')

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

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

Salidas:

info: test log with a second parameter

¿Qué esperas que suceda en su lugar?


Debería generar:

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

Comentario más útil

Este es un ENORME cambio radical para nosotros. Si es intencional, debe detallarse en https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Todos 43 comentarios

Hoy me encontré con el mismo problema. Creo que es un error ya que la documentación de actualización también tiene un ejemplo como ese aquí :

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

Actualmente, para evitar esto, puede crear una función que analice los argumentos antes de pasar a winston

@ mulligan121 Está bien, pero no quiero reemplazar cada declaración de registro en mi base de código ...

@indexzero ¿

Solución temporal:

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

Este es un ENORME cambio radical para nosotros. Si es intencional, debe detallarse en https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md

Entonces, después de investigar un poco más, descubrí que necesita el formateador de símbolos para que se impriman varios argumentos. Pensé que era solo para la interpolación de argumentos (es decir, cosas con% s, etc.), pero parece que lo necesitas solo para hacer logger.info("something", value) .

Pero esto sigue siendo un poco extraño para mí: obtengo algo que se parece a JSON en la salida con una clave "meta", aunque no estoy usando un formato JSON:

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

Produce:

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

Incluso el ejemplo de los ejemplos no produce lo que dice que lo hará:

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

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

Lo resolví usando algo como esto:

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

Aquí hay una solución común para permitir múltiples parámetros:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

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

Hay demasiadas cosas que no funcionan igual.

Hola, ¿alguna actualización aquí? Gracias.

También esperando la forma adecuada de emular console.log(...args) en winston ...

Arreglamos varios casos de borde / esquina alrededor de splat y meta en 3.2.0 , pero parece que esto sigue siendo un problema (consulte: https://github.com / winstonjs / winston / pull / 1576 para el CHANGELOG.md ).

Se asegurará de que esto se maneje en 3.3.0 . Gracias por su paciencia amigos.

Esta fue una solución para mí:

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

Cuando intento iniciar sesión con argumentos, obtengo un resultado extraño
var s = "Hello" log.info("asdasda", s)

Obteniendo el resultado

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

Usé el código de inicio rápido normal

Una cosa que noté es esto:

  • obtuve un registrador con 2 transportes (consola y archivo giratorio)
  • Ambos usan el formato splat
  • La llamada a la función de transformación de splat para el primer transporte borra la matriz almacenada en la información bajo el símbolo SPLAT
  • En la llamada a la función de transformación de splat para el segundo transporte, la matriz SPLAT está vacía y, por lo tanto, los argumentos adicionales ya no se registran.

Espero que esto pueda / será arreglado

Nota: probé esto con la última versión lanzada de Winston, un vistazo rápido al código parece sugerir que todavía es así en el maestro (que parece tener otras correcciones)

Algunas mejoras sobre solución @luislobo 's 👏

  • Ignore el mensaje si tiene un estilo de splat, es decir, contiene %s %d or %j para mensajes como logger.info(`hello %s`,'world')
  • reordenó formateadores para hacer que el color sea lo primero https://github.com/winstonjs/winston#colorizing -standard-logging-levels
  • Se eliminó twise formateObject en printf
  • trimEnd si no se proporcionan argumentos de suma en el 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()]
});

¿Alguna actualización adicional de esto que pueda estar perdiendo? Parece que hay un apoyo bastante sólido para que se vuelva a agregar esta función, pero el último compromiso con esto fue hace casi 6 meses.

Hola, enorme Bumper y show stopper, tratando de migrar al 3.X, probablemente mantendrá el 2.x por un tiempo.

Un poco decepcionado :(

Esto es lo que funcionó para mí:

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 da como resultado: 2019-07-04T21:30:08.455Z [myLabel] info: foo "bar" 1 [2,3] true {"name":"John"}

Básicamente, format.combine establece una canalización para un objeto info . Para cada función de formato, se llama transform y el mensaje de registro final debe escribirse en info[Symbol.for('message')]
espero que esto ayude

Quería una solución que admita %d , %o , etc., pero aún así, por defecto, incluye cualquier argumento de descanso, de modo que logger.info('hello %d %j', 42, {a:3}, 'some', 'more', 'arguments') se muestre así:

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

Mi solución para esto terminó usando el símbolo splat pero invocando manualmente util.format() directamente desde printf , que por defecto incluye cualquier argumento de resto:

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 no desea printf , por supuesto, podría agregar una transformación que simplemente expanda los argumentos en info.message , y luego formatee el resultado final de la manera que desee:

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

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

      return info;
    }
  },
  simple()
)  

El problema con el uso de format.splat() es que consume los argumentos coincidentes pero parece desechar el resto. Al invocar util.format() yo mismo, puedo anular ese comportamiento.

El mismo problema, ¿alguna actualización con respecto a este? Amo a Winston, pero me vuelve loco cuando no puedo imprimir todos los argumentos pasados ​​al registrador, lo que puedo hacer con console.log() .

@ fr1sk ¿ console.log() comportamiento similar a console.log() usa internamente util.format() afaik).

Es decepcionante que este problema aún no se haya resuelto y que mi problema relacionado se haya bloqueado.
Estoy trabajando con muchos equipos diferentes y todos comparten la frustración de perder DX anterior al migrar a v3.

Me he pasado un día entero probando cosas.
Las soluciones anteriores estaban cerca, pero me faltaba la coloración y el salto de línea de los argumentos adicionales.

En mi opinión, iniciar sesión en la consola debería ser una delicia.
Aquí está mi intento de hacerlo realidad ...

configuración v2 para comparación

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

configuración 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

Hay muchas cosas que manejar, algunas de las cuales

  • Un valor similar a un objeto como primer argumento
  • Varios argumentos de valores mixtos
  • Cadenas con códigos de escape ANSI, por ejemplo, usando chalk
  • Valores esotéricos como función y expresión regular
  • Un error como primer argumento o en cualquier lugar después de eso
  • Debería funcionar bien junto con otros formateadores

Errores actuales con esta solución

  • Pasar un error como primer argumento no imprime el seguimiento de la pila

    • Esto se debe a que info.message es solo una cadena y el tracto de pila está en la propiedad stack

  • Pasar un objeto con una propiedad message como primer argumento solo imprime message , descartando cualquier otra propiedad

    • La misma razón que la anterior

  • Pasar un error como segundo argumento de alguna manera concatena el mensaje de error encima de info.message (que suele ser el valor del primer argumento), lo que da como resultado ver el mensaje de error dos veces

    • Seguimiento en https://github.com/winstonjs/winston/issues/1660

    • En realidad, esto es parte del archivo README:

      > NOTA: cualquier propiedad {mensaje} en un metaobjeto proporcionado se concatenará automáticamente a cualquier mensaje ya proporcionado: Por ejemplo, lo siguiente concatenará 'mundo' en 'hola':

Jugar con el formateador errors no ayudó.

Mejoras positivas

  • El registro de valores primitivos ahora se embellece
  • El registro de varios valores similares a objetos ahora agrega una nueva línea entre ellos
  • El registro de una matriz ahora está embellecido en lugar de verse como un objeto con claves de índices

Editar:

Podemos manejar los errores de formato, pero es un truco:

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

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

Entiendo que las versiones principales introducen cambios importantes, pero Dios mío ...

Ninguna de las soluciones me dio el mismo comportamiento que winston v2.
Las soluciones de @henhal y @yamadashy tienen el mismo problema: el valor del mensaje se muestra como la cadena.
logger.debug(err) crear mensaje de registro:

debug:  Error: ETIMEDOUT

mientras que logger.debug('anystringhere', err) crea:

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>

El segundo problema es que los argumentos adicionales se suprimen cuando se usa con el nivel de información; winston v3 parece manejar eso de la otra manera antes de formatear.

El tercer problema es el espacio que falta entre 2 mensajes (observe "anystringhereError").

Mi configuración actual del registrador 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;

Ya tuve suficiente de eso y volveré a winston v2 con solo esto:

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 configuración v2 no tiene los problemas mencionados anteriormente.

Quiero compartir mi configuración de 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

Aquí hay una solución común para permitir múltiples parámetros:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

aquí está el enlace actualizado a la solución propuesta
https://github.com/rooseveltframework/roosevelt/blob/0.13.0/lib/tools/logger.js

La mía es una variación de las grandes sugerencias de

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

Produce así, lo cual es bueno para todos mis escenarios:

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

Oye, estoy comenzando con Winston, nunca usé v2, así que estoy en v3.2.1

Estaba tratando de hacer simplemente:

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

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

Y esperando que la interpolación de cuerdas funcione; pero no.

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

Cuando estaba esperando

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

¿Este problema es lo mismo de alguna manera? ¿O me veo obligado a usar la función logger.log('info', .....) lugar?

Editar

El evento que intenta esto no funciona.

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

producción:

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

Gracias a quien publicó las sugerencias anteriores.
Maldita sea, he desperdiciado un día solo para integrar Winston 3 en mi proyecto: /

Este me golpeó hoy: me di cuenta de que mi nuevo y elegante registrador estaba lanzando todos mis argumentos ...rest . Es posible que esto no funcione para todos o para todos los casos de uso, pero en mi caso me pareció aceptable simplemente envolver todo lo que quería que se registrara como una matriz. No es el más bonito, pero es más ligero que algunas de las soluciones alternativas [muy inteligentes] que requieren más código. ¡Espero que esto ayude a alguien más!

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

Realmente molesto que decidieron hacer un cambio tan importante y TODAVÍA no lo han mencionado en la guía de migración o en la documentación.

Independientemente, quería compartir mi solución que encontré bastante compacta y sigue cómo el nodo 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'),
};

Me gustaría intervenir y solicitar que esta función se agregue al núcleo de Winston. Actualmente, estoy usando el formateador personalizado con splat para lograr esta funcionalidad, que honestamente se siente muy mal. Sería bueno tener una funcionalidad que coincida con console.log

¿Alguna actualización?

Además de los comportamientos anteriores para v3, también quiero agregar esto:

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

producirá esto

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

la versión que estoy usando: (v3.2.1)
Las configuraciones:

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

Todavía no entiendo cómo generar varios valores en Winston.

Solo quería reemplazar console.log('Hello', var1, '!') con logger.log('Hello', var1, '!') .

Para ser honesto, intentar usar Winston siempre conduce a una gran pérdida de tiempo y a problemas sorprendentes con el registro.

Realmente molesto que decidieron hacer un cambio tan importante y TODAVÍA no lo han mencionado en la guía de migración o en la documentación.

Independientemente, quería compartir mi solución que encontré bastante compacta y sigue cómo el nodo 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'),
};

Además, use util.formatWithOptions({ colors: true }, ...args); para obtener resultados impresos en color como con console.log normales

Esto funciona para mi. combineMessageAndSplat que combina mensaje y 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> 

Tuve que ajustarme a los métodos de la consola. *, Orientándome en todo lo anterior, y ...

  • métodos de consola predeterminados (colorean de forma predeterminada) para chrome-dev-tools
  • salida de la consola terminal de electrones (también coloreada por defecto)
  • escribir en un archivo

Pero: la salida del archivo debe mostrar detalles del objeto.

Así que terminé con:

// 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 ¿Se preguntaba si todavía planeaba abordar esto en algún momento?

Estoy explorando winston para usarlo en mi proyecto, ahora me pregunto si vale la pena correr el riesgo, ya que este problema está abierto durante más de 2 años. para mí, esto parece una función básica para cualquier marco de registro

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