【日志】koa-log4
安装配置
-
安装
npm i -S koa-log4 -
根目录下新建文件夹logs
mkdir logs -
根目录下新建配置文件
log4js-conf.json{ "appenders": { "log": { "type": "dateFile", "layout": { "type": "colored" }, "filename": "logs/app.log", "pattern": "yyyy-MM-dd", "compress": false, "daysToKeep": 30 }, "body_log": { "type": "dateFile", "layout": { "type": "colored" }, "filename": "logs/body_app.log", "pattern": "yyyy-MM-dd", "compress": false, "daysToKeep": 30 }, "access_log": { "type": "dateFile", "layout": { "type": "colored" }, "filename": "logs/access_app.log", "pattern": "yyyy-MM-dd", "compress": false, "daysToKeep": 30 }, "out": { "type": "stdout" } }, "categories": { "default": { "appenders": ["out"], "level": "trace" }, "log": { "appenders": ["log", "out"], "level": "trace" }, "body_log": { "appenders": ["body_log"], "level": "trace" }, "access_log": { "appenders": ["access_log", "out"], "level": "info" } } }log用于记录每个路由、helper和DB的执行body_log用于记录post请求体,单独存储,不输出到终端access_log用于记录全局的URL访问情况和响应时间
-
如果想要自定义输出内容,可以修改
layout选项,用pattern类型指定"layout": { "type": "pattern", "pattern": "[%d{ISO8601}] [%p] %c - %m%n" },- 但这样又会丢掉颜色提示...不好用
- 这种专用于下一节给logstash输送时定制数据格式,并用grok按相应格式解析后索引
使用方法
-
安装
ip模块npm i -S ip -
lib/utils下新建api_log.js文件,内容如下import ip from "ip"; import log4js from "koa-log4"; log4js.configure('log4js-conf.json'); export class Log { constructor(name) { this.name = name; this.log = log4js.getLogger('log'); } format(explaination, user_uuid, obj) { let str = `==[${this.name}]${explaination}==[${user_uuid ? user_uuid : ''}]`; if (obj) { Object.keys(obj).forEach((key) => { str += `[${key}]${obj[key]}`; }) } return str; } debug(explaination, user_uuid, obj) { if (typeof explaination !== 'string') return; this.log.debug(this.format(explaination, user_uuid, obj)); } info(explaination, user_uuid, obj) { if (typeof explaination !== 'string') return; this.log.info(this.format(explaination, user_uuid, obj)); } warn(explaination, user_uuid, obj) { if (typeof explaination !== 'string') return; this.log.warn(this.format(explaination, user_uuid, obj)); } error(explaination, user_uuid, obj) { if (typeof explaination !== 'string') return; this.log.error(this.format(explaination, user_uuid, obj)); } } const formatAccess = (ctx, costTime) => { const user = ctx.api_user ? ctx.api_user.user_uuid : "anonymous_user"; return `${ctx.method} ${ctx.url} ${ctx.response.status} ${costTime}ms ${user} ${ip.address()}`; } export const accessLogger = (ctx, costTime) => log4js.getLogger('access_log').info(formatAccess(ctx, costTime))- 这里增加了ip的记录
- 这里对于
formatAccess不应该添加返回post的相关body记录,不然用户请求体容易被后面logstash的tcp传输泄密
-
在全局注册
accessLogger的use代码import { log, accessLogger } from "../utils/api_log.js";log.debug("== set accessLogger"); app.use(async (ctx, next) => { const start = new Date(); await next(); const ms = new Date() - start; accessLogger(ctx, ms); }); //中间的其他代码 app.on('error', (err) => { log.error(err); })- 这里监听app的error事件,专门捕获记录那些没有被程序处理而错误,便于及时发现并修复bug
-
在router中使用
logimport { Log } from "../../../utils/api_log.js"; const log = new Log("index");log.info("get index", null, { traceId: ctx.traceId }); -
在 db 中使用
logimport { Log } from "../utils/api_log.js"; const log = new Log("db_user");log.info("getUserByMobile"); -
在代码中人为抛出错误(但这样貌似就不会被accessLogger记录?)
ctx.throw()- 默认抛出
500,返回客户端显示Internal Server Error
ctx.throw(404)- 可指定返回
404,返回客户端显示Not Found
ctx.throw(500, "网页暂时无法访问")- 可指定
500并写提示信息,会返回给客户端显示
- 默认抛出
pm2集群开启会使日志记录出现错误!!!
-
问题产生的原因
- Log4js 在 Cluster 模式下,worker 将日志发送至 master,master 实现日志写入文件
- 但在 PM2 Cluster 模式下,所有进程皆为 worker
-
官方提供的解决方案-
运行命令安装
pm2 install pm2-intercom- 安装会卡死在
idealTree:pm2-intercom: sill idealTree buildDeps - 办法: 设置代理
export https_proxy=http://127.0.0.1:8889 http_proxy=http://127.0.0.1:8889 all_proxy=socks5://127.0.0.1:1080 - 安装完成后,使用
pm2 ls会列出Module表格,要确保里面有online状态的pm2-intercom- 否则就要卸载,然后重启,重新安装
- 安装会卡死在
-
找到配置文件,名称如下
log4js.configure('log4js-conf.json'); -
需要在
log4js-conf.json文件中添加"pm2": true, "pm2InstanceVar": "INSTANCE_ID",- 和
appenders同级 - 用于选举出一个负责写文件的主进程
- 和
-
原理
- 各自进程的日志首先发送到 pm2-intercom
- 由 pm2-intercom 分发到全部进程
- 但只有 log4js isMaster 才会写文件,主进程压力也会大
-
-
另一种方案: (不推荐)
- 设置log4js的disableClustering 选项为 true
- 这样所有进程都将作为 master 进而拥有写文件的权限
- 缺点:每个进程写自己的日志,日志分散不好查阅
- 设置log4js的disableClustering 选项为 true
-
也可以考虑直接放弃pm2(好像不行)
- 自己用 node 原生的 cluster模块实现“压榨CPU多核”
import { cpus } from "os"; import cluster from "cluster"; import Koa from 'koa'; const app = new Koa(); if(cluster.isPrimary){ for (let i = 0; i < cpus().length; i++) cluster.fork(); console.log('master', process.pid); }else { app.listen(3000); console.log('worker', process.pid); }- node16抛弃了isMaster,换成isPrimary,造成
[ERROR] log4js - Unable to parse log:错误
- node16抛弃了isMaster,换成isPrimary,造成
- 自己用 node 原生的 cluster模块实现“压榨CPU多核”