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.
Related
I am reading System.Logger API that was introduced in Java 9. And I can't understand why they developed so strange API:
System.Logger {
public default void log(Level level, String msg){...}
}
I call it strange because all popular logging frameworks (I know) don't put level as argument, but name the calling method by level name. For example:
//Log4j
logger.error("This is error : " + parameter);
//SLF4J
logger.debug("Printing variable value: {}", variable);
//apache.commons.logging
log.debug(Object message);
//and even sun.util.logging.PlatformLogger
logger.warning(String msg)
How to explain it?
Questions about the intentions of developers are inherently difficult to answer, unless you are the developer.
That being said, we do have access to the original proposal for this feature - JEP 264.
The summary:
Define a minimal logging API which platform classes can use to log messages, together with a service interface for consumers of those messages. A library or application can provide an implementation of this service in order to route platform log messages to the logging framework of its choice. If no implementation is provided then a default implementation based upon the java.util.logging API is used.
From the goals:
Be easily adoptable by applications which use external logging framework, such as SLF4J or Log4J.
From the non-goals:
It is not a goal to define a general-purpose interface for logging. The service interface contains only the minimal set of methods that the JDK needs for its own usage.
So what we have here is not "Yet another logging framework" like SLF4J, Log4J, etc. What we have is an interface that allows you to tell the JVM to use the same logging tool that you are using for the classes in your application, for logging its own stuff.
The typical usage scenario would be an application which has a complex setup in, say, SLF4J, logging to console, files, databases, or sending text to phones. You want the JVM classes to use the same system. So you write an adapter - a class that implements the System.Logger interface, using your SLF4J setup.
It's not that you can't use the current system logger for logging - you can - but that's not what it was created for. It was created for you to implement and set the system logger so that it calls your logging framework of choice.
In its current form, when you implement, you only need to implement four methods:
getName()
isLoggable(System.Logger.Level)
log(System.Logger.Level, ResourceBundle, String, Object...)
log​(System.Logger.Level, ResourceBundle, String, Throwable)
Now, System.Logger.Level has seven levels. Imagine if instead of having to implement two logging methods, you had to implement 14 logging methods? And more often than not, those implementation would look exactly the same, with one little name change. This is not wise.
As it stands, almost every existing logging framework has a log(level,...) method, and then the implementation of System.Logger's log(...) can usually be done simply by mapping from the System.Logger.Level to your framework's definition of Level.
And if you want to log a message?
Well, if you are using a complex logging platform, you log your message there directly, you don't need to go through the system logger. If you insist on using it - you'll need to use the levels as arguments or write your own wrappers. This is simply not the use case the developers had in mind.
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.
We are currently using CAL10N to localize log messages generated by SLF4J. To do this, we need to use the LocLogger class to define a logger for every class.
Few weeks into development, there comes a time where you have a few bug reports with logs attached - they do have nicely logged stack traces (thanks to SLF4J) for exceptions being thrown, but turns out we have a hard time figuring out the flow since since everyone's new to the code and the flow of things keeps changing every other day!
What we need is ENTRY/EXIT logs for every method. I was hoping that SLF4J will provide a way to do that in the least painful and fastest possible way: and ho behold! It certainly does!
The XLogger class provides methods that aid in such verbose logging (see this link), but after looking at the API docs, looks like both LocLogger and XLogger implement the Logger interface.
So the question is (we got to it finally!) - is it possible to use the LocLogger with all the localization benefits of CAL10N in peaceful harmony with XLogger, that provides all the verbose logging goodies?
No real answer for this yet - worked around it by leveraging a feature of the underlying implementation.
We're using log4j as the implementation under slf4j. The PatternLayout supports printing the package name+method name of the origination of the log message with these specifiers: %C.%M
So we simply log well-defined strings, +++ as the first line in every method and --- just before returning, to a logger for that class.
Solves the purpose, but not a solution to the original question.
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.
Someone might yell at me to read the faqqing faq, but I'm in a hurry ...
Does anyone have a way to make javax or log4j logger refactor-sensitive?
Say, that currently utils.Something has the logger handle:
final static private Logger logger = Logger.getLogger(Something.class.getName());
And logging.properties has
.level = warning
utils.Something.level=info
Then using Eclipse to refactor Something to
nutilla.Somewhere
resulting in my logger handle and logger property becoming out of sync.
Perhaps, set logging levels programmatically?
Has anyone bothered to do it and was it worth the trouble?
Clarification:
After refactoring utils.Something to nutilla.Somewhere, the logger handle now would only log warning and not info, because of the entry in logging.properties file. So the question is, is there a way to replace the function of logging.properties file with programmatic means and if so, is it worth the trouble?
Reason and Motivation for question
I'm obstinate at not listening when advising me to avoid refactoring because ...
Refactoring is a constant habit of mine. I create classes by the hour, merge them, delete them, extract methods, etc ... I'm a restless class creator who finds no time wondering where to initially place a class. I dislike sitting down wasting time wondering where to place them initially - so I just place them in the most convenient package namespace.
After building a good amount of class/interface structure, it becomes apparent to me where certain classes, interfaces or methods shd have been then all the refactoring activities take place and ... tada ... that's when my logging.properties file is ruined a hundred lines.
If you configure logging using class (as opposed to package) names, checking "Update fully qualified class names in non-Java text files" in eclipse's rename refactoring dialog should do the trick.
I do not think there is a way out of the box that updated the package names and class names in your properties file as a result of refactoring actions.
You can:
update the properties file by hand when refactoring is done (refactoring should be an action that is not undertaken eveery week :=)
use fixed strings to create loggers (make logging more functional instead of physical)
load the properties file and adjust the property names on the basis of constants you declare in your class before initialising log4j with that properties collection
I would go for the first option myself, too much automagic behaviour can get you in a very non-transparent situation quickly.
I wouldn't use it (I think it makes more sense to be careful when refactoring) but here it goes:
private static Logger logger = Logger.getLogger(new Exception().getStackTrace()[0].getClassName());