1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.spring.boot;
18
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.io.UnsupportedEncodingException;
23 import java.net.MalformedURLException;
24 import java.net.URISyntaxException;
25 import java.net.URL;
26 import java.net.URLConnection;
27 import java.net.URLDecoder;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.Properties;
32 import javax.net.ssl.HttpsURLConnection;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36 import org.apache.logging.log4j.core.LoggerContext;
37 import org.apache.logging.log4j.core.config.AbstractConfiguration;
38 import org.apache.logging.log4j.core.config.Configuration;
39 import org.apache.logging.log4j.core.config.ConfigurationFactory;
40 import org.apache.logging.log4j.core.config.ConfigurationSource;
41 import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
42 import org.apache.logging.log4j.core.net.ssl.LaxHostnameVerifier;
43 import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
44 import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
45 import org.apache.logging.log4j.core.util.AuthorizationProvider;
46 import org.apache.logging.log4j.core.util.FileUtils;
47 import org.apache.logging.log4j.status.StatusLogger;
48 import org.apache.logging.log4j.util.PropertiesUtil;
49 import org.apache.logging.log4j.util.Strings;
50 import org.springframework.boot.logging.LogFile;
51 import org.springframework.boot.logging.LoggingInitializationContext;
52 import org.springframework.boot.logging.log4j2.Log4J2LoggingSystem;
53 import org.springframework.util.Assert;
54 import org.springframework.util.ClassUtils;
55 import org.springframework.util.ResourceUtils;
56
57
58
59
60 public class Log4j2CloudConfigLoggingSystem extends Log4J2LoggingSystem {
61 private static final String HTTPS = "https";
62 public static final String ENVIRONMENT_KEY = "SpringEnvironment";
63 private static final String OVERRIDE_PARAM = "override";
64 private static Logger LOGGER = StatusLogger.getLogger();
65
66 public Log4j2CloudConfigLoggingSystem(ClassLoader loader) {
67 super(loader);
68 }
69
70
71
72
73
74
75
76
77
78 @Override
79 public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
80 getLoggerContext().putObjectIfAbsent(ENVIRONMENT_KEY, initializationContext.getEnvironment());
81 super.initialize(initializationContext, configLocation, logFile);
82 }
83
84 @Override
85 protected String[] getStandardConfigLocations() {
86 String[] locations = super.getStandardConfigLocations();
87 PropertiesUtil props = new PropertiesUtil(new Properties());
88 String location = props.getStringProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
89 if (location != null) {
90 List<String> list = new ArrayList<>(Arrays.asList(super.getStandardConfigLocations()));
91 list.add(location);
92 locations = list.toArray(Strings.EMPTY_ARRAY);
93 }
94 return locations;
95 }
96
97 @Override
98 protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
99 if (logFile != null) {
100 this.loadConfiguration(this.getBootPackagedConfigFile("log4j2-file.xml"), logFile);
101 } else {
102 this.loadConfiguration(this.getBootPackagedConfigFile("log4j2.xml"), logFile);
103 }
104 }
105
106 private String getBootPackagedConfigFile(String fileName) {
107 String defaultPath = ClassUtils.getPackageName(Log4J2LoggingSystem.class);
108 defaultPath = defaultPath.replace('.', '/');
109 defaultPath = defaultPath + "/" + fileName;
110 defaultPath = "classpath:" + defaultPath;
111 return defaultPath;
112 }
113
114 @Override
115 protected void loadConfiguration(String location, LogFile logFile) {
116 Assert.notNull(location, "Location must not be null");
117 try {
118 LoggerContext ctx = getLoggerContext();
119 String[] locations = parseConfigLocations(location);
120 if (locations.length == 1) {
121 final URL url = ResourceUtils.getURL(location);
122 final ConfigurationSource source = getConfigurationSource(url);
123 if (source != null) {
124 ctx.start(ConfigurationFactory.getInstance().getConfiguration(ctx, source));
125 }
126 } else {
127 final List<AbstractConfiguration> configs = new ArrayList<>();
128 for (final String sourceLocation : locations) {
129 final ConfigurationSource source = getConfigurationSource(ResourceUtils.getURL(sourceLocation));
130 if (source != null) {
131 final Configuration config = ConfigurationFactory.getInstance().getConfiguration(ctx, source);
132 if (config instanceof AbstractConfiguration) {
133 configs.add((AbstractConfiguration) config);
134 } else {
135 LOGGER.warn("Configuration at {} cannot be combined in a CompositeConfiguration", sourceLocation);
136 return;
137 }
138 }
139 }
140 if (configs.size() > 1) {
141 ctx.start(new CompositeConfiguration(configs));
142 } else {
143 ctx.start(configs.get(0));
144 }
145 }
146 }
147 catch (Exception ex) {
148 throw new IllegalStateException(
149 "Could not initialize Log4J2 logging from " + location, ex);
150 }
151 }
152
153 @Override
154 public void cleanUp() {
155 getLoggerContext().removeObject(ENVIRONMENT_KEY);
156 super.cleanUp();
157 }
158
159 private String[] parseConfigLocations(String configLocations) {
160 final String[] uris = configLocations.split("\\?");
161 final List<String> locations = new ArrayList<>();
162 if (uris.length > 1) {
163 locations.add(uris[0]);
164 try {
165 final URL url = new URL(configLocations);
166 final String[] pairs = url.getQuery().split("&");
167 for (String pair : pairs) {
168 final int idx = pair.indexOf("=");
169 try {
170 final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
171 if (key.equalsIgnoreCase(OVERRIDE_PARAM)) {
172 locations.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
173 }
174 } catch (UnsupportedEncodingException ex) {
175 LOGGER.warn("Bad data in configuration string: {}", pair);
176 }
177 }
178 return locations.toArray(Strings.EMPTY_ARRAY);
179 } catch (MalformedURLException ex) {
180 LOGGER.warn("Unable to parse configuration URL {}", configLocations);
181 }
182 }
183 return new String[] {uris[0]};
184 }
185
186 private ConfigurationSource getConfigurationSource(URL url) throws IOException, URISyntaxException {
187 URLConnection urlConnection = url.openConnection();
188 AuthorizationProvider provider = ConfigurationFactory.authorizationProvider(PropertiesUtil.getProperties());
189 provider.addAuthorization(urlConnection);
190 if (url.getProtocol().equals(HTTPS)) {
191 SslConfiguration sslConfiguration = SslConfigurationFactory.getSslConfiguration();
192 if (sslConfiguration != null) {
193 ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslConfiguration.getSslSocketFactory());
194 if (!sslConfiguration.isVerifyHostName()) {
195 ((HttpsURLConnection) urlConnection).setHostnameVerifier(LaxHostnameVerifier.INSTANCE);
196 }
197 }
198 }
199 File file = FileUtils.fileFromUri(url.toURI());
200 try {
201 if (file != null) {
202 return new ConfigurationSource(urlConnection.getInputStream(), FileUtils.fileFromUri(url.toURI()));
203 }
204 return new ConfigurationSource(urlConnection.getInputStream(), url, urlConnection.getLastModified());
205 } catch (FileNotFoundException ex) {
206 LOGGER.info("Unable to locate file {}, ignoring.", url.toString());
207 return null;
208 }
209 }
210
211 private LoggerContext getLoggerContext() {
212 return (LoggerContext) LogManager.getContext(false);
213 }
214 }