001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.logging;
018
019import java.util.Collections;
020import java.util.EnumSet;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.springframework.util.ClassUtils;
027import org.springframework.util.StringUtils;
028
029/**
030 * Common abstraction over logging systems.
031 *
032 * @author Phillip Webb
033 * @author Dave Syer
034 * @author Andy Wilkinson
035 * @author Ben Hale
036 */
037public abstract class LoggingSystem {
038
039        /**
040         * A System property that can be used to indicate the {@link LoggingSystem} to use.
041         */
042        public static final String SYSTEM_PROPERTY = LoggingSystem.class.getName();
043
044        /**
045         * The value of the {@link #SYSTEM_PROPERTY} that can be used to indicate that no
046         * {@link LoggingSystem} should be used.
047         */
048        public static final String NONE = "none";
049
050        /**
051         * The name used for the root logger. LoggingSystem implementations should ensure that
052         * this is the name used to represent the root logger, regardless of the underlying
053         * implementation.
054         */
055        public static final String ROOT_LOGGER_NAME = "ROOT";
056
057        private static final Map<String, String> SYSTEMS;
058
059        static {
060                Map<String, String> systems = new LinkedHashMap<>();
061                systems.put("ch.qos.logback.core.Appender",
062                                "org.springframework.boot.logging.logback.LogbackLoggingSystem");
063                systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
064                                "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
065                systems.put("java.util.logging.LogManager",
066                                "org.springframework.boot.logging.java.JavaLoggingSystem");
067                SYSTEMS = Collections.unmodifiableMap(systems);
068        }
069
070        /**
071         * Reset the logging system to be limit output. This method may be called before
072         * {@link #initialize(LoggingInitializationContext, String, LogFile)} to reduce
073         * logging noise until the system has been fully initialized.
074         */
075        public abstract void beforeInitialize();
076
077        /**
078         * Fully initialize the logging system.
079         * @param initializationContext the logging initialization context
080         * @param configLocation a log configuration location or {@code null} if default
081         * initialization is required
082         * @param logFile the log output file that should be written or {@code null} for
083         * console only output
084         */
085        public void initialize(LoggingInitializationContext initializationContext,
086                        String configLocation, LogFile logFile) {
087        }
088
089        /**
090         * Clean up the logging system. The default implementation does nothing. Subclasses
091         * should override this method to perform any logging system-specific cleanup.
092         */
093        public void cleanUp() {
094        }
095
096        /**
097         * Returns a {@link Runnable} that can handle shutdown of this logging system when the
098         * JVM exits. The default implementation returns {@code null}, indicating that no
099         * shutdown is required.
100         * @return the shutdown handler, or {@code null}
101         */
102        public Runnable getShutdownHandler() {
103                return null;
104        }
105
106        /**
107         * Returns a set of the {@link LogLevel LogLevels} that are actually supported by the
108         * logging system.
109         * @return the supported levels
110         */
111        public Set<LogLevel> getSupportedLogLevels() {
112                return EnumSet.allOf(LogLevel.class);
113        }
114
115        /**
116         * Sets the logging level for a given logger.
117         * @param loggerName the name of the logger to set ({@code null} can be used for the
118         * root logger).
119         * @param level the log level ({@code null} can be used to remove any custom level for
120         * the logger and use the default configuration instead)
121         */
122        public void setLogLevel(String loggerName, LogLevel level) {
123                throw new UnsupportedOperationException("Unable to set log level");
124        }
125
126        /**
127         * Returns a collection of the current configuration for all a {@link LoggingSystem}'s
128         * loggers.
129         * @return the current configurations
130         * @since 1.5.0
131         */
132        public List<LoggerConfiguration> getLoggerConfigurations() {
133                throw new UnsupportedOperationException("Unable to get logger configurations");
134        }
135
136        /**
137         * Returns the current configuration for a {@link LoggingSystem}'s logger.
138         * @param loggerName the name of the logger
139         * @return the current configuration
140         * @since 1.5.0
141         */
142        public LoggerConfiguration getLoggerConfiguration(String loggerName) {
143                throw new UnsupportedOperationException("Unable to get logger configuration");
144        }
145
146        /**
147         * Detect and return the logging system in use. Supports Logback and Java Logging.
148         * @param classLoader the classloader
149         * @return the logging system
150         */
151        public static LoggingSystem get(ClassLoader classLoader) {
152                String loggingSystem = System.getProperty(SYSTEM_PROPERTY);
153                if (StringUtils.hasLength(loggingSystem)) {
154                        if (NONE.equals(loggingSystem)) {
155                                return new NoOpLoggingSystem();
156                        }
157                        return get(classLoader, loggingSystem);
158                }
159                return SYSTEMS.entrySet().stream()
160                                .filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
161                                .map((entry) -> get(classLoader, entry.getValue())).findFirst()
162                                .orElseThrow(() -> new IllegalStateException(
163                                                "No suitable logging system located"));
164        }
165
166        private static LoggingSystem get(ClassLoader classLoader, String loggingSystemClass) {
167                try {
168                        Class<?> systemClass = ClassUtils.forName(loggingSystemClass, classLoader);
169                        return (LoggingSystem) systemClass.getConstructor(ClassLoader.class)
170                                        .newInstance(classLoader);
171                }
172                catch (Exception ex) {
173                        throw new IllegalStateException(ex);
174                }
175        }
176
177        /**
178         * {@link LoggingSystem} that does nothing.
179         */
180        static class NoOpLoggingSystem extends LoggingSystem {
181
182                @Override
183                public void beforeInitialize() {
184
185                }
186
187                @Override
188                public void setLogLevel(String loggerName, LogLevel level) {
189
190                }
191
192                @Override
193                public List<LoggerConfiguration> getLoggerConfigurations() {
194                        return Collections.emptyList();
195                }
196
197                @Override
198                public LoggerConfiguration getLoggerConfiguration(String loggerName) {
199                        return null;
200                }
201
202        }
203
204}