Log4j2 - Why is my "Logger" element hiding content from my "Root" logger? - java

Before someone closes this question off as a duplicate, just hear me out... I have read through countless blog posts, tutorials, FAQs and SO questions for days now and I'm no closer to understanding why I'm getting this specific behaviour.
Configuration
My log4j2.xml config file contains the following:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Properties>
<Property name="APP_NAME">MyCoolApp</Property>
<Property name="BASE_PACKAGE">my.cool.package</Property>
<Property name="LOG_DIR">${env:LOG_ROOT:-logs}</Property>
<Property name="LOG_PATTERN">%d [%t] %-5level %c{1.}:%L:%M | %m%n</Property>
</Properties>
<Appenders>
<RollingFile name="AppLogFile" fileName="${LOG_DIR}/${APP_NAME}.log" filePattern="${LOG_DIR}/archive/${APP_NAME}.%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
</Appenders>
<Loggers>
<Logger name="${BASE_PACKAGE}" level="INFO">
<AppenderRef ref="AppLogFile"/>
</Logger>
<Root level="TRACE">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Then each class initialises a logger with private static final Logger LOGGER = LogManager.getLogger();.
Runtime
From my understanding of log levels, loggers and appenders, this should give me the following:
All INFO and higher level logging output to file
All TRACE and higher level logging output to console
Meanwhile, if I run the app when the AppLogFile appender is enabled, I get less output to the console; counting from only after the Log4j initialisation, I get 255 lines compared to 367.
Looking through the console and file output, when the AppLogFile appender is enabled, I don't get any TRACE or DEBUG output, only INFO and higher. When I comment out just that appender (without changing anything else), then I get everything to console, including TRACE and DEBUG.
I've tried playing around with reordering the "console" and "file" related elements, I've tried explicitly enabling and disabling the logger's additivity property, I've tried using filters in the appenders and loggers, and even multiple appender references inside one logger with explicit level properties.
All I want is to get everything to go to the console and everything INFO level and higher to go to a file. What am I missing here..?

The level attribute and the <AppenderRef> element are entirely independent:
Specifying level changes the logging level of the given logger and all sub-loggers.
Specifying <AppenderRef> adds1 another appender to the given logger and all sub-loggers.
The fact that you do both at the same time, doesn't affect those independent effects.
If you want to limit log entries for the Appender, specify the level attribute on the <AppenderRef> instead.
1) If you wanted the Appender to replace, you need to specify additivity="false"
(OP edit) Just for the sake of clarity, between what I learned from this answer and the Log4j2 FAQ; I streamlined things a bit and ended up with the following <Loggers> configuration:
<Loggers>
<Root level="TRACE">
<AppenderRef ref="AppLogFile" level="INFO"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>

Related

How to find out which Log4j configuration is loaded?

We're using Log4j (via Slf4j) for logging.
Our log4j configuration is rather simple:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
<Logger name="net.sf.jsi.rtree.RTree" level="INFO">
<AppenderRef ref="Console"/>
</Logger>
</Loggers>
</Configuration>
Note that root logger is set to INFO.
However, we're getting a huge amount of DEBUG logs from a third-party library (net.sf.jsi). Logs we're absolutely not interested in.
My guess is that there's a different Log4j configuration on the classpath somewhere which gets picked up and sets root logger to DEBUG.
My question is, how could I found which configuration is actually loaded and used by log4j?
Update:
I have seen this "possible duplicate". However, the answer which suggests to use -Dlog4j.debug does not work for me.
I've added -Dlog4j.debug to the VM arguments. I only get:
DEBUG StatusLogger org.slf4j.helpers.Log4jLoggerFactory is not on classpath. Good!
DEBUG StatusLogger Using ShutdownCallbackRegistry class org.apache.logging.log4j.core.util.DefaultShutdownCallbackRegistry
WARN StatusLogger Multiple logging implementations found:
Factory: org.apache.logging.log4j.core.impl.Log4jContextFactory, Weighting: 10
Factory: org.apache.logging.slf4j.SLF4JLoggerContextFactory, Weighting: 15
Using factory: org.apache.logging.slf4j.SLF4JLoggerContextFactory
which is not enough info for me.

log from static method using Log4j2

I'm trying to use Log4j2 in an OSGi environment. I've got it to work so far, but while inspecting the logs from the console and from the file, I noticed that some of them were missing, specifically logs that were called from a static method.
The Log class in the example below is just a convenience class to let my colleagues call the logging functionality more easily (in the example for just a String it seems like overkill of course) through a create method. It does nothing more than create an instance of the Log class that has a Logger internally that calls the respective method from the Log4j2 logger.
My question is: Do I just have a simple error in my project or is Log4j2 not able to log to files from static methods?
Here's a code example to make it a bit more clear:
Log log = Log.testLog();
log.info("non static log" );
That's the code I call from a non-static method.
And here's the testLog()-method:
public static Log testLog() {
Log.create( Log.class ).info( "static log" );
return Log.create( Log.class );
}
Results:
Both #info() calls write to the Console Appender, but only the "non static log" message is written to the file.
Here's my log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="!ENTRY %logger{1.} %level %d{DEFAULT} [%t]%n!MESSAGE %msg%n%n"/>
</Console>
<RollingFile name="RollingFile" fileName="${sys:osgi.logfile}.log4j.log"
filePattern="${sys:osgi.logfile}.log4j_bak_%i.log">
<PatternLayout>
<pattern>!ENTRY %logger{1.} %level %d{DEFAULT} [%t]\n!MESSAGE %msg%n%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="1 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"
fileIndex="min"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="TRACE" additivity="false">
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Finally found the source of my particular problem, which is OSGi (in this case the Equinox Framework). My application uses the osgi.logfile system property to point to the location where the logs should be saved at.
Unfortunately Equinox not only creates that property, but also changes it at startup to a different location. For Log4j2 I used ${sys:osgi.logfile} to get this system property, but because a few particular plugins started so early, Log4j2 still had the wrong (aka. old) location configured for these plugins (more specifically: their LoggerContext).
What helped me in this case was a simple LoggerContext.reconfigure() on the LoggerContext that still had the old location.

How can I specify a specific format for each Log4j log level

First time trying to use log4j version log4j-1.2.17.jar.
On an existing application the client has log4j in place and there is a log4j.properties file which specifies a light log output. What I want to do is depending on the log level (ERROR & WARN) output a more refined entry.
On the log4j site I came across this but I think it is to be in some .xml file. I need some assistance in understanding how I can put in place the formatting option to alter based on log level.
You don't need to declare separate loggers to achieve this. You can set the logging level on the AppenderRef element.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<File name="file" fileName="app.log">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m %ex%n</Pattern>
</PatternLayout>
</File>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%m%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="file" level="DEBUG"/>
<AppenderRef ref="STDOUT" level="INFO"/>
</Root>
</Loggers>
</Configuration>
Would I put this xml content into the web.xml file or another file?
a) If another file what file name and where would it go?
How do I get log4j to realize that I need it to use the xml file?
Will the use of the xml ignore the log4j.properties file?
I know it is a lot of questions but there is only me on the project and the client has a production crisis that needs to be figured out today so I don't have time to go off to read and do tutorials with the client calling me every hour. I figured it may help to get this logging more useful. As the logs are right now I have a date and message output to the log but no idea where the entries are created from without doing extensive searches through the code.
You could do this by defining multiple non-additive Loggers so that the first one only logs errors, the next one warnings and the final one infos and debug.

Log4j 2 - Deactivate drools logger

I am using Log4j 2 and I am unsuccessfully trying to change the logging level of jBPM/Drools, having it as a reference. The drools class that keeps logging is ExtensibleXmlParser.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
<Logger name="org.drools.core.xml.ExtensibleXmlParser" level="off">
<AppenderRef ref="Console" />
</Logger>
</Loggers>
</Configuration>
If I get it right, ExtensibleXmlParser uses slf4j and Log4j is compatible with sl4j.
Why doesn't this Log4j configuration work? Could someone provide me a working configuration? Should I configure jBPM/Drools logger indendently?
When log4j seems to ignore your tweaks to the log4j.properties/xml file, it most often means you're not fiddling with the right file. It is likely that there is another configuration file in your classpath that gets found earlier and is taken into account.
Search your entire classpath for log4j* pattern and see what comes out (include the contents of .jar files and application server /lib and /ext folders, if any).
If I get it right, ExtensibleXmlParser uses slf4j and Log4j is compatible with sl4j.
"Is compatible" is not equivalent with "is configured to use". SLF4J plugs into appropriate logging framework through the org.slf4j.impl.StaticLoggerBinder class. Search the classpath (best in your IDE) to see what library this class came with. If there is no such class, then SLF4J logs nothing.

How to disable additivity on marker logger?

I have defined a Marker and would like to log this only to a specific file. Therefore I'm trying to set additivity = false. But it does not work and is still also logged to my global logfile in addition. What might be wrong here?
<Configuration>
<Appenders>
<RollingFile name="TEST" fileName="test.log" filePattern="test1.log">
<MarkerFilter marker="TEST_LOG" onMatch="ACCEPT" onMismatch="DENY"/>
</RollingFile>
<RollingFile name="GLOBAL" fileName="global.log" filePattern="global.log">
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<MarkerFilter marker="TEST_LOG" onMatch="DENY" onMismatch="ACCEPT"/>
</RollingFile>
</Appenders>
<Loggers>
<logger name="foo" additivity="false">
<appender-ref ref="TEST" />
</logger>
</Loggers>
</Configuration>
usage:
LogManager.getRootLogger().info(MarkerManager.getMarker("TEST_LOG"), "test log");
In the sample code the marker is named "TEST", but in the configuration the MarkerFilter only accepts log events with marker "TEST_LOG". Config and code should use the same string.
In addition, you probably need to define two Appenders: one for your global log file and one for your specific (marked) log events. Then ACCEPT the marked log events only in your specific appender, and DENY the marked log events in the global appender.
Additivity is not going to work in your case, coz additivity is controlling a specific logger not to inherit parent's appender.
In your config, you are setting logger foo to have additivity = false, which means, unless you are using foo or its children loggers for your logging, you will still follow root loggers' config. (I can't see your root logger config in your post, I suspect it is referring to both appenders). From your quoted code, you are using the root logger for your logging, the configuration of foo logger simply won't take effect.
There are two solution I can suggest:
For all log messages you log with TEST_LOG marker, make sure you log it using foo logger. or,
If you need to use any logger for your TEST_LOG messages, then you should reconfigure your appender, so that your GLOBAL file appender accept anything BUT TEST_LOG. (Currently you are only denying SELL_FAILURE marker)
Correct solution depends on your actual requirement. Make sure you understand the basic concepts so you can choose the right solution for you.
Edit:
First, even with your "correct" config you mentioned in the comment, the issue of different loggers still holds. Which mean, coz you are using root logger to do your logging, your config for foo logger has nothing to do with your log message, and additivity is out of picture in your case.
I am not using Log4J2, a brief check on the usage of filter lead me to two issues:
First, I believe proper way to define more than 1 filter is to make use of Composite Filter (which means defining in a <Filters> element, though I don't quite get the syntax from Log4J's doc).
Second, even you define it in composite filter, your config will still have problem. When a log event is having INFO or higher level, you declare in filter it is an ACCEPT or DENY which will prohibit subsequent filter evaluation. If you want to log messages with INFO and not containing TEST_LOG marker, you should do something like:
<MarkerFilter marker="TEST_LOG" onMatch="DENY" onMismatch="NETURAL"/>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
NEUTRAL means current filter cannot determine whether to accept or deny the message, and will evaluate next filter to determine it.
Couple of things I noticed:
You should use the same marker string in the code, and in the config for both MarkerFilters.
You need to add AppenderRef elements to connect your logger to both appenders
You can specify a level in the AppenderRef, this is easier than using a ThresholdFilter
Which gives you this config:
<Configuration>
<Appenders>
<RollingFile name="MARKED_ONLY" fileName="markedOnly.log" filePattern="markedOnly1.log">
<MarkerFilter marker="MY_MARKER" onMatch="ACCEPT" onMismatch="DENY"/>
</RollingFile>
<RollingFile name="GLOBAL" fileName="global.log" filePattern="global.log">
<MarkerFilter marker="MY_MARKER" onMatch="DENY" onMismatch="ACCEPT"/>
</RollingFile>
</Appenders>
<Loggers>
<root level="trace">
<appender-ref ref="MARKED_ONLY" level="trace" />
<appender-ref ref="GLOBAL" level="info" />
</root>
</Loggers>
</Configuration>
And this code:
Logger logger = LogManager.getLogger(MyClass.class); // or .getRootLogger()
logger.info(MarkerManager.getMarker("MY_MARKER"), "test log");
Note it is important to use the same "MY_MARKER" string in the code, and in the filters for both appenders. That way, the appender for the global file can use that filter to DENY the marked log events.

Categories

Resources