demo演示如何写一个无配置格式统一的日志
一、背景
大量项目在使用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演示如何写一个无配置格式统一的日志的更多相关文章
- 学记:为spring boot写一个自动配置
		
spring boot遵循"约定优于配置"的原则,使用annotation对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来.spring boot的神奇 ...
 - 自己动手用Javascript写一个无刷新分页控件
		
.NET技术交流群:337901356 ,欢迎您的加入! 对 于一个用户体验好的网站来说,无刷新技术是很重要的,无刷新,顾名思义,就是局部刷新数据,有用过Asp.net Web Form技术开发网页的 ...
 - 写一个EF的CodeFirst的Demo
		
写一个EF的CodeFirst的Demo 今天打算写一个关于EF的CodeFirs的一个小Demo.先略说一个EF的三种与数据库,怎么说,叫映射么,好吧,那就这么叫吧,就是一个是ModelFirst就 ...
 - [pixhawk笔记]4-如何写一个简单的应用程序
		
本文主要内容来自于:https://dev.px4.io/en/tutorials/tutorial_hello_sky.html,并对文档中的部分问题进行更正. 本文假设已经建立好开发环境并能正确编 ...
 - 比最差的API(ETW)更差的API(LTTng)是如何炼成的, 谈如何写一个好的接口
		
最近这几天在帮柠檬看她的APM系统要如何收集.Net运行时的各种事件, 这些事件包括线程开始, JIT执行, GC触发等等. .Net在windows上(NetFramework, CoreCLR)通 ...
 - 打算写一个《重学Node.js》系列,希望大家多多支持
		
先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...
 - 写一个兼容性比较好的拖拽DEMO
		
写一个兼容性比较好的拖拽DEMO 查看Demo 思路 div盒子 鼠标按下事件onmousedown 鼠标移动事件onmousemove,获得鼠标的坐标,将div移动至鼠标的当前坐标 鼠标抬起事件om ...
 - 写一个限制上传文件大小和格式的jQuery插件
		
在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...
 - 无监控不运维——使用 Python 写一个小小的项目监控
		
在公司里做的一个接口系统,主要是对接第三方的系统接口,所以,这个系统里会和很多其他公司的项目交互.随之而来一个很蛋疼的问题,这么多公司的接口,不同公司接口的稳定性差别很大,访问量大的时候,有的不怎么行 ...
 
随机推荐
- 运行所选代码生成器时出错:“值-1超出了可接受的[0,2147483647]范围。参数名称:value”
			
在使用vs2019添加mvc控制器的时候 这已经是第二次遇到这个问题了.常言道,多喝热水,重启试试.有时候当应用工作不正常,重启也许能解决问题.但是程序员通常接触不到服务器系统权限.而运维人员和公司流 ...
 - sea.js的同步魔法
			
前些时间也是想写点关于CMD模块规范的文字,以便帮助自己理解.今天看到一篇知乎回答,算是给了我一点启发. 同步写法却不阻塞? 先上一个sea.js很经典的模块写法: // 定义一个模块 define( ...
 - Demo小细节-2
			
今天在牛客的题海中再次找虐,题目如下: public class B { public static B t1 = new B(); public static B t2 = new B(); { S ...
 - CSS3 transform封装
			
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
 - HBase的安装和使用
			
文章作者:foochane 原文链接:https://foochane.cn/article/2019062801.html 1 Hbase基本介绍 Hbase是一个分布式数据库,可以提供数据的实时 ...
 - LiteORM-For-DotNet,我的第一个开源库
			
这是一个DotNet轻量级ORM框架,解决C#.Net开发过程中重复繁琐的数据库CURD操作. 前言 因工作中接手的.net项目,源码里面都用了动软代码生成的源码做为数据库操作类库.其中,有些根本就没 ...
 - CodeForces 909E
			
题意略. 思路:一个拓扑排序的题目吧.肯定是要先处理后面那个任务,再处理前面那个任务,我的思路是尽力先把主处理器能操作的先操作完,然后再把副处理器能操作完的再操作完,这样循环,直到处理完全部. 定义t ...
 - 解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xyfer.dao.UserDao.findById
			
在使用Spring整合MyBatis的时候遇到控制台报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (no ...
 - Redis数据类型的基本操作
			
Redis数据类型的基本操作 一.string类型 1.设置value
 - ccflow关于流程引擎的父子流程的基本概念
			
关键字:驰骋BPM jflow,ccflow, 驰骋工作流引擎 父子流程概念:在管理活动中,通用的部分,公用的部分,可以被重复执行的部分的流程管理活动我们把他独立出来,成为子流程,可以被其他流程的管理 ...