Log4J dynamically create log file - java

I have successfully created a log at each process run time. The issue I am having now is any packages that are called that are not children of the current running process does not write its log to my file. For example I create a new log file called running-.log. The process that is running is com.me.foo inside of this class there is a call to a method in com.you and another one in com.zee . I would like to have com.you and com.zee logs write to the running-.log and not to the console log. It isn't as simple as just changing the getLogger() method to be a child of com.me.foo. Some of the logs are written out from third party jars. I am at a loss. If you need to see more code or some additional info, please let me know. There has to be another way to handle this.
Thanks
Code to create the log file dynamically
public void createLogInstance(String packaging,String appenderName, String logFileName){
Logger logger = Logger.getLogger(packaging);
Appender fileAppender = logger.getAppender(appenderName);
if(fileAppender != null){
logger.removeAppender(fileAppender);
}
//Create the root appender
ConsoleAppender console = new ConsoleAppender();
String pattern = ....;
console.setLayout(new PatternLayout(pattern));
console.setThreshold(Level.FATAL);
console.activateOptions();
logger.addAppender(console);
FileAppender fa = new FileAppender();
fa.setName(appenderName);
fa.setFile(logFileName);
fa.setLayout(new PatternLayout(..));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
logger.setAdditivity(false);
logger.addAppender(fa);
}
com.zee log
private static Logger logger = LoggerFactory.getLogger(Zee.class);
com.you log
private static Logger logger = LoggerFactory.getLogger(You.class);

I was missing the rootLogger. Changed this line
Logger logger = Logger.getLogger(packaging);
to
Logger logger = Logger.getRootLogger();
If someone has a better way please let me know.

Related

Log4j2 logs are not directing to correct log file

I am facing an issue with configuring log4j2 logs programmatically.
Please see the below code, I am creating 2 objects of the class App3, and I want to create 2 debug log files(a log file per App3 object), thereafter each object should be able to log to corresponding log file.
But my code is logging all the logs to the second log file after 2nd log is created. Can someone help on this.
Output of the program
file Name: app3_logger1.log_debug.log
2020-06-16 16:19:31,100 DEBUG app3_logger1.log [main] Hello from Log4j 2app3_logger1
file Name: app3_logger2.log_debug.log
2020-06-16 16:19:31,211 DEBUG app3_logger2.log [main] Hello from Log4j 2app3_logger2
2020-06-16 16:19:31,216 DEBUG app3_logger2.log [main] Hello from Log4j 2app3_logger2
2020-06-16 16:19:31,216 DEBUG app3_logger1.log [main] Hello from Log4j 2app3_logger1
2020-06-16 16:19:31,216 DEBUG app3_logger1.log [main] Hello from Log4j 2app3_logger1
2020-06-16 16:19:31,217 DEBUG app3_logger2.log [main] Hello from Log4j 2app3_logger2
Java Class - you just need to add log4j2 dependencies to compile
public class App3 {
public Logger logger;
public static void main(String[] args) {
App3 app1 = new App3();
app1.initializeYourLogger("app3_logger1.log", "%d %p %c [%t] %m%n");
app1.testProg("app3_logger1");
App3 app2 = new App3();
app2.initializeYourLogger("app3_logger2.log", "%d %p %c [%t] %m%n");
app2.testProg("app3_logger2");
app2.testProg("app3_logger2");
app1.testProg("app3_logger1");
app1.testProg("app3_logger1");
app2.testProg("app3_logger2");
}
public void testProg(String s) {
logger.debug("Hello from Log4j 2" + s);
}
public void initializeYourLogger(String fileName, String pattern) {
this.logger = LogManager.getLogger(fileName);
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.DEBUG);
builder.setConfigurationName(fileName);
AppenderComponentBuilder componentBuilder = builder.newAppender("log", "File");
componentBuilder.add(builder.newLayout("PatternLayout").addAttribute("pattern", pattern));
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG);
LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout").addAttribute("pattern", pattern);
ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "10MB"));
componentBuilder = builder.newAppender("LogToRollingErrorFile", "RollingFile")
.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
.addAttribute("level", Level.ERROR))
.addAttribute("fileName", fileName + "_error.log")
.addAttribute("filePattern", fileName + "-%d{MM-dd-yy-HH-mm-ss}_error.log").add(layoutBuilder)
.addComponent(triggeringPolicy);
builder.add(componentBuilder);
componentBuilder = builder.newAppender("LogToRollingDebugFile", "RollingFile")
.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
.addAttribute("level", Level.DEBUG))
.addAttribute("fileName", fileName + "_debug.log")
.addAttribute("filePattern", fileName + "-%d{MM-dd-yy-HH-mm-ss}_debug.log").add(layoutBuilder)
.addComponent(triggeringPolicy);
builder.add(componentBuilder);
AppenderRefComponentBuilder rollingError = rootLogger.getBuilder().newAppenderRef("LogToRollingErrorFile");
AppenderRefComponentBuilder rollingDebug = rootLogger.getBuilder().newAppenderRef("LogToRollingDebugFile");
rootLogger.add(rollingError);
rootLogger.add(rollingDebug);
builder.add(rootLogger);
Configurator.reconfigure(builder.build());
}
}
This is exactly what I want to do in log4j older version, I am still struggling with log4j2,
private void initLogger(String serviceName, String instance) throws IOException {
String loggerName = serviceName+"_"+instance;
this.logger = Logger.getLogger(loggerName);
this.logger.removeAllAppenders();
PatternLayout layout = new PatternLayout();
layout.setConversionPattern("%d: %m%n");
String loggingFolder = this.properties.getLoggingFolder();
String debugFileName = loggingFolder+"/"+loggerName+"_debug.log";
String errorFileName = loggingFolder+"/"+loggerName+"_error.log";
RollingFileAppender debugAppender = new RollingFileAppender(layout, debugFileName, true);
debugAppender.setThreshold(Level.DEBUG);
debugAppender.setMaxFileSize("10000000");
debugAppender.setMaxBackupIndex(49);
logger.addAppender(debugAppender);
RollingFileAppender errorAppender = new RollingFileAppender(layout, errorFileName, true);
errorAppender.setThreshold(Level.ERROR);
errorAppender.setMaxFileSize("10000000");
errorAppender.setMaxBackupIndex(49);
logger.addAppender(debugAppender);
}
Actually, I am quite sure your Logger is being updated correctly. The problem is that both application instances are going to use the same Logging configuration.
If you look at Log4j's architecture you will see that the Loggers and the configuration are anchored in the LoggerContext. By default, Log4j uses the ClassLoaderContextSelector, which means that every ClassLoader can have its own LoggerContext. Your sample application only has a single ClassLoader and so will only have a single LoggerContext and, therefore, only a single Configuration.
So when you reconfigured the second time you simply replaced the prior configuration with the new one. Since the root logger is handling all log events it will route the events from both Loggers you have created to the file created in the second configuration.
If you want logs to end up in two different files then create a configuration with both files and figure out a way to route them to the correct file, either via logger names or filters.
As per observation, your logger is updated with the last initialization so after app2.initializeYourLogger() your object-level logger object updated with App2 configuration.
You need to return the logger object from initializeYourLogger() and also create separate Logger object.

Replacing ConsoleAppender to FileAppender in log4j

I have created a Logger over the log4j logger with ConsoleAppender.
The code is as follows,
public class AppLogger{
static {
ConsoleAppender ca = new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN));
org.apache.log4j.Logger.getRootLogger().addAppender(ca);
}
public AppLogger(Class classname) {
logger = LoggerFactory.getLogger(classname);
}
public void debug(String message){
logger.debug(message);
}
...
...
}
I have not used the log4j.properties file because I am not sure where to put it. The above code is working fine and logging properly on the console. Now I want to change it to use it with a FileAppender.
I simply updated the static constructors as follows,
FileAppender fa = new FileAppender();
fa.setFile("d:/log.txt");
fa.setLayout(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN));
org.apache.log4j.Logger.getRootLogger().addAppender(fa);
And added fa insted of ca. But now I am getting this error,
log4j:ERROR No output stream or file set for the appender named [null].
I am not sure which property I am missing to define fro the File Appender.
You forgot to activate the options:
FileAppender fa = new FileAppender();
fa.setFile("d:/log.txt");
fa.setLayout(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN));
fa.activateOptions(); \\ ← activate the options
org.apache.log4j.Logger.getRootLogger().addAppender(fa);
The method OptionHandler#activateOptions() activate the options that were previously set with calls to option setters.

Log4j get Appender from configfile by name without using logger

I would like to retrieve all the appenders from my log4j.xml by name to my Java class. there is no logger that has all appenders attached Because I cannot change the log4j.xml. Is there any way to retrieve those appenders? Some appenders aren't attached to any Logger. searching the internet it seemed not to be possible.
Not sure but maybe this code will help you:
static Set<Appender> getAllAppenders() {
Set<Appender> allAppenders = new HashSet<>();
LoggerRepository repository = LogManager.getLoggerRepository();
Enumeration<Logger> loggers = repository.getCurrentLoggers();
while (loggers.hasMoreElements()) {
Logger logger = loggers.nextElement();
Enumeration<Appender> appenders = logger.getAllAppenders();
while (appenders.hasMoreElements()) {
allAppenders.add(appenders.nextElement());
}
}
return allAppenders;
}

sl4j and logback - Is it possible to programmatically set the logging level for package?

I'm able to programmatically set the logging level on the application with the following code, but is it also possible to do this on a package level, say com.somepackage.* where I want the level to be only ERROR rather than DEBUG or INFO on said package?
// Sets the logging level to INFO
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.INFO);
But I can't seem to find a way to set it on a package level...
You should set the package name as logger-name
// Sets the package level to INFO
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger("com.somepackage");
rootLogger.setLevel(Level.INFO);
You should be able to get the package name more elegant, but this is basically it.
This follows the tree like hierarchy for the Logger Context:
Logger Context
You can do it by using logback..
Logger LOG = (Logger) org.slf4j.LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
LOG.setLevel(Level.WARN);
This solved my problem.
You can get the SLF4J logger for the package and cast it to Logback logger. Less code that #dimitri-dewaele's.
((Logger) LoggerFactory.getLogger("com.somepackage")).setLevel(DEBUG)
#nandu-prajapati's approach is similar, except that it sets the root logger level, not the one desired.
In case you don't want to change the logback-classic to a compile-time dependency, here is some code that uses reflection to set this level assuming that logback is used as the slf4j runtime-binding. Here AbcClass is the class whose logger level you want to change to TRACE:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SomeClass {
private static final Logger logger = LoggerFactory.getLogger(SomeClass .class);
public static void beforeEachTest() throws Exception {
final Logger loggerInterface = LoggerFactory.getLogger(AbcClass.class);
String loggerLevelNew = "TRACE";
if (!loggerInterface.isTraceEnabled()) {
try {
Class<?> levelLogBackClass = Class.forName("ch.qos.logback.classic.Level");
Method toLevelMethod = levelLogBackClass.getDeclaredMethod("toLevel", String.class);
Object traceLvel = toLevelMethod.invoke(null, loggerLevelNew);
Method loggerSetLevelMethod= loggerInterface.getClass().getDeclaredMethod("setLevel", levelLogBackClass);
loggerSetLevelMethod.invoke(loggerInterface, traceLvel);
} catch (Exception e) {
logger.warn("Problem setting logger level to:{}, msg: {}", loggerLevelNew, e.getMessage());
throw e;
}
}
}
}
Something similar can be done for log4j and JDK logging to make this (kind-of) library agnostic

Java Logger with Log4j

Am processing files using java , there are 2 or 3 Java components to process files.My requirement is for file must have a log file and its processing details will be logged in relevant log file.
Problem is for single file it works well but with multiple file am getting problem.
When multiple files are getting processed logger is logging log detail of file1.txt logs to file2.log instead of file1.log...
public Class FileProcessComponent1
{
public void process(String fileName)
{
Logger Log = Logg.getLogger1(fileName,this.getClass());
log.info("file1 logging");
}
}
public Class FileProcessComponent2
{
public void process(String fileName)
{
Logger Log = Logg.getLogger1(fileName,this.getClass());
log.info("file1 logging");
}
}
public Class Logg
{
public static Logger getLogger1(String fileName,Class clazz) throws Exception
{
if(fileName == null || "".equals(fileName.trim()))
throw new Exception("File Name or Map for File Name is Null");
fileName = "/home/logs/"+fileName+".log";
Logger logger = Logger.getLogger(clazz.getCanonicalName()+":"+System.nanoTime());
logger.setAdditivity(false);
FileAppender appender = new DailyRollingFileAppender(new PatternLayout("%d{ISO8601}\t%p\t%c\t%m%n"), fileName, "'.'yyyy-MM-dd");
logger.addAppender(appender);
logger.setLevel(Level.DEBUG);
return logger;
}
}
I think you want to create a logger pointing to a unqiue file for each files comes for processing.
Try this,
a) At the point where you start processing a new file, Create a new Logger with file name as Loggername.
b) Create the Appender with filename.log and assign the appender to the logger.
Now, when you try to get the logger, always use the filename in getLogger().
Once you are done with processing a file, remove the logger/appender. (this is very important)

Categories

Resources