1818
1919import java .io .FileNotFoundException ;
2020import java .io .IOException ;
21- import java .io .InputStream ;
22- import java .net .URL ;
23- import java .net .URLConnection ;
2421import java .util .ArrayList ;
2522import java .util .Collections ;
2623import java .util .LinkedHashMap ;
3734import org .apache .logging .log4j .core .LoggerContext ;
3835import org .apache .logging .log4j .core .config .AbstractConfiguration ;
3936import org .apache .logging .log4j .core .config .Configuration ;
37+ import org .apache .logging .log4j .core .config .ConfigurationException ;
4038import org .apache .logging .log4j .core .config .ConfigurationFactory ;
41- import org .apache .logging .log4j .core .config .ConfigurationSource ;
4239import org .apache .logging .log4j .core .config .LoggerConfig ;
4340import org .apache .logging .log4j .core .config .composite .CompositeConfiguration ;
4441import org .apache .logging .log4j .core .filter .DenyAllFilter ;
45- import org .apache .logging .log4j .core .net .UrlConnectionFactory ;
46- import org .apache .logging .log4j .core .net .ssl .SslConfiguration ;
47- import org .apache .logging .log4j .core .net .ssl .SslConfigurationFactory ;
48- import org .apache .logging .log4j .core .util .AuthorizationProvider ;
4942import org .apache .logging .log4j .core .util .NameUtil ;
5043import org .apache .logging .log4j .jul .Log4jBridgeHandler ;
5144import org .apache .logging .log4j .status .StatusConsoleListener ;
7265import org .springframework .core .io .ResourceLoader ;
7366import org .springframework .util .Assert ;
7467import org .springframework .util .ClassUtils ;
75- import org .springframework .util .CollectionUtils ;
7668import org .springframework .util .StringUtils ;
7769
7870/**
@@ -100,43 +92,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
10092 */
10193 static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager" ;
10294
103- /**
104- * JSON tree parser used by Log4j 2 (optional dependency).
105- */
106- static final String JSON_TREE_PARSER_V2 = "com.fasterxml.jackson.databind.ObjectMapper" ;
107-
108- /**
109- * JSON tree parser embedded in Log4j 3.
110- */
111- static final String JSON_TREE_PARSER_V3 = "org.apache.logging.log4j.kit.json.JsonReader" ;
112-
113- /**
114- * Configuration factory for properties files (Log4j 2).
115- */
116- static final String PROPS_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory" ;
117-
118- /**
119- * Configuration factory for properties files (Log4j 3, optional dependency).
120- */
121- static final String PROPS_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory" ;
122-
123- /**
124- * YAML tree parser used by Log4j 2 (optional dependency).
125- */
126- static final String YAML_TREE_PARSER_V2 = "com.fasterxml.jackson.dataformat.yaml.YAMLMapper" ;
127-
128- /**
129- * Configuration factory for YAML files (Log4j 2, embedded).
130- */
131- static final String YAML_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory" ;
132-
133- /**
134- * Configuration factory for YAML files (Log4j 3, optional dependency).
135- */
136- static final String YAML_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.yaml.YamlConfigurationFactory" ;
137-
138- private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource ();
139-
14095 static final String ENVIRONMENT_KEY = Conventions .getQualifiedAttributeName (Log4J2LoggingSystem .class ,
14196 "environment" );
14297
@@ -157,78 +112,60 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
157112
158113 private static final Filter FILTER = DenyAllFilter .newBuilder ().build ();
159114
160- public Log4J2LoggingSystem (ClassLoader classLoader ) {
115+ private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource ();
116+
117+ private static final org .apache .logging .log4j .Logger statusLogger = StatusLogger .getLogger ();
118+
119+ private final LoggerContext loggerContext ;
120+
121+ /**
122+ * Create a new {@link Log4J2LoggingSystem} instance.
123+ * @param classLoader the class loader to use.
124+ * @param loggerContext the {@link LoggerContext} to use.
125+ */
126+ Log4J2LoggingSystem (ClassLoader classLoader , LoggerContext loggerContext ) {
161127 super (classLoader );
128+ this .loggerContext = loggerContext ;
162129 }
163130
164131 @ Override
165132 protected String [] getStandardConfigLocations () {
166- List <String > locations = new ArrayList <>();
167- addLocationsFromProperties (locations );
168- addStandardLocations (locations );
169- return StringUtils .toStringArray (locations );
170- }
171-
172- private void addLocationsFromProperties (List <String > locations ) {
173- for (String property : List .of ("log4j2.configurationFile" , "log4j.configuration.location" )) {
174- String propertyDefinedLocation = PropertiesUtil .getProperties ().getStringProperty (property );
175- if (propertyDefinedLocation != null ) {
176- locations .add (propertyDefinedLocation );
177- }
178- }
133+ // With Log4J2 we use the ConfigurationFactory
134+ throw new IllegalStateException ("Standard config locations cannot be used with Log4J2" );
179135 }
180136
181- private void addStandardLocations (List <String > locations ) {
182- LoggerContext loggerContext = getLoggerContext ();
183- String contextName = loggerContext .getName ();
184- List <String > extensions = getStandardConfigExtensions ();
185- addLocation (locations , "log4j2-test" + contextName , extensions );
186- addLocation (locations , "log4j2-test" , extensions );
187- addLocation (locations , "log4j2" + contextName , extensions );
188- addLocation (locations , "log4j2" , extensions );
137+ @ Override
138+ protected @ Nullable String getSelfInitializationConfig () {
139+ return getConfigLocation (getLoggerContext ().getConfiguration ());
189140 }
190141
191- private List <String > getStandardConfigExtensions () {
192- List <String > extensions = new ArrayList <>();
193- // These classes need to be visible by the classloader that loads Log4j Core.
194- ClassLoader classLoader = LoggerContext .class .getClassLoader ();
195- // The order of the extensions corresponds to the order in which Log4j Core 2 and
196- // 3 will try to load them, in decreasing value of @Order.
197- if (isPresent (classLoader , PROPS_CONFIGURATION_FACTORY_V2 )
198- || isPresent (classLoader , PROPS_CONFIGURATION_FACTORY_V3 )) {
199- extensions .add (".properties" );
200- }
201- if (isPresent (classLoader , YAML_CONFIGURATION_FACTORY_V2 , YAML_TREE_PARSER_V2 )
202- || isPresent (classLoader , YAML_CONFIGURATION_FACTORY_V3 )) {
203- Collections .addAll (extensions , ".yaml" , ".yml" );
204- }
205- if (isPresent (classLoader , JSON_TREE_PARSER_V2 ) || isPresent (classLoader , JSON_TREE_PARSER_V3 )) {
206- Collections .addAll (extensions , ".json" , ".jsn" );
142+ @ Override
143+ protected @ Nullable String getSpringInitializationConfig () {
144+ ConfigurationFactory configurationFactory = ConfigurationFactory .getInstance ();
145+ try {
146+ Configuration springConfiguration = configurationFactory .getConfiguration (getLoggerContext (), "-spring" ,
147+ null , getClassLoader ());
148+ String configLocation = getConfigLocation (springConfiguration );
149+ return (configLocation != null && configLocation .contains ("-spring" )) ? configLocation : null ;
207150 }
208- extensions .add (".xml" );
209- return extensions ;
210- }
211-
212- private void addLocation (List <String > locations , String location , List <String > extensions ) {
213- extensions .forEach ((extension ) -> locations .add (location + extension ));
214- }
215-
216- private boolean isPresent (ClassLoader classLoader , String ... classNames ) {
217- for (String className : classNames ) {
218- if (!isClassAvailable (classLoader , className )) {
219- return false ;
220- }
151+ catch (ConfigurationException ex ) {
152+ statusLogger .warn ("Could not load Spring-specific Log4j Core configuration" , ex );
153+ return null ;
221154 }
222- return true ;
223- }
224-
225- protected boolean isClassAvailable (ClassLoader classLoader , String className ) {
226- return ClassUtils .isPresent (className , classLoader );
227155 }
228156
229- @ Deprecated (since = "4.0.0" , forRemoval = true )
230- protected boolean isClassAvailable (String className ) {
231- return ClassUtils .isPresent (className , getClassLoader ());
157+ /**
158+ * Return the configuration location. The result may be:
159+ * <ul>
160+ * <li>{@code null}: if DefaultConfiguration is used (no explicit config loaded)</li>
161+ * <li>A file path: if provided explicitly by the user</li>
162+ * <li>A URI: if loaded from the classpath default or a custom location</li>
163+ * </ul>
164+ * @param configuration the source configuration
165+ * @return the config location or {@code null}
166+ */
167+ private @ Nullable String getConfigLocation (Configuration configuration ) {
168+ return configuration .getConfigurationSource ().getLocation ();
232169 }
233170
234171 @ Override
@@ -335,7 +272,7 @@ private void load(LoggingInitializationContext initializationContext, String loc
335272 Environment environment = initializationContext .getEnvironment ();
336273 Assert .state (environment != null , "'environment' must not be null" );
337274 applySystemProperties (environment , logFile );
338- loadConfiguration (location , logFile , overrides );
275+ reconfigure (location , overrides );
339276 }
340277
341278 private List <String > getOverrides (LoggingInitializationContext initializationContext ) {
@@ -346,66 +283,51 @@ private List<String> getOverrides(LoggingInitializationContext initializationCon
346283 return overrides .orElse (Collections .emptyList ());
347284 }
348285
349- /**
350- * Load the configuration from the given {@code location}, creating a composite using
351- * the configuration from the given {@code overrides}.
352- * @param location the location
353- * @param logFile log file configuration
354- * @param overrides the overriding locations
355- * @since 2.6.0
356- */
357- protected void loadConfiguration (String location , @ Nullable LogFile logFile , List <String > overrides ) {
286+ private void reconfigure (String location , List <String > overrides ) {
358287 Assert .notNull (location , "'location' must not be null" );
359288 try {
360289 List <Configuration > configurations = new ArrayList <>();
361- LoggerContext context = getLoggerContext ();
362- ResourceLoader resourceLoader = ApplicationResourceLoader .get ();
363- configurations .add (load (resourceLoader .getResource (location ), context ));
290+ ResourceLoader resourceLoader = ApplicationResourceLoader .get (getClassLoader ());
291+ configurations .add (load (resourceLoader , location ));
364292 for (String override : overrides ) {
365- Configuration overrideConfiguration = loadOverride (resourceLoader , override , context );
293+ Configuration overrideConfiguration = loadOverride (resourceLoader , override );
366294 if (overrideConfiguration != null ) {
367295 configurations .add (overrideConfiguration );
368296 }
369297 }
370- context .reconfigure (mergeConfigurations (configurations ));
298+ this . loggerContext .reconfigure (mergeConfigurations (configurations ));
371299 }
372300 catch (Exception ex ) {
373- throw new IllegalStateException ("Could not initialize Log4J2 logging from " + location , ex );
301+ throw new IllegalStateException ("Could not initialize Log4J2 logging from %s%s" .formatted (location ,
302+ (overrides .isEmpty () ? "" : " with overrides " + overrides )), ex );
374303 }
375304 }
376305
377- private Configuration load (Resource resource , LoggerContext context ) throws IOException {
378- ConfigurationFactory factory = ConfigurationFactory .getInstance ();
379- if (resource .isFile ()) {
380- try (InputStream inputStream = resource .getInputStream ()) {
381- return factory .getConfiguration (context , new ConfigurationSource (inputStream , resource .getFile ()));
382- }
383- }
384- URL url = resource .getURL ();
385- AuthorizationProvider authorizationProvider = ConfigurationFactory
386- .authorizationProvider (PropertiesUtil .getProperties ());
387- SslConfiguration sslConfiguration = url .getProtocol ().equals ("https" )
388- ? SslConfigurationFactory .getSslConfiguration () : null ;
389- URLConnection connection = UrlConnectionFactory .createConnection (url , 0 , sslConfiguration ,
390- authorizationProvider );
391- try (InputStream inputStream = connection .getInputStream ()) {
392- return factory .getConfiguration (context ,
393- new ConfigurationSource (inputStream , url , connection .getLastModified ()));
306+ private Configuration load (ResourceLoader resourceLoader , String location ) throws IOException {
307+ ConfigurationFactory configurationFactory = ConfigurationFactory .getInstance ();
308+ Resource resource = resourceLoader .getResource (location );
309+ Configuration configuration = configurationFactory .getConfiguration (getLoggerContext (), null , resource .getURI (),
310+ getClassLoader ());
311+ // The error handling in Log4j Core 2.25.x is not consistent, some loading and
312+ // parsing errors result in a null configuration, others in an exception.
313+ if (configuration == null ) {
314+ throw new ConfigurationException ("Could not load Log4j Core configuration from " + location );
394315 }
316+ return configuration ;
395317 }
396318
397- private @ Nullable Configuration loadOverride (ResourceLoader resourceLoader , String location , LoggerContext context )
398- throws IOException {
319+ private @ Nullable Configuration loadOverride (ResourceLoader resourceLoader , String location ) throws IOException {
399320 if (location .startsWith (OPTIONAL_PREFIX )) {
400- Resource resource = resourceLoader .getResource (location .substring (OPTIONAL_PREFIX .length ()));
321+ String actualLocation = location .substring (OPTIONAL_PREFIX .length ());
322+ Resource resource = resourceLoader .getResource (actualLocation );
401323 try {
402- return (resource .exists ()) ? load (resource , context ) : null ;
324+ return (resource .exists ()) ? load (resourceLoader , actualLocation ) : null ;
403325 }
404326 catch (FileNotFoundException ex ) {
405327 return null ;
406328 }
407329 }
408- return load (resourceLoader . getResource ( location ), context );
330+ return load (resourceLoader , location );
409331 }
410332
411333 private Configuration mergeConfigurations (List <Configuration > configurations ) {
@@ -417,33 +339,9 @@ private Configuration mergeConfigurations(List<Configuration> configurations) {
417339
418340 @ Override
419341 protected void reinitialize (LoggingInitializationContext initializationContext ) {
420- List <String > overrides = getOverrides (initializationContext );
421- if (!CollectionUtils .isEmpty (overrides )) {
422- reinitializeWithOverrides (overrides );
423- }
424- else {
425- LoggerContext context = getLoggerContext ();
426- context .reconfigure ();
427- }
428- }
429-
430- private void reinitializeWithOverrides (List <String > overrides ) {
431- LoggerContext context = getLoggerContext ();
432- List <Configuration > configurations = new ArrayList <>();
433- configurations .add (context .getConfiguration ());
434- ResourceLoader resourceLoader = ApplicationResourceLoader .get ();
435- for (String override : overrides ) {
436- try {
437- Configuration overrideConfiguration = loadOverride (resourceLoader , override , context );
438- if (overrideConfiguration != null ) {
439- configurations .add (overrideConfiguration );
440- }
441- }
442- catch (IOException ex ) {
443- throw new RuntimeException ("Failed to load overriding configuration from '" + override + "'" , ex );
444- }
445- }
446- context .reconfigure (mergeConfigurations (configurations ));
342+ String currentLocation = getSelfInitializationConfig ();
343+ Assert .notNull (currentLocation , "'currentLocation' must not be null" );
344+ load (initializationContext , currentLocation , null );
447345 }
448346
449347 @ Override
@@ -584,8 +482,8 @@ public void cleanUp() {
584482 return configuration .getLoggers ().get (name );
585483 }
586484
587- private LoggerContext getLoggerContext () {
588- return ( LoggerContext ) LogManager . getContext ( false ) ;
485+ LoggerContext getLoggerContext () {
486+ return this . loggerContext ;
589487 }
590488
591489 private boolean isAlreadyInitialized (LoggerContext loggerContext ) {
@@ -630,7 +528,11 @@ public static class Factory implements LoggingSystemFactory {
630528 @ Override
631529 public @ Nullable LoggingSystem getLoggingSystem (ClassLoader classLoader ) {
632530 if (PRESENT ) {
633- return new Log4J2LoggingSystem (classLoader );
531+ org .apache .logging .log4j .spi .LoggerContext spiLoggerContext = LogManager .getContext (classLoader , false );
532+ Assert .state (spiLoggerContext instanceof LoggerContext , "" );
533+ if (spiLoggerContext instanceof LoggerContext coreLoggerContext ) {
534+ return new Log4J2LoggingSystem (classLoader , coreLoggerContext );
535+ }
634536 }
635537 return null ;
636538 }
0 commit comments