2021年了,该会日志脱敏了吧(Node篇)
2021.01.28
Kobayashi
前端
 热度
℃
日志可以为我们提供关于系统行为的必要信息,便于定位线上代码问题,不至于抓瞎🤠,所以说日志是非常重要的,对于Node程序也是一样,日志工具也是多种多样,有log4js
、bunyan
、winston
,今天就来讲讲在GitHub
上面star
最多的winston
安装不用说吧,就是下面的代码
npm install winston
基本使用 const logger = require ('winston' ) logger.info('is info' ) logger.warn('is warn' ) logger.error('is error' )
就会把日志打印到控制台中
[winston] Attempt to write logs with no transports {"message" :"is info" ,"level" :"info" } [winston] Attempt to write logs with no transports {"message" :"is warn" ,"level" :"warn" } [winston] Attempt to write logs with no transports {"message" :"is error" ,"level" :"error" }
存储/输出机制(transports) 内置transports 有时候我们希望在控制台接收日志,并把日志保存到文件中
单纯使用pm2
的out.log
来记录日志的同学上述代码就可以实现,并且有level
分类
使用docker
等非pm2
启动的同学,也很简单,比如保存到指定为的的server.log
里面,只需要使用以下代码
const winston = require ('winston' )const path = require ('path' )const logger = winston.createLogger({ transports: [ new winston.transports.Console(), new winston.transports.File({filename : path.resolve(__dirname, '../logs/server.log' )}) ] }) logger.info('print to the console and the file' )
以上是使用到winston
内置存储/输出机制(transports):
除此之外内置的transports还有
自定义transports 可以自己编写一个继承自 winston.Transport
类,并实现类的log
方法
const Transport = require ('winston-transport' );const util = require ('util' );module .exports = class YourCustomTransport extends Transport { constructor (opts ) { super (opts); } log (info, callback ) { setImmediate(() => { this .emit('logged' , info); }); callback(); } };
日志文件根据时间切割(翻转) 安装日志翻转组件
npm install winston-daily-rotate-file
const dailyRotateFile = require ('winston-daily-rotate-file' );const dateTransport = new dailyRotateFile({ filename: path.resolve(__dirname, './logs/server.log' ), maxSize: '50m' , createSymlink: true , symlinkName: 'server.log' })const logger = winston.createLogger({ transports: [ dateTransport ] });
上面的maxSize
就是文件旋转后的最大大小,单位为k
(kb)、m
(mb)、g
(gb)
会得到文件server.log.2021.01.28
当日志文件大小大于maxSize
的时候,能得到
server.log.2021.01.28.1
、server.log.2021.01.28.2
、server.log.2021.01.29
…
createSymlink
为真的时候,创建一个从指定名称(symlinkName
)连接当前活动日志文件的符号连接
方便ELK
扫描
日志脱敏 为了符合国家等保三级规定,必须对日志文件中存在的用户隐私信息进行加密,例如手机号,身份证等等
这时候可以用到winston
的自定义格式化功能
即在createLogger
的时候使用自定义format
const { createLogger, format } = require ('winston' );const util = require ('util' )const formatLog = format.printf(info => { const msg = info.message; if (typeof (msg) === 'string' && msg.includes('%s' )) { const splat = info[SPLAT] || info.splat || []; const splatInfo = splat.map(item => { if (typeof item === 'object' ) { return util.inspect(encryptionLog(item), false , null ) } return item }) info.message = util.format(msg, ...splatInfo); } const finalLog = `[${info.timestamp} ] [${info.level} ] ${info.message} ` return finalLog })exports .businessLog = createLogger({ format: format.combine( format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), formatLog ) });
附上我自己写的脱敏 下面是我自己简单写的脱敏规则,即对日志进来的对象进行脱敏
const encryRules = require ('./encryRules' ).encryRulesconst encryptionLog = (splat, keyName ) => { if (splat === null ) return splat; if (splat instanceof Date ) return new Date (splat); if (splat instanceof RegExp ) return new RegExp (splat); if (typeof splat !== "object" ) { if (encryRules.rules.hasOwnProperty(keyName)) { return encryRules.rules[keyName](splat) } return splat }; let cloneSplat = new splat.constructor(); for (let key in splat) { if (splat.hasOwnProperty(key)) { cloneSplat[key] = encryptionLog(splat[key], key); } } return cloneSplat; }
加密规则(encryRules.js)
const regularEncrypt = (str, ruleName ) => { if (str != null && str != undefined ) { switch (ruleName) { case 'phone' : return String (str).replace(/(\d{3})\d{4}(\d{4})/g , '$1****$2' ) case 'email' : return str.replace(/([^@]*)/ , word => { word.slice(0 , 3 ) + word.slice(3 ).replace(/.{1}/g , '*' ) }) } } return str }const encryptPhone = (str ) => regularEncrypt(str, 'phone' )const encryptEmail = (str ) => regularEncrypt(str, 'email' )const rules = { phone: encryptPhone, email: encryptEmail }exports .encryRules = { rules }
最后 上述的功能对于一般场景来说就已经完全足够了🤪,对于winston
的其他功能,这里就不一一叙述了,具体可以到Github
上面查看相关文档,如果有什么更好的方法,欢迎评论区交流哈哈😜。
最后最后,希望觉得文章还行的靓仔靓女们可以给我点个赞,mua ~