morgan+file-stream-rotator日志管理

419 阅读1分钟

初始项目【传送门】

安装

npm i  morgan

使用

编辑app.js,新增:

const morgan = require('morgan');
app.use(morgan('short'))

用apifox调用一个接口localhost:3000/api/sign-up看看输出,没有使用morgan 的时候输出是:

Executing (default): SELECT "id", "firstName", "lastName", "email" FROM "Users" AS "User" ORDER BY "User"."id" DESC LIMIT 5;

使用morgan的时候,输出增加了日志

Executing (default): SELECT "id", "firstName", "lastName", "email" FROM "Users" AS "User" ORDER BY "User"."id" DESC LIMIT 5;
::1 - GET /api/sign-up HTTP/1.1 200 152 - 45.585 ms

打印日志到本地

morgan支持stream配置项,可以实现日志落地:

const fs = require('fs')
const path = require('path')

const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {flags: 'a'});
app.use(morgan('short', {stream: accessLogStream}));

调用一下接口,项目根目录下多出了一个access.log 文件,这里记录日志内容

::1 - GET /api/sign-up HTTP/1.1 200 152 - 43.344 ms

morgan(format, options)

  • format可以是预定义的几种字符串类型或者是自定义方法,预定义的类型有:

tiny / short / dev / common / combined

  • token是format的组成部分,例如:remote-user :method :url 等等
// common的输出格式
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]

// short的输出格式
:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms

使用预定义格式

morgan('tiny')

使用预定义的token自由组合输出格式

morgan(':method :url :status :res[content-length] - :response-time ms')

使用自定义方法的输出格式

morgan(function (tokens, req, res) {
  return [
    tokens.method(req, res),
    tokens.url(req, res),
    tokens.status(req, res),
    tokens.res(req, res, 'content-length'), '-',
    tokens['response-time'](req, res), 'ms'
  ].join(' ')
})

使用morgan.format()方法自定义format

morgan.format('common', ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]')
app.use(morgan('common', {
  stream: accessLogStream,
  // skip: function(req,res) { return res.statusCode < 400 }
}));

使用morgan.token()方法自定义token

// 自定义token
morgan.token('body',function(req,res){
  return req.body ? JSON.stringify(req.body) : '-'
})

// 自定义format,其中包含自定义的token
morgan.format('short', ':remote-addr :remote-user [:date[clf]] :method :body :url HTTP/:http-version :status :res[content-length] - :response-time ms');

// 使用自定义的format
app.use(morgan('short'));

options可选配置

  • immediate 布尔值,默认是false,请求返回后再记录日志;当为true时,一收到请求就记录日志。
  • skip 是否跳过记录
  • stream 日志的输出流配置
// skip配置
app.use(morgan('common', {
  stream: accessLogStream,
  skip: function(req,res) { return res.statusCode < 400 }
}));

日志切割file-stream-rotator

把日志全部放在一个文件里,没有进行管理,时间长了文件会变得很大,而且不利于查看,这时候就需要用日志分割了。我们使用file-stream-rotator这个工具

npm install file-stream-rotator

编辑app.js

const FileStreamRotator = require('file-stream-rotator')
const logDirectory = path.join(__dirname, 'log')

fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)
const rotateLogStream = FileStreamRotator.getStream({
  date_format: 'YYYYMMDD',
  filename: path.join(logDirectory,'access-%DATE%.log'),
  frequency: 'daily',
  verbose: false,
  max_logs: 10
})
rotateLogStream.on('rotate',function(oldFile,newFile)){
  // 旧日志删除或压缩前的回调
}

参数解析

  • filename: 定义日志文件的名称
  • date_format: 根据moment.js替换filename中的DATA字段
  • frequency: 切割的频率,可选值有daily、custom、test,如果设置为test则会按分钟进行日志切割,前提是没有filename的设置和分钟切割不冲突。若没有设置frequency,日志名称末尾也会自带'YYYY-MM-DD'
  • size: 日志的大小限制,采用数字+单位,可用单位有k / m / g,例如10k
  • max_logs: 需要保留的日志文件数,多余的自动删除,可以直接用数字,表示文件个数;或者数字+d,表示保留天数
// test模式
FileStreamRotator.getStream({filename: '/logs/access-%DATE%.log',frequency: 'test', verbose: false})

// 根据日志大小切割,并配置保存的日志文件数量,audit_file存放位置
FileStreamRotator.getStream({filename: '/logs/access.log',size: '50k' max_logs: 5, verbose: false, audit_file:"/tmp/logaudit.json", extension: '.log'})

最后编辑.gitignore文件让git不要追踪logs文件的变化

logs/

最终代码

const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const multer  = require('multer')
const PATH = './uploads/'
const fs = require('fs')
const path = require('path')
// router import
const user = require('./routes/user')
const app = express();
const FileStreamRotator = require('file-stream-rotator')

const logDirectory = path.join(__dirname, 'log')
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)
const rotateLogStream = FileStreamRotator.getStream({
  date_format: 'YYYYMMDD',
  filename: path.join(logDirectory,'access-%DATE%.log'),
  frequency: 'daily',
  verbose: false,
  max_logs: 10,
  size: '50k'
})
// const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {flags: 'a'});
// morgan.token('from',function(req,res){
//   return req.query.from || '-'
// })
morgan.token('body',function(req,res){
  return req.body ? JSON.stringify(req.body) : '-'
})
morgan.format('short', ':remote-addr :remote-user [:date[clf]] :method :body :url HTTP/:http-version :status :res[content-length] - :response-time ms');
app.use(express.json())
app.use(morgan('short', {
  stream: rotateLogStream,
  // stream: accessLogStream,
  skip: function(req,res) { return res.statusCode < 400 }
}));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// Routing
app.use('/api', user)


// set port, listen for requests
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

参考

www.cnblogs.com/chyingp/p/n… github.com/expressjs/m… github.com/rogerc/file…