方法级别的java日志输出控制(一)
前面学习了log4j以及log4j2 java日志控制,通过配置可以实现java日志的输出级别,输出位置,输出格式等日志控制,甚至可通过配置控制不同java类的日志输出方式。在大型web系统中,这些基本的日志控制可能还是不够的。由于日志输出是非常耗费资源的事情,特别在大型应用特定场景中。所以一般情况下只做warn或者只做error级别的日志输出。然而,我们可能需要更加精细的日志控制,比如说想控制每一个方法的输出级别,同时想在打印错误日志的时候把某一个方法的输入参数也打印出来,以便更方便的查找问题的所在。本文将介绍通过配置文件和AOP技术控制方法级别的日志输出。
先看LogActive日志类:
package com.lf.testLog4j.aop; import com.google.gson.Gson; import com.lf.testLog4j.Util.PropertiesUtil; import com.lf.testLog4j.common.CommonLogUtil; import com.lf.testLog4j.domain.ConfigLog; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.aspectj.lang.JoinPoint; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * Created by lufei3 on 2015/7/14. */ public class LogActive { private static final Logger logger = LogManager.getLogger(LogActive.class); //前切入方法,该方法会在所有切入点之前执行。 public void before(JoinPoint jp){ //从配置文件读取配置信息 Properties prop = PropertiesUtil.getProperties("/ConfigCenter.properties"); Map<String, String> map = new HashMap<String, String>((Map) prop); Gson gson = new Gson(); ConfigLog configLog = null; String cName = jp.getThis().toString(); Object[] args = jp.getArgs(); //获得切入点参数列表 String className = cName.substring(cName.lastIndexOf(".")+1,cName.lastIndexOf("@")); String methodName = jp.getSignature().getName(); //获得方法名 String key = className + "." + methodName; //获取配置信息 String configValue = map.get(key); try { configLog = gson.fromJson(configValue,ConfigLog.class); } catch (Exception e) { logger.error("Gson Format Exception!! logLevel:{}",configValue); e.printStackTrace(); return; } if(configLog == null) { return; } //通过配置信息控制日志输出 String logLevel = configLog.getLevel(); int offset = configLog.getOn(); if(StringUtils.isBlank(logLevel)){ logger.warn("method:{} log not config", key); return; } if(CommonLogUtil.isInfoEnable(logLevel,offset)) { logger.info("====Method:{};", key); if(args.length <=0){ logger.info("===={}方法没有参数", methodName); } else{ for(int i=0; i<args.length; i++){ logger.info("====参数 {}:{} ", (i + 1), args[i]); } } } } public void after(){ logger.info("调用完毕!!"); } }
下面看具体配置信息ConfigCenter.properties:
TestLogAOP.test={"level":"TRACE","on":0} TestLogAOP.test2={"level":"TRACE","on":1}
配置说明执行test()方法的日志开关是0,即不打印日志;test2方法的日志级别是trace,开关为1,即可以打印trace级别的日志。而trace比info级别高,所以在LogActive 方法里info级别的日志是可以打印的。而如果level为warn或者error则info级别的日志就不会输出。
CommonLogUtil类是用来确定是否可以输出info级别的日志:
package com.lf.testLog4j.common; /** * Created by lufei3 on 2015/7/20. */ public class CommonLogUtil { public static boolean isInfoEnable(String logLevel,int offset) { if (offset == 1&&(logLevel.equalsIgnoreCase(LogLevelEnum.INFO_LEVEL.getLogLevel()) ||logLevel.equalsIgnoreCase(LogLevelEnum.DEBUG_LEVEL.getLogLevel()) ||logLevel.equalsIgnoreCase(LogLevelEnum.TRACE_LEVEL.getLogLevel()))){ return true; } return false; } }
Spring配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" default-autowire="byName"> <bean id="logActive" class="com.lf.testLog4j.aop.LogActive"></bean> <!--将日志类注入到bean中。--> <bean id="testLogAOP" class="com.lf.testLog4j.service.TestLogAOP"></bean> <aop:aspectj-autoproxy proxy-target-class="true"/> <aop:config> <!--拦截service层的所有方法--> <aop:pointcut id="service" expression="execution(* com.lf.testLog4j.service.*.*(..))"/> <aop:aspect id="log" ref="logActive"> <aop:before pointcut-ref="service" method="before"/> <aop:after pointcut-ref="service" method="after"/> </aop:aspect> </aop:config> </beans>
TestLogAOP类:
package com.lf.testLog4j.service; import org.springframework.stereotype.Component; /** * Created by lufei3 on 2015/7/14. */ @Component public class TestLogAOP { public void test(){ System.out.println("测试类的test方法被调用"); } public void test2() { System.out.println("测试2的方法被调用!"); } }
最后main方法:
package com.lf.testLog4j; import com.lf.testLog4j.service.TestLogAOP; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by lufei3 on 2015/7/14. */ public class LogAOPMain { @Autowired public static void main(String... args) { ApplicationContext act = new ClassPathXmlApplicationContext("spring/spring-config.xml"); TestLogAOP testLogAOP = (TestLogAOP) act.getBean("testLogAOP"); testLogAOP.test(); testLogAOP.test2(); } }
输出:
测试类的test方法被调用 13:43:35.673 [main] INFO com.lf.testLog4j.aop.LogActive - 调用完毕!! 13:43:35.713 [main] INFO com.lf.testLog4j.aop.LogActive - ====Method:TestLogAOP.test2; 13:43:35.713 [main] INFO com.lf.testLog4j.aop.LogActive - ====test2方法没有参数 测试2的方法被调用! 13:43:35.714 [main] INFO com.lf.testLog4j.aop.LogActive - 调用完毕!!
可以看出日志根据配置文件打印了TestLogAOP.test2方法调用前后的日志输出。