實現全鏈路日誌追蹤,便於日誌監控、問題排查、介面響應耗時資料統計等,首先 api 介面服務接收到呼叫方請求,根據呼叫方傳的 traceid,在該次呼叫鏈中處理業務時,如需列印日誌的,日誌資訊按照約定的規範進行列印,並記錄 traceid,實現日誌鏈路追蹤。
/var/logs/$/bizlog/$-yyyymmdd.log
複製**
日誌時間traceid服務端ip客戶端ip日誌級別日誌內容
複製**
採用 egg.js 框架 egg-logger 中介軟體,在實現過程中發現對於按照以上日誌格式列印是無法滿足需求的(至少目前我還沒找到可實現方式),如果要自己實現,可能要自己造輪子了,好在官方的 egg-logger 中介軟體提供了自定義日誌擴充套件功能,參考 高階自定義日誌,本身也提供了日誌分割、多程序日誌處理等功能。
egg-logger 提供了多種傳輸通道,我們的需求主要是對請求的業務日誌自定義格式儲存,主要用到 filetransport 和 consoletransport 兩個通道,分別列印日誌到檔案和終端。
基於 egg-logger 定製開發乙個外掛程式專案,參考 外掛程式開發,以下以 egg-logger-custom 為專案,展示核心**編寫
egg-logger-custom/lib/logger.js
const moment = require('moment');
const filetransport = require('egg-logger').filetransport;
const utils = require('./utils');
const util = require('util');
/** * 繼承 filetransport
*/class
extends
filetransport
log(level, args, meta) );
// 針對 error 訊息列印出錯誤的堆疊
if (args[0] instanceof
error) ;
args[0] = util.format('%s: %s\n%s\npid: %s\n', err.name, err.message, err.stack, process.pid);
} else
// 這個是必須的,否則日誌檔案不會寫入
super.log(level, args, meta);
}/**
* 自定義訊息格式
* 可以根據自己的業務需求自行定義
* @param level
*/messageformat() = this;
const params = json.stringify(object.assign({}, ctx.request.query, ctx.body));
return [
moment().format('yyyy/mm/dd hh:mm:ss'),
ctx.request.get('traceid'),
utils.serviceipaddress,
utils.clientipaddress(ctx.req),
level,
].join(utils.loggerdelimiter) + utils.loggerdelimiter;
}}複製**
egg-logger-custom/lib/utils.js
const inte***ces = require('os').networkinte***ces();
module.exports = }}
})(),
/*** 獲取當前請求客戶端ip
* 不安全的寫法
*/clientipaddress: req => ,
clientipaddress: ctx => ,
}複製**
注意:以上獲取當前請求客戶端ip的方式,如果你需要對使用者的 ip 做限流、防刷限制,請不要使用如上方式,參見 科普文:如何偽造和獲取使用者真實 ip ?,在 egg.js 裡你也可以通過 ctx.ip 來獲取,參考 前置**模式。
複製**
const logger = require('egg-logger').logger;
const consoletransport = require('egg-logger').consoletransport;
module.exports = (ctx, options) => , ctx));
logger.set('console', new consoletransport());
return logger;
}複製**
module.exports = );
}}複製**
建議:對於日誌級別,可以採用配置中心如 consul 進行配置,上線時日誌級別設定為 info,當需要生產問題排查時,可以動態開啟 debug 模式。關於 consul 可以關注我之前寫的 服務註冊發現 consul 系列
錯誤日誌記錄,直接會將錯誤日誌完整堆疊資訊記錄下來,並且輸出到 errorlog 中,為了保證異常可追蹤,必須保證所有丟擲的異常都是 error 型別,因為只有 error 型別才會帶上堆疊資訊,定位到問題。
const controller = require('egg').controller;
class
examplecontroller
extends
controller
= this;
ctx.logger.error(new
error('程式異常!'));
ctx.logger.debug('測試');
ctx.logger.info('測試');
}}複製**
最終日誌列印格式如下所示:
2019/05/30 01:50:21d373c38a-344b-4b36-b931-1e8981aef14f192.168.1.20221.69.245.153info測試
複製**
egg-logger 最新版本支援通過 contextformatter 函式自定義日誌格式,參見之前 pr:support contextformatter #51
應用也很簡單,通過配置 contextformatter 函式即可,以下是簡單的應用
config.logger = ,
...};複製**
同樣的在你的業務裡對於需要列印日誌的地方,和之前一樣
ctx.logger.info('這是乙個測試資料');
複製**
輸出結果如下所示:
2019-06-04 12:20:10,421這是乙個測試資料
複製**
框架提供了 egg-logrotator 中介軟體,預設切割為按天切割,其它方式可參考官網自行配置。
egg-logger 模組 lib/egg/config/config.default.js
config.logger = ;
複製**
很簡單按照我們的需求在專案配置檔案重新定義 logger 的 dir 路徑
config.logger =
複製**
這樣是否就可以呢?按照我們上面自定義的日誌檔名格式($-yyyymmdd.log
),貌似是不行的,在日誌分割過程中預設的檔名格式為.log.yyyy-mm-dd
,參考原始碼
_setfile(srcpath, files)
// don't rotate logpath in filesrotatebyhour
if (this.filesrotatebyhour.indexof(srcpath) > -1)
if (!files.has(srcpath)) );
}}複製**
const moment = require('moment');
return ,
async task()
};};function
class
customrotator
extends
.log`;
files.set(srcpath, );
return files;}}
return
}複製**
經過分割之後檔案展示如下:
$ ls -lh /var/logs/test/bizlog/
total 188k
-rw-r--r-- 1 root root 135k jun 1 11:00 test-2019-06-01.log
-rw-r--r-- 1 root root 912 jun 2 09:44 test-2019-06-02.log
-rw-r--r-- 1 root root 40k jun 3 11:49 test.log
複製**
擴充套件:基於以上日誌格式,可以採用 elk 做日誌蒐集、分析、檢索。 輕量級日誌鏈路追蹤框架 TLog
先引入jar包 1.1.0 t log.version com.yomahub groupid tlog all spring boot starter artifactid version dependency 有兩種使用方式 方式一 位元組碼增強 public class runner 進行日誌...
swoft 日誌鏈路追蹤
該庫主要通過設定traceid,spanid,來實現日誌鏈路記錄,保證同一請求的鏈路traceid一致 並且增加redishandler可以將日誌直接記錄到redis中 協程方式 後續可以通過elk同步日誌 另外通過日誌配置增加version inte ce method params cost 時...
docker zipkin(分布式鏈路追蹤)實踐
參考 dependenciesspring name test 在zipkin上顯示的服務名,不寫則是 default zipkin base url zipkin服務的位址 sender type web 網上有人在zipkin上查不到記錄,說加上這個即可,但本人親測不加也是可以查到記錄 sleu...