Logback multiple loggers with different configuration - java

I want to use 2 different loggers in a class where one logger uses one logback configuration file and another uses another configuration file. eg:
class Sample {
// LOGGER 1 follows configuration from logback1.xml
private static final LOGGER1 = LoggerFactory.getLogger(Sample.class);
// LOGGER 2 follows configuration from logback2.xml
private static final LOGGER2 = LoggerFactory.getLogger(Sample.class);
public myFunc(){
LOGGER1.info("In myFunc"); // writes to file as configured in logback1.xml
LOGGER2.info("Entered myFunc"); // writes to graylog server as configured in logback2.xml
}
}
The reason I want to do this is that I'm injecting a second logger into code at runtime, and I don't want the injected logger to collide with the logger used in the main project. How should I go about doing this?
I went through this post: How to use multiple configurations with logback in a single project? But it seems to address the problem of choosing one configuration file from many but not "being able to use 2 configuration files simultaneously as in the above example."

This is one way to get two loggers from different configs:
LoggerContext c1 = new LoggerContext();
LoggerContext c2 = new LoggerContext();
new ContextInitializer(c1).configureByResource(new URL("file:logback1.xml"));
new ContextInitializer(c2).configureByResource(new URL("file:logback2.xml"));
Logger l1 = c1.getLogger("x");
Logger l2 = c2.getLogger("x");

Related

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.

Shutting up log4j for good

Im using a 3rd party jar that uses log4j and spams really way too much (makes eclipse crash), is there a way to wipe all logging as if log4j never existed in the first place? programmatically or by project setup anything is fine as long as it stfu.
i tried
List<Logger> loggers = Collections.<Logger>list(LogManager.getCurrentLoggers());
loggers.add(LogManager.getRootLogger());
for ( Logger logger : loggers ) {
logger.setLevel(Level.OFF);
}
But doesnt compile in my setup:
LogManager.getCurrentLoggers() and
LogManager.getRootLogger() do not exist.
Log4j2 uses multiple contexts. Adapting the above code:
LoggerContext ctx = (LoggerContext) LogManager.getContext();
Configuration config = ctx.getConfiguration();
Collection<LoggerConfig> loggers = config.getLoggers().values();
for(LoggerConfig cfg: loggers) {
cfg.setLevel(Level.OFF);
}

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)

Creating multiple log instances using log4j to output log files in separate locations

Hello StackOverflow community,
I am writing a multi-threaded application in which I need a log file output for each thread using log4j. Ideally, I would like to have a separate log instance running for each thread. I am debating as to how I should tackle this. Because the location of the file output is different for each log instance, I need to update the log4j.appender.BrokerFile.File= property for each instance. As of right now I am trying to create a new Logger, set the new properties into a properties object, and load the new properties into my newly created Logger object using PropertyConfigurator.configure(log4jProperties); While the new Logger instance is being created just fine, the new properties do not seem to be recognized by the new instance. Do I need to create a new FileAppender for each new Logger instance?
public class Broker implements Runnable{
private Settings ss; //The Settings object associated with this broker.
private String loggerName = null;
private Logger log = null;
private Properties log4jProperties = new Properties();
}
//Constructor
public Broker(Settings ss){
this.ss = ss;
}
public void run() {
loggerName = ss.getBrokerName() + "_Logger";
log = Logger.getLogger(loggerName);
log4jProperties.setProperty("log4j.loggerName", "DEBUG, FILE");
log4jProperties.setProperty("log4j.appender.FILE","org.apache.log4j.FileAppender");
log4jProperties.setProperty("log4j.appender.FILE.File", "C:/Logz/"+loggerName+".out");
log4jProperties.setProperty("log4j.appender.FILE.ImmediateFlush", "true");
log4jProperties.setProperty("log4j.appender.FILE.Threshold", "DEBUG");
log4jProperties.setProperty("log4j.appender.FILE.Append", "FALSE");
log4jProperties.setProperty("log4j.appender.FILE.layout", "org.apache.log4j.PatternLayout");
log4jProperties.setProperty("log4j.appender.FILE.layout.conversionPattern", "%m%n");
PropertyConfigurator.configure(log4jProperties);
log.debug("This is a debug message");
// This will be set to true as long as the Broker is running
while(isRunnable){
//Listen for and process files
}
}
Here is the console output. As you can see the new logger instances are being created but the new properties are not being recognized.
log4j:WARN No appenders could be found for logger (Broker1_Logger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
log4j:WARN No appenders could be found for logger (Broker2_Logger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
log4j:WARN No appenders could be found for logger (Broker3_Logger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
This goes on for every broker. Is there any way to directly apply my configuration settings to the Logger object? For example, something like
log.PropertyConfigurator.configure(log4jproperties);
There must be a way to apply the configuration settings to each logger object created.
Thank You,
Jim
It seems that creating a new appender for each new instance is necessary, since each instance needs to print to a different file output location. I solved this problem by creating a generateLogger() method with the necessary parameters to create new loggers and appenders programmitcally. If you will notice the first parameter contains the name of a parent logger. If I include this in my new logger name, it will inherit the properties of its parent, which is configured in my master.properties file, and vice versa. For example, if my parent logger is "Broker" and my logger name is "Broker1", then the full loggerName will be "Broker.Broker1". This way I can track all the brokers in one log file while also having separate log files for each broker in their own separate file output locations. Not to mention, this method can be re-used with many different kinds of Logs using different parent loggers. Though the parent Logger is not even necessary if you choose. If you choose to not have a parent Logger, simply delete it from the method and disregard my master.properties file.
public Logger generateLogger(String parent, String name, String logDirectory, String filePattern, String fileThreshold) {
// TODO Auto-generated method stub
//Create Logger
String loggerName = parent + "." + name;
Logger log = Logger.getLogger(loggerName);
//Create Logging File Appender
RollingFileAppender fileApp = new RollingFileAppender();
fileApp.setName("Broker." + loggerName + "_FileAppender");
fileApp.setFile(logDirectory +"/"+ name+".log");
fileApp.setLayout(new PatternLayout(filePattern));
fileApp.setThreshold(Level.toLevel(fileThreshold));
fileApp.setAppend(true);
fileApp.activateOptions();
log.addAppender(fileApp);
return log;
}
Here is the Broker logger configuration inside my master.properties file for reference.
log4j.logger.Broker=DEBUG, BrokerFile
# Broker Appenders
# Broker File Appender
log4j.appender.BrokerFile=org.apache.log4j.RollingFileAppender
log4j.appender.BrokerFile.File=C:/Documents and Settings/gr2cher/My Documents/KTLO/Java/CMInbound/BrokerLogs/Logs/Broker.log
log4j.appender.BrokerFile.MaxFileSize=1MB
log4j.appender.BrokerFile.MaxBackupIndex=1
log4j.appender.BrokerFile.layout=org.apache.log4j.PatternLayout
log4j.appender.BrokerFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.appender.BrokerFile.threshold=DEBUG

Log into different files with the same logger with Log4j

I have a Class "Example.class". I get my Logger with:
private final Logger log = Logger.getLogger(Example.class);
My log4j.properties looks like:
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=log.txt
log4j.appender.file.MaxFileSize=1024KB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%p %t %c - %m%n
My Class is logging via log.info(..), log.debug(..) and so on into the file log.txt.
Now i want to have a different logger for the same class e.g.:
private final Logger differentLogger = Logger.getLogger(Example.class);
and this logger should log into a different file for example
differentLogger.info("Hello World");
//writes 'Hello World' into differentLogFile.txt
Is this possible somehow?
No, the category identifies the logger.
That said, you are not forced to use the class as the category. You could use in the same class:
private final Logger log = Logger.getLogger(Example.class);
private final Logger differentLogger = Logger.getLogger(mylogs.different);
After that, in the configuration file you can define several appenders and assign each category to a different appender.
Calling Logger.getLogger(Example.class) twice will return the same or undistinguishable instances of Logger. This means Log4J cannot make any distinction between them and they will always land up in the same file.
The solution is obvious: use a different logger!
Logger differentLogger = Logger.getLogger("differentLogger");
And configure log4j.xml to use different appender for differentLogger.

Categories