什麼是Log?為什麼Log也要處理格式?
Log的重要程度佔了整個維運工作的其最重要的角色,有專門在處理Log的大型框架ELK,Log的用途甚廣,可以記錄使用者於其應用介面的任何操作,尤其蠻常見的就是與廠商介接掉單處理,查詢應用層的各項缺失,所以要將Log統一風格化處理,我的習慣是將Log輸出成Json格式。
也不是將所有不重要的資訊都通通倒垃圾般的導出Log,Log也是有分程度的,常見的分法有 error(錯誤)、warn(警告)、info(信息)、http、verbose(冗長)、debug(調試)、silly(愚蠢)
開發過程會有多個環境去處理,dev/qa/prod環境不同而提出不同的環境。
套件網址:
winston - 286 kB
npm i winston
特色
- 可以分程度導出
- 可自組輸出格式
- 彈性輸出log資料
封裝自用
import { createLogger, format, transports } from 'winston';
export default createLogger({
level: process.env.NODE_ENV == "development" ? 'info' : 'warn', // 輸出 Level
format: format.combine(
format.timestamp(),
format.json(),
),
transports: [
new transports.Console(),
],
});
所以如果你要引用在其他Route使用就import package logger就可以了。
與 Express 框架使用
常見的紀錄時機有兩個時機,也是最常用的時機,就是 Request/Response 這兩個時機,可能使用者送進來的資料有問題,在查詢問題時會導出Log,或是我方錯誤資訊記錄起來。
middleware
這是一段 typescript,也不用特別緊張,重點是組 Log 可能要有什麼資訊。
import { NextFunction, Request, Response } from 'express';
import { ExpressMiddlewareInterface } from 'routing-controllers';
import { nanoid } from 'nanoid'
import logger from '../packages/logger';
export class ReqLoggerMiddleware implements ExpressMiddlewareInterface {
use(req: Request, res: Response, next?: (err?: any) => NextFunction): any {
let log = {
// Request
pid: nanoid(),
method: req.method, // Request 的 http method : 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' ...
url: req.protocol + '://' + req.get('host') + req.originalUrl, // Request 的 URL
body: req.body, // Request 的 body
params: req.params, // Request 的 params
query: req.query, // Request 的 query
clientAuth: req.headers.Authorization, // Request 的 clientAuth
clientIP: req.headers['x-forwarded-for'], // Request 的 clientIP
}
res.locals.pid = log.pid // pass pid ,Express 提供的一個功能想傳遞就綁在 `res.locals` 上面
logger.info('Request Data', log)
logger.info('Request Data', log) // 如果你是 info 就是用 info() warn 就用 warn()
// {"pid":"IGjI3iV_GLXXMq6sB_jqW","url":"http://127.0.0.1:30080/api/v1/route?rows=10&sort=sales&page=1&memberId=0","body":{},"params":{},"query":{"rows":"10","sort":"sales","page":"1","memberId":"0"},"level":"info","message":"Request Data","timestamp":"2021-08-27T03:47:13.893Z"}
// 如果是 logger.info('message'),會有 pid 就是因為 我在製作package的時候有加入 defaultMeta 我把它稱作為 pid
// {"pid":"IGjI3iV_GLXXMq6sB_jqW","level":"info","message":"Request Data","timestamp":"2021-08-27T03:47:13.893Z"}
// Response 不建議每一筆都要 回傳 但如果你要的話 將回傳的資料都取出。
res.on('finish', function () { logger.info('Response Data', { ...res.locals }) });
next();
}
}
什麼是 pid ?
pid 又稱為 process id 進程ID,他是代表這一個執行緒所執行的進程ID,沒錯 NodeJS 預設也有一個 process.pid
,你們可以使用看看,他是一個數字而且永遠不會增加。
查詢問題的時候可以用兩個依據去查詢一個是時間區間,一個就是pid,我們可以使用 uuid4
或著是向下方介紹的 nanoid
當作 你的 pid
nanoid - 59.5 kB
npm i nanoid
特色
- 非常輕量
- 短加密
用法簡單:
import { nanoid } from 'nanoid'
console.log(nanoid()) // zpSZymgimvr7jrTF0G3HR