日志切面
@Aspect @Component public final class LogAspect { private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class); /** * 线程本地变量 */ private ThreadLocal<String> traceId = new ThreadLocal<String>(); /** * 切入点,方法调用记录日志 */ @Pointcut("@annotation(com.huawei.it.hwpay.common.log.MethodInvoke)") public void methodInvokeAspect() { } /** * 前置通知 * * @param joinPoint 切点 */ public void doBefore(JoinPoint joinPoint) { LOGGER.info("前置通知"); // 获得日志注解 MethodInvoke methodLog = giveAnnotation(joinPoint); handleLog(methodLog, joinPoint, null); } /** * 后置通知 * * @param joinPoint 切点 * @param ret ret */ // @AfterReturning(pointcut="methodInvokeAspect()",argNames = "joinPoint,retVal",returning = "retVal") // @AfterReturning(value = "methodInvokeAspect()", returning = "ret") public void doAfter(JoinPoint joinPoint, Object ret) { LOGGER.info("后置通知"); // 打印返回参数 if (ret != null) { try { // 获得日志注解 MethodInvoke methodLog = giveAnnotation(joinPoint); LOGGER.info("方法调用返回参数:", ret.getClass().getName(), methodLog.format() == LogFormat.JSON ? JsonUtil.formatJson(ret) : ret.toString()); } catch (Exception e) { LOGGER.warn("打印返回参数异常"); } } } /** * 返回异常例外通知 * * @param joinPoint 切点 * @param e 异常信息 */ @AfterThrowing(value = "methodInvokeAspect()", throwing = "e") public void doAfter(JoinPoint joinPoint, Exception e) throws Throwable { LOGGER.info("[traceId:", traceId.get(), "] Abnormal method call: ", getMethodName(joinPoint), e); traceId.remove(); } /** * 最终通知 * * @param joinPoint 切点 */ // @After("methodInvokeAspect()") public void after(JoinPoint joinPoint) { LOGGER.info("最终通知"); } /** * Log Starting * * @param methodLog * @param methodName */ private void logStarting(MethodInvoke methodLog, String methodName) { String preTraceidMsg = ""; if (methodLog.type() == ServiceType.DUBBO) { preTraceidMsg = "[dubbo-remote]"; } else if (methodLog.type() == ServiceType.REST) { preTraceidMsg = "[rest-remote]"; } else { preTraceidMsg = "[local]"; } log(methodLog.logLevel(), preTraceidMsg + "[traceId-", traceId.get(), "][service-", methodLog.serviceClass() == MethodInvoke.class ? methodLog.service() : methodLog.serviceClass(), "][method-", methodLog.method(), "][invoke-", methodName, "]Starting..."); } /** * Log end * * @param methodLog * @param methodName * @param timeCost */ private void logEnd(MethodInvoke methodLog, String methodName, long timeCost) { String preTraceidMsg = ""; if (methodLog.type() == ServiceType.DUBBO) { preTraceidMsg = "[dubbo-remote]"; } else if (methodLog.type() == ServiceType.REST) { preTraceidMsg = "[rest-remote]"; } else { preTraceidMsg = "[local]"; } log(methodLog.logLevel(), preTraceidMsg + "[traceId-", traceId.get(), "][service-", methodLog.serviceClass() == MethodInvoke.class ? methodLog.service() : methodLog.serviceClass(), "][method-", methodLog.method(), "][invoke-", methodName, "][time-elapsed:", timeCost, "(ms)]End."); } /** * 环绕通知 * * @param joinPoint 切点 * @return 返回参数 * @throws Throwable 异常 */ @Around("methodInvokeAspect()") public Object doBasicProfiling(ProceedingJoinPoint joinPoint) throws Throwable { Date inDate = new Date(); boolean startFlag = false; try { if (traceId.get() == null) { traceId.set(DateUtil.formatDatetime14(new Date()) + RandomGenerator.getRandomStrByLength(8)); startFlag = true; } // 获得日志注解 MethodInvoke methodLog = giveAnnotation(joinPoint); String methodName = getMethodName(joinPoint); // Log starting logStarting(methodLog, methodName); // 前置 handleLog(methodLog, joinPoint, null); // 执行 Object object = joinPoint.proceed(); // 执行该方法 // 后置 // 打印返回参数 if (object != null && methodLog.isPrint()) { try { log(methodLog.logLevel(), "[traceId:", traceId.get(), "] return parameters: ", object.getClass().getName(), methodLog.format() == LogFormat.JSON ? JsonUtil.formatJson(object) : object.toString()); } catch (Exception e) { LOGGER.warn("Print the return parameter error."); } } // 统计耗时时间 Date outDate = new Date(); // log end logEnd(methodLog, methodName, outDate.getTime() - inDate.getTime()); return object; } catch (HwPayException e) { throw new HwPayException(e); } catch (Throwable e) { LOGGER.warn("Logging failure:", e.getMessage()); throw new HwPayException(e); } finally { if (startFlag) { traceId.remove(); } } } /** * 处理日志 * * @param methodLog 日志注解 * @param joinPoint 切点 * @param e 异常信息 */ private void handleLog(MethodInvoke methodLog, JoinPoint joinPoint, Exception e) { try { // 获得注解 if (methodLog == null) { return; } // 打印方法信息 /* * log(methodLog.logLevel(),"[traceId:", traceId.get(),"] ", getMethodName(joinPoint), * " The method name: ", methodLog.method(), ", the serviceName=", methodLog.service(), * ", desc=", methodLog.desc()); */ if (!methodLog.isPrint()) { return; } // 打印请求参数 Object[] args = joinPoint.getArgs(); for (Object arg : args) { if (arg == null) { continue; } log(methodLog.logLevel(), "[traceId:", traceId.get(), "] Request parameters:", arg.getClass().getName(), methodLog.format() == LogFormat.JSON ? JsonUtil.formatJson(arg) : arg.toString()); } } catch (Exception exp) { LOGGER.warn("Exception information:[traceId:", traceId.get(), "] ", exp.getMessage()); } } /** * 是否存在注解,如果存在就记录日志 * * @param joinPoint 切入点 * @return 方法调用注解对象 * @throws Exception 异常 */ private MethodInvoke giveAnnotation(JoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); return method.getAnnotation(MethodInvoke.class); } /** * 获取方法名 * * @param joinPoint 切入点 * @return 方法名 */ private String getMethodName(JoinPoint joinPoint) { String className = joinPoint.getTarget().getClass().getName(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); if (method != null) { return className + "." + method.getName() + "()"; } return className; } /** * 记录日志信息 * * @param logLevel 日志级别 * @param objects 参数列表 */ private void log(LogLevel logLevel, Object... objects) { switch (logLevel) { case DEBUG: LOGGER.debug(objects); break; case INFO: LOGGER.info(objects); break; case WARN: LOGGER.warn(objects); break; case ERROR: LOGGER.error(objects); break; default: LOGGER.all(objects); break; } } }
public enum LogFormat { /** * Json格式输出参数日志 */ JSON, /** * toString格式输出参数日志 */ TO_STRING, ; }
public class Logger extends LoggerBase { /** * 构造函数 */ public Logger() { super(Logger.class); } /** * 构造函数 */ public Logger(String cls) { super(cls); } @SuppressWarnings("unchecked") public Logger(Class cls) { super(cls); } }
class LoggerBase { private static final String NA = "UnknowClass"; private static final org.slf4j.Logger ACCESS_LOGGER; private static final org.slf4j.Logger PERF_LOGGER; private static ThreadLocal<Long> tlAccessStart = new ThreadLocal<Long>(); private static final String NULL = "NULL"; public static final String LINE_SEP = System.getProperty("line.separator"); public static final int LINE_SEP_LEN = LINE_SEP.length(); public static final String FQCN = Logger.class.getName(); private final org.slf4j.Logger delegate; static { ACCESS_LOGGER = LoggerFactory.getLogger(System.getProperty("logger.access.name", "ACCESS")); PERF_LOGGER = LoggerFactory.getLogger(System.getProperty("logger.performance.name", "PERFORMANCE")); } /** * 构造函数 * * @param cls */ public LoggerBase(String cls) { delegate = LoggerFactory.getLogger(cls); } @SuppressWarnings("unchecked") public LoggerBase(Class clazz) { if (clazz == com.huawei.it.hwpay.common.log.LoggerFactory.class || clazz == com.huawei.it.hwpay.common.log.Logger.class) { final String clazzName = clazz.getName() + "."; String cls = getCallingClassName(clazzName); delegate = LoggerFactory.getLogger(cls); } else { delegate = LoggerFactory.getLogger(clazz); } } /** * isTraceEnabled * * @return TraceEnabled */ public boolean isTraceEnabled() { return delegate.isTraceEnabled(); } /** * isDebugEnabled * * @return isDebugEnabled */ public boolean isDebugEnabled() { return delegate.isDebugEnabled(); } /** * isInfoEnabled * * @return isInfoEnabled */ public boolean isInfoEnabled() { return delegate.isInfoEnabled(); } /** * isWarnEnabled * * @return isWarnEnabled */ public boolean isWarnEnabled() { return delegate.isWarnEnabled(); } /** * isErrorEnabled * * @return isErrorEnabled */ public boolean isErrorEnabled() { return delegate.isErrorEnabled(); } /** * 标记access的开始 */ public static void accessStart() { tlAccessStart.set(Long.valueOf(System.nanoTime())); } /** * 记录access日志 * * @param args */ public static void access(Object... args) { if (args == null) { args = new Object[] {NULL}; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.length; i++) { if (i > 0) { sb.append(','); } sb.append(unull(args[i])); } // 如果标记了开始时间,则在access最后追加耗时 Long startNS = tlAccessStart.get(); if (startNS != null) { sb.append(',').append(Formatter.formatNS(System.nanoTime() - startNS.longValue())).append("ns"); tlAccessStart.remove(); } ((LocationAwareLogger) ACCESS_LOGGER).log(null, FQCN, LocationAwareLogger.TRACE_INT, null, null, null); } private static Object unull(Object obj) { return obj == null ? NULL : JsonUtil.formatJson(obj); } private static String concat(Object... objects) { StringBuilder sb = new StringBuilder(); concat(sb, objects); return sb.toString(); } private static void concat(StringBuilder sb, Object... objects) { for (int i = 0; i < objects.length; i++) { if ((i + 1) == objects.length && objects[i] instanceof Throwable) { break; } if (objects[i] != null && objects[i].getClass().isArray()) { concat(sb, (Object[]) objects[i]); } else { sb.append(unull(objects[i])); } } } private String logf(String format, Object... args) { StringBuilder sb = new StringBuilder(); java.util.Formatter formatter = new java.util.Formatter(sb); formatter.format(format, args); return sb.toString(); } /** * allformat * * @param format format * @param args args */ public void allf(String format, Object... args) { if (delegate.isTraceEnabled()) { all(logf(format, args)); } } /** * all log format * * @param objects objects */ public void all(Object... objects) { if (delegate.isTraceEnabled()) { if (objects[objects.length - 1] instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.TRACE_INT, concat(objects), null, (Throwable) objects[objects.length - 1]); } else { String obj = concat(objects); ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj, null, null); } } } /** * all log format * * @param obj obj */ public void all(Object obj) { if (!delegate.isTraceEnabled()) { return; } if (obj instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.TRACE_INT, "StackTrace:", null, (Throwable) obj); } else { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.TRACE_INT, obj == null ? NULL : obj.toString(), null, null); } } /** * debug format * * @param format format * @param args args */ public void debugf(String format, Object... args) { if (delegate.isDebugEnabled()) { debug(logf(format, args)); } } /** * debug * * @param objects */ public void debug(Object... objects) { if (delegate.isDebugEnabled()) { if (objects[objects.length - 1] instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.DEBUG_INT, concat(objects), null, (Throwable) objects[objects.length - 1]); } else { String obj = concat(objects); ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj, null, null); } } } /** * debug * * @param obj */ public void debug(Object obj) { if (!delegate.isDebugEnabled()) { return; } if (obj instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.DEBUG_INT, "StackTrace:", null, (Throwable) obj); } else { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.TRACE_INT, obj == null ? NULL : obj.toString(), null, null); } } /** * info format * * @param format * @param args */ public void infof(String format, Object... args) { if (delegate.isInfoEnabled()) { info(logf(format, args)); } } /** * info * * @param objects */ public void info(Object... objects) { if (delegate.isInfoEnabled()) { if (objects[objects.length - 1] instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, concat(objects), null, (Throwable) objects[objects.length - 1]); } else { String obj = concat(objects); ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj, null, null); } } } /** * info * * @param obj */ public void info(Object obj) { if (!delegate.isInfoEnabled()) { return; } if (obj instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, "StackTrace:", null, (Throwable) obj); } else { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj == null ? NULL : obj.toString(), null, null); } } /** * warn format * * @param format * @param args */ public void warnf(String format, Object... args) { if (delegate.isWarnEnabled()) { warn(logf(format, args)); } } /** * warn * * @param objects */ public void warn(Object... objects) { if (delegate.isWarnEnabled()) { if (objects[objects.length - 1] instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.WARN_INT, concat(objects), null, (Throwable) objects[objects.length - 1]); } else { String obj = concat(objects); ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj, null, null); } } } /** * warn * * @param obj */ public void warn(Object obj) { if (!delegate.isWarnEnabled()) { return; } if (obj instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.WARN_INT, "StackTrace:", null, (Throwable) obj); } else { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.WARN_INT, obj == null ? NULL : obj.toString(), null, null); } } /** * error format * * @param format * @param args */ public void errorf(String format, Object... args) { if (delegate.isErrorEnabled()) { error(logf(format, args)); } } /** * error * * @param objects */ public void error(Object... objects) { if (delegate.isErrorEnabled()) { if (objects[objects.length - 1] instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.ERROR_INT, concat(objects), null, (Throwable) objects[objects.length - 1]); } else { String obj = concat(objects); ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.INFO_INT, obj, null, null); } } } /** * error * * @param obj */ public void error(Object obj) { if (!delegate.isErrorEnabled()) { return; } if (obj instanceof Throwable) { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.ERROR_INT, "StackTrace:", null, (Throwable) obj); } else { ((LocationAwareLogger) delegate).log(null, FQCN, LocationAwareLogger.ERROR_INT, obj == null ? NULL : obj.toString(), null, null); } } private String getCallingClassName(String className) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); (new Throwable()).printStackTrace(pw); String stack = sw.toString(); // Given the current structure of the package, the line // containing "org.apache.log4j.Category." should be printed just // before the caller. // This method of searching may not be fastest but it's safer // than counting the stack depth which is not guaranteed to be // constant across JVM implementations. int ibegin = stack.lastIndexOf(className); if (ibegin == -1) { return NA; } ibegin = stack.indexOf(LINE_SEP, ibegin); if (ibegin == -1) { return NA; } ibegin += LINE_SEP_LEN; // determine end of line int iend = stack.indexOf(LINE_SEP, ibegin); if (iend == -1) { return NA; } // VA has a different stack trace format which doesn't // need to skip the inital 'at' // back up to first blank character ibegin = stack.lastIndexOf("at ", iend); if (ibegin == -1) { return NA; } // Add 3 to skip "at "; ibegin += 3; // everything between is the requested stack item String fullInfo = stack.substring(ibegin, iend); // Starting the search from '(' is safer because there is // potentially a dot between the parentheses. iend = fullInfo.lastIndexOf('('); if (iend == -1) { return NA; } iend = fullInfo.lastIndexOf('.', iend); /* * This is because a stack trace in VisualAge looks like: * java.lang.RuntimeException * java.lang.Throwable() * java.lang.Exception() * java.lang.RuntimeException() * void test.test.B.print() * void test.test.A.printIndirect() * void test.test.Run.main(java.lang.String []) */ if (iend == -1) { return NA; } else { return fullInfo.substring(0, iend); } } private static String getShortName(TimeUnit unit) { if (unit == TimeUnit.NANOSECONDS) { return "ns"; } else if (unit == TimeUnit.MICROSECONDS) { return "micro"; } else if (unit == TimeUnit.MILLISECONDS) { return "ms"; } else if (unit == TimeUnit.SECONDS) { return "sec"; } else if (unit == TimeUnit.MINUTES) { return "min"; } return "..."; } /** * 输出性能日志 * * @param info 输出信息 * @param startTime 开始时间 * @param unit 时间单位 * @param threshold 阈值 * @param delta 增量,超过一倍增量输出info日志,超过2倍增量以上输出warn */ public static final void timeSpent(String info, long startTime, TimeUnit unit, long threshold, long delta) { long now; if (unit == TimeUnit.NANOSECONDS) { // 计算方法不一样 now = System.nanoTime(); } else { now = unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); } long spent = now - startTime; if (spent >= threshold) { if (delta > 0) { // 按级别输入日志 int multiple = (int) ((spent - threshold) / delta); if (multiple == 0) { ((LocationAwareLogger) PERF_LOGGER).log(null, FQCN, LocationAwareLogger.DEBUG_INT, concat(info, " spent ", spent, getShortName(unit), ", but expect in ", threshold, getShortName(unit)), null, null); } else if (multiple == 1) { ((LocationAwareLogger) PERF_LOGGER).log(null, FQCN, LocationAwareLogger.INFO_INT, concat(info, " spent ", spent, getShortName(unit), ", but expect in ", threshold, getShortName(unit)), null, null); } else { ((LocationAwareLogger) PERF_LOGGER).log(null, FQCN, LocationAwareLogger.WARN_INT, concat(info, " spent ", spent, getShortName(unit), ", but expect in ", threshold, getShortName(unit)), null, null); } } else { // 统一debug ((LocationAwareLogger) PERF_LOGGER).log(null, FQCN, LocationAwareLogger.DEBUG_INT, concat(info, " spent ", spent, getShortName(unit), ", but expect in ", threshold, getShortName(unit)), null, null); } } } /** * @see #timeSpent(String, long, TimeUnit, long, long) */ public static final void timeSpent(String info, long startTime, TimeUnit unit, long threshold) { timeSpent(info, startTime, unit, threshold, threshold); } /** * @see #timeSpent(String, long, TimeUnit, long, long) */ public static final void timeSpentNan(String info, long startTime, long threshold) { timeSpent(info, startTime, TimeUnit.NANOSECONDS, threshold, threshold); } /** * @see #timeSpent(String, long, TimeUnit, long, long) */ public static final void timeSpentNan(String info, long startTime, long threshold, long delta) { timeSpent(info, startTime, TimeUnit.NANOSECONDS, threshold, delta); } /** * @see #timeSpent(String, long, TimeUnit, long, long) */ public static final void timeSpentMillSec(String info, long startTime, long threshold) { timeSpent(info, startTime, TimeUnit.MILLISECONDS, threshold, threshold); } /** * @see #timeSpent(String, long, TimeUnit, long, long) */ public static final void timeSpentMillSec(String info, long startTime, long threshold, long delta) { timeSpent(info, startTime, TimeUnit.MILLISECONDS, threshold, delta); } }
public class LoggerFactory { private static final ConcurrentMap<String, Logger> LOGGERS = new ConcurrentHashMap<String, Logger>(); public static Logger getLogger() { return new Logger(LoggerFactory.class); } public static Logger getLogger(String className) { Logger logger = LOGGERS.get(className); if (logger == null) { LOGGERS.putIfAbsent(className, new Logger(className)); logger = LOGGERS.get(className); } return logger; } public static Logger getLogger(Class clazz) { return getLogger(clazz.getName()); } }
public enum LogLevel { ALL, TRACE, DEBUG, INFO, WARN, ERROR; }
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodInvoke { /** * 方法名,同method() */ @AliasFor("method") String value() default ""; /** * 方法名,同value() */ @AliasFor("value") String method() default ""; /** * 服务接口,与serviceClass 二选一 */ String service() default ""; /** * 服务接口Class,与service 二选一 */ Class serviceClass() default MethodInvoke.class; /** * 服务类型 */ ServiceType type() default ServiceType.LOCAL; /** * 方法描述 */ String desc() default ""; /** * 是否打印请求参数和返回参数,默认true-打印 */ boolean isPrint() default false; /** * 日志格式-打印请求参数和返回参数,默认JSON-打印 */ LogFormat format() default LogFormat.JSON; /** * 日志级别,默认INFO */ LogLevel logLevel() default LogLevel.INFO; }
public enum ServiceType { /** * DUBBO-服务调用 */ DUBBO, /** * REST-服务调用 */ REST, /** * LOCAL-服务调用 */ LOCAL; }