基於Egg框架的日誌鏈路追蹤實踐分享

2021-09-24 07:29:13 字數 4766 閱讀 5992

實現全鏈路日誌追蹤,便於日誌監控、問題排查、介面響應耗時資料統計等,首先 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...