SLF4J - Logback: How to configure loggers in runtime? - java

we are using LogBack with our project, I want to configure logger according to some Data Base values, i.e If some DB value is set to true, then logger should use both file and DB appenders, if it's false so logger must use only DB appender,
I also want to preserve using static final loggers, so I won't create a new instance each time logger is called,
so how could I do something like this?
Regards,

You should configure Logback programmatically as described in this example.
public class Main {
public static void main(String[] args) {
Logger logger = (Logger) LoggerFactory.getLogger("abc.xyz");
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
FileAppender<LoggingEvent> fileAppender =
(FileAppender<LoggingEvent>) logger.getAppender("file");
if(fileAppender != null) {
fileAppender.stop();
fileAppender.setFile("new.log");
PatternLayout pl = new PatternLayout();
pl.setPattern("%d %5p %t [%c:%L] %m%n)");
pl.setContext(lc);
pl.start();
fileAppender.setLayout(pl);
fileAppender.setContext(lc);
fileAppender.start();
}
... etc
}
}

Is there a specific reason behind reading the configuration property from the database? One suggestion would be to use JNDI. Logback can read JNDI configured values using the tag.

Related

Migration from log4j to log4j2 - FileAppender set new file name & acitvateOptions

We are migrating to log4j2. I can't find anywhere how to rewrite a part of code.
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
RollingFileAppender fileAppender = config.getAppender("file");
(...)
fileAppender.setFile(newFileName); //this part
fileAppender.activateOptions(); //and this part
Does anyone know how it should be written correctly? Any help would be greatly appreciated and I am sorry if it is a dumb question.
EDIT:
I also have a problem with my class which (with log4j v.1) implements LoggerFactory. I found here: log4j migration to log4j2 that I should use "other mechanism". I am not sure if I should use LoggerContextFactory here or something else:
class MyNameLoggerFactory implements LoggerFactory {
(...)
void init() {
//sets quiet mode
//init DOMConfigutator
//configure using log4j config xml
}
Appender getFileAppender(File file) {
//returns a FileAppender
}
#Override
public MyNameLoggerFactory makeNewLoggerInstance(String name) {
return new MyNameLoggerFactory (name);
}
}
The XML configuration loaded here has appenders like an EmailSender, ConsoleAppender and RollingFileAppender (I will need to convert the xml too, I think).
If I understood this How to specify Log4J 2.x config location? correctly, instead of DOMConfigurator I will use here (in my init method) an initialize method with null ClassLoader?
This is really an old project, written by many different people during the years, and it is a mess. Thank you for any help.
You can try this way.
Layout<? extends Serializable> old_layout = fileAppender.getLayout();
fileAppender.stop();
//delete old appender
((org.apache.logging.log4j.core.Logger)logger).removeAppender(fileAppender);
RollingFileAppender appender = RollingFileAppender.newBuilder().withFileName(newFileName).withAppend(true).withLocking(false)
.setName(fileAppender.getName()).setIgnoreExceptions(true).withFilePattern(newFileName.concat(".%d")).withPolicy(fileAppender.getTriggeringPolicy())
.withStrategy(DefaultRolloverStrategy.newBuilder().withMax(String.valueOf(5)).build())
.setLayout(old_layout).setConfiguration(config).build();
appender.start();
((org.apache.logging.log4j.core.Logger)logger).addAppender(appender);

Logback multiple loggers with different configuration

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");

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);
}

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