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.Comparator;
020import java.util.EnumMap;
021import java.util.HashMap;
022import java.util.LinkedHashSet;
023import java.util.Map;
024import java.util.Set;
025
026import org.springframework.core.env.Environment;
027import org.springframework.core.io.ClassPathResource;
028import org.springframework.util.ClassUtils;
029import org.springframework.util.StringUtils;
030import org.springframework.util.SystemPropertyUtils;
031
032/**
033 * Abstract base class for {@link LoggingSystem} implementations.
034 *
035 * @author Phillip Webb
036 * @author Dave Syer
037 */
038public abstract class AbstractLoggingSystem extends LoggingSystem {
039
040        protected static final Comparator<LoggerConfiguration> CONFIGURATION_COMPARATOR = new LoggerConfigurationComparator(
041                        ROOT_LOGGER_NAME);
042
043        private final ClassLoader classLoader;
044
045        public AbstractLoggingSystem(ClassLoader classLoader) {
046                this.classLoader = classLoader;
047        }
048
049        @Override
050        public void beforeInitialize() {
051        }
052
053        @Override
054        public void initialize(LoggingInitializationContext initializationContext,
055                        String configLocation, LogFile logFile) {
056                if (StringUtils.hasLength(configLocation)) {
057                        initializeWithSpecificConfig(initializationContext, configLocation, logFile);
058                        return;
059                }
060                initializeWithConventions(initializationContext, logFile);
061        }
062
063        private void initializeWithSpecificConfig(
064                        LoggingInitializationContext initializationContext, String configLocation,
065                        LogFile logFile) {
066                configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
067                loadConfiguration(initializationContext, configLocation, logFile);
068        }
069
070        private void initializeWithConventions(
071                        LoggingInitializationContext initializationContext, LogFile logFile) {
072                String config = getSelfInitializationConfig();
073                if (config != null && logFile == null) {
074                        // self initialization has occurred, reinitialize in case of property changes
075                        reinitialize(initializationContext);
076                        return;
077                }
078                if (config == null) {
079                        config = getSpringInitializationConfig();
080                }
081                if (config != null) {
082                        loadConfiguration(initializationContext, config, logFile);
083                        return;
084                }
085                loadDefaults(initializationContext, logFile);
086        }
087
088        /**
089         * Return any self initialization config that has been applied. By default this method
090         * checks {@link #getStandardConfigLocations()} and assumes that any file that exists
091         * will have been applied.
092         * @return the self initialization config or {@code null}
093         */
094        protected String getSelfInitializationConfig() {
095                return findConfig(getStandardConfigLocations());
096        }
097
098        /**
099         * Return any spring specific initialization config that should be applied. By default
100         * this method checks {@link #getSpringConfigLocations()}.
101         * @return the spring initialization config or {@code null}
102         */
103        protected String getSpringInitializationConfig() {
104                return findConfig(getSpringConfigLocations());
105        }
106
107        private String findConfig(String[] locations) {
108                for (String location : locations) {
109                        ClassPathResource resource = new ClassPathResource(location,
110                                        this.classLoader);
111                        if (resource.exists()) {
112                                return "classpath:" + location;
113                        }
114                }
115                return null;
116        }
117
118        /**
119         * Return the standard config locations for this system.
120         * @return the standard config locations
121         * @see #getSelfInitializationConfig()
122         */
123        protected abstract String[] getStandardConfigLocations();
124
125        /**
126         * Return the spring config locations for this system. By default this method returns
127         * a set of locations based on {@link #getStandardConfigLocations()}.
128         * @return the spring config locations
129         * @see #getSpringInitializationConfig()
130         */
131        protected String[] getSpringConfigLocations() {
132                String[] locations = getStandardConfigLocations();
133                for (int i = 0; i < locations.length; i++) {
134                        String extension = StringUtils.getFilenameExtension(locations[i]);
135                        locations[i] = locations[i].substring(0,
136                                        locations[i].length() - extension.length() - 1) + "-spring."
137                                        + extension;
138                }
139                return locations;
140        }
141
142        /**
143         * Load sensible defaults for the logging system.
144         * @param initializationContext the logging initialization context
145         * @param logFile the file to load or {@code null} if no log file is to be written
146         */
147        protected abstract void loadDefaults(
148                        LoggingInitializationContext initializationContext, LogFile logFile);
149
150        /**
151         * Load a specific configuration.
152         * @param initializationContext the logging initialization context
153         * @param location the location of the configuration to load (never {@code null})
154         * @param logFile the file to load or {@code null} if no log file is to be written
155         */
156        protected abstract void loadConfiguration(
157                        LoggingInitializationContext initializationContext, String location,
158                        LogFile logFile);
159
160        /**
161         * Reinitialize the logging system if required. Called when
162         * {@link #getSelfInitializationConfig()} is used and the log file hasn't changed. May
163         * be used to reload configuration (for example to pick up additional System
164         * properties).
165         * @param initializationContext the logging initialization context
166         */
167        protected void reinitialize(LoggingInitializationContext initializationContext) {
168        }
169
170        protected final ClassLoader getClassLoader() {
171                return this.classLoader;
172        }
173
174        protected final String getPackagedConfigFile(String fileName) {
175                String defaultPath = ClassUtils.getPackageName(getClass());
176                defaultPath = defaultPath.replace('.', '/');
177                defaultPath = defaultPath + "/" + fileName;
178                defaultPath = "classpath:" + defaultPath;
179                return defaultPath;
180        }
181
182        protected final void applySystemProperties(Environment environment, LogFile logFile) {
183                new LoggingSystemProperties(environment).apply(logFile);
184        }
185
186        /**
187         * Maintains a mapping between native levels and {@link LogLevel}.
188         *
189         * @param <T> the native level type
190         */
191        protected static class LogLevels<T> {
192
193                private final Map<LogLevel, T> systemToNative;
194
195                private final Map<T, LogLevel> nativeToSystem;
196
197                public LogLevels() {
198                        this.systemToNative = new EnumMap<>(LogLevel.class);
199                        this.nativeToSystem = new HashMap<>();
200                }
201
202                public void map(LogLevel system, T nativeLevel) {
203                        if (!this.systemToNative.containsKey(system)) {
204                                this.systemToNative.put(system, nativeLevel);
205                        }
206                        if (!this.nativeToSystem.containsKey(nativeLevel)) {
207                                this.nativeToSystem.put(nativeLevel, system);
208                        }
209                }
210
211                public LogLevel convertNativeToSystem(T level) {
212                        return this.nativeToSystem.get(level);
213                }
214
215                public T convertSystemToNative(LogLevel level) {
216                        return this.systemToNative.get(level);
217                }
218
219                public Set<LogLevel> getSupported() {
220                        return new LinkedHashSet<>(this.nativeToSystem.values());
221                }
222
223        }
224
225}