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}