基于AOP的日志调试
来源:互联网 发布:时间安排软件 编辑:程序博客网 时间:2024/06/08 11:49
断点 vs 日志
断点是我们日常开发最为常见和高效的调试手段, 相比较输入日志它给予更多的状态信息和灵活的观察角度, 但断点调试是有前提和局限的.
- 需要一个界面友好, 功能强大的IDE,
- 比较适合于在单机的开发环境中进行.
企业应用开发中, 我们常常会遇到无法断点调试的窘境, 例如:
- 这个异常仅在生产环境出现, 开发环境里无法重现;
- 存在外部系统依赖, 开发环境无法模拟等.
这迫使我们不得不回到日志调试的老路子上来.
Print vs Logging
简单点的话, 我们用
System.out.println("debug infomation");
就是因为过于简单, 当需要更多信息(如线程, 时间等), 或是定义输出模式和形式就需要编写更多代码, 于是我们有了Log4j.
为什么要基于AOP
Log4j挺好用的, 只是与System.out.print一样, 在代码中随处可见, 但却没有业务价值.
更令人头痛的是, 并非每次我们都有足够的经验告诉自己应该在哪里添加这些语句, 以致于调试中不断的因为调正它们的在代码中的位置, 而反复编译 – 打包 – 发布系统. 这种体力活, 太没艺术感了, 囧!
换而言之, 我们会希望:
- 将Logging剥离于业务之外, 让代码更易于维护,
- 无需重新编译,甚至能够运行时, 可调整输出日志的位置.
AOP完全可以帮助我们做到上述两点.
这完全不是什么新鲜观点, 这在任何介绍AOP文章中, 都会提到Logging是其最典型的应用场景.
所以这儿将基于Guice, 讨论如何实现一个非侵入式的, 且能无需重新编译即可调正Logging位置的简单示例.
一个基于Guice的示例
我曾经用过一个叫Log4E的Eclipse插件, 它可根据我们预先的配置, 自动的为我们在编写的代码中插入logging的语句, 如方法调用的进口和出口:
01 public int sum(int a, int b){02 if (logger.isDebugEnabled()){03 logger.debug("sum - start : a is " + a + ", b is " + b);04 }05 06 int result = a + b;07 08 if (logger.isDebugEnabled()){09 logger.debug("sum - end : return is " + result);10 }11 }
从上例不难发现, 我们在调试过程中, 往往会通过一个方法的进入或退出的状态(参数, 返回值或异常)来分析问题出在什么地方. 那么, 借助 MethodInterceptor 我们可以这样做:
Logging拦截器
01 public class LoggingInterceptor implements MethodInterceptor {02 03 @Override04 public Object invoke(MethodInvocation invocation) throws Throwable {05 try {06 Object result = invocation.proceed();07 08 // logging ??, ??????09 log(invocation.getMethod(), invocation.getArguments(), result);10 11 return result;12 } catch (Throwable throwable) {13 14 // logging ??, ?????15 error(invocation.getMethod(), invocation.getArguments(), throwable);16 17 throw throwable;18 }19 }20 }
接下来, 我们需要配置这个拦截器, 并向Guice声明它.
01 public class LoggingModule extends AbstractModule {02 03 @Override04 public void configure() {05 bindInterceptor(Matchers.any(), Matchers.any(), new LoggingInterceptor());06 }07 08 }09 10 public class Main {11 public static void main(String[] args) {12 Injector injector = Guice.createInjector(new BusinessModule(), new LoggingModule());13 14 }15 }
很简单, 不是吗? 这样我们的业务模块的代码完全不用编写输出日志的代码, 只需要在创建Injector的时候加入LoggingModule就可以了.
等等, 好像忘了去实现如何配置日志输出的位置. 好吧, 这个其实很简单:
配置Logging位置
01 bindInterceptor(Matchers.any(), Matchers.any(), new LoggingInterceptor());
bindInterceptor方法的第一个参数定义了拦截器将匹配所有类, 第二个参数定义了拦截器将匹配一个类所有方法. 那么, 我们要做的仅仅是通过外部参数调整这两个参数就可以啦. 这儿就演示一个用正则表达式匹配要Logging的方法的例子:
01 public class MethodRegexMatcher extends AbstractMatcher<Method> {02 03 private final Pattern pattern = Pattern.compile(System.getProperty("logging.method.regex", "*"));04 05 @Override06 public boolean matches(Method method) {07 return pattern.matcher(method.getName()).matches();08 }09 10 }
可惜这种方法不能在运行时调整, 但这也是可以实现的.
运行时配置Logging位置
还是以用正则表达式匹配要Logging的方法为例:
01 public class LoggingInterceptor implements MethodInterceptor {02 03 private String regex = "*";04 05 public void setMethodRegex(String regex){06 this.regex = regex;07 }08 09 @Override10 public Object invoke(MethodInvocation invocation) throws Throwable {11 String methodName = invocation.getMethod().getName();12 13 try {14 Object result = invocation.proceed();15 16 if (methodName.matches(regex))17 // logging ??, ??????18 log(invocation.getMethod(), invocation.getArguments(), result);19 20 return result;21 } catch (Throwable throwable) {22 23 if (methodName.matches(regex))24 // logging ??, ?????25 error(invocation.getMethod(), invocation.getArguments(), throwable);26 27 throw throwable;28 }29 }30 }
而后可借助JMX动态调整regex的值, 来实现运行时的配置. 当然, 肯定还会有其它更好的方法, 如果你知道了不妨分享一下.
小结
本文仅以Guice为例讨论如何改进我们日常开发中调试的问题, 其实这在Spring应用也同样能够实现的, 甚至其它应用AOP的场景都是可行的.
拓展开来, 不仅是Logging, 说不定验证(测试)也是可行的呢!
有句话不是这样说的吗, “思想有多远, 我们就能走多远!”
- 基于AOP的日志调试
- 基于AOP的日志实现!
- 基于aop的用户操作日志
- 基于AOP操作日志
- 基于AOP操作日志
- 基于linux 的 AM335X GPIO 调试日志
- 基于自定义日志打印的UDAF调试
- 基于schema的AOP,实现日志记录Demo
- 基于Spring的AOP实现自定义annotation操作日志
- SpringMVC中基于AOP的自定义注解记录日志
- 两种基于AOP的日志管理方法(springboot)
- 基于Spring的AOP实现自定义annotation操作日志
- 基于注解的aop实现用户操作日志管理
- spring mvc 基于aop日志管理
- 基于Schema的AOP
- 基于Schema的AOP
- 基于@AspectJ的AOP
- 基于@Aspect的AOP
- 并查集--小鑫的城堡
- 强制类型转换的运算优先级比较高。
- lock 语句(C# 参考)
- Unknown column 'filevalue' in 'field list'
- 森纵培训第二十七天课程总结
- 基于AOP的日志调试
- 互联网电视市场趋于细分,TCL爱奇艺“通吃”野心显现
- Pow(x, n)
- WEB开发技术储备
- SNMP的报文格式以及解析
- HP-UX /usr 扩容
- ScrollView页面进入后默认显示在了页面最底部
- DatePickerDialog的应用中的使用
- OJ_1082 代理服务器