博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【第四期】基于 @vue/cli3 插件,集成日志系统【SSR第三篇】
阅读量:6967 次
发布时间:2019-06-27

本文共 10153 字,大约阅读时间需要 33 分钟。

在上一篇文章中,我们创建了一个 @vue/cli3 插件,并将 ssr 服务整合到插件中。

这篇文章中,让我们来为插件中的 ssr 服务创建日志系统。

我们将从如下几个方面来逐步进行:

  • 选择日志工具库
  • 将日志工具集成到 ssr 插件中
  • 日志支持区分环境、日志支持分类、日志支持等级
  • 日志切割

选择日志工具库

基于 nodejs 有一些日志工具库可供选择:

这里我们从中选择 winston 作为基础日志工具,接入我们的 ssr 工程

将日志工具集成到 ssr 插件中

我们打开 winston 的 ,参照 一节,来开始创建我们的 logger

创建 logger

打开我们在上一篇文章中创建的 vue-cli-plugin-my_ssr_plugin_demo 工程

安装 winston

yarn add winston复制代码

在根目录文件夹下的 app 中创建文件夹 lib,并在 lib 中创建 logger.js 文件,我们在这个文件中定制自己的 logger

目录结构如下:

├── app│   ├── lib│   │   ├── logger.js│   ├── middlewares│   │   ├── dev.ssr.js│   │   ├── dev.static.js│   │   └── prod.ssr.js│   └── server.js...复制代码

logger.js 的内容如下:

const winston = require('winston')const logger = winston.createLogger({  transports: [    new winston.transports.Console(),    new winston.transports.File({ filename: 'combined.log' })  ]})module.exports = {  logger}复制代码

然后打开我们的 app/server.js ,在服务启动的过程中,为全局对象挂载上我们刚创建的 logger

...const { logger } = require('./lib/logger.js')...app.listen(port, host, () => {  logger.info(`[${process.pid}]server started at ${host}:${port}`)})...复制代码

启动服务,除了在终端看到输出外,还会发现在根目录下多了一个 combined.log 文件,里面的内容与终端输出一致

{
"message":"[46071]server started at 127.0.0.1:3000","level":"info"}复制代码

至此,我们已经为服务端接入了最基础的日志功能,接下来,让我们考虑一下实际的日志场景。

日志支持区分环境、日志支持分类、日志支持等级

简单起见,我们将环境区分、日志分类、日志等级简化为以下几个具体要求:

  1. 日志需要写入到文件中。
  2. 日志需要支持自定义级别,级别由大到小依次是:errorwarningnoticeinfodebug
  3. 开发环境,日志的输出最好能带颜色,格式能更加方便在终端阅读。
  4. 增加用户请求日志 access 类型,此日志需要写入到单独的文件中,与其他类型的日志区分开。

关于第一条要求,我们在上一个例子中,已经通过 winston.transports.File 实现了。

对于第二、三条要求,我们打开 lib/logger.js 添加相关的代码,最终代码如下:

const winston = require('winston')const options = {  // 我们在这里定义日志的等级  levels: { error: 0, warning: 1, notice: 2, info: 3, debug: 4 },  transports: [    // 文件中我们只打印 warning 级别以上的日志(包含 warning)    new winston.transports.File({ filename: 'combined.log', level: 'warning' })  ]}// 开发环境,我们将日志也输出到终端,并设置上颜色if (process.env.NODE_ENV === 'development') {  options.format = winston.format.combine(    winston.format.colorize(),    winston.format.json()  )  // 输出到终端的信息,我们调整为 simple 格式,方便看到颜色;  // 并设置打印 debug 以上级别的日志(包含 debug)  options.transports.push(new winston.transports.Console({    format: winston.format.simple(), level: 'debug'  }))}const logger = winston.createLogger(options)module.exports = {  logger}复制代码

我们在 app/servier.js 中输入以下代码:

...logger.error('this is the error log')logger.warning('this is the warning log')logger.notice('this is the info log')logger.info('this is the info log')logger.debug('this is the debug log')...复制代码

在发开环境启动服务后,能看到终端打印出如下内容:

error: this is the error logwarning: this is the warning lognotice: this is the info loginfo: this is the info logdebug: this is the debug log复制代码

而日志文件 combined.log 中的内容为:

{
"message":"this is the error log","level":"\u001b[31merror\u001b[39m"}{
"message":"this is the warning log","level":"\u001b[31mwarning\u001b[39m"}复制代码

在测试和产品环境启动服务后,日志并不会输出到终端,只输出到文件中:

{
"message":"this is the error log","level":"error"}{
"message":"this is the warning log","level":"warning"}复制代码

接下来我们来看第四条要求:

增加用户请求日志 access 类型,此日志需要写入到单独的文件中,与其他类型的日志区分开。

如果我们需要增加一个 access 日志类型,并将它的内容输出到独立的文件中,最简单的方式就是再创建一个 logger 实例:

...winston.loggers.add('access', {  levels: { access: 0 },  level: 'access',  format: winston.format.combine(    winston.format.json()  ),  transports: [    new winston.transports.File({ filename: 'access.log', level: 'access' })  ]})...复制代码

我们在 app/servier.js 中添加打印 access 日志的代码:

const { logger, accessLogger } = require('./log.js')...accessLogger.access('this is the access log')复制代码

在开发环境启动服务后,我们发现除了 combined.log 日志文件外,又多了一个 access.log 文件,内容为:

{
"message":"this is the access log","level":"access"}复制代码

至此,我们的日志中还没有自动记录当前的时间,我们在 lib/logger.js 中为两类日志都添加上时间,添加后的代码如下:

const winston = require('winston')const options = {  // 我们在这里定义日志的等级  levels: { error: 0, warning: 1, notice: 2, info: 3, debug: 4 },  format: winston.format.combine(    winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' })  ),  transports: [    // 文件中我们只打印 warning 级别以上的日志(包含 warning)    new winston.transports.File({ filename: 'combined.log', level: 'warning' })  ]}// 开发环境,我们将日志也输出到终端,并设置上颜色if (process.env.NODE_ENV === 'development') {  options.format = winston.format.combine(    winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),    winston.format.colorize(),    winston.format.json()  )  // 输出到终端的信息,我们调整为 simple 格式,方便看到颜色;  // 并设置打印 debug 以上级别的日志(包含 debug)  options.transports.push(new winston.transports.Console({    format: winston.format.simple(), level: 'debug'  }))}winston.loggers.add('access', {  levels: { access: 0 },  level: 'access',  format: winston.format.combine(    winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),    winston.format.json()  ),  transports: [    new winston.transports.File({ filename: 'access.log', level: 'access' })  ]})const logger = winston.createLogger(options)module.exports = {  logger,  accessLogger: winston.loggers.get('access')}复制代码

在开发环境启动服务后,我们发现日志携带了当前的时间信息,终端内容为:

error: this is the error log {
"timestamp":"2019-06-06 17:02:36.736"}warning: this is the warning log {
"timestamp":"2019-06-06 17:02:36.740"}notice: this is the info log {
"timestamp":"2019-06-06 17:02:36.741"}info: this is the info log {
"timestamp":"2019-06-06 17:02:36.741"}debug: this is the debug log {
"timestamp":"2019-06-06 17:02:36.741"}复制代码

``文件的内容为:

{
"message":"this is the error log","level":"\u001b[31merror\u001b[39m","timestamp":"2019-06-06 17:02:36.736"}{
"message":"this is the warning log","level":"\u001b[31mwarning\u001b[39m","timestamp":"2019-06-06 17:02:36.740"}复制代码

``文件的内容为:

{
"message":"this is the access log","level":"access","timestamp":"2019-06-06 17:02:36.741"}复制代码

日志切割

将来我们的服务部署上线后,服务器端记录的日志会不断得往同一个文件中写入日志内容,这对于长时间运行的服务来说,是有日志过大隐患的。

这里,我们按照每小时分割日志,将每个小时内的日志内容,写入不同的文件中。

另外,因为部署产品服务会有多个 worker 进程服务,所以,我们为每个进程分配一个独立的文件夹(以进程id+日期区分),来存储此进程的全部日志:

logs├──your_project_name│   ├──pid_1236_2019_01_01│   │   ├──access-2019-01-01-23.log│   │   └──combined-2019-01-01-23.log│   └──pid_1237_2019_01_01│      ├──access-2019-01-01-23.log│      └──combined-2019-01-01-23.log复制代码

为了实现我们预计的日志切割功能,我们需要引入一个库:winston-daily-rotate-file

yarn add winston-daily-rotate-file复制代码

安装完后,我们在 lib/logger.js 中引入

require('winston-daily-rotate-file')复制代码

引入后,我们可以通过 winston.transports.DailyRotateFile 创建拥有自动切割功能的 tracsports 实例

const _getToday = (now = new Date()) => `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`let dirPath += '/pid_' + pid + '_' + _getToday() + '/'let accessTransport = new (winston.transports.DailyRotateFile)({  filename: dirPath + 'access-%DATE%.log', // 日志文件存储路径 + 日志文件名称  datePattern: 'YYYY-MM-DD-HH', // 日志文件切割的粒度,这里为每小时  zippedArchive: true, // 是否压缩  maxSize: '1g', // 每个日志文件最大的容量,如果达到此容量则触发切割  maxFiles: '30d' // 日志文件保留的时间,这里为 30 天,30天之前的日志会被删除掉})复制代码

添加切割功能后的 lib/logger.js 内容如下:

const winston = require('winston')const { format } = winstonconst { combine, timestamp, json } = formatconst _getToday = (now = new Date()) => `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`const rotateMap = {  'hourly': 'YYYY-MM-DD-HH',  'daily': 'YYYY-MM-DD',  'monthly': 'YYYY-MM'}module.exports = (dirPath = './', rotateMode = '') => {  if (!~Object.keys(rotateMap).indexOf(rotateMode)) rotateMode = ''  let accessTransport  let combineTransport  if (rotateMode) {    require('winston-daily-rotate-file')    const pid = process.pid    dirPath += '/pid_' + pid + '_' + _getToday() + '/'    const accessLogPath = dirPath + 'access-%DATE%.log'    const combineLogPath = dirPath + 'combine-%DATE%.log'    const datePattern = rotateMap[rotateMode] || 'YYYY-MM'    accessTransport = new (winston.transports.DailyRotateFile)({      filename: accessLogPath,      datePattern: datePattern,      zippedArchive: true,      maxSize: '1g',      maxFiles: '30d'    })    combineTransport = new (winston.transports.DailyRotateFile)({      filename: combineLogPath,      datePattern: datePattern,      zippedArchive: true,      maxSize: '500m',      maxFiles: '30d'    })  }  const options = {    // 我们在这里定义日志的等级    levels: { error: 0, warning: 1, notice: 2, info: 3, debug: 4 },    format: combine(      timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' })    ),    transports: rotateMode ? [      combineTransport    ] : []  }  // 开发环境,我们将日志也输出到终端,并设置上颜色  if (process.env.NODE_ENV === 'development') {    options.format = combine(      timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),      winston.format.colorize(),      json()    )    // 输出到终端的信息,我们调整为 simple 格式,方便看到颜色;    // 并设置打印 debug 以上级别的日志(包含 debug)    options.transports.push(new winston.transports.Console({      format: format.simple(), level: 'debug'    }))  }  winston.loggers.add('access', {    levels: { access: 0 },    level: 'access',    format: combine(      timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),      json()    ),    transports: rotateMode ? [      accessTransport    ] : []  })  const logger = winston.createLogger(options)  return {    logger: logger,    accessLogger: winston.loggers.get('access')  }}复制代码

app/server.js 中引入 lib/logger.js 也需要调整为以下方式:

const { logger, accessLogger } = require('./lib/logger.js')('./', 'hourly')复制代码

在开发环境启动服务,我们会发现除了终端输出了日志外,我们的日志文件变成了如下的结构:

./pid_48794_2019-6-6├── access-2019-06-06-18.log└── combine-2019-06-06-18.log复制代码

最终,插件 vue-cli-plugin-my_ssr_plugin_demo 的完整目录结构如下:

├── app│   ├── middlewares│   │   ├── dev.ssr.js│   │   ├── dev.static.js│   │   └── prod.ssr.js│   ├── lib│   │   └── logger.js│   └── server.js├── generator│   ├── index.js│   └── template│       ├── src│       │   ├── App.vue│       │   ├── assets│       │   │   └── logo.png│       │   ├── components│       │   │   └── HelloWorld.vue│       │   ├── entry-client.js│       │   ├── entry-server.js│       │   ├── main.js│       │   ├── router│       │   │   └── index.js│       │   ├── store│       │   │   ├── index.js│       │   │   └── modules│       │   │       └── book.js│       │   └── views│       │       ├── About.vue│       │       └── Home.vue│       └── vue.config.js├── index.js└── package.json复制代码

至此,我们的日志系统完成了。下一篇文章,我们讲如何为 vue-cli-plugin-my_ssr_plugin_demo 设计并集成监控系统。


水滴前端团队招募伙伴,欢迎投递简历到邮箱:fed@shuidihuzhu.com

转载于:https://juejin.im/post/5d030cc4f265da1bd04edc64

你可能感兴趣的文章
win10下搭建jz2440v3(arm s3c2440)开发及gdb调试环境【转】
查看>>
安装 Percona XtraBackup 2.3
查看>>
Linux网络编程“惊群”问题总结
查看>>
002-redis-数据类型
查看>>
mysql 锁的现象和解决
查看>>
py 行者 the5fire
查看>>
小型网络存储服务器(转)
查看>>
Ext DeskTop的使用方法简易教程及相关例子Demo(转)
查看>>
KD Tree
查看>>
[Cocoa]XCode下的iOS单元测试
查看>>
Centos 中使用 FTP 命令时出现“-bash: ftp: command not found”
查看>>
控件-TextField、Slider、
查看>>
java中ArrayList 、LinkList区别
查看>>
C#怎么做系统托盘
查看>>
ORA-01940: 无法删除当前连接的用户
查看>>
metasploit nessus & db_autopwn
查看>>
Web API在OWIN下实现OAuth
查看>>
git repository 的使用
查看>>
Android Studio的SVN Performing VCS Refresh/Commit 长时间不结束
查看>>
Cannot call sendError() after the response has been committed(filter问题)
查看>>