常规日志中看到异常堆栈信息大部分时候是类似这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.chineseall.user.logic.UserServiceLogic$$EnhancerBySpringCGLIB$$ad3e32b0.userInfoBatch(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:244)
	at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:494)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)

不控制堆栈打印深度的时候,日志文件会变得非常大,这里介绍一种方法可以降低日志文件的存储空间。

解决方案

logback.xml配置文件中增加如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  <!-- 日志输出格式 -->
  <conversionRule conversionWord="exception" converterClass="com.au92.common.log.CustomStackTraceConverter"/>
  <conversionRule conversionWord="logger" converterClass="com.au92.common.log.CustomPackageNameConverter"/>

  <property name="LOG_COMMON_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50}#%M [%X{traceId}] - %msg%n%exception{10}%n"/>
  <property name="LOG_DEV_PATTERN" value="%d{HH:mm:ss.SSS} [%thread] %-5level %boldGreen(%logger{50})#%boldYellow(%M):%line [%X{traceId}] - %msg%n%exception%n"/>
  
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_DEV_PATTERN}</pattern>
        </encoder>
  </appender>

  <appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
        <file>${LOG_HOME}/warn.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/warn/warn-%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>
            <maxFileSize>${LOG_MAX_SIZE}</maxFileSize>
            <maxHistory>3</maxHistory>
            <totalSizeCap>1GB</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_COMMON_PATTERN}</pattern>
        </encoder>
   </appender>

java代码中增加三个类:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/**
 * 自定义日志打印堆栈转换器
 *
 * @author p_x_c
 */
public class CustomStackTraceConverter extends ThrowableProxyConverter {
    int stackDepth = -1;

    @Override
    public void start() {
        super.start();
        stackDepth = NumberUtils.toInt(getFirstOption(), -1);
    }

    @Override
    protected String throwableProxyToString(IThrowableProxy throwableProxy) {
        Throwable throwable = ((ThrowableProxy) throwableProxy).getThrowable();
        StringBuilder sb = new StringBuilder();
        doPrintStack(throwable, sb);
        return sb.toString();
    }

    /**
     * 打印堆栈
     *
     * @param t  异常
     * @param sb 返回字符串
     */
    public void doPrintStack(Throwable t, StringBuilder sb) {
        StackTraceElement[] stackTraceElements = t.getStackTrace();
        if (stackDepth < 0) {
            stackDepth = stackTraceElements.length;
        }
        if (sb.lastIndexOf("\t") > -1) {
            sb.deleteCharAt(sb.length() - 1);
            sb.append("Caused: ");
        }
        sb.append(t.getClass()
                   .getName())
          .append(": ")
          .append(t.getMessage())
          .append("\n\t");
        for (int i = 0; i < stackDepth; ++i) {
            if (i >= stackTraceElements.length) {
                break;
            }
            StackTraceElement element = stackTraceElements[i];
            sb.append(ShortClassNameUtil.simplifyClassName(element.getClassName()))
              .append("#")
              .append(element.getMethodName())
              .append(":")
              .append(element.getLineNumber())
              .append("\n\t");
        }
    }
}

/**
 * 自定义日志打印包名转换器,需要将越来打印logger的地方替换为该转换器
 *
 * @author p_x_c
 */
public class CustomPackageNameConverter extends CompositeConverter<ILoggingEvent> {

    @Override
    protected String transform(ILoggingEvent event, String in) {
        return ShortClassNameUtil.simplifyClassName(event.getLoggerName());
    }
}

/**
 * 更短的类名
 *
 * @author p_x_c
 */
@UtilityClass
public class ShortClassNameUtil {
    // 白名单,不需要简化的类名
    public static final String PACKAGE_NAME = "com.au92";

    /**
     * 简化类名
     *
     * @param fullClassName
     * @return
     */
    public String simplifyClassName(String fullClassName) {
        if (fullClassName.contains(PACKAGE_NAME)) {
            return fullClassName;
        }
        String[] parts = fullClassName.split("\\.");
        StringBuilder simplifiedName = new StringBuilder();
        for (int i = 0; i < parts.length - 1; i++) {
            simplifiedName.append(parts[i].charAt(0))
                          .append('.');
        }
        simplifiedName.append(parts[parts.length - 1]);
        return simplifiedName.toString();
    }
}

效果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
21:03:37.369 [XNIO-1 task-1] ERROR com.au92.common.web.exception.WebExceptionHandler#unknownException [42b240a7-6234-4354-a02f-42f682319db7] - 异常
com.au92.common.util.exception.WebException: ddsfsdf
	com.au92.api.controller.category.CategoryCommonController#list:40
	com.au92.api.controller.category.CategoryCommonController$$FastClassBySpringCGLIB$$9cea966b#invoke:-1
	o.s.c.p.MethodProxy#invoke:218
	o.s.a.f.CglibAopProxy$CglibMethodInvocation#invokeJoinpoint:792
	o.s.a.f.ReflectiveMethodInvocation#proceed:163
	o.s.a.f.CglibAopProxy$CglibMethodInvocation#proceed:762
	o.s.a.a.MethodInvocationProceedingJoinPoint#proceed:89
	com.au92.common.web.aop.LogAOP#doInvoke:52
	s.r.NativeMethodAccessorImpl#invoke0:-2
	s.r.NativeMethodAccessorImpl#invoke:62