I have an application with multiple "controllers", and I'd like to have each log to their own file. This is easy enough for their own code, but I'm also using some library code which uses commons logging. Could I somehow get that code to log to the controller-specific file as well?
I was thinking I could somehow do it by thread:
class Controller {
public void action() {
setCurrentThreadLogFile(myLogFile);
try {
Library.stuff();
} finally {
restoreCurrentThreadLogFile();
}
}
}
Currently I'm using commons-logging for my own logging as well, with log4j as backend. But I could change that, if needed, or use a mix (is that's possible within the commons logging framework).
One way I could do this would be to write my own commons logging implementation (possibly a wrapper around log4j), but is there an existing solution?
You probably want to be looking at mapped diagnostic contexts (MDCs). Commons logging does not support these but Log4J does, so you would have to go directly to Log4J to set this up. You will probably have to roll your own filter to filter using the MDC and apply to the appenders.
If you willing to change logging implementations then you could use SL4J as the logging facade and Logback as the logging implementation. Have the controller or some sort of filter/interceptor add a discriminator value for a key you are going to use to discriminate the controllers with to the MDC. Use a SiftingAppender to separate the log event into separate files.
A logger per thread? Put the logger in a ThreadLocal. If you are using java.util.logging, a Handler could make this transparent to the caller.
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.
We have migrated all our code to use the slf4 API to use generic APIs, however now we are thinking of upgrading from log4j 1.x to log4j 2.x. Will we be able to use all the features of log4j2 if we use the slf4j API and log4j2 as the implementation?
The Log4j2 API is richer than the SLF4J API, and many Log4j2 API features are not accessible via SLF4J. See below for details.
Features of the Log4j2 implementation, like Async Loggers, Lookups, Filters, Layouts and Appenders are controlled with configuration and are available regardless of the logging API you use in your application.
Please also see this answer to the different but related question of why it is safe to program to the Log4j2 API.
10 Log4j2 API features not available in SLF4J
(1) The Message API allows applications to log structured objects in addition to just text. Internally Log4j2 converts everything that is logged to a Message, and exposing this to the API opens up all kinds of possibilities for applications to interact with downstream logging components (filters, layouts, appenders). This can be useful if you're developing custom components as plugins to Log4j2, as well as when you're using the built-in ones. For a built-in example, see how StructuredDataMessage is used for fine-grained control over Rfc5424Layout.
(2) Java 8 lambda support allows you to lazily create parameters or log messages without explicitly checking if the requested log level is enabled.
// Java-8 style optimization: no need to explicitly check the log level:
// the lambda expression is not evaluated if the TRACE level is not enabled
logger.trace("Some long-running operation returned {}", () -> expensiveOperation());
(3) Mixing {}-style parameters with String::format %s %d-style parameters. The {} style has better performance and can be used with any parameter type, but the printf style gives fine grained control over the formatting. Log4j2 allows you to easily mix these parameter styles. For example:
logger.debug("Opening connection to {}...", someDataSource);
logger.printf(Level.INFO, "Logging in user %1$s with birthday %2$tm %2$te,%2$tY", user.getName(), user.getBirthdayCalendar());
(4) CloseableThreadContext offers some extra convenience over the normal ThreadContext (MDC) in SLF4J: it automatically removes items when you're done. For example:
// Add to the ThreadContext map for this try block only;
try (final CloseableThreadContext.Instance ctc = CloseableThreadContext
.put("id", UUID.randomUUID().toString())
.put("loginId", session.getAttribute("loginId"))) {
logger.debug("Message 1");
// call some other code that also does logging
...
logger.debug("Message 2");
...
} // "id" and "loginId" are now removed from the ThreadContext map
(5) Log4j2's ThreadContext, in addition to key-value pairs, also has push and pop methods to support stack functionality (what used to be called NDC in Log4j 1).
(6) SLF4J does not support the FATAL log level.
(7) Log4j2 has support for custom log levels. These can be used with the log methods, for example: logger.log(Level.getLevel("FINE"), "... msg"), or you can generate a custom logger wrapper with convenience methods for your custom log levels.
(8) The Log4j2 API accepts any Object, not just Strings. This is one of the things that allow Log4j2 to be "garbage-free", meaning it will avoid allocating new Objects. Your Object is logged without creating any temporary Strings if it is a Number, a CharSequence or when it implements the (Log4j2) StringBuilderFormattable interface.
The Log4j2 API will also avoid creating vararg arrays if you log 10 parameters or less. SLF4J creates vararg arrays if you log more than 2 parameters.
(9) The above you get for free just by using the Log4j2 API directly. On top of that, if you really care about avoiding creating temporary objects (like some interactive games and low-latency financial applications do), you can avoid auto-boxing primitive parameters with the Unbox utility class.
(10) SLF4J Markers' use of coarse-grained synchronization may have performance impact for multi-threaded applications (SLF4J-240). See the Advanced Filtering section of this performance test results page.
Disclaimer: I contribute to Log4j2.
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.
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.
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.