日志对 Web 框架的重要性不言而喻,在监控应用状态、排查问题、关键动作记录等有非常重要的作用,egg.js 对日志提供了内置的支持

日志分类

egg.js 按照场景对日志做了分类

  • core log:框架内核、插件打印的日志,存储在 egg-web.log
  • app log:应用相关日志,供应用开发者使用,存储在 ${appinfo.name}-web.log
  • agent log:agent 进程的日志,egg.js 框架和使用到 agent 进程的插件 在执行任务时把日志打印到 egg-agent.log
  • error log:使用 egg.js 打印的错误日志会存储在 common-error.log 下,帮助开发者定位问题

可以修改 config 来自定义日志文件名称

  1. config.logger = {
  2. coreLogName: 'egg-web.log',
  3. appLogName: `${appInfo.name}-web.log`,
  4. agentLogName: 'egg-agent.log',
  5. errorLogName: 'common-error.log',
  6. };

日志路径

  • 在本地开发环境中所有日志存储在 /logs/${appinfo.name}
  • 在线上环境日志存放在 ${appinfo.root}/logs/${appinfo.name}

可以修改不同环境的配置文件,自定义日志存储路径

  1. // config/config.${env}.js
  2. config.logger = {
  3. dir: '/path/to/your/custom/log/dir',
  4. };

打印日志

app log

如果想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,可以通过 App Logger 来完成

  1. // app.js
  2. module.exports = app => {
  3. app.logger.debug('debug info');
  4. app.logger.info('启动耗时 %d ms', Date.now() - start);
  5. app.logger.warn('warning!');
  6. app.logger.error(someErrorObj);
  7. };

框架和插件开发者还会使用到 app.coreLogger

  1. // app.js
  2. module.exports = app => {
  3. app.coreLogger.info('启动耗时 %d ms', Date.now() - start);
  4. };

Agent Logger

在开发框架和插件时有时会需要在 Agent 进程运行代码,这时使用 agent.coreLogger

  1. // agent.js
  2. module.exports = agent => {
  3. agent.logger.debug('debug info');
  4. agent.logger.info('启动耗时 %d ms', Date.now() - start);
  5. agent.logger.warn('warning!');
  6. agent.logger.error(someErrorObj);
  7. };

Context Logger

egg.js 的业务开发者最常使用的是请求的 log,用来记录 Web 行为相关日志

  1. ctx.logger.debug('debug info');
  2. ctx.logger.info('some request data: %j', ctx.request.body);
  3. ctx.logger.warn('WARNNING!!!!');
  4. // 错误日志记录,直接会将错误日志完整堆栈信息记录下来,并且输出到 errorLog 中
  5. ctx.logger.error(new Error('whoops'));

每行日志会自动记录上当前请求的一些基本信息, 如

  • userId
  • ip
  • 耗时
  • traceId
  • method
  • url

框架开发者和插件开发者还会使用到 ctx.coreLogger

  1. ctx.coreLogger.info('info');

日志级别

日志分为 NONEDEBUGINFOWARNERROR 5 个级别,和 egg.js logger 中方法名对应,为了方便开发,在开发环境会同步输出到终端,可以通过配置修改打印到终端、输出到日志文件的级别

打印所有级别日志到终端

  1. // config/config.${env}.js
  2. exports.logger = {
  3. consoleLevel: 'DEBUG',
  4. };

关闭所有打印到文件的日志

  1. // config/config.${env}.js
  2. exports.logger = {
  3. level: 'NONE',
  4. };

生产环境打印 DEBUG 日志

为了避免一些插件的调试日志在生产环境打印导致性能问题,生产环境默认禁止打印 DEBUG 级别的日志

  1. // config/config.prod.js
  2. exports.logger = {
  3. level: 'DEBUG',
  4. allowDebugAtProd: true,
  5. };

日志切割

日积月累日志文件会变得非常庞大,egg.js 提供了自动切割日志的功能

按天切割

egg.js 默认按天切割日志,在每日 00:00 按照 .log.YYYY-MM-DD 文件名进行切割
image.png

按小时切割

如果日志量非常大,可以修改配置,按照小时切割,例如把 common-error.log 按照小时进行切割:

  1. // config/config.${env}.js
  2. const path = require('path');
  3. module.exports = appInfo => {
  4. return {
  5. logrotator: {
  6. filesRotateByHour: [
  7. path.join(appInfo.root, 'logs', appInfo.name, 'common-error.log'),
  8. ],
  9. },
  10. };
  11. };

按文件大小切割

egg.js 也可以按照文件大小对日志进行切割,例如把 egg-web.log 按照大小进行切割:

  1. // config/config.${env}.js
  2. const path = require('path');
  3. module.exports = appInfo => {
  4. return {
  5. logrotator: {
  6. filesRotateBySize: [
  7. path.join(appInfo.root, 'logs', appInfo.name, 'egg-web.log'),
  8. ],
  9. maxFileSize: 2 * 1024 * 1024 * 1024, // 当文件超过 2G 时进行切割
  10. },
  11. };
  12. };

性能

通常 Web 访问是高频访问,每次打印日志都写磁盘会造成频繁磁盘 IO,为了提高性能 egg.js 不会实时把日志内容写入磁盘,而是把日志内容先缓存一段时间(默认 1s)到内存中,然后再异步写入磁盘

egg.js 日志完整功能可以查看官方文档 Egg.js 日志