背景
大量项目在使用 logback 记日志,有部分项目使用日志混乱,格式不统一,多数人搞不懂配置文件,导致配置错误,现在需要开发一套统一的、少配置的日志组件,使用方便
设计思路
尽量采用 0 配置,无 logback.xml
日志格式统一,方便后续日志分析系统
只有两个日志级别,一个是正常日志,一个是异常日志
提供 log4j、jcl、logback、commons-log 等桥接方案及版本兼容方案
提子线程、json 格式化输出、map 格式化、数组格式化、请求响应参数(供耗时)等便捷日志输出方法
支持 redis、db、http 自动开关配置****
新增日志类型(logger)
api 采用流式结构,类似 StringBuffer
概要设计
零配置
调研代码
上面代码等价于下面的 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][http-8091-7]
复制代码
固定格式的核心代码,拦截到日志请求,按照格式拼装,主要方法为继承 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 依赖
com.cdc.ecliptic virgo 1.5_1.6-SNAPSHOT
复制代码
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", "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/detail/206
评论