content: /*
   * Copyright (c) 2024 .
   *
   * SnailJob - 灵活，可靠和快速的分布式任务重试和分布式任务调度平台
   * > ✅️ 可重放，可管控、为提高分布式业务系统一致性的分布式任务重试平台
   * > ✅️ 支持秒级、可中断、可编排的高性能分布式任务调度平台
   *
   * Aizuda/SnailJob 采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点:
   *
   *
   * 1. 不得修改产品相关代码的源码头注释和出处;
   * 2. 不得应用于危害国家安全、荣誉和利益的行为，不能以任何形式用于非法目的;
   *
   */
  package com.aizuda.snailjob.client.common.appender;
  
  import ch.qos.logback.classic.spi.IThrowableProxy;
  import ch.qos.logback.classic.spi.LoggingEvent;
  import ch.qos.logback.classic.spi.StackTraceElementProxy;
  import ch.qos.logback.classic.spi.ThrowableProxyUtil;
  import ch.qos.logback.core.CoreConstants;
  import ch.qos.logback.core.UnsynchronizedAppenderBase;
  import com.aizuda.snailjob.client.common.log.report.LogReportFactory;
  import com.aizuda.snailjob.client.common.log.support.SnailJobLogManager;
  import com.aizuda.snailjob.client.common.rpc.client.grpc.GrpcChannel;
  import com.aizuda.snailjob.common.log.constant.LogFieldConstants;
  import com.aizuda.snailjob.common.log.dto.LogContentDTO;
  import org.slf4j.MDC;
  
  import java.util.Objects;
  import java.util.Optional;
  
  /**
   * @author wodeyangzipingpingwuqi
   * @date 2023-12-27
   * @since 2.6.0
   */
  public class SnailLogbackAppender<E> extends UnsynchronizedAppenderBase<E> {
  
      @Override
      public void start() {
          super.start();
      }
  
      @Override
      protected void append(E eventObject) {
  
          // Not job context
          if (!(eventObject instanceof LoggingEvent)
                  || Objects.isNull(SnailJobLogManager.getLogMeta())
                  || Objects.isNull(MDC.get(LogFieldConstants.MDC_REMOTE))) {
              return;
          }
  
          MDC.remove(LogFieldConstants.MDC_REMOTE);
          LogContentDTO logContentDTO = new LogContentDTO();
  
          // Prepare processing
          ((LoggingEvent) eventObject).prepareForDeferredProcessing();
          LoggingEvent event = (LoggingEvent) eventObject;
  
          logContentDTO.addTimeStamp(event.getTimeStamp());
          logContentDTO.addLevelField(event.getLevel().levelStr);
          logContentDTO.addThreadField(event.getThreadName());
          logContentDTO.addMessageField(event.getFormattedMessage());
          logContentDTO.addLocationField(getLocationField(event));
          logContentDTO.addThrowableField(getThrowableField(event));
          logContentDTO.addHostField(GrpcChannel.getClientHost());
          logContentDTO.addPortField(GrpcChannel.getClientPort());
  
          // slidingWindow syncReportLog
          Optional.ofNullable(LogReportFactory.get()).ifPresent(logReport -> logReport.report(logContentDTO));
      }
  
      private String getThrowableField(LoggingEvent event) {
          IThrowableProxy iThrowableProxy = event.getThrowableProxy();
          if (iThrowableProxy != null) {
              String throwable = getExceptionInfo(iThrowableProxy);
              throwable += formatThrowable(event.getThrowableProxy().getStackTraceElementProxyArray());
              return throwable;
          }
          return null;
      }
  
      private String getLocationField(LoggingEvent event) {
          StackTraceElement[] caller = event.getCallerData();
          if (caller != null && caller.length > 0) {
              return caller[0].toString();
          }
          return null;
      }
  
      private String formatThrowable(StackTraceElementProxy[] stackTraceElementProxyArray) {
          StringBuilder builder = new StringBuilder();
          int stackDeep = 0;
          for (StackTraceElementProxy step : stackTraceElementProxyArray) {
              builder.append(CoreConstants.LINE_SEPARATOR);
              String string = step.toString();
              builder.append(CoreConstants.TAB).append(string);
              ThrowableProxyUtil.subjoinPackagingData(builder, step);
              // 最多显示30行
              if (++stackDeep >= 30) {
                  break;
              }
          }
          return builder.toString();
      }
  
      private String getExceptionInfo(IThrowableProxy iThrowableProxy) {
          String s = iThrowableProxy.getClassName();
          String message = iThrowableProxy.getMessage();
          return (message != null) ? (s + ": " + message) : s;
      }
  }
  