Log4J 1.2 PropertyConfigurator -> Log4J2 - java

Currently, our application uses Log4J 1.2 and configures it using either
File file = ...
PropertyConfigurator.configure(file.getAbsolutePath());
or
URL url = ...
PropertyConfigurator.configure(url);
I know that the property file format has changed from 1.2 to 2, but what would be a similar way to configure Log4J 2 using a property file at an arbitrary file or URL?

You can use PropertiesConfigurationBuilder as follows:
// Custom-loaded properties.
Properties props = ...
// Beware it should be org.apache.logging.log4j.core.LoggerContext class,
// not the one ins spi package!
// Not sure about the meaning of "false".
LoggerContext context = (LoggerContext)LogManager.getContext(false);
Configuration config = new PropertiesConfigurationBuilder()
.setConfigurationSource(ConfigurationSource.NULL_SOURCE)
.setRootProperties(props)
.setLoggerContext(context)
.build();
context.setConfiguration(config);
Configurator.initialize(config);
It's true that using the core classes looks like a hack but the author himself uses them in his tutotrial: https://logging.apache.org/log4j/log4j-2.3/manual/customconfig.html .

From Log4J 2's documentation:
// import org.apache.logging.log4j.core.LoggerContext;
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
File file = new File("path/to/a/different/log4j2.xml");
// this will force a reconfiguration
context.setConfigLocation(file.toURI());
Make sure to refer to org.apache.logging.log4j.core.LoggerContext (defined in the log4j-core artifact, not the log4j-api one) and not to org.apache.logging.log4j.spi.LoggerContext.

Related

Loading a datePattern from application-properties file in Logback and Spring Boot

In my logback-spring.xml file, I have a defined a timestamp like so:
<timestamp key="date" datePattern="yyyyMMdd"/>
I wanted to know if it is possible to load this datePattern value from my application-properties file. I have defined a property logging.date.format=yyyyMMdd which I am using in other parts of the code and it would be really helpful if I could use this in my logback file as well so that I have to only make changes in a single place.
I pass properties to logback in my application. In my webapp initializer obtain the LoggerContext and input the properties. I reset the context because I change some other settings, don't know if it is required.
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
context.reset();
context.putProperty("prop-name", "prop-value");
jc.doConfigure(config)
In logback file, you can then use the properties line any other
${prop-name}

capture logs in a test

I'm trying to capture bean allocation logs in a test - I've got code that I've tested, and will successfully capture logs from my classes - but when I try to do it on spring classes it is seemingly not working - here is the code I use to try and capture:
LoggerContext context = (LoggerContext) (LoggerFactory.getILoggerFactory());
Logger log = context.getLogger("org.springframework.beans.factory.support.DefaultListableBeanFactory");
log.setLevel(Level.DEBUG);
MyAppender appender = new MyAppender();
appender.setContext( context);
log.addAppender( appender );
SpringApplication newApplication = new SpringApplication( Application.class);
newApplication.run( new String [] {});
Now if I trace in and look at the logger that spring is using - it looks like a completely different style of logger - (its hooked to a logmanager, not a loggercontext) - and go into that and it seems like it might be a different context?
Any idea what I'm doing wrong, and how I can in a unit test capture spring bean creation logs?
Spring boot is using Logback logger by default
It uses LogbackLoggingSystem implementation which
extends from AbstractLoggingSystem
Spring boot LoggingSystem runs before context is initialized
To override default properties you can define logback.xml or logback-spring.xml
Or you can use application.yml or properties file to define log configurations :
logging.level.* : It is used as prefix with package name to set log level.
logging.file : It configures a log file name to log message in file. We can also configure file name with absolute path.
logging.path : It only configures path for log file. Spring boot creates a log file with name spring.log
logging.pattern.console : It defines logging pattern in console.
logging.pattern.file: It defines logging pattern in file.
logging.pattern.level: It defines the format to render log level. Default is %5p.
As documentation says:
You can force Spring Boot to use a particular logging system by using the org.springframework.boot.logging.LoggingSystem system property. The value should be the fully qualified class name of a LoggingSystem implementation. You can also disable Spring Boot’s logging configuration entirely by using a value of none.
If you use static Loggers in your Class under Test, you could use Powermock to mock the logger and assert the output, as descirbed in this question.
We use it in our Spring-Tests and formatting and style is the same.
for anyone interested - this finally worked for me:
Logger logger = Logger.getLogger(
"org.springframework.beans.factory.support.DefaultListableBeanFactory");
logger.addHandler( this );
logger.setLevel( java.util.logging.Level.FINE);
_logger = logger;
now I can capture, trace and time all bean allocations.

Equivalent programmatic logger initialization for log4j to log4j2 migration

I am trying to understand the programmatic side of log4j2, as I am migrating a lot of log4j 1.2 code. The following seems to be very different and more complicated to accomplish:
org.apache.log4j.Logger.getRootLogger().setLevel(Level.FATAL);
org.apache.log4j.Logger.getRootLogger().addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN), "System.err"));
Can someone with plenty of log4j2 experience explain to me what the simple way to migrate the 2 above lines is?
EDIT: This is what I have so far:
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME).setLevel(Level.FATAL);
PatternLayout patternLayout = PatternLayout.createLayout(PatternLayout.TTCC_CONVERSION_PATTERN, config, null, null, true, true, null, null);
Layout<? extends Serializable> layout = patternLayout;
ConsoleAppender consoleAppender = ConsoleAppender.createAppender(layout , null, "SYSTEM_ERR", "System.err", null, null);
consoleAppender.start();
config.addAppender(consoleAppender);
context.updateLoggers();
There is no way it is that complicated, right?
Unfortunately creating programmatic configuration wasn't the main concern of Log4j2. You may check on their AbstractConfiguration class sources line 580 to see how default configuration is set programmatically internally.
Log4j2 library have good support for different types of configuration files (xml, json, properties, yaml) or you can build composite configuration out of several sources. Also it tracks configuration files and is capable of dynamic reloading.
I would remmend you evaluating features mentioned above to update configuration from code. E.g.
final URL log4j = Resources.getResource("log4j2-test.xml");
ConfigurationSource configurationSource = new ConfigurationSource(
Resources.asByteSource(log4j).openStream(), log4j);
LoggerContext context = LoggerContext.getContext(false);
XmlConfiguration xmlConfiguration = new XmlConfiguration(context, configurationSource);
context.start(xmlConfiguration);
Seem easier to manage than programmatic way with larger configurations.

How to allow user to make permanent changes to log levels?

I have a Spring Boot application that uses log4j2 for logging. I have a need to adjust the logging level at run time and I have done that with a simple RESTful interface that accepts a logger name and the level it needs to be set at. I also need to be able to make permanent changes to the logging level (on just certain loggers).
a) Is there a way to persist my changes back to the log4j config file so that the next time the application is brought up, the log levels are where they had been left at, on the previous run?
b) Is there a way to read the list of loggers listed in the config file?
Thank you
a) If you have a config file, every time the server starts it will be used to configure log4j2. You could create a new config file (outside the container) and use it to configure log4j2 when the server starts:
File file = new File("/config/new/log4j2.xml");
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
ctx.setConfigLocation(file.toURI());
Or just store the new info and modify log4j programatically
b) Try this:
public Collection<Logger> getLoggers()
https://logging.apache.org/log4j/2.0/log4j-core/apidocs/org/apache/logging/log4j/core/LoggerContext.html#getLoggers()
Here's a code example (executed at the application startup):
File file = new File(log4jConfigFilePath);
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
ctx.setConfigLocation(file.toURI());
Collection<org.apache.logging.log4j.core.Logger> collection = ctx.getLoggers();
System.out.println(collection.size()); // returns 0 (No loggers instantiated)
Logger logger = LoggerFactory.getLogger("myLogger");
collection = ctx.getLoggers();
System.out.println(collection.size()); // returns 1 (myLogger instantiated)

What is an equivalent of LoggerRepository in log4j2

I want to migrate from Log4j 1.x to log4j2 so I read this article:
http://logging.apache.org/log4j/2.x/manual/migration.html
In our application we are using LoggerRepository:
Logger.getRootLogger().getLoggerRepository().getCurrentLoggers();
what is the equivalent of this piece of code in log4j2. Because in article they said we can't access to this method but they don't mention how should I replace it
If I understand your need correctly, you can go via LogManager to get the LoggerContext's configuration and retrieve the Loggers from that:
LoggerContext ctx = LogManager.getContext();
Configuration cfg = ctx.getConfiguration();
Map<String, LoggerConfig> loggers = cfg.getLoggers();

Categories