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

来源:互联网 发布:炫浪网络社区百合 编辑:程序博客网 时间:2024/06/05 16:39
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@Auditpublic 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");        }    }}

原创粉丝点击