这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 | ||
分享:技术:log4j:实现log4j打日志带uuid [2016/12/24 08:41] gxx 创建 |
分享:技术:log4j:实现log4j打日志带uuid [2016/12/24 11:12] (当前版本) gxx |
||
---|---|---|---|
行 1: | 行 1: | ||
====== 实现log4j打日志带uuid(同一个请求线程同一个uuid) ====== | ====== 实现log4j打日志带uuid(同一个请求线程同一个uuid) ====== | ||
===== 实现思路 ===== | ===== 实现思路 ===== | ||
+ | - [目标前提]:尽量不改变原有的写法Logger logger = Logger.getLogger(A.class); | ||
- 新建Logger子类BaseUuidLogger,重写debug,info等方法;在ThreadLocal中获取同一个uuid;打印日志前带上这个uuid | - 新建Logger子类BaseUuidLogger,重写debug,info等方法;在ThreadLocal中获取同一个uuid;打印日志前带上这个uuid | ||
- spring能管理到的类(@controller,@service),使用aop切面,扫描Logger变量强制设置成BaseUuidLogger | - spring能管理到的类(@controller,@service),使用aop切面,扫描Logger变量强制设置成BaseUuidLogger | ||
行 9: | 行 10: | ||
所以,修改log4j配置文件把log4j捕获的类型和行号删除,然后,在BaseUuidLogger中重写的debug,info等方法打印日志前带上ThreadLocal中获取的同一个uuid之前再带上调用BaseUuidLogger的堆栈前面一个类的类名和行号信息。 | 所以,修改log4j配置文件把log4j捕获的类型和行号删除,然后,在BaseUuidLogger中重写的debug,info等方法打印日志前带上ThreadLocal中获取的同一个uuid之前再带上调用BaseUuidLogger的堆栈前面一个类的类名和行号信息。 | ||
- | <code> | + | <code java BaseUuidLogger.java> |
package com.shhxzq.fin.lifeapp.model.base; | package com.shhxzq.fin.lifeapp.model.base; | ||
行 120: | 行 121: | ||
==== log4j.xml ==== | ==== log4j.xml ==== | ||
[%C:%L]指的是[类名:行号],因为同一个线程请求打印出同一个uuid,所以把%t(指的是线程名)也去掉了。 | [%C:%L]指的是[类名:行号],因为同一个线程请求打印出同一个uuid,所以把%t(指的是线程名)也去掉了。 | ||
- | <code> | + | <code xml log4j.xml> |
<appender name="ELP-ALL" class="org.apache.log4j.DailyRollingFileAppender"> | <appender name="ELP-ALL" class="org.apache.log4j.DailyRollingFileAppender"> | ||
<param name="file" value="${la.log4j.dir}/lifeapp.log" /> | <param name="file" value="${la.log4j.dir}/lifeapp.log" /> | ||
行 152: | 行 153: | ||
==== application-context.xml ==== | ==== application-context.xml ==== | ||
配置AOP切面,可以扫描到指定路径下所有@Service注解的服务类 | 配置AOP切面,可以扫描到指定路径下所有@Service注解的服务类 | ||
- | <code> | + | <code xml application-context.xml> |
<!-- 服务层AOP begin --> | <!-- 服务层AOP begin --> | ||
<bean id="serviceAop" class="com.shhxzq.fin.lifeapp.biz.utils.ServiceAop" /> | <bean id="serviceAop" class="com.shhxzq.fin.lifeapp.biz.utils.ServiceAop" /> | ||
行 165: | 行 166: | ||
==== spring-mvc.xml ==== | ==== spring-mvc.xml ==== | ||
要让AOP扫描到@Controller注解的控制器类(虽然是spring管理,但是AOP默认扫描不到),必须加一行配置 | 要让AOP扫描到@Controller注解的控制器类(虽然是spring管理,但是AOP默认扫描不到),必须加一行配置 | ||
- | <code> | + | <code xml spring-mvc.xml> |
<!-- 扫描控制器类 --> | <!-- 扫描控制器类 --> | ||
<context:component-scan base-package="com.shhxzq.fin.lifeapp" /> | <context:component-scan base-package="com.shhxzq.fin.lifeapp" /> | ||
行 174: | 行 175: | ||
==== ServiceAop.java ==== | ==== ServiceAop.java ==== | ||
扫面到切面指向的类和方法时候,程序会进入切面类的实现逻辑:通过反射扫描当前执行对象的Logger类型成员变量,new一个BaseUuidLogger子类对象,把这个Logger类型成员变量的所有属性拷贝赋值给BaseUuidLogger,再反射强制set给这个Logger类型成员变量。 | 扫面到切面指向的类和方法时候,程序会进入切面类的实现逻辑:通过反射扫描当前执行对象的Logger类型成员变量,new一个BaseUuidLogger子类对象,把这个Logger类型成员变量的所有属性拷贝赋值给BaseUuidLogger,再反射强制set给这个Logger类型成员变量。 | ||
- | <code> | + | <code java ServiceAop.java> |
package com.shhxzq.fin.lifeapp.biz.utils; | package com.shhxzq.fin.lifeapp.biz.utils; | ||
行 312: | 行 313: | ||
==== BeanUtils.java ==== | ==== BeanUtils.java ==== | ||
一般采用org.apache.commons.beanutils.BeanUtils.java:copyProperties(Object dest, Object orig)来拷贝两个对象的相同属性,但是这个类不能拷贝private,protected以及父类的所有属性,所以这里使用反射机制新写一个Bean工具类。 | 一般采用org.apache.commons.beanutils.BeanUtils.java:copyProperties(Object dest, Object orig)来拷贝两个对象的相同属性,但是这个类不能拷贝private,protected以及父类的所有属性,所以这里使用反射机制新写一个Bean工具类。 | ||
- | <code> | + | <code java BeanUtils.java> |
package com.shhxzq.fin.lifeapp.biz.utils; | package com.shhxzq.fin.lifeapp.biz.utils; | ||
行 405: | 行 406: | ||
==== BaseUuidLoggerUtils.java ==== | ==== BaseUuidLoggerUtils.java ==== | ||
前面配置AOP,那么AOP扫描到的@Service和@Controller自动Logger类型成员变量会变成BaseUuidLogger对象;那脱离了spring管理的其它类就只能还一种方法,使用这个工具类手动获取BaseUuidLogger对象。 | 前面配置AOP,那么AOP扫描到的@Service和@Controller自动Logger类型成员变量会变成BaseUuidLogger对象;那脱离了spring管理的其它类就只能还一种方法,使用这个工具类手动获取BaseUuidLogger对象。 | ||
- | <code> | + | <code java BaseUuidLoggerUtils.java> |
package com.shhxzq.fin.lifeapp.biz.utils; | package com.shhxzq.fin.lifeapp.biz.utils; | ||
行 440: | 行 441: | ||
==== 用法1 ==== | ==== 用法1 ==== | ||
spring能管理到的类(@controller,@service),使用aop切面,扫描Logger变量强制设置成BaseUuidLogger | spring能管理到的类(@controller,@service),使用aop切面,扫描Logger变量强制设置成BaseUuidLogger | ||
- | <code> | + | <code java LaCreditServiceImpl.java> |
/** | /** | ||
* 信用卡服务接口实现类 | * 信用卡服务接口实现类 | ||
行 456: | 行 457: | ||
} | } | ||
</code> | </code> | ||
- | <code> | + | <code java CreditController.java> |
/** | /** | ||
* 信用卡相关接口 | * 信用卡相关接口 | ||
行 475: | 行 476: | ||
==== 用法2 ==== | ==== 用法2 ==== | ||
spring管理不到的类,比如util工具类,使用BaseUuidLoggerUtils.getBaseUuidLogger()赋值给Logger变量 | spring管理不到的类,比如util工具类,使用BaseUuidLoggerUtils.getBaseUuidLogger()赋值给Logger变量 | ||
- | <code> | + | <code java HawkeyeUtil.java> |
/** | /** | ||
* 监控工具类 | * 监控工具类 | ||
行 492: | 行 493: | ||
} | } | ||
</code> | </code> | ||
- | ===== 待探讨另一种实现方式 ===== | + | ===== 打印效果 ===== |
- | 解压log4j.jar,拿到Logger.class(或者父类),使用ASM字节码技术框架,修改debug,info等方法(或者修改这些方法统一调后面的某个方法),在打印信息之前带上uuid(在ThreadLocal中获取同一个uuid) | + | <code> |
+ | 2016-12-24 09:28:46,503 INFO [com.shhxzq.fin.lifeapp.biz.utils.ServiceAop.java:around():93][uuid:f9ca7f9e-3d01-4fb3-be1e-30866c598f35] : log - com.shhxzq.fin.lifeapp.biz.impl.credit.LaCreditServiceImpl.queryShowCreditMenu request - {"acceptMode":"Mobile","custNo":"0000045412","deviceId":"BiHo7J4ppFXL8vAmQ3WZ7M"} | ||
+ | 2016-12-24 09:28:46,503 INFO [com.shhxzq.fin.lifeapp.remote.impl.SafeCenterServiceImpl.java:checkShowCreditMenu():63][uuid:f9ca7f9e-3d01-4fb3-be1e-30866c598f35] : 判断用户是否可见信用卡服务请求:BeidouRiskQueryDO{appName='LIFEAPP', dataMap={}, moduleType=101, isMeetBreak=true, memberId='0000045412', deviceId='BiHo7J4ppFXL8vAmQ3WZ7M', ip='null', weixin='null'} | ||
+ | 2016-12-24 09:28:46,505 INFO [com.shhxzq.fin.lifeapp.remote.impl.SafeCenterServiceImpl.java:checkShowCreditMenu():78][uuid:f9ca7f9e-3d01-4fb3-be1e-30866c598f35] : 判断用户能否还款返回:AccessStatus=[0],Reason=[null] | ||
+ | 2016-12-24 09:28:46,506 INFO [com.shhxzq.fin.lifeapp.biz.utils.ServiceAop.java:around():120][uuid:f9ca7f9e-3d01-4fb3-be1e-30866c598f35] : com.shhxzq.fin.lifeapp.biz.impl.credit.LaCreditServiceImpl.queryShowCreditMenu use time : 0.003s | ||
+ | 2016-12-24 09:28:46,506 INFO [com.shhxzq.fin.lifeapp.biz.utils.ServiceAop.java:around():130][uuid:f9ca7f9e-3d01-4fb3-be1e-30866c598f35] : log - com.shhxzq.fin.lifeapp.biz.impl.credit.LaCreditServiceImpl.queryShowCreditMenu response - true | ||
+ | </code> | ||
+ | 根据uuid | ||
+ | <code> | ||
+ | grep 'f9ca7f9e-3d01-4fb3-be1e-30866c598f35' lifeapp.log --color | ||
+ | </code> | ||
+ | 可以查到日志中同一次请求的所有日志 | ||
+ | ===== [待探讨方式1]ASM字节码技术 ===== | ||
+ | lifeapp项目运行,自动解压log4j.jar,拿到Logger.class(或者父类),使用ASM字节码技术框架,修改debug,info等方法(或者修改这些方法统一调后面的某个方法),在打印信息之前带上uuid(在ThreadLocal中获取同一个uuid)。使用新实现的ClassLoader类重新加载新的Logger.class创建新对象。 | ||
+ | ==== 用法 ==== | ||
+ | 和原始的Logger对象用法一样 | ||
+ | <code java HawkeyeUtil.java> | ||
+ | /** | ||
+ | * 监控工具类 | ||
+ | * | ||
+ | * @author Gxx | ||
+ | * @since 2016/12/2 | ||
+ | */ | ||
+ | public class HawkeyeUtil { | ||
+ | |||
+ | /** | ||
+ | * 日志处理器 | ||
+ | */ | ||
+ | private static Logger logger = Logger.getLogger(HawkeyeUtil.class); | ||
+ | |||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | ===== [待探讨方式2]修改Log4j源码 ===== | ||
+ | 拿出Log4j源码,修改debug,info等方法(或者修改这些方法统一调后面的某个方法),在打印信息之前带上uuid(在ThreadLocal中获取同一个uuid)。重新编译,放到src/main/java/目录下Logger对应的包路径下。 | ||
+ | ==== 用法 ==== | ||
+ | 和原始的Logger对象用法一样 | ||
+ | <code java HawkeyeUtil.java> | ||
+ | /** | ||
+ | * 监控工具类 | ||
+ | * | ||
+ | * @author Gxx | ||
+ | * @since 2016/12/2 | ||
+ | */ | ||
+ | public class HawkeyeUtil { | ||
+ | |||
+ | /** | ||
+ | * 日志处理器 | ||
+ | */ | ||
+ | private static Logger logger = Logger.getLogger(HawkeyeUtil.class); | ||
+ | |||
+ | ... | ||
+ | } | ||
+ | </code> |