I have the log4j2.xml which is partially configured as:
<Loggers>
<Root level="debug" includeLocation="true">
<AppenderRef ref="FileInfo" level="info" />
<AppenderRef ref="FileDebug" level="debug" />
</Root>
<Logger name="com.mycompany.domain.XYZ" level="TRACE" />
<Logger name="com.mycompany.domain.ABC" level="TRACE" />
</Loggers>
However, the TRACE messages doesn't come in either of the File appenders (FileInfo/FileDebug). When I change the FileDebug log level = "TRACE", then TRACE messages come.
Question: There are multiple places to specify log 'level'. Whats the precedence order for these levels. I thought level defined at Logger tag should be highest.
You should take the time to read the log4j2 manual regarding the architecture of log4j2. In particular the sections on "LoggerConfig" and "Appender".
The behavior you describe is due to the fact that you don't have any appenders associated with your com.mycompany.domain.XYZ and com.mycompany.domain.ABC loggers, and the default value of additivity is true.
When a message reaches one of the com.mycompany... loggers and is TRACE level it will be accepted by the logger and sent to all appenders of that logger - which is NONE since there are no appenders for these loggers.
Next, since additivity is true by default the message goes to the appenders of the next ancestor in the logger hierarchy, which is root in your case. The level of the root logger does not affect whether the message is accepted since it was already accepted by the child logger. However, the message still would not appear in the output since you have the appenders' levels set more specific than TRACE, so the appenders both reject the message.
This brings me to your question about precedence. The answer to which is that the logger's level takes precedence over the appender's level. If the message is rejected by the logger it will never reach the appenders of that logger. If the message is accepted by the logger it can still be rejected by the appender if the appender's level is more specific than the message level. The tricky part is that when additivity is true (which it is by default) the appenders of ancestor loggers are treated as appenders of the child logger as I explained above.
In other words - since TRACE is the second least specific level you would need to set both your logger and your appender to a level that is TRACE or lower (e.g. ALL) in order to see those messages appear in the output. Alternatively you can simply set your logger to a level of TRACE or lower and do not set the level on the appender.
Related
I inherited an anylogic project where parts of the model logic have been implemented in a java project, from which this model is also launched.
This project has multiple logger configurations since in most parts of the project log4j2 is used, in few parts it is slf4j which uses the logback backend.
In the anylogic UI/Browser-Window, you can view some Console in which some output is printed:
Interestingly there are no logs printed, that haven been recorded with the log4j-library.
Those beeing recorded with slf4j and logback however, are also shown in the Console.
The appender configurations read like this:
# log4j2 appender
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.target = SYSTEM_OUT
appender.console.layout.type = PatternLayout
<!--- logback.xml appender --->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%highlight(%d{yyyy.MM.dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%L - %msg%n)</pattern>
</encoder>
</appender>
Now for the question: What is difference between the two logging frameworks/appenders, that can cause the observed behavior?
It seems, the SYSTEM_OUT is reassigned after configuration.
If I set the Log4J2 Console Appender to follow
appender.console.follow = true
The log is also appended to the console in the AnyLogic UI.
Apparently logback follows by default.
I have in my test automation project problem with logging. I'm using log4j2 logger with FileAppender. The way I'm using it is:
Logger logger = (Logger) LogManager.getLogger(loggerName);
Appender appender = FileAppender.newBuilder()
.withAppend(false)
.withBufferedIo(true)
.withFileName(DIR_NAME + File.separator + loggerName + ".log")
.withIgnoreExceptions(false)
.withImmediateFlush(true)
.withLocking(false)
.withLayout(PatternLayout.newBuilder().withPattern("%d{HH:mm:ss.SSS} [%-5level] %msg%n").withCharset(Charset.forName("UTF-8")).build())
.withName(loggerName)
.build();
appender.start();
logger.addAppender(appender);
It works when I'm running single test. All data are visible in console, the file is created and test log is written in the file. Problem occurs in case of tests are running in parallel - in different threads.
In this case, two different loggers and file appenders are created. Log files from both file appenders are created too and logs from both tests are visible in console. Everything seems to be fine, but every time one of these log files is empty. The empty log belongs to test which started later.
I suspect problem with caching. The first file appender holds all cache for writing so the second one is not able to write. Am I right? What is the solution for this?
Thank you.
You should be able to achieve what you want without using programmatic configuration. There are many reasons not to configure log4j2 programmatically, but the best one, in my opinion, is that in doing so you would make your code dependent on aspects of log4j2 that are not part of the public API. This means that if the implementation of log4j2 changes your code has to change as well. This creates more work for you in the long run.
So, with that in mind I will provide a demo of how to set up log4j2 using an XML config file such that it will generate separate logs for each test. I am assuming, since it was not specified in your question, that your goal is to create a log for each method with a Test annotation and that each of these methods is executed in parallel.
First, here is my TestNG class:
package testpkg;
import java.lang.reflect.Method;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class NewTest {
private static final Logger log = LogManager.getLogger();
#BeforeMethod
public void setThreadName(Method method){
ThreadContext.put("threadName", method.getName());
}
#Test
public void test1() {
log.info("This is the first test!");
log.warn("Something may be wrong, better take a look.");
}
#Test
public void test2() {
log.info("Here's the second test!");
log.error("There's a problem, better fix it");
}
}
As you can see here I have two Test methods and a BeforeMethod called setThreadName. The setThreadName method is, obviously, executed before each of the Test methods. It places a key named threadName into the log4j2 ThreadContext using the name of the method that is about to be run. This will be used as part of the log file name in the log4j2 config file.
Here is the log4j2.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Routing name="MyRoutingAppender">
<Routes pattern="$${ctx:threadName}">
<Route>
<File
fileName="logs/${ctx:threadName}.log"
name="appender-${ctx:threadName}"
append="false">
<PatternLayout>
<Pattern>[%date{ISO8601}][%-5level][%t] %m%n</Pattern>
</PatternLayout>
</File>
</Route>
</Routes>
</Routing>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="[%date{ISO8601}][%-5level][%t] %m%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="testpkg" level="TRACE" additivity="false">
<AppenderRef ref="STDOUT" />
<AppenderRef ref="MyRoutingAppender" />
</Logger>
<Root level="WARN">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
As you can see I've set up the config file to use a RoutingAppender to dynamically generate appenders at runtime based on the ThreadContext key threadName and that threadName is also used in the fileName attribute of the FileAppender.
Here is my testNG config file:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="My suite" parallel="methods" thread-count="5" verbose="1">
<test name="testpkg" >
<classes>
<class name="testpkg.NewTest" />
</classes>
</test>
</suite>
As you can see here I've set it up so that each Test method within my class is run in parallel.
When executed this results in the following console output:
[RemoteTestNG] detected TestNG version 6.14.3
[2018-05-04T21:54:54,703][INFO ][TestNG-test=testpkg-2] Here's the second test!
[2018-05-04T21:54:54,703][INFO ][TestNG-test=testpkg-1] This is the first test!
[2018-05-04T21:54:54,709][WARN ][TestNG-test=testpkg-1] Something may be wrong, better take a look.
[2018-05-04T21:54:54,709][ERROR][TestNG-test=testpkg-2] There's a problem, better fix it
===============================================
My suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================
You can clearly see that the output of the two methods is interleaved, so we know that the methods are indeed running in parallel.
The execution of the test class also creates two log files as expected. They are named test1.log and test2.log
Here are their contents:
test1.log:
[2018-05-04T21:54:54,703][INFO ][TestNG-test=testpkg-1] This is the first test!
[2018-05-04T21:54:54,709][WARN ][TestNG-test=testpkg-1] Something may be wrong, better take a look.
test2.log:
[2018-05-04T21:54:54,703][INFO ][TestNG-test=testpkg-2] Here's the second test!
[2018-05-04T21:54:54,709][ERROR][TestNG-test=testpkg-2] There's a problem, better fix it
So we see here that as expected the logs from the first method went to test1.log and the logs from the second method went to test2.log
Enjoy!
Below is the sample used in log4j 1.x. I am not getting any example to convert the same in log4j2.
<appender name="CoalescingStatisticsAppender"
class="org.perf4j.log4j.AsyncCoalescingStatisticsAppender">
<!--
The TimeSlice option is used to determine the time window for which
all received StopWatch logs are aggregated to create a single
GroupedTimingStatistics log. Here we set it to 10 seconds, overriding
the default of 30000 ms
-->
<param name="TimeSlice" value="30000" />
<appender-ref ref="perf4jFileAppender" />
</appender>
The Appender won't work as is in Log4j 2. It would have to be rewritten.
You may be interested to know that Log4j 2 supports nanoTime timestamps in PatternLayout. This, in combination with the low overhead Async Loggers, allow you to use Log4j as a rough profiling tool.
Similar question but i'm using log4j2.
I need a way to disable All logs from freemarker, in their documentation they say we can do it by calling Logger.selectLoggerLibrary(Logger.LIBRARY_NONE) but they say
selectLoggerLibrary must be called early, before FreeMarker could log anything, or else it will not have (consistent) effect.
Where do I call this in a struts2 application? (I tried calling it in prepare() method in my action class but its not working.) or is there any other way to disable the logs?
Question is, why do you need to disable it like that?
You shouldn't need that, so I guess that's where the real problem lies. Is there some kind of malfunction? Because if there isn't, why not just set the freemarker logger category to be ignored in your logger configuration? That's the normal way of doing this, FreeMarker or not.
Anyway, in 2.3.22 (release expected in early 2015) you can use the -Dorg.freemarker.loggerLibrary=none where you start the JVM (that is, you set the org.freemarker.loggerLibrary system property). Otherwise, if you could call that method in a ServletContextListener that's certainly early enough.
Update:
Reacting to the comments... in most applications you will have 3rd party libraries that use various logging "frameworks", like SLF4J, commons-logging, JUL, Log4j, Log4j2. Thus you have to ensure that all these get redirected into the same logger library, which is certainly Log4j2 in your case. I suspect that wasn't properly done in your case, so now multiple logger libraries log to the console, each with its own configuration settings.
FreeMarker 2.3.x uses Log4j 1.x if it detects that org.apache.log4j.Logger is present. Other logger libraries that it also can detect and use (Log4j2 is not amongst them) have lower priority. (FreeMarker 2.4.x will always use SLF4J if it's present.) Thus, if you add org.apache.logging.log4j:log4j-1.2-api to your dependencies, then FM will use org.apache.log4j.Logger, and so log4j-1.2.-api will redirect the FM log messages to Log4j2. That worked for me, with this Log4j2 configuration:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
<Logger name="freemarker" level="off">
<AppenderRef ref="Console" />
</Logger>
</Loggers>
</Configuration>
This came up as the first search result for "disable freemarker logging" which I searched for because I got double error logs for template errors, one from within the Freemarker library and one from my own code catching the same exception and logging it. The solution to this is simple and different from the answers already given: call setLogTemplateExceptions(false) on the Freemarker Configuration. In full:
Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
configuration.setLogTemplateExceptions(false);
The default behavior of logging the exception even though it propagates out of the Freemarker library is mentioned as a quirk on the Freemarker Logging documentation.
Use this statement:
freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE);
<Loggers>
<Logger name="X.Y.Z">
<AppenderRef ref="Console" />
</Logger>
<Logger name="X.Y">
<AppenderRef ref="Console" />
</Logger>
<Logger name="X">
<AppenderRef ref="Console" />
</Logger>
<Root level="trace" additivity="false">
<AppenderRef ref="Console" />
</Root>
</Loggers>
This is my log4j.xml file.
The below details are what I am getting if I use Configuration object in Java code.
X.Y.Z --- ERROR ----Parent : X.Y
X.Y --- ERROR ----Parent : X
X --- ERROR ----Parent : root
root --- TRACE ----Parent : null
The above details tell us the proper Parent name but it's not inheriting levels from parent logger "root".
It is more common to use the additivity="false" attribute on the named Loggers, instead of on the Root logger.
The final part of your question is unclear to me. What do you mean exactly by "... if I use Configuration object..."? Are you configuring log4j2 with Java source code in addition to the log4j2.xml configuration file? Or are you simply calling Logger.trace? Can you show the source code?
Also, can you show your complete log4j2.xml configuration?
When you say "The below details are what I am getting...", do you mean that this is the output generated by Log4j on the console?
In the docs it says that you can't put the additivity attribute on the Root logger because it doesn't make sense. There is no logger above it to add to.
Additivity basically means "pass on log entries to the parent logger in the hierarchy"
It doesn't inherit levels from the parent. A log entry has a level. Whether it is printed depends on whether there is a logger to catch such an entry (one that accepts that level).
So X.Y.Z will pass logs to X.Y unless additivity=false is set on the X.Y.Z logger config. And so on