bigo-frontend / blog

👨🏻‍💻👩🏻‍💻 bigo前端技术博客
https://juejin.cn/user/4450420286057022/posts
MIT License
129 stars 9 forks source link

【bigo】Nuxt的日志记录分析实践 #42

Open MillionQW opened 3 years ago

MillionQW commented 3 years ago

为了应对Chrome对Flash播放的限制,在2020年我们对Bigo Live直播官网进行了一次重构,将原本的Flash直播转为HTML5播放,将一个原本是前后端耦合的项目重构成了一个基于Nuxt的服务端渲染项目。由于服务端渲染依赖了一个node后台做页面渲染,因此,保证后台可以顺利完成页面组装,不会在项目运行阶段出现错误是至关重要的,为了了解node后台的运行情况,检测后台的运行健康状况,项目使用了几种日志记录方法,便于多方位观察项目的运行。由于现在关于 Nuxt 项目日志方面的文章不是特别多,因此做个记录和分享,有更好的日志记录方法欢迎一起交流~

一、使用PM2日志

项目使用了PM2做进程管理,而PM2自带了完整的日志功能,查看PM2日志的时候,日志可以随着项目运行实时加载,且PM2会自动处理日志的分割和整合,所以我们可以通过观察PM2的日志了解程序的运行状态。

查看日志:
pm2 logs [项目名] --lines [行数]

PM2 查看日志默认显示15行,如果15行无法完整查看日志内容,可以通过--lines设置查看的行数。

配置时间戳和日志存储位置

PM2的日志打印默认没记录时间,但是日志的打印时间对排期故障是很重要的,可以通过修改配置的方式增加时间戳,还有修改日志位置:

module.exports = {
  apps: [
    {
      name: 'app',
      args: 'start',
      time: true, // 日志显示时间戳
      error_file: './err.log', // 错误日志存放地址
      out_file: './out.log', // 输出日志存放地址
    }
  ]
};

这样日志就可以按照配置的位置存放,且打印的日志都有了对应的时间戳。

因为PM2的日志系统没有自带日志自动压缩和日志过期清除的功能,所以需要借助插件pm2-logrotate做日志的存储管理,具体可以看看这里.

PM2的日志展示的内容类似开发阶段的命令行输出,程序的报错和运行在后台的console也会保留在PM2的日志中。如果是程序运行阶段的报错,一般都可以在PM2的错误日志中找到,PM2日志会展示错误的堆栈和具体命令行,对故障排查有很大的帮助。

有时候我们需要自定义打印特定的日志信息,例如在请求失败的阶段打印请求信息,对于这种日志打印需求,可以使用nuxt-winson-log记录日志。

二、使用 nuxt-winson-log 打印日志

nuxt-winson-log 是 Nuxt 社区推荐的日志打印工具。是基于日志打印神器 winston 改造的 Nuxt 版。需要注意的是,nuxt-winson-log 只支持服务端打印日志,因此使用的时候需要区分运行环境。

nuxt-winson-log 会在有访问访问的时候默认打印一条日志。允许自定义日志的日志名,存放位置,和对日志进行分级,不同级别的日志有不同的文件名。允许设置日志文件数,单个文件的最大大小等。

修改日志的保存位置

nuxt-winson-log 的默认保存路径是当前文件夹下的logs文件夹。修改配置让日志保存在其他路径:

// nuxt.config.js
export default {
 modules:[
     'xxxx其他modules',
     [
       'nuxt-winston-log',
       {
          logPath:
             process.env.npm_lifecycle_event === 'build' ||
             process.env.NODE_ENV === 'development'
               ? './logs'
               : `/data/weblog/nodejs/${process.env.npm_package_name}`,
           logName: `${process.env.npm_package_name}.log`
       }
     ]
  ]
}

区分了开发和生产的日志存放目录。 同时使用npm_lifecycle_eventNODE_ENV而不是process.env.NODE_ENV === 'production'去做判断是因为构建过程中的process.env.NODE_ENV也是production,有可能因为构建机器上没有这个日志存放目录导致构建失败。

日志分级与日志存储管理

日志简单地区分的话有两种,日常日志(info)和错误(error)日志。nuxt-winson-log默认会把两种日志都放在同一个日志文件里面。可以通过配置将两种日志打到不同的日志文件。

// nuxt.config.js
import path from 'path';
import { format, transports } from 'winston';
const { combine, timestamp } = format;

// 日志存放路径
const infoLogPath = path.resolve(process.cwd(), './logs', `info.log`);
const errorLogPath = path.resolve(process.cwd(), './logs', `error.log`);

export default {
  modules: ['nuxt-winston-log'],
  winstonLog: {
    loggerOptions: {
      transports: [
        new transports.File({
          format: combine(timestamp()),
          level: 'info',
          filename: infoLogPath,
          maxsize: 5 * 1024 * 1024,  // 单个日志文件大小
          maxFiles: 3 // 最大文件数
        }),
        new transports.File({
          format: combine(timestamp()),
          level: 'error',
          filename: errorLogPath,
          maxsize: 5 * 1024 * 1024,
          maxFiles: 3
        })
      ]
    }
  },
}

这样配置就可以实现 info 和 error 日志分级,如果发现启动的时候两个日志文件没有生成,可以检查一下设置的保存路径是否存在。 在代码中使用$winstonLog.error方法打印的日志与请求异常的日志都会去到“error"级日志,使用$winstonLog.info方法打印的日志与正常请求的日志都会去到“info"日志,如果单个文件的大小超过配置大小,会自动生成一个文件名递增的日志文件。超过了配置的文件数,旧的日志文件会被自动删除。

在 axios 里做日志打点

因为在 node 端发起的接口请求如果500了是会导致返回错误页面的,所以在做好try...catch之外,还要对接口的成功与否与日志记录。

// plugins/axios.js

export default ({ $axios, $winstonLog }) => {
  $axios.onResponse((response) => {
    // $winstonLog只在服务端存在,需要判断是否存在
    if ($winstonLog) {
      $winstonLog.info(`[${response.status}] ${response.request.path}`);
    }
    return response.data;
  });

  $axios.onError((err) => {
    if ($winstonLog) {
      $winstonLog.error(
        `[${err.status}] | ${err.request.path} | ${err.message}`
      );
      $winstonLog.error(err.response && err.response.data);
    }
  });
};

在plugins文件夹里新建一个axios.js插件,正常的请求打在info日志,错误请求打error日志。

三、@bigo/node-log+Clickhouse+Grafana打造日志监控平台

上面两种日志打印方法存在的问题是,当需要查看项目日志的时候,需要去到对应的机器上,手动查看对应的日志,如果部署的机器太多,查看日志无疑是一件费力的事情。 于是,我们基于我司的Clickhouse集群+Grafana,开发自己的上报工具——@bigo/node-log,打造一个实时的日志监控平台。

@bigo/node-log同时支持eggjs和nuxt两种框架的数据上报,而且不止支持后端上报,更支持前端数据上报,前后端同时监控。 Clickhouse是一款列式存储数据库,性能是市面上现存数据库的几十倍以上,因此常用于云计算,大数据等场景。可以很好地支持实时数据查询。 Grafana是一款支持多数据源的时实数据展示工具,具备多种数据展示模型,用它可以可视化收集到的实时数据,除了展示数据,更有定时监测报警功能,通过对接企业IM,实现实时监控。

@bigo/node-log的使用:

// {app_root}/nuxt.config.js
modules: [
 ['@bigo/node-log', {
 name: 'bigo.tv' // 应用名称
 }]
]

上报实例:

// vue component
mounted() {
 this.$log.error('nuxt测试', {msg: '6666'});
}

// nuxt ssr
async asyncData({$log}) {
 $log.error('asyncData测试', {msg: '6666'});
}

通过@bigo/node-log+Clickhouse+Grafana实现的日志监控平台如下:

在每个监控图表中都配置了对应的告警,如果错误到达阈值,就会触发告警到企业微信中,24小时不间断监控,提高了项目运行的稳定性,确保项目运行状态可以实时查看,及时的告警也便于更快地处理线上问题。

以上就是我们项目中用到的几种日志上报方式,如果有其他更好的上报方式,欢迎一起交流~

参考文章: PM2 - Logs aaronransley/nuxt-winston-log: Nuxt module for logging SSR errors + client-side Vue errors using winston