log4j非常堆栈文件输出

打印 上一主题 下一主题

主题 820|帖子 820|积分 2460

目的:log4j非常堆栈关联到traceId一句话中,方便搜索
1、获取堆栈后一起打印

  1. private void logException(Throwable t, ProceedingJoinPoint joinPoint) {
  2.         if (this.printErrorStackSys) {
  3.             StringWriter sw = new StringWriter();
  4.             PrintWriter pw = new PrintWriter(sw);
  5.             t.printStackTrace(pw);
  6.             String errorMsg = sw.toString();
  7.             log.error("error->" + errorMsg);
  8.         } else {// 子定义打印
  9.             Signature signature = joinPoint.getSignature();
  10.             String service = signature.toShortString();
  11.             StringBuilder builder = new StringBuilder();
  12.             builder.append("\r\n========================================== \r\n");
  13.             builder.append("Invoke exception!!! \r\n");
  14.             builder.append("Service = ").append(service).append("\r\n");
  15.             Throwable cause = t.getCause();
  16.             while (cause != null) {
  17.                 builder.append("\n\t\tCause: ").append(cause.getClass().getCanonicalName());
  18.                 builder.append(": ").append(cause.getMessage());
  19.                 cause = cause.getCause();
  20.             }
  21.             builder.append(" StackTrace=");
  22.             StackTraceElement[] stackTraceElements = t.getStackTrace();
  23.             for(int i = 0; i<stackTraceElements.length; i++){
  24.                 StackTraceElement stackTrace = stackTraceElements[i];
  25.                 builder.append(stackTrace.getClassName()).append(":").append(stackTrace.getMethodName()).append(":").append(stackTrace.getLineNumber()).append("\r\n;");
  26.             }
  27.             builder.append("\r\n==========================================");
  28.             log.error(builder);
  29.         }
  30.     }
复制代码
效果示例:
利用printStackTrace方法打印

自界说打印

2、利用%throwable打印堆栈跟踪信息

%throwable 用于在日志配置文件中输出非常的堆栈跟踪信息。
它不是 XML 的尺度标签或属性,而是日志框架中的占位符
  1. <!--控制台输出-->
  2. <Console name="Console" target="SYSTEM_OUT">
  3.                         <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}|${ctx:uuid}|%t|%-5p|%c{1}:%L|%msg%n" />
  4.                 </Console>
  5. <!--文件输出-->
  6.                 <RollingFile name="RollingFile" filename="${logPath}/${rollingLogName}.log" filepattern="${logPath}/%d{yyyyMMdd}/${rollingLogName}-%i.log">
  7.                         <PatternLayout pattern='{"time":"%d{yyyy-MM-dd HH:mm:ss.SSS}","thread":"%t","tid":"${ctx:uuid}","level":"%-5p","class_line":"%c:%L","msg":"{%msg}%throwable"}%n'>
  8.                         </PatternLayout>
  9.                         <Policies>
  10.                                 <TimeBasedTriggeringPolicy interval="1" modulate="true" />
  11.                                 <SizeBasedTriggeringPolicy size="1024 MB" />
  12.                         </Policies>
  13.                         <DefaultRolloverStrategy max="100" />
  14.                 </RollingFile>
复制代码
  %d{HH:mm:ss,SSS} 表示日期和时间。
[%t] 表示线程名称。
%-5p 表示日志级别,左对齐,宽度为 5。
%c{1} 表示日志记载器的名称,只显示末了一级。
:%L 表示日志记载器的行号。
%m 表示日志消息。
%throwable 表示非常的堆栈跟踪信息。
%n 表示换行符。
  效果示例:

3、自界说格式化插件

这种方式与2一样,2利用的是ThrowablePatternConverter
  1. import org.apache.logging.log4j.core.LogEvent;
  2. import org.apache.logging.log4j.core.config.Configuration;
  3. import org.apache.logging.log4j.core.config.plugins.Plugin;
  4. import org.apache.logging.log4j.core.impl.LocationAware;
  5. import org.apache.logging.log4j.core.pattern.ConverterKeys;
  6. import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter;
  7. @Plugin(
  8.         name = "CustomerThrowablePatternConverter",
  9.         category = "Converter"
  10. )
  11. @ConverterKeys({"ct", "customerThrowable"})
  12. public class CustomerThrowablePatternConverter extends ThrowablePatternConverter implements LocationAware {
  13.     protected CustomerThrowablePatternConverter(String[] options, Configuration config) {
  14.         super("CT", "CTStyle", options, config);
  15.     }
  16.     // 被触发的得到实例的方法
  17.     public static CustomerThrowablePatternConverter newInstance(Configuration config, String[] options) {
  18.         return new CustomerThrowablePatternConverter(options, config);
  19.     }
  20.     @Override
  21.     public void format(LogEvent event, StringBuilder buffer) {
  22.         Throwable thrown = event.getThrown();
  23.         if (thrown != null && thrown instanceof CustomerException) {
  24.             buffer.append("自定义异常");
  25.         }
  26.         super.format(event, buffer);
  27.     }
  28.     // 位置信息
  29.     @Override
  30.     public boolean requiresLocation() {
  31.         return true;
  32.     }
  33. }
复制代码
  <atternLayout pattern=‘{“tid”:“${ctx:uuid}”,“time”:“%d{yyyy-MM-dd HH:mm:ss.SSS}”,“thread”:“%t”,“level”:“%-5p”,“class_line”:“%c:%L”,“msg”:“%enc{%msg-%customerThrowable}{JSON}”}%n’>
  

4、源码分析

Disruptor 是一个高性能的无锁并发框架,常用于异步日志记载、消息传递等场景。


  • LogEvent:表示一个日志变乱的类,将要输出的日志上下文信息封装成变乱。
  1. public LogEvent createEvent(String loggerName, Marker marker, String fqcn, StackTraceElement location, Level level, Message data, List<Property> properties, Throwable t) {
  2.         return new Log4jLogEvent(loggerName, marker, fqcn, location, level, data, properties, t);
  3.     }
复制代码


  • RingBuffer:一个循环缓冲区,用于存储变乱数据。
  1. private static final EventTranslatorTwoArg<Log4jEventWrapper, LogEvent, AsyncLoggerConfig> TRANSLATOR = new EventTranslatorTwoArg<Log4jEventWrapper, LogEvent, AsyncLoggerConfig>() {
  2.         public void translateTo(Log4jEventWrapper ringBufferElement, long sequence, LogEvent logEvent, AsyncLoggerConfig loggerConfig) {
  3.             ringBufferElement.event = logEvent;
  4.             ringBufferElement.loggerConfig = loggerConfig;
  5.         }
  6.     };
复制代码


  • Sequencer:负责管理和分配序列号的组件。 方法用于发布一个序列号,表示某个变乱已经预备好,可以被消费者线程处理。发布后的消息通常由一个或多个消费者类(也称为变乱处理器或 EventHandler)举行消费。
  1. private <A, B> void translateAndPublish(EventTranslatorTwoArg<E, A, B> translator, long sequence, A arg0, B arg1) {
  2.         try {
  3.             translator.translateTo(this.get(sequence), sequence, arg0, arg1);
  4.         } finally {
  5.             this.sequencer.publish(sequence);
  6.         }
  7.     }
复制代码


  • EventHandler:负责处理变乱的消费者类,界说了一个 onEvent 方法,该方法会在变乱预备好后被调用。
  1.    public void onEvent(Log4jEventWrapper event, long sequence, boolean endOfBatch) throws Exception {
  2.             event.event.setEndOfBatch(endOfBatch);
  3.             // 去消费
  4.             event.loggerConfig.logToAsyncLoggerConfigsOnCurrentThread(event.event);
  5.             event.clear();
  6.             this.notifyIntermediateProgress(sequence);
  7.         }
复制代码
5、主要流程

org.apache.logging.log4j.core.async.AsyncLoggerConfig#log
->org.apache.logging.log4j.core.Logger#logMessage
->org.apache.logging.log4j.core.config.LoggerConfig#log(java.lang.String, java.lang.String, org.apache.logging.log4j.Marker, org.apache.logging.log4j.Level, org.apache.logging.log4j.message.Message, java.lang.Throwable)
->org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier<org.apache.logging.log4j.core.config.LoggerConfig>, java.lang.String, java.lang.String, org.apache.logging.log4j.Marker, org.apache.logging.log4j.Level, org.apache.logging.log4j.message.Message, java.lang.Throwable)
->org.apache.logging.log4j.core.appender.RollingFileAppender#append
->org.apache.logging.log4j.core.layout.PatternLayout#encode
->org.apache.logging.log4j.core.layout.PatternLayout.PatternSerializer#toSerializable(org.apache.logging.log4j.core.LogEvent, java.lang.StringBuilder)
->LogEventPatternConverter的子类举行格式化操纵拼接打印日志输出
  1. // 异步输出日志方法重写
  2. protected void log(LogEvent event, LoggerConfig.LoggerConfigPredicate predicate) {
  3.         if (predicate == LoggerConfigPredicate.ALL && ASYNC_LOGGER_ENTERED.get() == Boolean.FALSE && this.hasAppenders()) {
  4.             ASYNC_LOGGER_ENTERED.set(Boolean.TRUE);
  5.             try {
  6.                 super.log(event, LoggerConfigPredicate.SYNCHRONOUS_ONLY);
  7.                 this.logToAsyncDelegate(event);
  8.             } finally {
  9.                 ASYNC_LOGGER_ENTERED.set(Boolean.FALSE);
  10.             }
  11.         } else {
  12.             super.log(event, predicate);
  13.         }
  14.     }
  15. //父类的方法,1、同步日志调用, 2、EventHandler回调使用
  16. protected void log(LogEvent event, LoggerConfigPredicate predicate) {
  17.         if (!this.isFiltered(event)) {
  18.             this.processLogEvent(event, predicate);
  19.         }
  20.     }
  21. // 日志处理--打印输出
  22.   private void processLogEvent(LogEvent event, LoggerConfigPredicate predicate) {
  23.         event.setIncludeLocation(this.isIncludeLocation());
  24.         // 判断当前 !config instanceof AsyncLoggerConfig是否为非异步
  25.         if (predicate.allow(this)) {
  26.         // 打印处理
  27.             this.callAppenders(event);
  28.         }
  29.         this.logParent(event, predicate);
  30.     }
  31. // 队列处理
  32. private void logToAsyncDelegate(LogEvent event) {
  33.         if (!this.isFiltered(event)) {
  34.             this.populateLazilyInitializedFields(event);
  35.             // 发布序列号,添加到缓冲区
  36.             if (!this.delegate.tryEnqueue(event, this)) {
  37.             // 缓存区满了处理
  38.                 this.handleQueueFull(event);
  39.             }
  40.         }
  41.     }
  42.   public void format(LogEvent event, StringBuilder buf) {
  43.         if (this.skipFormattingInfo) {
  44.             this.converter.format(event, buf);
  45.         } else {
  46.             this.formatWithInfo(event, buf);
  47.         }
  48.     }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表