1616
1717package  org .springframework .boot .logging .log4j2 ;
1818
19- import  java .io .FileNotFoundException ;
2019import  java .io .IOException ;
21- import  java .io .InputStream ;
22- import  java .net .URL ;
23- import  java .net .URLConnection ;
2420import  java .util .ArrayList ;
2521import  java .util .Collections ;
2622import  java .util .LinkedHashMap ;
3733import  org .apache .logging .log4j .core .LoggerContext ;
3834import  org .apache .logging .log4j .core .config .AbstractConfiguration ;
3935import  org .apache .logging .log4j .core .config .Configuration ;
36+ import  org .apache .logging .log4j .core .config .ConfigurationException ;
4037import  org .apache .logging .log4j .core .config .ConfigurationFactory ;
41- import  org .apache .logging .log4j .core .config .ConfigurationSource ;
4238import  org .apache .logging .log4j .core .config .LoggerConfig ;
4339import  org .apache .logging .log4j .core .config .composite .CompositeConfiguration ;
4440import  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 ;
4941import  org .apache .logging .log4j .core .util .NameUtil ;
5042import  org .apache .logging .log4j .jul .Log4jBridgeHandler ;
5143import  org .apache .logging .log4j .status .StatusConsoleListener ;
7264import  org .springframework .core .io .ResourceLoader ;
7365import  org .springframework .util .Assert ;
7466import  org .springframework .util .ClassUtils ;
75- import  org .springframework .util .CollectionUtils ;
7667import  org .springframework .util .StringUtils ;
7768
7869/** 
8879 */ 
8980public  class  Log4J2LoggingSystem  extends  AbstractLoggingSystem  {
9081
82+ 	private  static  final  org .apache .logging .log4j .Logger  STATUS_LOGGER  = StatusLogger .getLogger ();
83+ 
9184	private  static  final  String  OPTIONAL_PREFIX  = "optional:" ;
9285
9386	/** 
@@ -100,41 +93,6 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
10093	 */ 
10194	static  final  String  LOG4J_LOG_MANAGER  = "org.apache.logging.log4j.jul.LogManager" ;
10295
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- 
13896	private  static  final  SpringEnvironmentPropertySource  propertySource  = new  SpringEnvironmentPropertySource ();
13997
14098	static  final  String  ENVIRONMENT_KEY  = Conventions .getQualifiedAttributeName (Log4J2LoggingSystem .class ,
@@ -157,73 +115,69 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
157115
158116	private  static  final  Filter  FILTER  = DenyAllFilter .newBuilder ().build ();
159117
160- 	public  Log4J2LoggingSystem (ClassLoader  classLoader ) {
161- 		super (classLoader );
162- 	}
118+ 	private  final  LoggerContext  loggerContext ;
163119
164- 	@ Override 
165- 	protected  String [] getStandardConfigLocations () {
166- 		List <String > locations  = new  ArrayList <>();
167- 		addLocationsFromProperties (locations );
168- 		addStandardLocations (locations );
169- 		return  StringUtils .toStringArray (locations );
120+ 	/** 
121+ 	 * Create a new {@link Log4J2LoggingSystem} instance. 
122+ 	 * @param classLoader the class loader to use. 
123+ 	 * @param loggerContext the {@link LoggerContext} to use. 
124+ 	 */ 
125+ 	Log4J2LoggingSystem (ClassLoader  classLoader , LoggerContext  loggerContext ) {
126+ 		super (classLoader );
127+ 		this .loggerContext  = loggerContext ;
170128	}
171129
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- 			}
130+ 	/** 
131+ 	 * Create a new {@link Log4J2LoggingSystem} instance. 
132+ 	 * @param classLoader the class loader to use 
133+ 	 * @return a new {@link Log4J2LoggingSystem} instance 
134+ 	 * @throws IllegalStateException if Log4j Core is not the active Log4j API provider. 
135+ 	 */ 
136+ 	private  static  Log4J2LoggingSystem  createLoggingSystem (ClassLoader  classLoader ) {
137+ 		org .apache .logging .log4j .spi .LoggerContext  loggerContext  = LogManager .getContext (classLoader , false );
138+ 		if  (loggerContext  instanceof  LoggerContext ) {
139+ 			return  new  Log4J2LoggingSystem (classLoader , (LoggerContext ) loggerContext );
178140		}
141+ 		throw  new  IllegalStateException ("Log4j Core is not the active Log4j API provider" );
179142	}
180143
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 );
189- 	}
190- 
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" );
207- 		}
208- 		extensions .add (".xml" );
209- 		return  extensions ;
144+ 	/** 
145+ 	 * {@inheritDoc} 
146+ 	 * @deprecated Since 4.0.0, in favor of the {@link ConfigurationFactory} SPI. 
147+ 	 */ 
148+ 	@ Override 
149+ 	@ Deprecated (since  = "4.0.0" , forRemoval  = true )
150+ 	protected  String [] getStandardConfigLocations () {
151+ 		return  new  String [] { "log4j2.xml"  };
210152	}
211153
212- 	private  void  addLocation (List <String > locations , String  location , List <String > extensions ) {
213- 		extensions .forEach ((extension ) -> locations .add (location  + extension ));
154+ 	@ Override 
155+ 	protected  @ Nullable  String  getSelfInitializationConfig () {
156+ 		Configuration  currentConfiguration  = getLoggerContext ().getConfiguration ();
157+ 		return  getConfigLocation (currentConfiguration );
214158	}
215159
216- 	private  boolean  isPresent (ClassLoader  classLoader , String ... classNames ) {
217- 		for  (String  className  : classNames ) {
218- 			if  (!isClassAvailable (classLoader , className )) {
219- 				return  false ;
220- 			}
160+ 	@ Override 
161+ 	protected  @ Nullable  String  getSpringInitializationConfig () {
162+ 		ConfigurationFactory  configurationFactory  = ConfigurationFactory .getInstance ();
163+ 		try  {
164+ 			Configuration  springConfiguration  = configurationFactory .getConfiguration (getLoggerContext (), "-spring" ,
165+ 					null , getClassLoader ());
166+ 			String  configLocation  = getConfigLocation (springConfiguration );
167+ 			return  (configLocation  != null  && configLocation .contains ("-spring" )) ? configLocation  : null ;
168+ 		}
169+ 		catch  (ConfigurationException  ex ) {
170+ 			STATUS_LOGGER .warn ("Could not load Spring-specific Log4j Core configuration" , ex );
171+ 			return  null ;
221172		}
222- 		return  true ;
223173	}
224174
225- 	protected  boolean  isClassAvailable (ClassLoader  classLoader , String  className ) {
226- 		return  ClassUtils .isPresent (className , classLoader );
175+ 	private  @ Nullable  String  getConfigLocation (Configuration  configuration ) {
176+ 		// The location may be: 
177+ 		// - null: if DefaultConfiguration is used (no explicit config loaded) 
178+ 		// - a file path: if provided explicitly by the user 
179+ 		// - a URI: if loaded from the classpath default or a custom location 
180+ 		return  configuration .getConfigurationSource ().getLocation ();
227181	}
228182
229183	@ Deprecated (since  = "4.0.0" , forRemoval  = true )
@@ -335,7 +289,7 @@ private void load(LoggingInitializationContext initializationContext, String loc
335289		Environment  environment  = initializationContext .getEnvironment ();
336290		Assert .state (environment  != null , "'environment' must not be null" );
337291		applySystemProperties (environment , logFile );
338- 		loadConfiguration (location ,  logFile , overrides );
292+ 		reconfigure (location , overrides );
339293	}
340294
341295	private  List <String > getOverrides (LoggingInitializationContext  initializationContext ) {
@@ -346,66 +300,56 @@ private List<String> getOverrides(LoggingInitializationContext initializationCon
346300		return  overrides .orElse (Collections .emptyList ());
347301	}
348302
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 ) {
303+ 	private  void  reconfigure (String  location , List <String > overrides ) {
358304		Assert .notNull (location , "'location' must not be null" );
359305		try  {
360306			List <Configuration > configurations  = new  ArrayList <>();
361- 			LoggerContext  context  = getLoggerContext ();
362- 			ResourceLoader  resourceLoader  = ApplicationResourceLoader .get ();
363- 			configurations .add (load (resourceLoader .getResource (location ), context ));
307+ 			ResourceLoader  resourceLoader  = ApplicationResourceLoader .get (getClassLoader ());
308+ 			configurations .add (load (resourceLoader , location ));
364309			for  (String  override  : overrides ) {
365- 				Configuration  overrideConfiguration  = loadOverride (resourceLoader , override ,  context );
310+ 				Configuration  overrideConfiguration  = loadOverride (resourceLoader , override );
366311				if  (overrideConfiguration  != null ) {
367312					configurations .add (overrideConfiguration );
368313				}
369314			}
370- 			context .reconfigure (mergeConfigurations (configurations ));
315+ 			this . loggerContext .reconfigure (mergeConfigurations (configurations ));
371316		}
372317		catch  (Exception  ex ) {
373- 			throw  new  IllegalStateException ("Could not initialize Log4J2 logging from "  + location , ex );
318+ 			String  message  = "Could not initialize Log4J2 logging from "  + location ;
319+ 			if  (!overrides .isEmpty ()) {
320+ 				message  += " with overrides "  + overrides ;
321+ 			}
322+ 			throw  new  IllegalStateException (message , ex );
374323		}
375324	}
376325
377- 	private  Configuration  load (Resource   resource ,  LoggerContext   context ) throws  IOException  {
326+ 	private  Configuration  load (ResourceLoader   resourceLoader ,  String   location ) throws  IOException  {
378327		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 ()));
328+ 		Resource  resource  = resourceLoader .getResource (location );
329+ 		Configuration  configuration  = factory .getConfiguration (getLoggerContext (), null , resource .getURI (),
330+ 				getClassLoader ());
331+ 		// The error handling in Log4j Core 2.25.x is not consistent: 
332+ 		// some loading and parsing errors result in a null configuration, 
333+ 		// others in an exception. 
334+ 		if  (configuration  == null ) {
335+ 			throw  new  ConfigurationException ("Could not load Log4j Core configuration from "  + location );
394336		}
337+ 		return  configuration ;
395338	}
396339
397- 	private  @ Nullable  Configuration  loadOverride (ResourceLoader  resourceLoader , String  location , LoggerContext  context )
398- 			throws  IOException  {
340+ 	private  @ Nullable  Configuration  loadOverride (ResourceLoader  resourceLoader , String  location ) throws  IOException  {
399341		if  (location .startsWith (OPTIONAL_PREFIX )) {
400- 			Resource  resource  = resourceLoader .getResource (location .substring (OPTIONAL_PREFIX .length ()));
342+ 			String  actualLocation  = location .substring (OPTIONAL_PREFIX .length ());
343+ 			Resource  resource  = resourceLoader .getResource (actualLocation );
401344			try  {
402- 				return  (resource .exists ()) ? load (resource ,  context ) : null ;
345+ 				return  (resource .exists ()) ? load (resourceLoader ,  actualLocation ) : null ;
403346			}
404- 			catch  (FileNotFoundException  ex ) {
347+ 			catch  (ConfigurationException  | IOException  ex ) {
348+ 				STATUS_LOGGER .debug ("Could not load optional Log4j2 override from {}" , actualLocation , ex );
405349				return  null ;
406350			}
407351		}
408- 		return  load (resourceLoader . getResource ( location ),  context );
352+ 		return  load (resourceLoader ,  location );
409353	}
410354
411355	private  Configuration  mergeConfigurations (List <Configuration > configurations ) {
@@ -417,33 +361,11 @@ private Configuration mergeConfigurations(List<Configuration> configurations) {
417361
418362	@ Override 
419363	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 ));
364+ 		String  currentLocation  = getSelfInitializationConfig ();
365+ 		// `reinitialize` is only triggered when `getSelfInitializationConfig` returns a 
366+ 		// non-null value 
367+ 		Assert .notNull (currentLocation , "'currentLocation' must not be null" );
368+ 		load (initializationContext , currentLocation , null );
447369	}
448370
449371	@ Override 
@@ -584,8 +506,8 @@ public void cleanUp() {
584506		return  configuration .getLoggers ().get (name );
585507	}
586508
587- 	private   LoggerContext  getLoggerContext () {
588- 		return  ( LoggerContext )  LogManager . getContext ( false ) ;
509+ 	LoggerContext  getLoggerContext () {
510+ 		return  this . loggerContext ;
589511	}
590512
591513	private  boolean  isAlreadyInitialized (LoggerContext  loggerContext ) {
@@ -622,15 +544,20 @@ protected String getDefaultLogCorrelationPattern() {
622544	@ Order (0 )
623545	public  static  class  Factory  implements  LoggingSystemFactory  {
624546
625- 		static  final  String  LOG4J_CORE_CONTEXT_FACTORY  = "org.apache.logging.log4j.core.impl.Log4jContextFactory" ;
547+ 		private   static  final  String  LOG4J_CORE_CONTEXT_FACTORY  = "org.apache.logging.log4j.core.impl.Log4jContextFactory" ;
626548
627549		private  static  final  boolean  PRESENT  = ClassUtils .isPresent (LOG4J_CORE_CONTEXT_FACTORY ,
628550				Factory .class .getClassLoader ());
629551
630552		@ Override 
631553		public  @ Nullable  LoggingSystem  getLoggingSystem (ClassLoader  classLoader ) {
632554			if  (PRESENT ) {
633- 				return  new  Log4J2LoggingSystem (classLoader );
555+ 				try  {
556+ 					return  createLoggingSystem (classLoader );
557+ 				}
558+ 				catch  (IllegalStateException  ex ) {
559+ 					// Log4j Core is not the active Log4j API provider 
560+ 				}
634561			}
635562			return  null ;
636563		}
0 commit comments