使用自定义@Logger注解实现日志记录

前言:曾一直想拥有自己的的博客,将自己对java的感悟记录下来,由于时间原因一直没有行动,这是我的第一次随笔,好的话关注一下,谢谢。


创建@Logger注解的作用

1.封装一些常用的日志记录以及统计功能
2.可以自定义记录方法,解耦,脱离传统的业务方法里面记录日志

@Logger注解自定义之前我们需要用到以下技术:

1.java反射机制 2.动态代理 3.Spring AOP(实际aop的实现可以归结动态代理)

对以下技术做一个简单的概括:

**(1).java反射:
反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
(2)动态代理:动态代理的实现也是根据反射实现,可以说优秀的代码设计,都离不开反射。

(3)Spring Aop:spring框架提供的其中之一模块就是Aop,它是对OOP的一个补充,OOP是面向对象,它则是面向切面。

Aop的应用场景:

流量统计 日志记录 程序监控 归结一点:一切和业务逻辑无关的,都可以用AOP来完成。**

说了这么多,接下来我们具体来使用自定义注解,将以上的类容全部运用一下:
(该注解目前只实现了一些最基本的记录,后面会继续完善)
首先定义一个@Logger注解:

package com.ph.licenceserver.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 日志注解类,提供简单的日志操作功能
 * @author LT
 *
 */
@Target({PARAMETER,METHOD})
 @Retention(RUNTIME)
 @Documented
public @interface Logger {
    String value() default "";//设置打印信息
    String type() default Constant.INFO;//设置打印类型
    boolean isPrintTimeStamp() default true;//是否答应timestamp类型的时间,主要用于性能测试
    boolean EnableIpAddress() default true;//是否开启ip记录功能
    boolean EnableURL() default true;//是否开启url记录功能
    boolean EnableRequestParmas() default true;//请求参数记录
    boolean EnableSigar() default false;//开启监控功能
}

创建一个注解的处理类(Handle):

package com.ph.licenceserver.AnnotationHandle;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.ph.licenceserver.Annotation.Logger;

/**
 * 注解处理类
 * 
 * @author LT
 * 
 */
public class LoggerHandles {
    String value = "";
    String type = "INFO";
    boolean timestampe;
    boolean address;
    boolean url;
    boolean sigar;
    boolean requestParmas;
    JoinPoint joinPoint;
    org.apache.log4j.Logger logger;

    public void setLoggerInfo(Class clazz,JoinPoint joinPoint) {
        this.joinPoint=joinPoint;
        logger = org.apache.log4j.Logger.getLogger(clazz);//获取日志对象
        Method[] methods = clazz.getMethods();获取所有切入点所在类的所有方法
        for (Method method : methods) {//遍历切入方法所在类的所有成员方法,是否有Logger.class类型的注解并获取它的值
            if (method.isAnnotationPresent(Logger.class)) {
                value = method.getAnnotation(Logger.class).value();
                type = method.getAnnotation(Logger.class).type();
                timestampe = method.getAnnotation(Logger.class)
                        .isPrintTimeStamp();
                address = method.getAnnotation(Logger.class).EnableIpAddress();
                url = method.getAnnotation(Logger.class).EnableURL();
                sigar = method.getAnnotation(Logger.class).EnableSigar();
                requestParmas = method.getAnnotation(Logger.class)
                        .EnableRequestParmas();
            }
        }

    }

    public void LoggerHandle() {

        if ("ERROR".equals(type)) {
            logger.error(value);
            setLoggerMessage(type,joinPoint);

        } else if ("INFO".equals(type))
            logger.info(value);
        setLoggerMessage(type,joinPoint);

    }

    public void setLoggerMessage(String value,JoinPoint joinPoint) {
        System.out.println(value);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Method method;
        /**
        利用反射,实现方法的动态获取,如果这里不用method.invoke()方法,logger的info和error方法就要进行大量的判断
        */
        try {
            method = this.logger.getClass().getMethod(value.toLowerCase(),Object.class);
            method.invoke(this.logger, value);
            if (timestampe) {
                method.invoke(this.logger,"当前时间戳:" + System.currentTimeMillis());
            }
            if (address) {
                method.invoke(this.logger,"IpAddress:" + request.getRemoteAddr());
            }
            if (url) {
                method.invoke(this.logger,"URL:" + request.getRequestURL().toString());
            }
            if (requestParmas) {
                method.invoke(this.logger,"HTTP_METHOD:"+request.getMethod());
                method.invoke(this.logger,"CLASS_METHOD : "
                        + joinPoint.getSignature().getDeclaringTypeName() + "."
                        + joinPoint.getSignature().getName());
                @SuppressWarnings("unchecked")
                Enumeration enu = request.getParameterNames();
                String message = "";
                while (enu.hasMoreElements()) {
                    String paraName = (String) enu.nextElement();
                    message = (paraName + ": " + request.getParameter(paraName));
                }
                method.invoke(this.logger,message);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();//这里就直接打印,不进行处理了,明白就行
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        /*
         * if(timestampe){ logger.error("当前时间戳:"+System.currentTimeMillis()); }
         * if(address){ logger.error("IpAddress:"+request.getRemoteAddr()); }
         * if(url){ logger.error("URL:"+request.getRequestURL().toString()); }
         * if(requestParmas){
         * 
         * @SuppressWarnings("unchecked") Enumeration enu =
         * request.getParameterNames(); String message=""; while
         * (enu.hasMoreElements()) { String paraName = (String)
         * enu.nextElement(); message=(paraName + ": " +
         * request.getParameter(paraName)); } logger.error(message); }
         */
    }
    public String DateNow(){
        return new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    }

}

创建spring Aop:

package com.ph.licenceserver.Aop;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.ph.licenceserver.AnnotationHandle.LoggerHandles;

@Aspect
@Component
public class LoggingAspect {
    private Logger logger = Logger.getLogger(this.getClass());
    // 对以下com.web.controller.Controller类中的所有方法进行切入
    public static Long starttime = null;

    @Pointcut("execution(public * com.ph.licenceserver.web.*.*(..))")//利用表达式定义需要切入的点
    private void method() {
    }
    @After("method()")
    //在切入方法执行后执行该方法
    public void recordLog(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String targetName = joinPoint.getTarget().getClass().getName();//获取连接点躲在目标对象的类信息的类名
        String methodName = joinPoint.getSignature().getName();//获取连接点的方法签名对象的方法名
        Class targetClass;
        try {
            targetClass = Class.forName(targetName);//获得类名对应的类
            Object[] arguments = joinPoint.getArgs();//或得连接点方法传入的参数
            Method[] methods = targetClass.getMethods();//得到方法名
             for (Method method : methods) {  
                                 if (method.getName().equals(methodName)) {  
                                   Class[] clazzs = method.getParameterTypes();  
                                    if (clazzs.length == arguments.length) {
                                        try {
                                            LoggerHandles handle=LoggerHandles.class.newInstance();
                                            handle.setLoggerInfo(joinPoint.getTarget().getClass(),joinPoint);
                                            handle.LoggerHandle();
                                        } catch (InstantiationException e) {
                                            e.printStackTrace();
                                        } catch (IllegalAccessException e) {
                                            e.printStackTrace();
                                        }
                                     break;  
                                    } 
                                 } 
             }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    @AfterReturning("method()")
    public void doAfterReturning(JoinPoint joinPoint) {
        Long time = System.currentTimeMillis() - starttime;
        logger.info("用时:" + time.toString() + "毫秒");

    }

}

使用自定义注解运用在之前定义的controller类上(以下涉及到隐私内容所以我将方法体中的内容去掉了,不影响日志记录结果)

@RequestMapping("/all.do")
    @ResponseBody
    @com.ph.licenceserver.Annotation.Logger(value="执行了logger注解",type="INFO")
    public PageUtil getCountAll(@RequestBody Query query,HttpServletRequest request){

}

来查看一下crontroller接受请求之后有@Logger注解有没有输出相应类容(刷新之后):
使用自定义@Logger注解实现日志记录_第1张图片

日志后台统计信息:
使用自定义@Logger注解实现日志记录_第2张图片

以上就是使用创建注解的全过程,可能有人要问了,为什么要使用这个注解,直接利用spring AOP来定义切面,在@Before注解的方法或@Around注解所在方法中直接实现日志记录不是也可以吗?

确实可以,但是使用注解的方式我们可以配置我们想配置的方法,只需要使用@Pointcut定义切入点,在切入点所在方法之内配置@Logger注解,该注解就会生效。利用xml配置的形式配置aop更方便,不用操作具体的类

可能也有人会问你所用实现的注解,注解的实现,是利用sping aop的动态代理,为何不自己脱离spring AOP自己利用动态代理来实现注解的功能呢,这样岂不解耦吗?

是的,脱离spring AOP来自己实现注解功能更好,所以在下一篇文章中,我将会分析一下spring AOP底层动态代理的实现,以及如何脱离AOP来实现该注解。

                                    本文中有什么不妥的地方希望大家指出
                                    QQ:1336576735  欢迎大家加我,共同学习

你可能感兴趣的:(java)