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}