Winston: 将多个参数传递给日志记录函数的行为不符合预期

创建于 2018-08-06  ·  43评论  ·  资料来源: winstonjs/winston

请告诉我们您的环境:

  • _ winston版本?_

    • [ ] winston@2

    • [x] 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"}
important

最有用的评论

这对我们来说是一个巨大的突破性变化。 如果是故意的,应该在https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md中有详细说明

所有43条评论

我今天遇到了同样的问题。 我相信这是一个错误,因为升级文档在这里也有一个这样的例子:

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

目前要解决这个问题,您可以创建一个函数,在将参数传递给 winston 之前对其进行解析

@mulligan121好的,但我不想替换代码库中的每个日志语句...

@indexzero这是在你的

临时解决方法:

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中有详细说明

因此,经过更多调查,我发现您需要 splat 格式化程序才能打印多个参数。 我认为这只是用于参数插值(即其中包含 %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/winstonjs/winston/issues/1377

有太多事情不一样了。

你好,这里有更新吗? 谢谢。

也在等待在温斯顿模拟console.log(...args)正确方法......

我们定了一些周围的边缘/角落案件splatmeta3.2.0 ,但现在看来这仍是一个问题(见:https://github.com /winstonjs/winston/pull/1576 为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 符号下的 info 上的数组
  • 在为第二次传输调用 splat 的转换函数时,SPLAT 数组为空,因此不再记录额外的参数

希望这可以/将被修复

注意:用最新发布的 Winston 版本对此进行了测试,快速查看代码似乎表明它在 master 中仍然是这样(似乎还有其他修复)

@luislobo的解决方案的一些改进👏

  • 如果消息样式%s %d or %j则忽略消息,例如logger.info(`hello %s`,'world')
  • 重新排序的格式化程序使 colorise 首先出现https://github.com/winstonjs/winston#colorizing -standard-logging-levels
  • 删除了printf twise formateObject
  • trimEnd如果在 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()]
});

对此我可能会遗漏的任何进一步更新吗? 似乎对重新添加此功能有很强的支持,但最近一次对此做出的承诺是在近 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.combineinfo对象设置管道。 对于每个格式函数,调用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符号,但直接从printf手动调用util.format() ,默认情况下包括任何其余参数:

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您当然可以添加一个简单地将 args 扩展为info.message ,然后按照您喜欢的方式格式化最终结果:

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

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

      return info;
    }
  },
  simple()
)  

使用format.splat()的问题在于它消耗了匹配的参数,但似乎丢弃了其余的参数。 通过自己调用util.format()我可以覆盖该行为。

同样的问题,关于这个的任何更新? 我喜欢 Winston,但是当我无法打印传递给记录器的所有参数时,它让我发疯,而我可以用console.log()

@fr1sk你试过我上面的格式吗? 它提供console.log()类似console.log()实现在内部使用util.format() afaik)。

令人失望的是,这个问题仍未解决,我的相关问题已被锁定。
我正在与许多不同的团队合作,他们都有在迁移到 v3 时失去以前的 DX 的挫败感。

我花了一整天的时间来尝试。
上述解决方案很接近,但我错过了额外参数的着色和换行。

IMO,登录到控制台应该是令人愉快的。
这是我试图让它发生的尝试......

用于比较的 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

有很多事情要处理,其中一些

  • 一个类对象值作为第一个参数
  • 混合值的几个参数
  • 带有 ANSI 转义码的字符串,例如使用chalk
  • 深奥的值,如 Function 和 RegEx
  • 错误作为第一个参数或之后的任何地方
  • 应该仍然可以与其他格式化程序一起正常工作

此解决方案的当前错误

  • 将错误作为第一个参数传递不会打印堆栈跟踪

    • 这是因为info.message只是一个字符串,堆栈区域位于stack属性中

  • 传递一个带有message属性的对象作为第一个参数只打印message ,丢弃任何其他属性

    • 原因和上面一样

  • 将错误作为第二个参数传递以某种方式将错误消息连接到info.message (通常是第一个参数的值)之上,导致错误消息出现两次

玩弄errors格式化程序并没有帮助。

积极的改进

  • 记录原始值现在得到了美化
  • 记录几个类似对象的值现在在它们之间添加一个换行符
  • 记录一个数组现在被美化而不是看起来像一个带有索引键的对象

编辑:

我们可以处理错误格式,但它很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}`;
    })
...

它适用于我使用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}`;
        })
    )
});

我知道主要版本引入了重大更改,但我的天哪......

没有一个解决方案给了我与 winston 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>

第二个问题是附加参数在使用信息级别时被抑制 - winston 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;

我受够了,只需要这样就可以切换回 winston 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

我的是@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

嘿,我从 Winston 开始,从未使用过 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"}

感谢谁发布了上述建议。
该死的,有一天我只是为了将 Winston 3 集成到我的项目中而扔掉了:/

今天这个问题让我印象深刻 - 我意识到我的新记录器正在抛弃我所有的...rest参数。 这可能不适用于每个人或每个用例,但就我而言,我发现将我想要记录的所有内容包装为数组是可以接受的。 它不是最漂亮的,但它比一些需要更多代码的 [非常聪明的] 解决方法更轻。 希望这对其他人有帮助!

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

他们决定进行如此大的重大更改,并且仍然没有在迁移指南或文档中提及它,这真的很令人沮丧。

不管我想分享我的解决方案,我发现它非常紧凑,并遵循 node js 如何使用util.format实现 console.log

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

我想插话并请求将此功能添加到核心 Winston。 目前,我正在使用带有splat的自定义格式化程序来实现此功能,老实说,这感觉很hacky。 拥有与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());

我仍然不明白如何在 Winston 中输出多个值。

我只是想用logger.log('Hello', var1, '!')替换console.log('Hello', var1, '!') logger.log('Hello', var1, '!')

老实说,尝试使用 Winston 总是会导致大量时间浪费和令人惊讶的日志记录问题。

他们决定进行如此大的重大更改,并且仍然没有在迁移指南或文档中提及它,这真的很令人沮丧。

不管我想分享我的解决方案,我发现它非常紧凑,并遵循 node js 如何使用util.format实现 console.log

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 结合消息和 splat

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> 

我必须遵守 console.* 方法,以上述所有方式为导向,并且......

  • chrome-dev-tools 的默认控制台方法(它们默认着色)
  • 电子终端控制台输出(默认情况下也是彩色的)
  • 写入文件

但是:文件输出应显示对象详细信息。

所以我结束了:

// 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想知道您是否仍打算在某个时候解决这个问题?

我正在探索在我的项目中使用 winston,现在想知道是否值得冒险,因为这个问题已经开放了 2 年多。 对我来说,这似乎是任何日志框架的基本功能

此页面是否有帮助?
0 / 5 - 0 等级