如何写一个无配置格式统一的日志


背景

大量项目在使用logback记日志,有部分项目使用日志混乱,格式不统一,多数人搞不懂配置文件,导致配置错误,现在需要开发一套统一的、少配置的日志组件,使用方便。

设计思路

尽量采用0配置,无logback.xml

日志格式统一,方便后续日志分析系统

只有两个日志级别,一个是正常日志,一个是异常日志

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

提子线程、json格式化输出、map格式化、数组格式化、请求响应参数(供耗时)等便捷日志输出方法

支持redis、db、http自动开关配置

*
*

新增日志类型(logger)

api采用流式结构,类似StringBuffer

概要设计

零配置

调研代码

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:同上

问题

1、密码脱敏、加解密有必要单独提取方法吗

2、提供父线程打印开关

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);
//        }
    }复制代码

作者:刘鹏飞    宜信技术学院官网:http://college.creditease.cn/#/index


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值