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+自定义注解方式输出日志的主要内容,如果未能解决你的问题,请参考以下文章

spring AOP自定义注解方式实现日志管理

Spring自定义注解以aop注入方式实现延时双删功能

AOP中获取自定义注解的参数值

spring AOP自定义注解 实现日志管理

SpringBoot 中的 Aop + 自定义注解

SpringBoot 中的 Aop + 自定义注解