断点 vs 日志
断点是我们日常开发最为常见和高效的调试手段, 相比较输入日志它给予更多的状态信息和灵活的观察角度, 但断点调试是有前提和局限的.
# 需要一个界面友好, 功能强大的IDE,
# 比较适合于在单机的开发环境中进行.
企业应用开发中, 我们常常会遇到无法断点调试的窘境, 例如:
# 这个异常仅在生产环境出现, 开发环境里无法重现;
# 存在外部系统依赖, 开发环境无法模拟等.
这迫使我们不得不回到*日志调试*的老路子上来.
Print vs Logging
简单点的话, 我们用
System.out.println("debug infomation");
就是因为过于简单, 当需要更多信息(如线程, 时间等), 或是定义输出模式和形式就需要编写更多代码, 于是我们有了[http://logging.apache.org/log4j Log4j].
为什么要基于AOP
Log4j挺好用的, 只是与`System.out.print`一样, 在代码中随处可见, 但却没有业务价值.
更令人头痛的是, 并非每次我们都有足够的经验告诉自己应该在哪里添加这些语句, 以致于调试中不断的因为调正它们的在代码中的位置, 而反复编译 - 打包 - 发布系统. 这种体力活, 太没艺术感了, 囧!
换而言之, 我们会希望:
# 将Logging剥离于业务之外, 让代码更易于维护,
# 无需重新编译,甚至能够运行时, 可调整输出日志的位置.
[http://en.wikipedia.org/wiki/Aspect-oriented_programming AOP]完全可以帮助我们做到上述两点.
这完全不是什么新鲜观点, 这在任何介绍[http://en.wikipedia.org/wiki/Aspect-oriented_programming AOP]文章中, 都会提到Logging是其最典型的应用场景.
所以这儿将基于[http://code.google.com/p/google-guice/ Guice], 讨论如何实现一个非侵入式的, 且能无需重新编译即可调正Logging位置的简单示例.
一个基于Guice的示例
我曾经用过一个叫[http://log4e.jayefem.de/ Log4E]的Eclipse插件, 它可根据我们预先的配置, 自动的为我们在编写的代码中插入logging的语句, 如方法调用的进口和出口:
public int sum(int a, int b){
if (logger.isDebugEnabled()){
logger.debug("sum - start : a is " + a + ", b is " + b);
}
int result = a + b;
if (logger.isDebugEnabled()){
logger.debug("sum - end : return is " + result);
}
}
从上例不难发现, 我们在调试过程中, 往往会通过一个方法的进入或退出的状态(参数, 返回值或异常)来分析问题出在什么地方. 那么, 借助 *MethodInterceptor* 我们可以这样做:
Logging拦截器
public class LoggingInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
Object result = invocation.proceed();
// logging 方法, 参数与返回值
log(invocation.getMethod(), invocation.getArguments(), result);
return result;
} catch (Throwable throwable) {
// logging 方法, 参数与异常
error(invocation.getMethod(), invocation.getArguments(), throwable);
throw throwable;
}
}
}
接下来, 我们需要配置这个拦截器, 并向[http://code.google.com/p/google-guice/ Guice]声明它.
public class LoggingModule extends AbstractModule {
@Override
public void configure() {
bindInterceptor(Matchers.any(), Matchers.any(), new LoggingInterceptor());
}
}
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BusinessModule(), new LoggingModule());
}
}
很简单, 不是吗? 这样我们的业务模块的代码完全不用编写输出日志的代码, 只需要在创建Injector的时候加入LoggingModule就可以了.
等等, 好像忘了去实现如何配置日志输出的位置. 好吧, 这个其实很简单:
配置Logging位置
bindInterceptor(Matchers.any(), Matchers.any(), new LoggingInterceptor());
bindInterceptor方法的第一个参数定义了拦截器将匹配所有类, 第二个参数定义了拦截器将匹配一个类所有方法. 那么, 我们要做的仅仅是通过外部参数调整这两个参数就可以啦. 这儿就演示一个用正则表达式匹配要Logging的方法的例子:
public class MethodRegexMatcher extends AbstractMatcher<Method> {
private final Pattern pattern = Pattern.compile(System.getProperty("logging.method.regex", "*"));
@Override
public boolean matches(Method method) {
return pattern.matcher(method.getName()).matches();
}
}
可惜这种方法不能在运行时调整, 但这也是可以实现的.
运行时配置Logging位置
还是以用正则表达式匹配要Logging的方法为例:
public class LoggingInterceptor implements MethodInterceptor {
private String regex = "*";
public void setMethodRegex(String regex){
this.regex = regex;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String methodName = invocation.getMethod().getName();
try {
Object result = invocation.proceed();
if (methodName.matches(regex))
// logging 方法, 参数与返回值
log(invocation.getMethod(), invocation.getArguments(), result);
return result;
} catch (Throwable throwable) {
if (methodName.matches(regex))
// logging 方法, 参数与异常
error(invocation.getMethod(), invocation.getArguments(), throwable);
throw throwable;
}
}
}
而后可借助JMX动态调整regex的值, 来实现运行时的配置. 当然, 肯定还会有其它更好的方法, 如果你知道了不妨分享一下.
小结
本文仅以Guice为例讨论如何改进我们日常开发中调试的问题, 其实这在Spring应用也同样能够实现的, 甚至其它应用AOP的场景都是可行的.
拓展开来, 不仅是Logging, 说不定验证(测试)也是可行的呢!
有句话不是这样说的吗, "思想有多远, 我们就能走多远!"
分享到:
相关推荐
aop日志记录。 4、调度 ====== Spring task, 可以查询已经注册的任务。立即执行一次任务。 5、缓存和Session =========== 注解redis缓存数据,Spring-session和redis实现分布式session同步(建议按功能模块...
aop日志记录。 4、调度 ====== Spring task, 可以查询已经注册的任务。立即执行一次任务。 5、缓存和Session =========== 注解redis缓存数据,Spring-session和redis实现分布式session同步(建议按功能模块...
本资源是一个基于SSM(Spring、Spring MVC、MyBatis)的汽车销售系统。它包含了完整的源代码、部署说明、演示视频以及源码介绍,旨在帮助开发者快速搭建一个功能完善的汽车销售管理系统。源码介绍:该汽车销售系统采用...
采用AOP技术处理事务管理和日志记录,增强系统的稳定性和调试能力。 数据库设计: 使用MySQL作为数据库管理系统,设计了学生信息表、成绩表、班级信息表、通知公告表等。 数据库设计合理,索引优化,确保查询效率和...
- 使用 AOP 为所有访问添加日志打印 ### Ver 3.0 - 使用 Spring IoC 管理对象 - 添加 database-mvc module,派生自 database module - web-mvc-context.xml 中添加 component-scan - jdbc 包均使用 @Component ...
- 新增AOP注解实现日志管理。 - 代码遵循阿里巴巴开发规范,利于开发者学习。 ## 技术选型 - 核心框架:Spring Boot 2.7.1 - 安全框架:Apache Shiro 1.9 - 持久层框架:MyBatis 3.5 - 定时器:Quartz 2.3 - ...
- 考虑到第一次制作,就不使用后端框架进行开发了,不过开发到后期,确实意识到使用Spring框架的AOP特性能更好地管理日志,MyBatis等ORM框架也能减少繁琐的JDBC的代码。 #### 1.2 查询相关网站,寻找一些基本思路...
9、日志:log4j2打印日志,业务日志和调试日志分开打印。同时基于时间和文件大小分割日志文件。10、QQ、微信、新浪微博第三方登录。11、工具类:excel导入导出,汉字转拼音,身份证号码验证,数字转大写人民币,FTP/...
ASP.NET 2.0基于SQLSERVER 2005的aspnetdb.mdf部署 ASP.NET 2.0 Security FAQs Asp.net 2.0功能体验,细节之Web控件(一) 隐藏控件 Asp.net 2.0功能体验,总体设计思想 Asp.net 2.0 WebPart使用经验点滴 革新:.NET...
只是小小的性能牺牲就能满足调试开发过程中的日志和分析需要,并确保将来的 部署顺利,一旦切换到部署模式则可以迅速提升性能。 ORM :简洁轻巧的ORM实现,配合简单的CURD以及AR模式,让开发效率无处不在。 数据库...