Audit注解拦截器,用于自动记录日志


import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.faces.context.FacesContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * {@literal @}Audit注解拦截器,用于自动记录日志
 *
 * 
 */
@Interceptor
@Audit
public class AuditInteceptor implements Serializable {

    private static final long serialVersionUID = 3173066019141998118L;

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private static String pattern = "yyyy-MM-dd HH:mm:ss";
    
    private static ConcurrentLinkedQueue<LogTask> pendingTaskList;
    
    private static ScheduledExecutorService schedulerTaskExecutor;
    
    static {
        pendingTaskList = new ConcurrentLinkedQueue<>();
        schedulerTaskExecutor = Executors.newSingleThreadScheduledExecutor();
        
        schedulerTaskExecutor.scheduleAtFixedRate(new Runnable() {
            
            @Override
            public void run() {
                try {
                    for (LogTask task; (task = pendingTaskList.poll()) != null;) {
                        task.run();
                    }
                } catch (Exception e) {
                }
                
            }
        } ,0 , 100, TimeUnit.MILLISECONDS);
    }

    //@PreDestroy
    protected void destroy(InvocationContext context) {
        if (schedulerTaskExecutor != null) {
            schedulerTaskExecutor.shutdown();
            schedulerTaskExecutor = null;
        }
        if (pendingTaskList != null) {
            pendingTaskList.clear();
            pendingTaskList = null;
        }
    }
    
    class LogTask implements Runnable {
        
        private Level logLevel;
        private String format;
        private Object[] arguments;
        
        public LogTask(Level logLevel, String format, Object... arguments) {
            this.logLevel = logLevel;
            this.format = format;
            this.arguments = arguments;
        }
        
        @Override
        public void run() {
            switch (logLevel) {
                case TRACE:
                    logger.trace(format, arguments);
                    break;
                case DEBUG:
                    logger.debug(format, arguments);
                    break;
                case INFO:
                    logger.info(format, arguments);
                    break;
                case WARN:
                    logger.warn(format, arguments);
                    break;
                case ERROR:
                    logger.error(format, arguments);
                    break;
            }
        }
    }

    /**
     * 加了@Audit注解的类或方法在执行前会先进这个方法,记录调用日志。目前记录的信息包括:
     * <ol>
     * <li>被调用方法所在类的类名</li>
     * <li>方法名</li>
     * <li>参数列表</li>
     * <li>调用时间</li>
     * <li>用户ID</li>
     * </ol>
     * 
     * @param context
     *            被执行方法的上下文
     * @return
     * @throws Exception
     */
    @AroundInvoke
    public Object audit(InvocationContext context) throws Exception {

        // 根据方法或类上的注解决定日志的等级
        Level logLevel = this.determineLogLevel(context);

        // 根绝日志的等级判断日志是否会被输出,由外部配置文件决定
        boolean currentLevelEnabled = this.isLevelEnabled(logLevel);

        try {
            // 实际调用被拦截的方法
            Object ret = context.proceed();

            if (currentLevelEnabled) {
                pendingTaskList.offer(new LogTask(logLevel, "调用成功: {}, 详情: {}", context.getMethod().toString(), buildLogDetailObj(context).toString(4)));
            }
            
            return ret;
        } catch (Exception e) {
            pendingTaskList.offer(new LogTask(Level.ERROR, "调用异常: {}, 详情: {}", context.getMethod().toString(), buildLogDetailObj(context).toString(4)));
            throw new LogException(e);
        }

    }

    /**
     * 根据方法或类上的注解决定日志的等级
     * 
     * @param context
     *            被执行方法的上下文
     * @return {@link com.taiji.framework.cdi.annotation.audit.Level Level}对象,代表日志级别
     * @throws IllegalAccessException
     */
    private Level determineLogLevel(InvocationContext context) throws IllegalAccessException {

        // 类名
        Class<?> clazz = context.getMethod().getDeclaringClass();

        // 方法名
        Method method = context.getMethod();

        /*
         * 根据 Method 上的 Annotations 设置 log 等级
         */
        Annotation methodAuditAnnotation = method.getAnnotation(Audit.class);
        Annotation methodAuditLevelAnnotation = method.getAnnotation(AuditLevel.class);
        Annotation classAuditAnnotation = clazz.getAnnotation(Audit.class);

        Level logLevel = Level.INFO;

        /*
         * if method do not have Audit Annotation, then set log level to class's Audit level
         */
        if (methodAuditAnnotation != null) {
            logLevel = ((Audit) methodAuditAnnotation).level();
        } else if (classAuditAnnotation != null) {
            logLevel = ((Audit) classAuditAnnotation).level();
        } else {
            // 方法和类上都没有Audit注解,那是怎么进这里来的?
            throw new IllegalAccessException();
        }

        /*
         * 决定日志Level优先级:@AuditLevel on method > @Audit on method > @Audit on class
         */
        if (methodAuditLevelAnnotation != null) {
            logLevel = ((AuditLevel) methodAuditLevelAnnotation).value();
        }
        return logLevel;
    }

    /**
     * 生成详细的调用信息
     * 
     * @param context
     *            被执行方法的上下文
     * @return
     */
    private JSONObject buildLogDetailObj(InvocationContext context) {

        JSONObject detailObj = new JSONObject();

        // 方法名
        Method method = context.getMethod();

        // 参数数组
        Object[] parameters = context.getParameters();

        User user = null;
        try {
            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            if (session != null) {
                user = (User) session.getAttribute("session_user");
            }

        } catch (Exception e) {
        }

        /*
         * 将调用栈中以"com.taiji"开头的类按顺序收集
         */
        List<String> classNameList = new ArrayList<String>();
        StackTraceElement[] currentStackTraces = Thread.currentThread().getStackTrace();
        for (StackTraceElement ste : currentStackTraces) {
            if (ste.getClassName().startsWith("com.taiji")) {
                classNameList.add(ste.toString());
            }
        }

        /*
         * build parameters info
         */
        JSONArray paramArray = new JSONArray();
        for (Object eachParamObj : parameters) {
            if ((eachParamObj != null) && eachParamObj.getClass().isArray()) {
                JSONArray subArray = new JSONArray();
                AuditInteceptor.extractArray(eachParamObj, subArray);
                paramArray.put(subArray);
            } else if (eachParamObj != null && eachParamObj instanceof JsonModel) {
                // detect is JsonModel or not
                JSONObject obj = ((JsonModel) eachParamObj).toJson();
                obj.put("class", eachParamObj.getClass().getName());
                paramArray.put(obj);
            } else {
                paramArray.put(eachParamObj);
            }
        }

        /*
         * build stack info
         */
        JSONArray stackArray = new JSONArray();
        for (int i = 2; i < classNameList.size(); i++) {
            stackArray.put(classNameList.get(i));
        }

        detailObj.put("Params", paramArray);
        detailObj.put("Stack", stackArray);
        detailObj.put("Method", method.toGenericString());
        detailObj.put("Time", DateFormatUtils.format(new Date(), pattern));
        detailObj.put("User", user != null ? user.getDisplayId() : "unknown");
        return detailObj;
    }

    /**
     * 根绝日志的等级判断日志是否会被输出,由外部配置文件决定
     * 
     * @param logLevel
     * @return
     */
    private boolean isLevelEnabled(Level logLevel) {
        boolean currentLevelEnabled = false;

        switch (logLevel) {
            case TRACE:
                currentLevelEnabled = this.logger.isTraceEnabled();
                break;
            case DEBUG:
                currentLevelEnabled = this.logger.isDebugEnabled();
                break;
            case INFO:
                currentLevelEnabled = this.logger.isInfoEnabled();
                break;
            case WARN:
                currentLevelEnabled = this.logger.isWarnEnabled();
                break;
            case ERROR:
                currentLevelEnabled = this.logger.isErrorEnabled();
                break;
        }
        return currentLevelEnabled;
    }

    /**
     * 深度遍历数组
     * 
     * @param arrayObject
     * @param ret
     *            用于接收结果
     */
    private static void extractArray(Object arrayObject, JSONArray ret) {
        if ((arrayObject != null) && arrayObject.getClass().isArray()) {
            int arrayLength = Array.getLength(arrayObject);
            for (int i = 0; i < arrayLength; i++) {
                Object eachObj = Array.get(arrayObject, i);
                if ((eachObj != null) && eachObj.getClass().isArray()) {
                    JSONArray subArray = new JSONArray();
                    AuditInteceptor.extractArray(eachObj, subArray);
                    ret.put(subArray);
                } else if (eachObj != null && eachObj instanceof JsonModel) {
                    // detect is JsonModel or not
                    JSONObject obj = ((JsonModel) eachObj).toJson();
                    obj.put("class", eachObj.getClass().getName());
                    ret.put(obj);
                } else {
                    ret.put(eachObj);
                }
            }
        } else {
            throw new IllegalArgumentException("not a array");
        }
    }

}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值