如何寫一個無配置格式統一的日誌

背景

大量項目在使用logback記日誌,有部分項目使用日誌混亂,格式不統一,多數人搞不懂配置文件,致使配置錯誤,如今須要開發一套統一的、少配置的日誌組件,使用方便。java

設計思路

儘可能採用0配置,無logback.xmlredis

日誌格式統一,方便後續日誌分析系統spring

只有兩個日誌級別,一個是正常日誌,一個是異常日誌json

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

提子線程、json格式化輸出、map格式化、數組格式化、請求響應參數(供耗時)等便捷日誌輸出方法數組

支持redis、db、http自動開關配置**app

新增日誌類型(logger)

api採用流式結構,相似StringBuffermaven

概要設計

零配置

調研代碼ui

java
static LoggerContext lc;
    static {
        lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        // 對應配置中的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();
        // 對應配置中的logger
        ch.qos.logback.classic.Logger rootLogger = lc.getLogger("com.test");
        rootLogger.addAppender(ca);}

上面代碼等價於下面的xml線程

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

由此能夠隨意把配置文件中的內容以代碼形式編寫,理論已經能夠實現0配置。

輸出路徑

約定固定將日誌輸出到,相對路徑log/xxx.yyyy-MM-dd-HH.log,其中xxx爲logger的name

日誌格式

格式固定:
MMddHHmmss.SSS||id||【交易名★子步驟】||context ||level
例:
150000.311||N-XrUTQzIc1531897200311||【CiTeeFilter★ci攔截器】||ci攔截器 請求的完整參數爲:{"merchantId":["0012444"],"userId":["13112341232"]} ||INFO
固定格式的核心代碼,攔截到日誌請求,按照格式拼裝,主要方法爲繼承ThrowableProxyConverter和MessageConverter來實現對日誌的攔截,並修改成想要的格式,其中使用的例如id等放到本地變量內,核心是對MDC的使用

基礎logger

全部日誌都默認輸出到這裏 logger name:service 系統初始化時,定義這個Logger和appender,即這個Logger爲root log

自定義的logger

提供addLogger方法,參數 packageName 包名,例如:com.test 必輸參數 若是name未設置時,name默認爲包名最後一個.後面的字符 name 名字,決定日誌文件的名字 非必輸 path 日誌路徑 非必輸 additivity 是否輸出到root log內

特殊的log
提供特殊組件的log配置,例如: redis 默認ERROR http 默認ERROR db鏈接池 默認ERROR kafka 默認ERROR schedul 默認ERROR spring 默認ERROR

異常、換行日誌處理

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

普通日誌api(VirgoLog)

方法 方法描述
setUniqKey(id) 設置當前線程id,線程開始時設置便可,後面無需設置
updateStep(trade, step) 更新當前id的步驟信息
log(msg, param) 記錄普通日誌,msg替換規則,普通替換爲{},若是想替換爲業務日誌api中的格式,使用``替換
logErr(msg, e) 記錄異常日誌
log( trade, step, msg, param) 記錄普通日誌,此方法會自動更新id、trade、step,不建議使用
logErr(trade, step, msg, e) 記錄異常日誌
log(cid, trade, step, msg, param) 記錄普通日誌,此方法會自動更新id、trade、step,不建議使用
logErr(cid, trade, step, msg, e) 記錄異常日誌
debug(msg, param) 記錄debug級別日誌,不建議使用

業務日誌api(VirgoLog)

平時記日誌時,若是某個類沒有時間toString方法,會沒法正確打印出數據,此時提供替換方法,直接將object替換爲json打印,核心代碼思路爲

MessageFormatter是處理{}替換的類,從新寫個類,稍加改動即支持{}也支持`` ,並判斷替換爲json仍是toString api以下

方法 方法描述
begin(msg) 記錄開始
end(msg) 記錄完成,會打印本線程內上一個begin到如今的耗時
logJson(json, format) 記錄json格式化日誌,format表示是否換行
logMap(map, format) 記錄map格式化日誌
logCollection(list, format) 記錄集合格式化日誌
logArray(array, format) 記錄數組格式化日誌
logObjct(obj, format) 記錄Object格式化日誌

系統api(LoggerHelper)

方法 方法描述
getLogger() 獲取logger,用於記日誌
getLogger(name) 經過name獲取logger
addLogger() 參考自定義Logger,若是logger已經建立,則再也不建立,通常不使用,除非想自定義日誌名等
consoleOpen() 打開控制檯日誌,系統啓動時默認配置控制檯日誌
commonOpen(name, level) 默認的組件都是error級別,這個方法能夠變動日誌級別,例如redis http等

特殊的格式化

map:即轉化爲json,而後再格式化
collection:同上
array:也同上
object:同上

問題

一、密碼脫敏、加解密有必要單獨提取方法嗎

二、提供父線程打印開關

maven依賴

<dependency>
            <groupId>com.cdc.ecliptic</groupId>
            <artifactId>virgo</artifactId>
            <version>1.5_1.6-SNAPSHOT</version>
        </dependency>

demo

public static void main(String[] args) throws InterruptedException {

        // 啓動
        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");
        // 打開控制檯
        LoggerHelper.consoleOpen();
        // 設置cid
        VirgoLog.setUniqKey(null);
        // 設置步驟名和交易名
        VirgoLog.updateStep("adfa", "saf");
        // 獲取Logger
        VirgoLog logger = VirgoLog.getLogger();

        // 打開debug級別(只有在開發階段能夠打開)
//        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<String, String> 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("我錯了:{},你沒錯:~~", new Exception("asdfsaflk"), "啊", map);
//        logger.log("----------------------------------------------");
//        // {}替換普通對象,調用toString()  ~~把對象轉換爲json而且格式化輸出 ``把對象轉換爲json不格式化輸出
        logger.log("你好{},你是誰~~``,sd~xx {}", map, map, map, "tttt");
        VirgoLog.updateStep("saf2");
//        // 把對象轉換爲json輸出
//        logger.logJson(map, false);
//        // 更新步驟名和交易名
//        VirgoLog.updateStep("bbbbb", "ccccc");
//        // 耗時日誌打印
        logger.begin("處理內容");
        logger.begin("處理第二個");
        logger.begin("處理第三個");
        Thread.sleep(3000L);
        logger.end();
        Thread.sleep(1000L);
        logger.end();
        VirgoLog.updateStep("saf3");
        logger.end();
//        // 記錄debug日誌,通常調試用
//        logger.logDebug("jajajajaja");

//        List l = new ArrayList();
//        B b = new B();
//        try {
//            b.b();
//        } catch (Exception e) {
//            logger.logErr("woqu", e);
//        }
    }

宜信技術學院

相關文章
相關標籤/搜索