溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

編程開發(fā)中怎么如何寫一個(gè)無配置格式統(tǒng)一的日志

發(fā)布時(shí)間:2021-12-07 11:41:26 來源:億速云 閱讀:112 作者:小新 欄目:編程語言

這篇文章主要介紹了編程開發(fā)中怎么如何寫一個(gè)無配置格式統(tǒng)一的日志,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一、背景

大量項(xiàng)目在使用logback記日志,有部分項(xiàng)目使用日志混亂,格式不統(tǒng)一,多數(shù)人搞不懂配置文件,導(dǎo)致配置錯(cuò)誤,現(xiàn)在需要開發(fā)一套統(tǒng)一的、少配置的日志組件,方便使用。

二、設(shè)計(jì)思路

盡量采用0配置,無logback.xml

日志格式統(tǒng)一,方便后續(xù)日志分析系統(tǒng)

只有兩個(gè)日志級(jí)別,一個(gè)是正常日志,一個(gè)是異常日志

提供log4j、jcl、logback、commons-log等橋接方案及版本兼容方案

提子線程、json格式化輸出、map格式化、數(shù)組格式化、請(qǐng)求響應(yīng)參數(shù)(供耗時(shí))等便捷日志輸出方法

支持redis、db、http自動(dòng)開關(guān)配置****

新增日志類型(logger)

api采用流式結(jié)構(gòu),類似StringBuffer

三、概要設(shè)計(jì)

1、零配置

調(diào)研代碼 

java
static LoggerContext lc;
    static {
        lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        // 對(duì)應(yīng)配置中的appender
        ConsoleAppender ca = new ConsoleAppender();
        ca.setContext(lc);
        ca.setName("console");
        // 格式
        PatternLayoutEncoder pl = new PatternLayoutEncoder();
        pl.setContext(lc);
        pl.setPattern("%d{MMddHHmmss.SSS} [%thread] %-5level %logger{36} - %msg%n");
        pl.start();
        ca.setEncoder(pl);
        ca.start();
        // 對(duì)應(yīng)配置中的logger
        ch.qos.logback.classic.Logger rootLogger = lc.getLogger("com.test");
        rootLogger.addAppender(ca);}

上面代碼等價(jià)于下面的xml

                    %d{MMddHHmmss.SSS} [%thread] %-5level %logger{36} - %msg%n

由此可以隨意把配置文件中的內(nèi)容以代碼形式編寫,理論已經(jīng)可以實(shí)現(xiàn)0配置。

2、輸出路徑

約定固定將日志輸出到,相對(duì)路徑log/xxx.yyyy-MM-dd-HH.log,其中xxx為logger的name

3、日志格式

格式固定:
MMddHHmmss.SSS||id||【交易名★子步驟】||context ||[level][線程號(hào)]
例:
150000.311||N-XrUTQzIc1531897200311||【CiTeeFilter★ci攔截器】||ci攔截器 請(qǐng)求的完整參數(shù)為:{"merchantId":["0012444"],"userId":["13112341232"]} ||[INFO][http-8091-7]

固定格式的核心代碼,攔截到日志請(qǐng)求,按照格式拼裝,主要方法為繼承ThrowableProxyConverter和MessageConverter來實(shí)現(xiàn)對(duì)日志的攔截,并修改為想要的格式,其中使用的例如id等放到本地變量?jī)?nèi),核心是對(duì)MDC的使用

4、基礎(chǔ)logger

所有日志都默認(rèn)輸出到這里 logger name:service 系統(tǒng)初始化時(shí),定義這個(gè)Logger和appender,即這個(gè)Logger為root log

5、自定義的logger

提供addLogger方法,參數(shù) packageName 包名,例如:com.test 必輸參數(shù) 如果name未設(shè)置時(shí),name默認(rèn)為包名最后一個(gè).后面的字符 name 名字,決定日志文件的名字 非必輸 path 日志路徑 非必輸 additivity 是否輸出到root log內(nèi)

6、特殊的log

提供特殊組件的log配置,例如:redis 默認(rèn)ERROR http 默認(rèn)ERROR db連接池 默認(rèn)ERROR kafka 默認(rèn)ERROR schedul 默認(rèn)ERROR spring 默認(rèn)ERROR

7、異常、換行日志處理

提供exception異常棧格式打印 提供帶換行的格式化打印 代碼思路:繼承ThrowableProxyConverter,獲取異常棧,在每行的前面插入固定格式文本

8、普通日志api(VirgoLog)

方法方法描述
setUniqKey(id)設(shè)置當(dāng)前線程id,線程開始時(shí)設(shè)置即可,后面無需設(shè)置
updateStep(trade, step)更新當(dāng)前id的步驟信息
log(msg, param)記錄普通日志,msg替換規(guī)則,普通替換為{},如果想替換為業(yè)務(wù)日志api中的格式,使用``替換
logErr(msg, e)記錄異常日志
log( trade, step, msg, param)記錄普通日志,此方法會(huì)自動(dòng)更新id、trade、step,不建議使用
logErr(trade, step, msg, e)記錄異常日志
log(cid, trade, step, msg, param)記錄普通日志,此方法會(huì)自動(dòng)更新id、trade、step,不建議使用
logErr(cid, trade, step, msg, e)記錄異常日志
debug(msg, param)記錄debug級(jí)別日志,不建議使用

業(yè)務(wù)日志api(VirgoLog)

平時(shí)記日志時(shí),如果某個(gè)類沒有時(shí)間toString方法,會(huì)無法正確打印出數(shù)據(jù),此時(shí)提供替換方法,直接將object替換為json打印,核心代碼思路為

MessageFormatter是處理{}替換的類,重新寫個(gè)類,稍加改動(dòng)即支持{}也支持`` ,并判斷替換為json還是toString

api如下

方法方法描述
begin(msg)記錄開始
end(msg)記錄完成,會(huì)打印本線程內(nèi)上一個(gè)begin到現(xiàn)在的耗時(shí)
logJson(json, format)記錄json格式化日志,format表示是否換行
logMap(map, format)記錄map格式化日志
logCollection(list, format)記錄集合格式化日志
logArray(array, format)記錄數(shù)組格式化日志
logObjct(obj, format)記錄Object格式化日志

系統(tǒng)api(LoggerHelper)

方法方法描述
getLogger()獲取logger,用于記日志
getLogger(name)通過name獲取logger
addLogger()參考自定義Logger,如果logger已經(jīng)創(chuàng)建,則不再創(chuàng)建,一般不使用,除非想自定義日志名等
consoleOpen()打開控制臺(tái)日志,系統(tǒng)啟動(dòng)時(shí)默認(rèn)配置控制臺(tái)日志
commonOpen(name, level)默認(rèn)的組件都是error級(jí)別,這個(gè)方法可以變更日志級(jí)別,例如redis http等

9、特殊的格式化

map:即轉(zhuǎn)化為json,然后再格式化 

collection:同上 

array:也同上 

object:同上

10、問題

  • 密碼脫敏、加解密有必要單獨(dú)提取方法嗎

  • 提供父線程打印開關(guān)

11、maven依賴

                    com.cdc.ecliptic            virgo            1.5_1.6-SNAPSHOT

12、demo

public static void main(String[] args) throws InterruptedException {
        // 啟動(dòng)
        VirgoLancher.start("hahaha", "com.cdc.virgo", "D:/test/hahah.log");
        LoggerHelper.commonOpen("hahaha", LogLevel.DEBUG);
        Logger logger1 = LoggerFactory.getLogger("druid");
//        VirgoLancher.commonStart("abc", "com.cdc.virgo");
        // 打開控制臺(tái)
        LoggerHelper.consoleOpen();
        // 設(shè)置cid
        VirgoLog.setUniqKey(null);
        // 設(shè)置步驟名和交易名
        VirgoLog.updateStep("adfa", "saf");
        // 獲取Logger
        VirgoLog logger = VirgoLog.getLogger();
        // 打開debug級(jí)別(只有在開發(fā)階段可以打開)
//        logger.changeLevel(LogLevel.DEBUG);
        // 記錄換行
        logger.log("a");
        logger1.info("dddddddddd");
        logger1.error("dddddddddd");
//        logger1.info("sfdasfaf" +
//                "\nafafdasfd" +
//                "\nasfdasf");
        logger.log("sfdasfaf" +
                "\nafafdasfd" +
                "\nasfdasf");
//        logger1.info("b");
        // 正常日志
//        logger.log("我只有一行");
        Map map = new HashMap();
        map.put("asdf", "1");
        map.put("asdf2", "2");
        map.put("asdf3", "13");
        map.put("asdf4", "14");
        map.put("asdf5", "15");
        map.put("asdf6", "16");
//        // 異常日志也支持格式化
//        logger.logErr("我錯(cuò)了:{},你沒錯(cuò):~~", new Exception("asdfsaflk"), "啊", map);
//        logger.log("----------------------------------------------");
//        // {}替換普通對(duì)象,調(diào)用toString()  ~~把對(duì)象轉(zhuǎn)換為json并且格式化輸出 ``把對(duì)象轉(zhuǎn)換為json不格式化輸出
        logger.log("你好{},你是誰~~``,sd~xx {}", map, map, map, "tttt");
        VirgoLog.updateStep("saf2");
//        // 把對(duì)象轉(zhuǎn)換為json輸出
//        logger.logJson(map, false);
//        // 更新步驟名和交易名
//        VirgoLog.updateStep("bbbbb", "ccccc");
//        // 耗時(shí)日志打印
        logger.begin("處理內(nèi)容");
        logger.begin("處理第二個(gè)");
        logger.begin("處理第三個(gè)");
        Thread.sleep(3000L);
        logger.end();
        Thread.sleep(1000L);
        logger.end();
        VirgoLog.updateStep("saf3");
        logger.end();
//        // 記錄debug日志,一般調(diào)試用
//        logger.logDebug("jajajajaja");
//        List l = new ArrayList();
//        B b = new B();
//        try {
//            b.b();
//        } catch (Exception e) {
//            logger.logErr("woqu", e);
//        }
    }

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“編程開發(fā)中怎么如何寫一個(gè)無配置格式統(tǒng)一的日志”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI