Filtering a subclass in log4j - java

I have a message driven system with, say, com.example.BaseMessagingAgent class, which is a basic class for many message agents. This base class logs message events. There are many subclasses of this base class, implementing different specific agents of system. Let us com.example.MyAgent which extends com.example.BaseMessagingAgent is one of them.
I want to log messages only related to class MyAgent. But I cannot define logging as:
log4j.logger.com.example.MyAgent=DEBUG, APPENDER
because logging occurs in parent class com.example.BasicMessagingAgent - I will record nothing.
And I also do not want to set logging in base class:
log4j.logger.com.example.BaseMessagingAgent=DEBUG, APPENDER
because it will log events for all agents, and I will have a lot of unnecessary logging.
Does enyone know how to limit logging to only one subclass?

You should write a filter for Log4j since AFAIK there is no way to put such information on log4j.properties file. More details at http://books.google.it/books?id=vHvY008Zq-YC&lpg=PA95&ots=yi335bZU7z&dq=&pg=PA95#v=onepage&q&f=false

It's pretty simple, actually.
First, add the appender to the root logger. Really. It will make your life much more simple.
Now configure the whole thing:
log4j.rootLogger=DEBUG, APPENDER
log4j.logger.com=ERROR
log4j.logger.com.example.MyAgent=DEBUG
The default for all classes below "com.*" will be to log only errors. The sole exception is com.example.MyAgent which will log at debug level.
You need to set the root logger to DEBUG as well or it will throw away all the DEBUG log messages.
The next step is to use one logger per instance. To get that, simply remove the static in the line which you create your logger and replace BaseMessagingAgent with getClass()
I know, it looks like overkill but that's how log4j works. Also creating a logger per instance isn't very expensive (unless you create millions of MyAgent per second).
If you really want to add an appender to a single class, then don't forget to turn off additivity (...Class.additivity=false) or you will get all log messages twice.

Related

Differentiate logging informations of multiple instances of the same class

I'm trying to log the methods call chain of some classes instances of a Java program using Log4j2. Each instance will behave differently based on the inputs they will recive (obviously).
The problem I'm facing is differentiating the logging messages of each instances for easly reconstructing the methods call chains I was talking about.
In addition to logging the entering and the leaving of each method, which is the default logging schema I'm using, I've tried to:
add method call parameters to the log: too few informations and not very readable;
add more informations about the method behaviour: too verbose and not very readable;
adding instance hash code to all the logging messages, which will become something like LOG.trace("{} - Leaving constructor(index:{})", System.identityHashCode(this), index);: good solution, but I've to add some boilerplate code to all the logging methods, which makes the code a little less readable;
using per-instance loggers (so not the per-class/static one) and adding the instance hash code in the logger name (so the logger name will be Classname#hashcode): seems the best solution in clean code terms, but I didn't found any way for declaring logger settings (like logging threshold) for multiple loggere, i.e. for all the loggers which name starts with Classname.
Which one do you think will be the best solution, or do you have any other way to suggest?
For this requirement, you can easily use an nested thread context: Look at "Fish Tagging" in https://logging.apache.org/log4j/2.x/manual/thread-context.html .
Excerpt:
ThreadContext.push(UUID.randomUUID().toString()); // Add the fishtag;
logger.debug("Message 1");
.
.
.
logger.debug("Message 2");
.
.
ThreadContext.pop();

What the difference in LoggerFactory.getLogger "root" vs "class" name?

I'm trying to find the answer about the differences between:
class MyClass {
private static Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
}
and:
class MyClass {
private static Logger logger = LoggerFactory.getLogger(MyClass.class);
}
Is it only useful with you are planning to do a fine logging setup? Like separate the log of the class in a different file, print more/less informations for each one, etc.
I have this doubt because most of my classes I use LoggerFactory.getLogger(MyClass.class) but I think the LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) is enough in the most of the cases.
Thanks!
Is it only useful with you are planning to do a fine logging setup? Like separate the log of the class in a different file, print more/less informations for each one, etc.
This is correct. By controlling your logging down to the class level, by giving each class their own logger, you can more finely control the logging. For example, we typically log all log entries (regardless of level) for classes in our packages, e.g. my.employer.com.team.project. We then log ERROR for all other loggers. We then have the ability to view all the loggers that are being used on the application and can remotely enable/disable any logger we want in real-time.
I have this doubt because most of my classes I use LoggerFactory.getLogger(MyClass.class) but I think the LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) is enough in the most of the cases.
If you give all your classes the same logger, then they will all behave in the same way. I think you are right that for most cases you will treat all your classes' logging the same way, but that is not always the case. Also, if you are writing library code, then you must not use the root logger because now you remove the ability of the user's of your library to tune your libraries' logs.

Why do we need to create loggers per class?

Many forums and stackoverflow questions suggest that the recommended approach for creating loggers is to create them per class.
A quick look at the Log4j's Logger getLogger(String name) implementation suggests that, all the loggers are stored in a static map.
I wonder if we have thousands of classes in an application and a logger is defined in each class, wouldn't it cause memory/performance issues.
Alternatively, why cant we define some standard loggers (based on some functional criteria) in the application and have the developers use them in the classes. I understand that having a separate logger allows us to change its logging level, but I believe its not big issue if there are sufficient predefined loggers.
I looked at the questions Is a logger per class or is a set of loggers that are accessed by the entire application perferred? and Log4J: Strategies for creating Logger instances
but they dont seem to cover this topic.
You don't have to, it's just easier to manage. Loggers follow parent-child relationship. Children pretty much inherit everything from their parents. This way you can define very specific logging behavior or have it inherited generically.
Alternatively, why cant we define some standard loggers (based on some
functional criteria) in the application and have the developers use
them in the classes. I understand that having a separate logger allows
us to change its logging level, but I believe it's not big issue if
there are sufficient predefined loggers.
This would require some pretty intense dependency injection to make those loggers available to every type, also potentially adding an extra dependency.

log4j property configuration question

I have to stop showing logging messages of some methods within the system without changing a Java code (loggers)...
I was thinking, is it possible to configure log4j.properties, where I could skip the logging for certain method? and is it possible to do log on method level at all with log4j?
Thanks,
K.
log4j does not support this level of configuration. But you can implement an appender that will extends your current appender (for example, create a MyConsoleAppender that extends ConsoleAppender). Then, add a check for the method that you which to skip in the checkEntryConditions() method.

Why is Log4j rootLogger not filtering log events according to event level?

Why is the Log4j rootLogger in my application not filtering log events according to level? In my log4j.properties, I have several loggers:
log4j.rootLogger=info,stdout
log4j.logger.com.name.myapp=debug,myapp
log4j.logger.org.castor=debug,castor
log4j.logger.org.exolab.castor=debug,castor
log4j.logger.org.hibernate=debug,hibernate
log4j.logger.org.springframework=debug,spring
Each of the loggers receive and record numerous log events at levels DEBUG and above, which is what I expect and desire. The rootLogger, however, despite being set to level INFO, is displaying all of these events, too, including the DEBUG events, which is not what I expect and not what I desire. Instead, I would expect it to filter the DEBUG events, but display only the events at level INFO and higher (WARN, ERROR, and FATAL), which is also what I want. Why is rootLogger displaying all of the events?
See this answer to a similar question about logger chaining in Log4j:
The way Log4j chaining works is a bit
counter intuitive (to me at least). If
the request level is equal to or above
the threshold of the most specific
matching logger, it is accepted. Once
the request is accepted, it gets
handled by the complete chain of
ancestors regardless of their
thresholds!
This means that no matter to what level you set the threshold of the root logger, it will always accept and output the log event that any other logger accepts, unless you disable chaining for that child logger or explicitly set the threshold of its appender to a higher level.
So, in this case, there are two ways that you can prevent root logger from capturing the events from the other loggers. The first is the more selective approach of disabling log event chaining:
log4j.additivity.com.name.myapp=false
log4j.additivity.org.castor=false
log4j.additivity.org.exolab.castor=false
log4j.additivity.org.hibernate=false
log4j.additivity.org.springframework=false
The second way is simpler, but more restrictive since it suppresses all events on the console that are lower than INFO (DEBUG and TRACE):
log4j.appender.stdout.Threshold=info
Check out the inheritance described in the intro. If you specify a level at the package level, it won't inherit the root logger's level. You're using debug in the packages you specify, not info. Specifying the level overrides whatever has been inherited.
If you want to inherit the root logger's level, get rid of the level specification in your logger configurations.
To retrieve the rootlogger, are you using Logger.getRootLogger()? If not, you may not be getting the real root logger. If so, make sure the Threshold of stdout isnt at debug; the Threshold of appenders override that of the logger Levels.

Categories