一、背景

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

二、设计思路

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

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

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

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

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

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

新增日志类型(logger)

api采用流式结构,类似StringBuffer

三、概要设计

1、零配置

调研代码

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{} - %msg%n

由此可以随意把配置文件中的内容以代码形式编写,理论已经可以实现0配置。

2、输出路径

约定固定将日志输出到,相对路径log/xxx.yyyy-MM-dd-HH.log,其中xxx为logger的name

3、日志格式

格式固定:
MMddHHmmss.SSS||id||【交易名★子步骤】||context ||[level][线程号]
例:
150000.311||N-XrUTQzIc1531897200311||【CiTeeFilter★ci拦截器】||ci拦截器 请求的完整参数为:{"merchantId":[""],"userId":[""]} ||[INFO][http--]

固定格式的核心代码,拦截到日志请求,按照格式拼装,主要方法为继承ThrowableProxyConverter和MessageConverter来实现对日志的拦截,并修改为想要的格式,其中使用的例如id等放到本地变量内,核心是对MDC的使用

4、基础logger

所有日志都默认输出到这里 logger name:service 系统初始化时,定义这个Logger和appender,即这个Logger为root log

5、自定义的logger

提供addLogger方法,参数 packageName 包名,例如:com.test 必输参数 如果name未设置时,name默认为包名最后一个.后面的字符 name 名字,决定日志文件的名字 非必输 path 日志路径 非必输 additivity 是否输出到root log内

6、特殊的log

提供特殊组件的log配置,例如:redis 默认ERROR http 默认ERROR db连接池 默认ERROR kafka 默认ERROR schedul 默认ERROR spring 默认ERROR

7、异常、换行日志处理

提供exception异常栈格式打印 提供带换行的格式化打印 代码思路:继承ThrowableProxyConverter,获取异常栈,在每行的前面插入固定格式文本

8、普通日志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等

9、特殊的格式化

map:即转化为json,然后再格式化

collection:同上

array:也同上

object:同上

10、问题

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

  • 提供父线程打印开关

11、maven依赖

com.cdc.ecliptic            virgo            .5_1.-SNAPSHOT

12、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 map = new HashMap();
map.put("asdf", "");
map.put("asdf2", "");
map.put("asdf3", "");
map.put("asdf4", "");
map.put("asdf5", "");
map.put("asdf6", "");
// // 异常日志也支持格式化
// 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);
// }
}

作者:刘鹏飞

来源:宜信技术学院

demo演示如何写一个无配置格式统一的日志的更多相关文章

  1. 学记:为spring boot写一个自动配置

    spring boot遵循"约定优于配置"的原则,使用annotation对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来.spring boot的神奇 ...

  2. 自己动手用Javascript写一个无刷新分页控件

    .NET技术交流群:337901356 ,欢迎您的加入! 对 于一个用户体验好的网站来说,无刷新技术是很重要的,无刷新,顾名思义,就是局部刷新数据,有用过Asp.net Web Form技术开发网页的 ...

  3. 写一个EF的CodeFirst的Demo

    写一个EF的CodeFirst的Demo 今天打算写一个关于EF的CodeFirs的一个小Demo.先略说一个EF的三种与数据库,怎么说,叫映射么,好吧,那就这么叫吧,就是一个是ModelFirst就 ...

  4. [pixhawk笔记]4-如何写一个简单的应用程序

    本文主要内容来自于:https://dev.px4.io/en/tutorials/tutorial_hello_sky.html,并对文档中的部分问题进行更正. 本文假设已经建立好开发环境并能正确编 ...

  5. 比最差的API(ETW)更差的API(LTTng)是如何炼成的, 谈如何写一个好的接口

    最近这几天在帮柠檬看她的APM系统要如何收集.Net运行时的各种事件, 这些事件包括线程开始, JIT执行, GC触发等等. .Net在windows上(NetFramework, CoreCLR)通 ...

  6. 打算写一个《重学Node.js》系列,希望大家多多支持

    先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...

  7. 写一个兼容性比较好的拖拽DEMO

    写一个兼容性比较好的拖拽DEMO 查看Demo 思路 div盒子 鼠标按下事件onmousedown 鼠标移动事件onmousemove,获得鼠标的坐标,将div移动至鼠标的当前坐标 鼠标抬起事件om ...

  8. 写一个限制上传文件大小和格式的jQuery插件

    在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...

  9. 无监控不运维——使用 Python 写一个小小的项目监控

    在公司里做的一个接口系统,主要是对接第三方的系统接口,所以,这个系统里会和很多其他公司的项目交互.随之而来一个很蛋疼的问题,这么多公司的接口,不同公司接口的稳定性差别很大,访问量大的时候,有的不怎么行 ...

随机推荐

  1. The used SELECT statements have a different number of columns???

    今天我们组就我一个人留守在这里修复bug了,有点小悲伤啊,他们都问我能不能hold得住啊,我当然能hold得住啊: 在看一个入库的存储过程中,在数据库运行的时候是没问题的,项目已启动,进行入库操作就是 ...

  2. python 36 进程池、线程池

    目录 1. 死锁与递归锁 2. 信号量Semaphor 3. GIL全局解释器锁:(Cpython) 4. IO.计算密集型对比 4.1 计算密集型: 4.2 IO密集型 5. GIL与Lock锁的区 ...

  3. 一键部署 Spring Boot 到远程 Docker 容器,就是这么秀!

    不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...

  4. jvm系列(四):jvm调优-命令篇

    运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole.大名鼎鼎的VisualVM,IBM的Memory Analyzer ...

  5. centos7yum安装mysql5.7

    https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-centos-7 https://typecodes. ...

  6. 微擎 人人商城 导出excel表分析

    在 数据处理上 ,有很多时候需要导出excel表  来当报表, 等 ,  php  人人商城导出报表过程简单分析 在导出时候发现 ca('statistics.order.export'); 出于好奇 ...

  7. 逆序对 线段树&树状数组 (重制版)

    逆序对的定义:长度为n的数组a,求满足i<j时a[i]>a[j]条件的数对个数. 第一次接触这种问题的人可能是更先想到的是n^2去暴力数前面有几个比他大的数. int main() { i ...

  8. CF - 1108 E 枚举上界+线段树维护

    题目传送门 枚举每个点作为最大值的那个点.然后既然是作为最大值出现的话,那么这个点就是不需要被减去的,因为如果最小值也在这个区间内的话,2者都减去1,对答案没有影响,如果是最小值不出现在这个区间内的话 ...

  9. [HNOI2002]沙漠寻宝 题解

    一道大模拟 代码 #include <cstdio> #include <iostream> #include <cstring> #include <str ...

  10. VS Code 前端开发常用快捷键插件

    一.vs code 的常用快捷键 1.注释:a) 单行注释:[ctrl+k,ctrl+c] 或 ctrl+/ b) 取消单行注释:[ctrl+k,ctrl+u] (按下ctrl不放,再按k + u) ...