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");
}
}
}
Audit注解拦截器,用于自动记录日志
最新推荐文章于 2024-05-06 14:26:36 发布