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