001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server.springboot; 018 019import org.slf4j.*; 020import org.springframework.beans.factory.*; 021import org.springframework.boot.autoconfigure.*; 022import org.springframework.boot.autoconfigure.condition.*; 023import org.springframework.context.annotation.*; 024import org.springframework.core.env.*; 025 026import ch.qos.logback.classic.*; 027import ch.qos.logback.classic.jul.*; 028 029/** 030 * Spring Boot logging bridge auto-configuration. 031 * 032 * <p> 033 * When enabled (default), and only when Logback is the active SLF4J backend, installs one JUL 034 * {@code LevelChangePropagator} listener so Spring logging-level changes propagate to JUL. 035 * 036 * <p> 037 * This is additive by default ({@code juneau.rest.logging.propagate-levels.reset-jul=false}) and 038 * silently no-ops when the backend is not Logback. 039 * 040 * @since 10.0.0 041 */ 042@AutoConfiguration 043@ConditionalOnClass(name="ch.qos.logback.classic.jul.LevelChangePropagator") 044@ConditionalOnProperty(name="juneau.rest.logging.propagate-levels", havingValue="true", matchIfMissing=true) 045public class JuneauRestLoggingAutoConfiguration { 046 047 @Bean 048 @ConditionalOnMissingBean(name="juneauRestLogLevelPropagatorInstaller") 049 public Runnable juneauRestLogLevelPropagatorInstaller(Environment env) { 050 var resetJul = env.getProperty("juneau.rest.logging.propagate-levels.reset-jul", Boolean.class, false); 051 return () -> install(LoggerFactory.getILoggerFactory(), resetJul); 052 } 053 054 @Bean 055 @ConditionalOnBean(name="juneauRestLogLevelPropagatorInstaller") 056 public SmartInitializingSingleton juneauRestLogLevelPropagatorBootstrap(Runnable juneauRestLogLevelPropagatorInstaller) { 057 return juneauRestLogLevelPropagatorInstaller::run; 058 } 059 060 void install(ILoggerFactory loggerFactory, boolean resetJul) { 061 if (! (loggerFactory instanceof LoggerContext context)) 062 return; 063 if (hasPropagator(context)) 064 return; 065 066 var propagator = new JuneauResetResistantLevelChangePropagator(); 067 propagator.setContext(context); 068 propagator.setResetJUL(resetJul); 069 propagator.start(); 070 context.addListener(propagator); 071 } 072 073 private static boolean hasPropagator(LoggerContext context) { 074 for (var listener : context.getCopyOfListenerList()) 075 if (listener instanceof LevelChangePropagator) 076 return true; 077 return false; 078 } 079}