spring-boot 에서 logback 을 사용 하 는 큰 구 덩이 를 해결 합 니 다.

7891 단어 spring-bootlogback
최근 에 logback 의 kafka appender 를 쓰 고 있 는데,뜻밖에 spring-boot 가 logback 을 사용 할 때의 구 덩이 를 발견 하 였 습 니 다.
ConsoleAppender.java 를 예 로 들 면,logback.xml 에서 이 appender 를 사용 했다 고 가정 하면,이와 관련 된 초기 화 방법 은 start()방법 과 같이 두 번 조 정 됩 니 다.
중단 점 에서 debug 를 진행 합 니 다.start()에 처음 들 어 가 는 방법 은 다음 과 같 습 니 다.

모든 호출 체인(자신의 코드 를 제외 한 방법)은 logback 또는 slf4j 와 관련 된 것 이 정상 적 인 것 을 볼 수 있 습 니 다.
이 정지점 을 뛰 어 넘 었 을 때 이 방법 으로 들 어 갑 니 다.하향 조정 용 체인 을 보 세 요.

이번 초기 화 는 spring-boot 에서 시 작 된 것 을 볼 수 있 습 니 다.따라서 logback 을 한 번 초기 화하 고 spring-boot 를 한 번 초기 화 합 니 다.모두 두 번 입 니 다.
저 희 는 이제 spring-boot 의 초기 화 를 지 울 수 있 습 니 다.
debug 코드 에서 LoggingApplicationListener.java 를 발견 할 수 있 습 니 다.이 모니터 는 주로 spring-boot 로그 시스템 을 초기 화 하 는 데 사 용 됩 니 다.현재 이 listener 를 시작 하기 전에 제거 하기 위해 서 입 니 다.
spring-boot 의 시작 코드 는:

new SpringApplicationBuilder(Launcher.class).application().run(args);
SpringApplicationBuilder.자바 의 구조 방법 중단 점 을 추적 합 니 다.
SpringAppication.java 에 들 어가 면 이 종류의 코드 를 발견 할 수 있 습 니 다.

private void initialize(Object[] sources) {
   if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
   }
   this.webEnvironment = deduceWebEnvironment();
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}
여덟 번 째 줄 은 모니터 를 등록 한 곳 일 것 입 니 다.계속 추적 하고 다음 과 같은 방법 으로 들 어 갑 니 다.

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
   // Use names and ensure unique to protect against duplicates
   Set<String> names = new LinkedHashSet<String>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}
loadFactory Names()방법 에 계속 들 어가 면 핵심 은 바로 여기에 있 습 니 다.

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   try {
      Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      List<String> result = new ArrayList<String>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
         String factoryClassNames = properties.getProperty(factoryClassName);
         result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
            "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}
FACTORIES_RESOURCE_LOCATION 이라는 상수 의 값 은 META-INF/spring.factories 입 니 다.
이 파일 을 열 면 다음 을 발견 할 수 있 습 니 다:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
applicationListener 는 우리 가 수정 해 야 할 부분 일 것 입 니 다.org.spring from work.boot.logging.Logging application Listener 를 없 애 면 됩 니 다.우 리 는 코드 에 이 코드 를 덮어 서 이 줄 을 없 앨 수 있 습 니 다.그러나 실제 적 으로 다시 한 번 달 려 야 합 니 다.현금 은 똑 같이 두 번 초기 화 됩 니 다.
문제

Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
      ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
이 코드 는 모든 META-INF/spring.factories 를 읽 고 합 친 것 입 니 다.그 러 니까 이 META-INF/spring.factories 는 내용 만 추가 할 수 있 지만 어떤 내용 은 삭제 할 수 없습니다.어 쩔 수 없 이 코드 가 모든 listener 를 초기 화한 후에 listener 를 제거 할 수 밖 에 없습니다.
구체 적 인 코드 는 다음 과 같 습 니 다.(spring-boot 를 시작 하 는 main 방법 중)

SpringApplicationBuilder builder = new SpringApplicationBuilder(Launcher.class);
Set<ApplicationListener<?>> listeners = builder.application().getListeners();
for (Iterator<ApplicationListener<?>> it = listeners.iterator(); it.hasNext();) {
    ApplicationListener<?> listener = it.next();
    if (listener instanceof LoggingApplicationListener) {
        it.remove();
    }
}
builder.application().setListeners(listeners);
builder.run(args);
PS:사실 log 를 두 번 초기 화 하 는 것 은 우아 함 을 해치 지 않 습 니 다.관건 은 문제 가 발생 하면 항상 해결 하거나 원 리 를 이해 하 는 것 입 니 다.
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기