利用Perf4j 对java项目进行性能监控
Perf4j 可以对自定义监控范围的java代码进行日志记录,再经统计分析生成所需性能数据。Perf4j 提供了对常用日志工具log4j的扩展以方便与产品集成,它产生的性能数据可被用于生成可视化的性能图表。Perf4j是一款专门用于java服务器端代码计时,记录日志和监控结果的开源工具包。Per4j对常用日志工具包进行了扩展,能够将得到的原始性能数据进行统计并发布到可定制的输出源,如控制台、日志文件、JMX等。本文主要面向JAVA EE项目,来说明Perf4j集成log4j来进行性能的的分析。首先要在项目中加入perf4j和log4j的jar包。然后修改log4j.xml,使得支持perf4j <!-- perf4j的Logger及Appender --> <!-- Perf4J appenders --> <!-- AsyncCoalescingStatisticsAppender收集StopWatch的日志信息并传送到下游的文件appenders。 --> <appender name="CoalescingStatistics" class="org.perf4j.log4j.AsyncCoalescingStatisticsAppender"> <!-- TimeSlice配置多少时间间隔去做一次汇总写入文件中 默认值是 30000 ms ,设置为10分钟--> <param name="TimeSlice" value="3600000" /> <appender-ref ref="performanceAppenderCSV" /><appender-ref ref="performanceAppenderLOG" /> </appender> <!-- 把汇总的perf4j的日志信息写到${web.XxxWeb.root}/WEB-INF/logs/perfmance/perfmance.log文件中去 --> <appender name="performanceAppenderLOG" class="org.apache.log4j.FileAppender"> <param name="File" value="${web.XxxWeb.root}/WEB-INF/logs/perfmance/perfmance.log" /> <param name="Append" value="true" /> <!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 --> <param name="DatePattern" value="'.'yyyy-MM-dd'.log'" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%m%n" /> </layout> </appender> <appender name="performanceAppenderCSV" class="org.apache.log4j.FileAppender"> <param name="File" value="${web.XxxWeb.root}/WEB-INF/logs/perfmance/perfmance.csv" /> <param name="Append" value="true" /> <!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 --> <param name="DatePattern" value="'.'yyyy-MM-dd'.csv'" /> <layout class="org.perf4j.log4j.StatisticsCsvLayout"> <param name="ConversionPattern" value="%m%n" /> </layout> </appender> <!-- 11、perf4j日志记录器org.perf4j.TimingLogger 日志记录在${web.XxxWeb.root}/WEB-INF/logs/performance/目录下 --> <logger name="org.perf4j.TimingLogger" additivity="false"> <level value="INFO" /> <appender-ref ref="CoalescingStatistics" /> </logger>简单的测试代码如下: public long add(DmModel dmModel) { StopWatch stopWatch = new Slf4JStopWatch("dmService.add", "textArgs"); long maxbh = fydmbDao.getMaxDmbh(); maxbh++; dmModel.setDqbs("1"); dmModel.setXgsj(new Date()); dmModel.setBh(maxbh); dmbDao.addFyDm(DmConvertor.modelToFydm(dmModel)); stopWatch.stop(); return dmModel.getBh(); }运行junit测试即可看到如下结果:Performance Statistics 2013-09-06 19:00:00 - 2013-09-06 20:00:00Tag Avg(ms) Min Max Std Dev CountFydmService.addFy 181.0 181 181 0.0 1最后,介绍perf4j在实战中的应用----借助于aop,实现对服务性能的批量记录1、配置log4j,如上 2、编写Perf4jInterceptor代码package nju.software.XxxWeb.performance;import java.lang.reflect.Method;import java.util.Arrays;import java.util.HashMap;import java.util.Map;import org.perf4j.StopWatch;import org.perf4j.slf4j.Slf4JStopWatch;import org.springframework.aop.AfterReturningAdvice;import org.springframework.aop.MethodBeforeAdvice;/** * @author typ * */public class Perf4jInterceptor implements MethodBeforeAdvice, AfterReturningAdvice { /** * 用于存放不同服务的不同方法的stopWatch */ private Map<String, StopWatch> watches = new HashMap<String, StopWatch>(); // 在服务中得方法执行之前加入before方法中得如下逻辑,stopWatch开始统计 public void before(Method method, Object[] args, Object target) throws Throwable { String completeMethodName = getCompleteMethodName(target, method); StopWatch stopWatch; if (watches.containsKey(completeMethodName)) { stopWatch = watches.get(completeMethodName); stopWatch.start(); } else { stopWatch = new Slf4JStopWatch(completeMethodName, Arrays.toString(args)); watches.put(completeMethodName, stopWatch); } } // 在服务执行完毕,返回值之前加入如下afterReturning逻辑,stopWatch结束统计 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { String completeMethodName = getCompleteMethodName(target, method); // logger.info("After:"+completeMethodName); // 记录性能 if (watches.containsKey(completeMethodName)) { StopWatch stopWatch = watches.get(completeMethodName); stopWatch.stop(); } } /** * 根据目标对象与方法获取方法完整名称. * * @param target * 目标对象 * @param method * 方法 * @return 方法完整名称 */ private String getCompleteMethodName(Object target, Method method) { String className = ""; if (target != null) { className = target.toString(); int loc = className.indexOf("@"); if (loc >= 0) { className = className.substring(0, loc); } } return className + "." + method.getName(); }}以上类是附加逻辑,用于对拦截器拦截下的服务添加before和after逻辑。即对所有的服务进行拦截,在服务方法执行之前加入before方法内的逻辑,在服务返回之后加入afterReturning方法逻辑。 3、配置applicationContext.xml <!-- 定义Perf4jInterceptor的bean --> <bean id="perf4jInterceptor" class="nju.software.XxxWeb.performance.Perf4jInterceptor"> </bean> <!-- 定义拦截器,定义拦截的服务的列表,以及拦截器的名字,在服务执行之前和之后加入该拦截器中代码 --> <bean id="perf4jProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <!--<value>fydmbService</value> --> <value>dmService</value> <value>xzqdmbService</value> <value>gjService</value> <value>xxxService</value> <value>gjXxxGxService</value> <value>gzService</value> <value>sxgzService</value> <value>xtyhService</value> <value>xtjsService</value> <value>qxService</value> <value>xtdxService</value> <value>bmbService</value> <value>gzbwlService</value> <value>executionService</value> <value>repositoryService</value> <value>taskService</value> <value>aydmbService</value> <value>hytglService</value> <value>hytcyService</value> <value>sftjPzxxbService</value> </list> </property> <property name="interceptorNames"> <list> <value>perf4jInterceptor</value> </list> </property> </bean>至此为止,java EE的项目中就可以使用perf4j来统计各种service的性能了