Winston: рд▓реЙрдЧрд┐рдВрдЧ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдореЗрдВ рдПрдХрд╛рдзрд┐рдХ рдкреИрд░рд╛рдореАрдЯрд░ рдкрд╛рд╕ рдХрд░рдирд╛ рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 6 рдЕрдЧре░ 2018  ┬╖  43рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: winstonjs/winston

рдХреГрдкрдпрд╛ рд╣рдореЗрдВ рдЕрдкрдиреЗ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрддрд╛рдПрдВ:

  • _ winston рд╕рдВрд╕реНрдХрд░рдг?_

    • [ ] winston@2

    • [рдПрдХреНрд╕] winston@3

  • _ node -v рдЖрдЙрдЯрдкреБрдЯ:_ v8.11.3
  • _рдСрдкрд░реЗрдЯрд┐рдВрдЧ рд╕рд┐рд╕реНрдЯрдо?_ macOS
  • _рднрд╛рд╖рд╛?_ рдкреНрд░рд╡рд╛рд╣ ES6/7

рд╕рдорд╕реНрдпрд╛ рдХреНрдпрд╛ рд╣реИ?


3.xx рдХреЗ рд╕рд╛рде:

const winston = require('winston')

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

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

рдЖрдЙрдЯрдкреБрдЯ:

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

2.xx рдХреЗ рд╕рд╛рде:

const winston = require('winston')

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

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

рдЖрдЙрдЯрдкреБрдЯ:

info: test log with a second parameter

рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдЖрдк рдХреНрдпрд╛ рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддреЗ рд╣реИрдВ?


рдЗрд╕реЗ рдЖрдЙрдЯрдкреБрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП:

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

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рдпрд╣ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдПрдХ рдмрд╣реБрдд рдмрдбрд╝рд╛ рдмреНрд░реЗрдХрд┐рдВрдЧ рдмрджрд▓рд╛рд╡ рд╣реИред рдпрджрд┐ рдпрд╣ рдЬрд╛рдирдмреВрдЭрдХрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md рдореЗрдВ рд╡рд┐рд╕реНрддреГрдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╕рднреА 43 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдореИрдВ рдЖрдЬ рдЙрд╕реА рдореБрджреНрджреЗ рдореЗрдВ рднрд╛рдЧ рдЧрдпрд╛ред рдореЗрд░рд╛ рдорд╛рдирдирд╛ тАЛтАЛтАЛтАЛрд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдмрдЧ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЕрдкрдЧреНрд░реЗрдб рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рди рдореЗрдВ рднреА рдРрд╕рд╛ рд╣реА рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ :

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

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЗрд╕рдХреЗ рдЖрд╕рдкрд╛рд╕ рдЬрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдк рдПрдХ рдРрд╕рд╛ рдлрд╝рдВрдХреНрд╢рди рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рд╡рд┐рдВрд╕реНрдЯрди рдореЗрдВ рдкрд╛рд░рд┐рдд рд╣реЛрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рддрд░реНрдХреЛрдВ рдХреЛ рдкрд╛рд░ рдХрд░рддрд╛ рд╣реИ

@ mulligan121 рдареАрдХ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдЕрдкрдиреЗ рдХреЛрдбрдмреЗрд╕ рдореЗрдВ рд╣рд░ рд▓реЙрдЧ рд╕реНрдЯреЗрдЯрдореЗрдВрдЯ рдХреЛ рдмрджрд▓рдирд╛ рдирд╣реАрдВ рдЪрд╛рд╣рддрд╛ ...

@indexzero рдХреНрдпрд╛ рдпрд╣ рдЖрдкрдХреЗ рд░рдбрд╛рд░ рдХреЗ рдиреАрдЪреЗ рд╣реИ? рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдмрдбрд╝рд╛ рдмреНрд░реЗрдХрд┐рдВрдЧ рдмрджрд▓рд╛рд╡ рд╣реИред рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ 2.x рд╕реЗ 3.x рддрдХ рдЬрд╛рдиреЗ рд╕реЗ рд░реЛрдХрддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рд▓реЙрдЧ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреЛ рдмрджрд▓рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ

рдЕрд╕реНрдерд╛рдпреА рдЙрдкрд╛рдп:

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

рдпрд╣ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдПрдХ рдмрд╣реБрдд рдмрдбрд╝рд╛ рдмреНрд░реЗрдХрд┐рдВрдЧ рдмрджрд▓рд╛рд╡ рд╣реИред рдпрджрд┐ рдпрд╣ рдЬрд╛рдирдмреВрдЭрдХрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЗрд╕реЗ https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md рдореЗрдВ рд╡рд┐рд╕реНрддреГрдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рддреЛ рдХреБрдЫ рдФрд░ рдЬрд╛рдВрдЪ рдХреЗ рдмрд╛рдж, рдореИрдВрдиреЗ рдкрд╛рдпрд╛ рдХрд┐ рдХрдИ рддрд░реНрдХреЛрдВ рдХреЛ рдореБрджреНрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЛ рд╕реНрдкреНрд▓реИрдЯ рдлреЙрд░реНрдореЗрдЯрд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдореИрдВрдиреЗ рд╕реЛрдЪрд╛ рдерд╛ рдХрд┐ рдпрд╣ рд╕рд┐рд░реНрдл рддрд░реНрдХ рдкреНрд░рдХреНрд╖реЗрдк рдХреЗ рд▓рд┐рдП рдерд╛ (рдпрд╛рдиреА рдЗрд╕рдореЗрдВ %s рдЖрджрд┐ рдХреЗ рд╕рд╛рде рд╕рд╛рдорд╛рди), рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдЗрд╕реЗ рдХреЗрд╡рд▓ logger.info("something", value) ред

рд▓реЗрдХрд┐рди рдпрд╣ рдЕрднреА рднреА рдореЗрд░реЗ рд▓рд┐рдП рдереЛрдбрд╝рд╛ рдЕрдЬреАрдм рд╣реИ - рдореБрдЭреЗ рдХреБрдЫ рдРрд╕рд╛ рдорд┐рд▓ рд░рд╣рд╛ рд╣реИ рдЬреЛ JSON рдХреА рддрд░рд╣ рдПрдХ рдХреБрдВрдЬреА "рдореЗрдЯрд╛" рдХреЗ рд╕рд╛рде рдЖрдЙрдЯрдкреБрдЯ рдореЗрдВ рджрд┐рдЦрддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рдореИрдВ JSON рдкреНрд░рд╛рд░реВрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ:

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

рдЙрддреНрдкрд╛рджрди рдХрд░рддрд╛ рд╣реИ:

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

рдпрд╣рд╛рдВ рддрдХ тАЛтАЛтАЛтАЛрдХрд┐ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ рдЙрджрд╛рд╣рд░рдг рднреА рд╡рд╣ рдирд╣реАрдВ рджреЗрддрд╛ рдЬреЛ рд╡рд╣ рдХрд╣рддрд╛ рд╣реИ:

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

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

рдореИрдВрдиреЗ рдЗрд╕реЗ рдЗрди рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдХреБрдЫ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╣рд▓ рдХрд┐рдпрд╛ рд╣реИ:

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

рдПрдХрд╛рдзрд┐рдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣рд╛рдВ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╕рдорд╛рдзрд╛рди рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд:
https://github.com/winstonjs/winston/issues/1377

рдРрд╕реА рдмрд╣реБрдд рд╕реА рдЪреАрдЬреЗрдВ рд╣реИрдВ рдЬреЛ рд╕рдорд╛рди рд░реВрдк рд╕реЗ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддреА рд╣реИрдВред

рдирдорд╕реНрддреЗ, рдпрд╣рд╛рдБ рдХреЛрдИ рдЕрдкрдбреЗрдЯ? рдзрдиреНрдпрд╡рд╛рджред

рд╡рд┐рдВрд╕реНрдЯрди рдореЗрдВ console.log(...args) рдХрд╛ рдЕрдиреБрдХрд░рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдЪрд┐рдд рддрд░реАрдХреЗ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░ рд░рд╣рд╛ рд╣реИ ...

рд╣рдордиреЗ splat рдФрд░ meta рдореЗрдВ 3.2.0 рдЖрд╕рдкрд╛рд╕ рдХрдИ рдХрд┐рдирд╛рд░реЗ/рдХреЛрдиреЗ рдХреЗ рдорд╛рдорд▓реЗ рддрдп CHANGELOG.md )ред

рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдЧрд╛ рдХрд┐ рдЗрд╕реЗ 3.3.0 рдореЗрдВ рд╕рдВрднрд╛рд▓рд╛ рдЬрд╛рдПред рдЖрдкрдХреЗ рдзреИрд░реНрдп рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж рджреЛрд╕реНрддреЛрдВред

рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдПрдХ рдХрд╛рдордХрд╛рдЬ рдерд╛:

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

рдЬрдм рдореИрдВ рддрд░реНрдХреЛрдВ рдХреЗ рд╕рд╛рде рд▓реЙрдЧ рдЗрди рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддрд╛ рд╣реВрдВ рддреЛ рдореБрдЭреЗ рдПрдХ рдЕрдЬреАрдм рдЖрдЙрдЯрдкреБрдЯ рдорд┐рд▓рддрд╛ рд╣реИ
var s = "Hello" log.info("asdasda", s)

рдЖрдЙрдЯрдкреБрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛

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

рдореИрдВрдиреЗ рд╕рд╛рдорд╛рдиреНрдп рддреНрд╡рд░рд┐рдд рдкреНрд░рд╛рд░рдВрдн рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛

рдПрдХ рдмрд╛рдд рдЬреЛ рдореИрдВрдиреЗ рдиреЛрдЯрд┐рд╕ рдХреА рд╡рд╣ рдпрд╣ рд╣реИ:

  • 2 рдЯреНрд░рд╛рдВрд╕рдкреЛрд░реНрдЯ рдХреЗ рд╕рд╛рде рдПрдХ рд▓рдХрдбрд╝рд╣рд╛рд░рд╛ рдорд┐рд▓рд╛ (рдХрдВрд╕реЛрд▓ рдФрд░ рд░реЛрдЯреЗрдЯрд┐рдВрдЧ рдлрд╛рдЗрд▓)
  • рджреЛрдиреЛрдВ splat рдкреНрд░рд╛рд░реВрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ
  • рдкрд╣рд▓реЗ рдкрд░рд┐рд╡рд╣рди рдХреЗ рд▓рд┐рдП рд╕реНрдкреНрд▓реИрдЯ рдХреЗ рдЯреНрд░рд╛рдВрд╕рдлрд╝реЙрд░реНрдо рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ SPLAT рдкреНрд░рддреАрдХ рдХреЗ рддрд╣рдд рдЬрд╛рдирдХрд╛рд░реА рдкрд░ рд╕рдВрдЧреНрд░рд╣реАрдд рд╕рд░рдгреА рдХреЛ рд╕рд╛рдлрд╝ рдХрд░рддрд╛ рд╣реИ
  • рджреВрд╕рд░реЗ рдкрд░рд┐рд╡рд╣рди рдХреЗ рд▓рд┐рдП splat рдХреЗ рдкрд░рд┐рд╡рд░реНрддрди рд╕рдорд╛рд░реЛрд╣ рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдкрд░, SPLAT рд╕рд░рдгреА рдЦрд╛рд▓реА рд╣реИ рдФрд░ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдЕрддрд┐рд░рд┐рдХреНрдд рддрд░реНрдХ рдЕрдм рд▓реЙрдЧ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ

рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдпрд╣ рддрдп рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ/

рдиреЛрдЯ: рд╡рд┐рдВрд╕реНрдЯрди рдХреЗ рдирд╡реАрдирддрдо рд░рд┐рд▓реАрдЬрд╝ рдХрд┐рдП рдЧрдП рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рде рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЧрдпрд╛, рдХреЛрдб рдкрд░ рддреНрд╡рд░рд┐рдд рд░реВрдк рд╕реЗ рджреЗрдЦрдиреЗ рд╕реЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЕрднреА рднреА рдорд╛рд╕реНрдЯрд░ рдореЗрдВ рдРрд╕рд╛ рд╣реА рд╣реИ (рдЬрд┐рд╕рдореЗрдВ рдЕрдиреНрдп рд╕реБрдзрд╛рд░ рд╣реИрдВ рдЬреЛ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ)

@luislobo рдХреЗ рд╕рдорд╛рдзрд╛рди рдкрд░ рдХреБрдЫ рд╕реБрдзрд╛рд░ ЁЯСП

  • рд╕рдВрджреЗрд╢ рдкрд░ рдзреНрдпрд╛рди рди рджреЗрдВ рдпрджрд┐ рдпрд╣ рд╢реИрд▓реА рдХреЛ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рддрд╛ рд╣реИ рдЕрд░реНрдерд╛рдд рдЗрд╕рдореЗрдВ %s %d or %j рдЬреИрд╕реЗ рд╕рдВрджреЗрд╢реЛрдВ рдХреЗ рд▓рд┐рдП logger.info(`hello %s`,'world')
  • рд░рдВрдЧ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдкреБрди: рд╡реНрдпрд╡рд╕реНрдерд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдкрд╣рд▓реЗ рдЖрддреЗ рд╣реИрдВ https://github.com/winstonjs/winston#colorizing -standard-logging-levels
  • printf рдлрд╝реЙрд░реНрдореЗрдЯ рдСрдмреНрдЬреЗрдХреНрдЯ рд╣рдЯрд╛рдпрд╛ рдЧрдпрд╛
  • trimEnd рдЕрдЧрд░ рд▓реЙрдЧрд░ рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рддрд░реНрдХ рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ
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()]
});

рдЗрд╕рдХреЗ рд▓рд┐рдП рдХреЛрдИ рдФрд░ рдЕрдкрдбреЗрдЯ рдЬреЛ рдореБрдЭреЗ рдпрд╛рдж рдЖ рд░рд╣рд╛ рд╣реИ? рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рд╕реБрд╡рд┐рдзрд╛ рдХреЛ рдлрд┐рд░ рд╕реЗ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдордЬрдмреВрдд рд╕рдорд░реНрдерди рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рд▓рд┐рдП рдЕрдВрддрд┐рдо рдкреНрд░рддрд┐рдмрджреНрдзрддрд╛ рд▓рдЧрднрдЧ 6 рдорд╣реАрдиреЗ рдкрд╣рд▓реЗ рдереАред

рдирдорд╕реНрддреЗ, рд╡рд┐рд╢рд╛рд▓ рдмрдореНрдкрд░ рдФрд░ рд╢реЛ рд╕реНрдЯреЙрдкрд░, 3.X рдкрд░ рдорд╛рдЗрдЧреНрд░реЗрдЯ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рд╣реИ, рд╢рд╛рдпрдж 2.x рдХреЛ рдереЛрдбрд╝реА рджреЗрд░ рдХреЗ рд▓рд┐рдП рд░рдЦреЗрдЧрд╛ред

рдереЛрдбрд╝рд╛ рдирд┐рд░рд╛рд╢ :(

рдЗрд╕реА рд╕реЗ рдореЗрд░рд╛ рдХрд╛рдо рдмрдирд╛ рд╣реИ:

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

рдЬрд┐рд╕рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк: 2019-07-04T21:30:08.455Z [myLabel] info: foo "bar" 1 [2,3] true {"name":"John"}

рдореВрд▓ рд░реВрдк рд╕реЗ format.combine рдПрдХ info рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╛рд░реВрдк рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓рд┐рдП, transform рдХреЛ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдЕрдВрддрд┐рдо рд▓реЙрдЧ рд╕рдВрджреЗрд╢ info[Symbol.for('message')] рд▓рд┐рдЦрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП
рдЙрдореНрдореАрдж рд╣реИ рдХреА рдпрд╣ рдорджрдж рдХрд░реЗрдЧрд╛

рдореИрдВ рдПрдХ рд╕рдорд╛рдзрд╛рди рдЪрд╛рд╣рддрд╛ рдерд╛ рдЬреЛ %d , %o рдЖрджрд┐ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдлрд┐рд░ рднреА рдХрд┐рд╕реА рднреА рдмрд╛рдХреА рддрд░реНрдХреЛрдВ рдХреЛ рд╢рд╛рдорд┐рд▓ рдХрд░рдиреЗ рдореЗрдВ рдЪреВрдХ рдХрд░рддрд╛ рд╣реИ, рддрд╛рдХрд┐ logger.info('hello %d %j', 42, {a:3}, 'some', 'more', 'arguments') рдЗрд╕ рддрд░рд╣ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗ:

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

рдЗрд╕рдХреЗ рд▓рд┐рдП рдореЗрд░рд╛ рд╕рдорд╛рдзрд╛рди splat рдкреНрд░рддреАрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рдорд╛рдкреНрдд рд╣реБрдЖ рд▓реЗрдХрд┐рди рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ util.format() рд╕реАрдзреЗ printf рд╕реЗ рдЖрдордВрддреНрд░рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛, рдЬрд┐рд╕рдореЗрдВ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдХреЛрдИ рднреА рдмрд╛рдХреА рддрд░реНрдХ рд╢рд╛рдорд┐рд▓ рд╣реИ:

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

рдпрджрд┐ рдЖрдк printf рдирд╣реАрдВ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдкрд░рд┐рд╡рд░реНрддрди рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдХреЗрд╡рд▓ рддрд░реНрдХреЛрдВ рдХреЛ info.message рдореЗрдВ рд╡рд┐рд╕реНрддрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдлрд┐рд░ рдЕрдВрддрд┐рдо рдкрд░рд┐рдгрд╛рдо рдХреЛ рдЕрдкрдиреА рдкрд╕рдВрдж рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдкреНрд░рд╛рд░реВрдкрд┐рдд рдХрд░рддрд╛ рд╣реИ:

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

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

      return info;
    }
  },
  simple()
)  

format.splat() рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдореЗрдВ рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдпрд╣ рдореЗрд▓ рдЦрд╛рдиреЗ рд╡рд╛рд▓реЗ рддрд░реНрдХреЛрдВ рдХрд╛ рдЙрдкрднреЛрдЧ рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдмрд╛рдХреА рдХреЛ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рд╕реНрд╡рдпрдВ util.format() рдЖрд╣реНрд╡рд╛рди рдХрд░рдХреЗ рдореИрдВ рдЙрд╕ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рдУрд╡рд░рд░рд╛рдЗрдб рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВред

рд╡рд╣реА рдореБрджреНрджрд╛, рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдХреЛрдИ рдЕрдкрдбреЗрдЯ? рдореИрдВ рд╡рд┐рдВрд╕реНрдЯрди рд╕реЗ рдкреНрдпрд╛рд░ рдХрд░рддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рдореБрдЭреЗ рдкрд╛рдЧрд▓ рдХрд░ рджреЗрддрд╛ рд╣реИ рдЬрдм рдореИрдВ рд▓рдХрдбрд╝рд╣рд╛рд░реЗ рдХреЛ рджрд┐рдП рдЧрдП рд╕рднреА рддрд░реНрдХреЛрдВ рдХреЛ рдкреНрд░рд┐рдВрдЯ рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛, рдЬреЛ рдореИрдВ console.log() рд╕рд╛рде рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВред

@ fr1sk рдХреНрдпрд╛ рдЖрдкрдиреЗ рдКрдкрд░ рдореЗрд░реЗ рд╕реНрд╡рд░реВрдкрдг рдХреА рдХреЛрд╢рд┐рд╢ рдХреА? рдпрд╣ console.log() рдЬреИрд╕рд╛ рд╡реНрдпрд╡рд╣рд╛рд░ рджреЗрддрд╛ рд╣реИ (рдиреЛрдб рдХрд╛ console.log() рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЖрдВрддрд░рд┐рдХ рд░реВрдк рд╕реЗ util.format() afaik рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ)ред

рдпрд╣ рдирд┐рд░рд╛рд╢рд╛рдЬрдирдХ рд╣реИ рдХрд┐ рдпрд╣ рд╕рдорд╕реНрдпрд╛ рдЕрднреА рднреА рд╣рд▓ рдирд╣реАрдВ рд╣реБрдИ рд╣реИ, рдФрд░ рдореЗрд░реА рд╕рдВрдмрдВрдзрд┐рдд рд╕рдорд╕реНрдпрд╛ рдХреЛ рд▓реЙрдХ рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред
рдореИрдВ рдХрдИ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдЯреАрдореЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдФрд░ рд╡реЗ рд╕рднреА v3 рдореЗрдВ рдорд╛рдЗрдЧреНрд░реЗрдЯ рдХрд░рддреЗ рд╕рдордп рдкрд┐рдЫрд▓реЗ рдбреАрдПрдХреНрд╕ рдХреЛ рдЦреЛрдиреЗ рдХреА рдирд┐рд░рд╛рд╢рд╛ рд╕рд╛рдЭрд╛ рдХрд░рддреЗ рд╣реИрдВред

рдореИрдВрдиреЗ рдкреВрд░рд╛ рджрд┐рди рдЪреАрдЬреЛрдВ рдХреЛ рдЖрдЬрдорд╛рдиреЗ рдореЗрдВ рдмрд┐рддрд╛рдпрд╛ рд╣реИред
рдЙрдкрд░реЛрдХреНрдд рд╕рдорд╛рдзрд╛рди рдХрд░реАрдм рдереЗ рд▓реЗрдХрд┐рди рдореБрдЭреЗ рдЕрддрд┐рд░рд┐рдХреНрдд рддрд░реНрдХреЛрдВ рдХрд╛ рд░рдВрдЧреАрдХрд░рдг рдФрд░ рд▓рд╛рдЗрди рдмреНрд░реЗрдХрд┐рдВрдЧ рдпрд╛рдж рдЖ рд░рд╣реА рдереАред

рдЖрдИрдПрдордУ, рдХрдВрд╕реЛрд▓ рдореЗрдВ рд▓реЙрдЧрд┐рдВрдЧ рдЖрдирдВрджрджрд╛рдпрдХ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред
рдпрд╣рд╛рдБ рдЗрд╕реЗ рд╕рд╛рдХрд╛рд░ рдХрд░рдиреЗ рдХрд╛ рдореЗрд░рд╛ рдкреНрд░рдпрд╛рд╕ рд╣реИ ...

рддреБрд▓рдирд╛ рдХреЗ рд▓рд┐рдП v2 рд╕реЗрдЯрдЕрдк

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

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

рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рд╕реА рдЪреАрдЬрд╝реЗрдВ рд╣реИрдВ, рдЬрд┐рдирдореЗрдВ рд╕реЗ рдХреБрдЫ

  • рдкрд╣рд▓реЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рд╡рд╕реНрддреБ рдЬреИрд╕рд╛ рдорд╛рди
  • рдорд┐рд╢реНрд░рд┐рдд рдореВрд▓реНрдпреЛрдВ рдХреЗ рдХрдИ рддрд░реНрдХ
  • рдПрдПрдирдПрд╕рдЖрдИ рдПрд╕реНрдХреЗрдк рдХреЛрдб рдХреЗ рд╕рд╛рде рд╕реНрдЯреНрд░рд┐рдВрдЧреНрд╕, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП chalk
  • рдЧреВрдврд╝ рдорд╛рди рдЬреИрд╕реЗ Function рдФрд░ RegEx
  • рдкрд╣рд▓реЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдпрд╛ рдЙрд╕рдХреЗ рдмрд╛рдж рдХрд╣реАрдВ рднреА рддреНрд░реБрдЯрд┐
  • рдЕрдиреНрдп рд╕реНрд╡рд░реВрдкрдХреЛрдВ рдХреЗ рд╕рд╛рде рдЕрднреА рднреА рдареАрдХ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП

рдЗрд╕ рд╕рдорд╛рдзрд╛рди рдХреЗ рд╕рд╛рде рд╡рд░реНрддрдорд╛рди рдмрдЧ

  • 1 рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рддреНрд░реБрдЯрд┐ рдкрд╛рд╕ рдХрд░рдирд╛ рд╕реНрдЯреИрдХ рдЯреНрд░реЗрд╕ рдХреЛ рдкреНрд░рд┐рдВрдЯ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ

    • рдРрд╕рд╛ рдЗрд╕рд▓рд┐рдП рд╣реИ рдХреНрдпреЛрдВрдХрд┐ info.message рд╕рд┐рд░реНрдл рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реИ рдФрд░ рд╕реНрдЯреИрдХ рдЯреНрд░реИрдХреНрдЯ stack рд╕рдВрдкрддреНрддрд┐ рдореЗрдВ рд╣реИ

  • рдХрд┐рд╕реА рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ message рд╕рдВрдкрддреНрддрд┐ рдХреЗ рд╕рд╛рде рдкрд╛рд╕ рдХрд░рдирд╛ рдкрд╣рд▓реЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдХреЗрд╡рд▓ message рдкреНрд░рд┐рдВрдЯ рдХрд░рддрд╛ рд╣реИ, рдХрд┐рд╕реА рднреА рдЕрдиреНрдп рдЧреБрдгреЛрдВ рдХреЛ рдЫреЛрдбрд╝рдХрд░

    • рдКрдкрд░ рд╡рд╛рд▓реЗ рдХреЗ рд╕рдорд╛рди рдХрд╛рд░рдг

  • рддреНрд░реБрдЯрд┐ рдХреЛ рджреВрд╕рд░реЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд╕ рдХрд░рдирд╛ рдХрд┐рд╕реА рднреА рддрд░рд╣ info.message (рдЬреЛ рдЖрдорддреМрд░ рдкрд░ рдкрд╣рд▓реЗ рддрд░реНрдХ рдХрд╛ рдорд╛рди рд╣реЛрддрд╛ рд╣реИ) рдХреЗ рд╢реАрд░реНрд╖ рдкрд░ рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рдХреЛ рдЬреЛрдбрд╝рддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рджреЛ рдмрд╛рд░ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИ

    • https://github.com/winstonjs/winston/issues/1660 . рдореЗрдВ рдЯреНрд░реИрдХ рдХрд┐рдпрд╛ рдЧрдпрд╛

    • рдпрд╣ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ README рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИ:

      > рдиреЛрдЯ: рдкреНрд░рджрд╛рди рдХреА рдЧрдИ рдореЗрдЯрд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдХреЛрдИ рднреА {рд╕рдВрджреЗрд╢} рд╕рдВрдкрддреНрддрд┐ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдкрд╣рд▓реЗ рд╕реЗ рдкреНрд░рджрд╛рди рдХрд┐рдП рдЧрдП рдХрд┐рд╕реА рднреА рд╕рдВрджреЗрд╢ рд╕реЗ рдЬреБрдбрд╝ рдЬрд╛рдПрдЧреА: рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ 'рджреБрдирд┐рдпрд╛' рдХреЛ 'рд╣реИрд▓реЛ' рдкрд░ рдЬреЛрдбрд╝ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛:

errors рдлреЙрд░реНрдореЗрдЯрд░ рдХреЗ рд╕рд╛рде рдЦреЗрд▓рдиреЗ рд╕реЗ рдХреЛрдИ рдлрд╛рдпрджрд╛ рдирд╣реАрдВ рд╣реБрдЖред

рд╕рдХрд╛рд░рд╛рддреНрдордХ рд╡реГрджреНрдзрд┐

  • рдЖрджрд┐рдо рдореВрд▓реНрдпреЛрдВ рдХреЛ рд▓реЙрдЧ рдХрд░рдирд╛ рдЕрдм рд╕реБрдВрджрд░ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ
  • рдХрдИ рдСрдмреНрдЬреЗрдХреНрдЯ-рдЬреИрд╕реЗ рдорд╛рдиреЛрдВ рдХреЛ рд▓реЙрдЧ рдХрд░рдирд╛ рдЕрдм рдЙрдирдХреЗ рдмреАрдЪ рдПрдХ рдирдИ рдкрдВрдХреНрддрд┐ рдЬреЛрдбрд╝рддрд╛ рд╣реИ
  • рд╕реВрдЪрдХрд╛рдВрдХ рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд╕реНрддреБ рдХреА рддрд░рд╣ рджрд┐рдЦрдиреЗ рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдРрд░реЗ рдХреЛ рд▓реЙрдЧ рдХрд░рдирд╛ рдЕрдм рд╕реБрдВрджрд░ рд╣реИ

рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ:

рд╣рдо рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рд╕реНрд╡рд░реВрдкрдг рдХреЛ рд╕рдВрднрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВ рд▓реЗрдХрд┐рди рдпрд╣ рд╣реИрдХреА рд╣реИ:

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

рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП 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}`;
        })
    )
});

рдореИрдВ рд╕рдордЭрддрд╛ рд╣реВрдВ рдХрд┐ рдкреНрд░рдореБрдЦ рд╕рдВрд╕реНрдХрд░рдг рдмреНрд░реЗрдХрд┐рдВрдЧ рдкрд░рд┐рд╡рд░реНрддрди рдкреЗрд╢ рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдореЗрд░реА рдЕрдЪреНрдЫрд╛рдИ ...

рдХрд┐рд╕реА рднреА рд╕рдорд╛рдзрд╛рди рдиреЗ рдореБрдЭреЗ рд╡рд┐рдВрд╕реНрдЯрди v2 рдХреЗ рд╕рдорд╛рди рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рджрд┐рдпрд╛ред
@henhal рдФрд░ @yamadashy рджреЛрдиреЛрдВ рдХреЗ рд╕рдорд╛рдзрд╛рди рдореЗрдВ рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рд╣реИ: рд╕рдВрджреЗрд╢ рдорд╛рди рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рд░реВрдк рдореЗрдВ рджрд┐рдЦрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред
logger.debug(err) рд▓реЙрдЧ рд╕рдВрджреЗрд╢ рдмрдирд╛рдПрдВ:

debug:  Error: ETIMEDOUT

рдЬрдмрдХрд┐ logger.debug('anystringhere', err) рдмрдирд╛рддрд╛ рд╣реИ:

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>

рджреВрд╕рд░рд╛ рдореБрджреНрджрд╛ рдпрд╣ рд╣реИ рдХрд┐ рд╕реВрдЪрдирд╛ рд╕реНрддрд░ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдЕрддрд┐рд░рд┐рдХреНрдд рддрд░реНрдХреЛрдВ рдХреЛ рджрдмрд╛ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рд╡рд┐рдВрд╕реНрдЯрди v3 рд╕реНрд╡рд░реВрдкрдг рд╕реЗ рдкрд╣рд▓реЗ рдЗрд╕реЗ рджреВрд╕рд░реА рддрд░рд╣ рд╕реЗ рд╕рдВрднрд╛рд▓рддрд╛ рд╣реИред

рддреАрд╕рд░рд╛ рдореБрджреНрджрд╛ 2 рд╕рдВрджреЗрд╢реЛрдВ рдХреЗ рдмреАрдЪ рдЕрдиреБрдкрд▓рдмреНрдз рд╕реНрдерд╛рди рд╣реИ (рдиреЛрдЯрд┐рд╕ "anystringhereError")ред

рдореЗрд░рд╛ рд╡рд░реНрддрдорд╛рди 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;

рдореЗрд░реЗ рдкрд╛рд╕ рдЗрд╕рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдерд╛ рдФрд░ рд╡рд┐рдВрд╕реНрдЯрди v2 рдкрд░ рд╡рд╛рдкрд╕ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдерд╛:

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;

рдЗрд╕ v2 рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ рдЙрдкрд░реНрдпреБрдХреНрдд рд╕рдорд╕реНрдпрд╛рдПрдБ рдирд╣реАрдВ рд╣реИрдВред

рдореИрдВ рдЕрдкрдирд╛ рд╡рд┐рдВрд╕реНрдЯрди рд╕реЗрдЯрдЕрдк рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:

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

рдПрдХрд╛рдзрд┐рдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣рд╛рдВ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╕рдорд╛рдзрд╛рди рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:
https://github.com/rooseveltframework/roosevelt/blob/master/lib/tools/logger.js#L29

рдпрд╣рд╛рдБ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рд╕рдорд╛рдзрд╛рди рдХреЗ рд▓рд┐рдП рдЕрджреНрдпрддрди рд▓рд┐рдВрдХ рд╣реИ
https://github.com/rooseveltframework/roosevelt/blob/0.13.0/lib/tools/logger.js

рдореЗрд░рд╛ @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}`;
    }),
  );
}

рдЬреИрд╕реЗ рд▓реЙрдЧ рдХреЗ рд▓рд┐рдП:

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

рдпрд╣ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдЖрдЙрдЯрдкреБрдЯ рдХрд░рддрд╛ рд╣реИ - рдЬреЛ рдореЗрд░реЗ рд╕рднреА рдкрд░рд┐рджреГрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЕрдЪреНрдЫрд╛ рд╣реИ:

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

рдЕрд░реЗ, рдореИрдВ рд╡рд┐рдВрд╕реНрдЯрди рд╕реЗ рд╢реБрд░реВ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рдХрднреА рднреА v2 рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛, рдЗрд╕рд▓рд┐рдП рдореИрдВ v3.2.1 рдкрд░ рд╣реВрдВ

рдореИрдВ рдмрд╕ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣рд╛ рдерд╛:

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

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

рдФрд░ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЗрдВрдЯрд░рдкреЛрд▓реЗрд╢рди рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ; рд▓реЗрдХрд┐рди рдирд╣реАрдВред

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

рдЬрдм рдореИрдВ рдЙрдореНрдореАрдж рдХрд░ рд░рд╣рд╛ рдерд╛

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

рдпрд╣ рдореБрджреНрджрд╛ рдХрд┐рд╕реА рддрд░рд╣ рдПрдХ рд╣реА рдмрд╛рдд рд╣реИ? рдпрд╛ рдореБрдЭреЗ рдЗрд╕рдХреЗ рдмрдЬрд╛рдп logger.log('info', .....) рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ?

рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ

рдпрд╣ рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рд╡рд╛рд▓реА рдШрдЯрдирд╛ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддреА рд╣реИред

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

рдЖрдЙрдЯрдкреБрдЯ:

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

рдКрдкрд░ рд╕реБрдЭрд╛рд╡реЛрдВ рдХреЛ рдкреЛрд╕реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред
рдзрд┐рдХреНрдХрд╛рд░ рд╣реИ рдореИрдВрдиреЗ рдЕрдкрдиреЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдкрд░ рд╡рд┐рдВрд╕реНрдЯрди 3 рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд┐рд░реНрдл рдПрдХ рджрд┐рди рдлреЗрдВрдХ рджрд┐рдпрд╛ рд╣реИ: /

рдЗрд╕рдиреЗ рдореБрдЭреЗ рдЖрдЬ рдорд╛рд░рд╛ - рдореБрдЭреЗ рдПрд╣рд╕рд╛рд╕ рд╣реБрдЖ рдХрд┐ рдореЗрд░рд╛ рдлреИрдВрд╕реА рдирдпрд╛ рд▓рдХрдбрд╝рд╣рд╛рд░рд╛ рдореЗрд░реЗ рд╕рднреА ...rest рддрд░реНрдХреЛрдВ рдХреЛ рдЙрдЫрд╛рд▓ рд░рд╣рд╛ рдерд╛ред рдпрд╣ рд╣рд░ рдХрд┐рд╕реА рдпрд╛ рд╣рд░ рдЙрдкрдпреЛрдЧ рдХреЗ рдорд╛рдорд▓реЗ рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдореИрдВрдиреЗ рдПрдХ рд╕рд░рдгреА рдХреЗ рд░реВрдк рдореЗрдВ рд▓реЙрдЧ рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬреЛ рдХреБрдЫ рднреА рдЪрд╛рд╣рддрд╛ рдерд╛ рдЙрд╕реЗ рд▓рдкреЗрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрд╡реАрдХрд╛рд░реНрдп рдкрд╛рдпрд╛ред рдпрд╣ рд╕рдмрд╕реЗ рд╕реБрдВрджрд░ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдХреБрдЫ [рдмрд╣реБрдд рдЪрд╛рд▓рд╛рдХ] рдХрд╛рдордХрд╛рдЬ рд╕реЗ рд╣рд▓реНрдХрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдХреЛрдб рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдордирд╛рдЗрдП рдХрд┐ рдпрд╣ рдХрд┐рд╕реА рдФрд░ рдХреЗ рд▓рд┐рдП рд╕рд╣рд╛рдпрдХ рд╣реЛ!

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

рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдкрд░реЗрд╢рд╛рди рд╣реИ рдХрд┐ рдЙрдиреНрд╣реЛрдВрдиреЗ рдЗрддрдирд╛ рдмрдбрд╝рд╛ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдФрд░ рдЕрднреА рднреА рдорд╛рдЗрдЧреНрд░реЗрд╢рди рдЧрд╛рдЗрдб рдпрд╛ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрд▓реНрд▓реЗрдЦ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред

рднрд▓реЗ рд╣реА рдореИрдВ рдЕрдкрдирд╛ рд╕рдорд╛рдзрд╛рди рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ рдЬреЛ рдореБрдЭреЗ рдХрд╛рдлреА рдХреЙрдореНрдкреИрдХреНрдЯ рд▓рдЧрд╛ рдФрд░ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ рдХрд┐ рдиреЛрдб рдЬреЗрдПрд╕ рдХрдВрд╕реЛрд▓ рдХреЛ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред 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'),
};

рдореИрдВ рдЗрд╕рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдФрд░ рдЗрд╕ рд╕реБрд╡рд┐рдзрд╛ рдХреЛ рдХреЛрд░ рд╡рд┐рдВрд╕реНрдЯрди рдореЗрдВ рдЬреЛрдбрд╝рдиреЗ рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реВрдВред рд╡рд░реНрддрдорд╛рди рдореЗрдВ, рдореИрдВ рдЗрд╕ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП splat рд╕рд╛рде рдХрд╕реНрдЯрдо рдлреЙрд░реНрдореЗрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рдЬреЛ рдИрдорд╛рдирджрд╛рд░реА рд╕реЗ рдмрд╣реБрдд рд╣реИрдХреА рдорд╣рд╕реВрд╕ рдХрд░рддрд╛ рд╣реИред console.log рдореЗрд▓ рдЦрд╛рдиреЗ рд╡рд╛рд▓реА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╣реЛрдирд╛ рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛

рдХреЛрдИ рд╕реБрдзрд╛рд░?

v3 рдХреЗ рд▓рд┐рдП рдЙрдкрд░реЛрдХреНрдд рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореИрдВ рдЗрд╕реЗ рднреА рдЬреЛрдбрд╝рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:

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

рдЗрд╕рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░реЗрдВрдЧреЗ

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

рдореИрдВ рдЬрд┐рд╕ рд╕рдВрд╕реНрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ: (v3.2.1)
рд╡рд┐рдиреНрдпрд╛рд╕:

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

рдореИрдВ рдЕрднреА рднреА рд╕рдордЭ рдирд╣реАрдВ рдкрд╛ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рд╡рд┐рдВрд╕реНрдЯрди рдореЗрдВ рдПрдХрд╛рдзрд┐рдХ рдорд╛рдиреЛрдВ рдХреЛ рдХреИрд╕реЗ рдЖрдЙрдЯрдкреБрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПред

рдореИрдВ рдмрд╕ console.log('Hello', var1, '!') рдХреЛ logger.log('Hello', var1, '!') рд╕реЗ рдмрджрд▓рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред

рдИрдорд╛рдирджрд╛рд░ рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП, рд╡рд┐рдВрд╕реНрдЯрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рд╕реЗ рд╣рдореЗрд╢рд╛ рдмрд╣реБрдд рд╕рдордп рдмрд░реНрдмрд╛рдж рд╣реЛрддрд╛ рд╣реИ рдФрд░ рд▓реЙрдЧрд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдЖрд╢реНрдЪрд░реНрдпрдЬрдирдХ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реЛрддреА рд╣реИрдВред

рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдкрд░реЗрд╢рд╛рди рд╣реИ рдХрд┐ рдЙрдиреНрд╣реЛрдВрдиреЗ рдЗрддрдирд╛ рдмрдбрд╝рд╛ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдФрд░ рдЕрднреА рднреА рдорд╛рдЗрдЧреНрд░реЗрд╢рди рдЧрд╛рдЗрдб рдпрд╛ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрд▓реНрд▓реЗрдЦ рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИред

рднрд▓реЗ рд╣реА рдореИрдВ рдЕрдкрдирд╛ рд╕рдорд╛рдзрд╛рди рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ рдЬреЛ рдореБрдЭреЗ рдХрд╛рдлреА рдХреЙрдореНрдкреИрдХреНрдЯ рд▓рдЧрд╛ рдФрд░ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ рдХрд┐ рдиреЛрдб рдЬреЗрдПрд╕ рдХрдВрд╕реЛрд▓ рдХреЛ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред 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'),
};

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдХрд╛ рдЙрдкрдпреЛрдЧ util.formatWithOptions({ colors: true }, ...args); рдирд┐рдпрдорд┐рдд рдХреЗ рд╕рд╛рде рдХреА рддрд░рд╣ рдкреНрд░рд┐рдВрдЯ рдЖрдЙрдЯрдкреБрдЯ рд░рдВрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП console.log

рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред combineMessageAndSplat рдЬреЛ 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> 

рдореБрдЭреЗ рдХрдВрд╕реЛрд▓ рдХреЗ рдЕрдиреБрд░реВрдк рд╣реЛрдирд╛ рдерд╛ред * рд╡рд┐рдзрд┐рдпрд╛рдВ, рдЙрдкрд░реЛрдХреНрдд рд╕рднреА рдкрд░ рдЙрдиреНрдореБрдЦ, рдФрд░ ...

  • рдХреНрд░реЛрдо-рджреЗрд╡-рдЯреВрд▓реНрд╕ рдХреЗ рд▓рд┐рдП рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХрдВрд╕реЛрд▓ рд╡рд┐рдзрд┐рдпрд╛рдВ (рд╡реЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд░рдВрдЧреАрди рд╣реЛрддреА рд╣реИрдВ)
  • рдЗрд▓реЗрдХреНрдЯреНрд░реЙрди рдЯрд░реНрдорд┐рдирд▓ рдХрдВрд╕реЛрд▓ рдЖрдЙрдЯрдкреБрдЯ (рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд░рдВрдЧреАрди рднреА)
  • рдПрдХ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд▓рд┐рдЦрдирд╛

рд▓реЗрдХрд┐рди: рдлрд╝рд╛рдЗрд▓ рдЖрдЙрдЯрдкреБрдЯ рдХреЛ рдСрдмреНрдЬреЗрдХреНрдЯ рд╡рд┐рд╡рд░рдг рджрд┐рдЦрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред

рддреЛ рдореИрдВ рдЗрд╕рдХреЗ рд╕рд╛рде рд╕рдорд╛рдкреНрдд рд╣реБрдЖ:

// 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 рд╕реЛрдЪ рд░рд╣рд╛ рдерд╛ рдХрд┐ рдХреНрдпрд╛ рдЖрдк рдЕрднреА рднреА рдЗрд╕реЗ рдХрд┐рд╕реА рдмрд┐рдВрджреБ рдкрд░ рд╕рдВрдмреЛрдзрд┐рдд рдХрд░рдиреЗ рдХреА рдпреЛрдЬрдирд╛ рдмрдирд╛ рд░рд╣реЗ рд╣реИрдВ?

рдореИрдВ рдЕрдкрдиреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рдВрд╕реНрдЯрди рдХреА рдЦреЛрдЬ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рдЕрдм рд╕реЛрдЪ рд░рд╣рд╛ рд╣реВрдВ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдЬреЛрдЦрд┐рдо рд▓реЗрдиреЗ рд▓рд╛рдпрдХ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдореБрджреНрджрд╛ 2 рд╕рд╛рд▓ рд╕реЗ рдЕрдзрд┐рдХ рд╕рдордп рд╕реЗ рдЦреБрд▓рд╛ рд╣реИред рдореЗрд░реЗ рд▓рд┐рдП рдпрд╣ рдХрд┐рд╕реА рднреА рд▓реЙрдЧрд┐рдВрдЧ рдврд╛рдВрдЪреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреБрдирд┐рдпрд╛рджреА рдХрд╛рд░реНрдп рдХреА рддрд░рд╣ рд▓рдЧрддрд╛ рд╣реИ

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

mohanen picture mohanen  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

Nepoxx picture Nepoxx  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

anks333 picture anks333  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

sinai-doron picture sinai-doron  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

Tonacatecuhtli picture Tonacatecuhtli  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ