log4j +spring aop+自定义注解方式输出日志
Posted 程序员的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j +spring aop+自定义注解方式输出日志相关的知识,希望对你有一定的参考价值。
log4j 基础
项目目录结构
log4j.properties是log4j的配置文件,文件名和所在目录(项目资源目录,或者根目录下)不建议修改,如需修改需要指明修改后的文件,log4j才能找到配置。
1、编写log4j的配置文件
# log4j 配置语法
# log4j.日志类别 = 日志级别, 日志名称[..]
# log4j.appender.日志名称.Option1 = value1
# …
# log4j.appender.日志名称.OptionN = valueN
# 日志类别有全局日志(rootLogger无论哪里输出日志都会调用它)和指定包日志(只有指定的包输出日志才会调用)
# 日志级别一般有:DEBUG、INFO、WARN、ERROR和FATAL
# Option常用的有 1、日志输出方式、日志输出格式
# 如:log4j.appender.日志名称 = 日志输出方式
# 日志输出方式有:
# org.apache.log4j.ConsoleAppender(控制台)
# org.apache.log4j.FileAppender(文件)
# org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
# org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
# org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
#
# 日志输出格式(layout)有:
# org.apache.log4j.htmlLayout(以HTML表格形式布局)
# org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
# org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
# org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
以下为log4j.properties示例
#全局log
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p [%c]\:%L Line - %m%n
#指定包log
log4j.logger.org.huangqsh.log4j.dao = ERROR,dao
log4j.appender.dao=org.apache.log4j.ConsoleAppender
log4j.appender.dao.layout=org.apache.log4j.PatternLayout
log4j.appender.dao.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} %p [%c]\:%L Line - %m%n
#邮件log
log4j.logger.org.huangqsh.log4j.service = ERROR,MAIL
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.BufferSize=10
log4j.appender.MAIL.From=huangqsh@aliyun.com #发件邮箱
log4j.appender.MAIL.SMTPHost=smtp.aliyun.com #发件邮箱所用服务器
log4j.appender.MAIL.Subject=Log4J Message
log4j.appender.MAIL.To=2443453005@qq.com #收件邮箱
log4j.appender.MAIL.SMTPUsername=huangqsh@aliyun.com #发件用户
log4j.appender.MAIL.SMTPPassword=******* #发件用户密码
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
Spring AOP
一、基本介绍:
(1)切面:通过@Aspect声明。切面是Aop的一个表达者,就是说aop的动作都是通过切面来定义实现的。比如里面定义了切入点和通知
(2)切入点:通过@Pointcut声明。定义程序中那些方法需要进行AOP拦截。表现形式就是定义切点表达式(表达式常用的是execution,除此之外还有within,this,bean,@annotation)
(3)通知: AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around(对于这些通知看名字也能猜出大致是什么意思)
(4)连接点: JoinPoint及其子接口ProceedingJoinPoint,这两个接口都是在切面中作为切入点的参数使用,可以获得被代理对象的信息。其中ProceedingJoinPoint 对象只用在@Around的切面方法中 。
二、添加一个切面配置类
@Aspect //声明这是一个切面类
@Component //注入到springIOC容器
public class AspectConfig {
/**
*
* 对add方法拦截
*/
// 第一个*表示匹配任意的方法返回值
// ..(两个点)表示零个或多个包,下面的第一个..表示service包及其子包
// 第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数
@Pointcut("execution(* org.huangqsh.aop.service..*.add*(..))")
public void checkAdd(){}
//声明前置通知
@Before("checkAdd()")
public void beforeAdd(){
System.out.println(">>>>>>>> add前执行..........");
}
//声明后置通知
@After("checkAdd()")
public void afterAdd(){
System.out.println(">>>>>>>> add后执行..........");
}
//声明环绕通知
@Around("checkAdd()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("进入方法---环绕通知");
Object o = pjp.proceed();
System.out.println("退出方法---环绕通知");
return o;
}
//声明正确执行通知
@AfterReturning("checkAdd()")
public void afterReturnAdd(){
System.out.println(">>>>>>>> add正确执行后执行..........");
}
//声明异常通知
@AfterThrowing("execution(public * org.huangqsh.aop..*.*(..))")
public void AfterThrowing() {
System.out.println("method AfterThrowing");
}
}
三、通知执行的优先级
进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After。
注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用,同时使用也没啥意义。
aop+log4j项目demo
项目结构:
1、pom文件中除了常规的spring配置就加上了log4j及邮件的依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
2、spring-core.xml中添加spring对aop的支持
3、编写切面类(注意切面中切点定义方式)
@Component
@Aspect
public class LogAspect {
//扫描指定包下面含有MyLog注解的方法
@Before("execution(* org.huangqsh.log.service..*.add*(..))&&@annotation(mylog)")
public void MethedBefore(JoinPoint joinpoint , MyLog mylog) {
//得到被代理方法的方法名
String className = joinpoint.getSignature().getDeclaringTypeName();
Logger aoplog = Logger.getLogger(className);
aoplog.setLevel(Level.DEBUG);
if(LogType.CONSOLE.equals(mylog.type())) {
//日志输出到控制台
aoplog.addAppender(new ConsoleAppender(new PatternLayout("AOP-%d{yyyy-MM-dd HH\\:mm\\:ss} %p [%c]\\:%L Line - %m%n")));
PrintLog(aoplog,mylog);
}else if(LogType.FILE.equals(mylog.type())){
String filename = "log/log.txt";
System.out.println(filename);
//文件日志
try {
aoplog.addAppender(new FileAppender(new PatternLayout("%d{yyyy-MM-dd HH\\:mm\\:ss} %p [%c]\\:%L Line - %m%n"), filename));
} catch (IOException e) {
e.printStackTrace();
}
PrintLog(aoplog,mylog);
}else {
//TODO 其他日志
}
}
/**
* 判断日志输出级别并输出
* @param log
* @param mylog
*/
private void PrintLog(Logger log, MyLog mylog) {
switch (mylog.level()) {
case DEBUG:
log.debug(mylog.message());
break;
case INFO:
log.info(mylog.message());
break;
case WARN:
log.warn(mylog.message());
break;
case ERROR:
log.error(mylog.message());
break;
default:
break;
}
}
}
4、定义一个自定义注解 @interface MyLog
5、编写业务类 LogService
@Service
public class LogService {
@MyLog( type = LogType.FILE,
level = LogLevel.INFO,
message = "这是一条日志",
remark = "remark"
)
public void add() {
System.out.println("LogService method ---->add()");
}
public void delete() {
System.out.println("LogService method ---->delete()");
}
}
6、编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/spring-config/spring-core.xml"})
public class TestLog {
@Autowired
private LogService logService;
@Test
public void test1() {
logService.delete();
}
@Test
public void test2() {
logService.add();
}
}
最后就是测试了。
总结一下这个demo,主要是理解aop中切点和连接点两个概念。切点和连接点都在切面中存在。
切点(Pointcut)是指代理对象与被代理对象相交的部分,通常是一个方法,切点需要用表达式进行匹配。
连接点(JoinPoint)是对被代理对象的一个封装。可以从连接点中得到被代理对象的信息。
https://github.com/huangqsh/logFrame.git
如有问题,希望指正
以上是关于log4j +spring aop+自定义注解方式输出日志的主要内容,如果未能解决你的问题,请参考以下文章