Which of the following is a better usage of logger?
Parametrize (log4j 2)
logger.info("User {} has logged in using id {}", map.get("Name"), user.getId());`
Using + operator (log4j)
logger.info("User"+ map.get("Name") +" has logged in using id " +user.getId());`
And why?
Even if there were nothing else, the additional StringBuilder shenanigans that happen when using + would make using parameters the obvious choice.
Not to mention that when concatenating the values, the toString() method of all the parameters will be called even if the logging level isn't enabled, meaning that you're wasting CPU to build a String that will never be logged.
This would have an (albeit minor) effect if there are lots of debug() statements, when DEBUG level is usually disabled in production environments.
Parameterized messages avoid formatting the text until Log4j is certain that the message will be logged. This allows you to avoid surrounding the logger.debug(...) call with checks like if (logger.isDebugEnabled())... which gives cleaner code.
This answer is based on modern logging frameworks, not outdated ones like Log4J.
When you say "outdated" you must be talking about Log4j 1.x. As of 2014, Log4j 2 is the cutting edge open source logging framework. It takes some ideas from SLF4J, like parameterized log messages, but adds a plugin system so you can easily add custom appenders, custom layouts and custom lookups. Furthermore Log4j 2 has support for custom log levels, lambda expressions and the lock-free and very performant Async Loggers. To spice things up, from release 2.6 Log4j 2 is garbage-free.
Kayaman is probably talking about Log4j 1, which has been End of Life since August 2015.
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.
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.
The first time I heard about markers was while reading:
http://slf4j.org/faq.html
I checked available methods for the Logger object:
http://www.slf4j.org/api/org/slf4j/Logger.html
http://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html
and found interfaces:
http://www.slf4j.org/api/org/slf4j/Marker.html
http://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Marker.html
The more in-depth info I got from:
http://logback.qos.ch/manual/filters.html
but I am still confused... Note that I asked why, not how to use them, so this is not a duplicate of:
Best practices for using Markers in SLF4J/Logback
UPDATE Seems that when you use markers you are also required to write custom Java code instead doing configuration in XML or .property files...
UPDATE 2 From http://logback.qos.ch/manual/appenders.html#OnMarkerEvaluator
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
logger.error(notifyAdmin,
"This is a serious an error requiring the admin's attention",
new Exception("Just testing"));
This is a rehashed version my answer to the question "Best practices for using Markers in SLF4J/Logback".
Markers can be used to color or mark a single log statement. What you do with these colors, i.e. markers, is entirely up to you. However, two patterns seem to be common for marker usage.
Triggering: Some appender could be instructed to take an action in the presence of a certain marker. For example, SMTPAppender can be configured to send an email whenever a logging event is marked with the NOTIFY_ADMIN marker regardless of the log level. See marker-based triggering in the logback documentation. You may also combine log levels and markers for triggering.
Filtering: Markers are very useful for making certain valuable log statements stand out. For example, you can color/mark all your persistence related logs (in various and multiple class files) with the color "DB". You could then filter for "DB": to disable logging except for log statements marked with DB. See the chapter on filters in the logback documentation for more information (search for MarkerFilter). Note that filtering on markers can be performed not just by logback but by log analysis tools as well.
Before the advent of Markers, to achieve similar behavior, you had the option 1) using custom levels 2) use modified logger names. SLF4J API currently does not support custom levels. As for option 2, suffixing (or prefixing) logger names is workable if a one or two loggers need to be modified. The approach becomes impractical as soon 3 or more loggers need to be "sub-classed" because the associated configuration files become unmanageable.
Even though a single marker can be already very useful, as of SLF4J version 2.0, it is possible to set multiple markers per log statement.
I add logging for Java classes.In particularly, logger.debug level (for development mode).
As far private method called within public ones, is there sense to provide logging within private methods?Is it efficiently?
Thank you.
It's not necessarily a matter of efficiency or not. You have to ask yourself: "Does that private method do something worthy of logging?"
In order to avoid performance issues, you can always check the logger whether it will log debug messages or not, before actually logging those messages. In log4j, you'd write
if (logger.isDebugEnabled()) {
logger.debug("complicated" + "innefficient" + "string concatenation");
}
I imagine, other loggers provide similar concepts
yeah, if you want to "debug" your private methods in maintance mode or development, why not?
in production you'll set your logging level either warn or error! that's upto you...
Of course. Private methods usually hide implementation detail, and that's where most bugs lurk. Put your logging statements where you feel the need, and disable them in production.
Logging might seem wasteful when you don't need it, but it is indispensable when you do. Always provide meaningful log messages in your classes and functions. The log levels are there for you to be able to filter out the noise, and increase verbosity when you need it. Good loggers provide capabilities to change log levels at runtime.
Still, if you are worried about performance for whatever reason, you can use some simple guards to reduce the overhead of logging, especially debug logging, which is rarely on. With log4j/slf4j for example, you can wrap log debug statements in:
if(logger.isDebugEnabled()) {
logger.debug("something");
}
slf4j additionally has a printf-like syntax which only does string formatting if the log level is correct.
logger.debug("Object {} is not valid!", obj);
Like darioo commented, this second form removed the need to check the log level before the log statement.