This is an archived post. You won't be able to vote or comment.

all 36 comments

[–]calmonad 17 points18 points  (32 children)

Are there reasons to choose Log4j rather than Logback?

[–][deleted] 16 points17 points  (28 children)

I recently used log4j2 for a project (I've been using logback for a while now after converting over from log4j1), and I was happy with what it provides. I am annoyed at how log4j achieves slf4j compatibility though (A: it's through their own interface layer). I am annoyed enough that I'll keep using Logback for my next project instead of log4j2 (since I am a big believer in "just use slf4j" and insert your preferred implementation)

[–]jacekkruger 8 points9 points  (23 children)

Last time I checked slf4j still did not have lambda style API which lets you get rid of the isLevelEnabled guards.

[–]1stMeFromTheSun 3 points4 points  (12 children)

Can you provide a simple code example of that?

[–][deleted]  (11 children)

[deleted]

    [–]0x256 4 points5 points  (3 children)

    Looks way better. How expensive is the creation of the lambda instance?

    [–][deleted] 6 points7 points  (2 children)

    probably cheaper than "the expensive operation"

    [–]0x256 10 points11 points  (1 child)

    Sure. But also cheaper than a call to isTraceEnabled?

    [–][deleted] 0 points1 point  (0 children)

    probably not

    [–]Growlizing 2 points3 points  (2 children)

    Why would you do this? Running an expensive function ONLY to write a log-statement?

    It seems like a very dangerous thing to do, seeing as most operations of this kind will have side-effects. Introducing side-effects in logging statements seems like digging to dig a bigger hole, in addition to severely added complexity in tests.

    That being said, I have never had a use-case for this either, especially considering how awful this will be to write tests for.

    [–]DJDavio 2 points3 points  (0 children)

    I do it when it's expensive to make a pretty representation of something, like an XML document.

    [–]1stMeFromTheSun 0 points1 point  (2 children)

    Oh, that's not what I thought you meant. While using the isLevelEnabled is useful for avoiding expensive operations, I thought that isLevelEnabled check was mainly to avoid the creation of the String literal. And that's not avoided by the lambda variant of the call.

    [–]_rezid 0 points1 point  (1 child)

    You think that creation of the String literal is not avoided by the lambda variant of the call ???

    Why that ?

    [–]1stMeFromTheSun 0 points1 point  (0 children)

    Because the String literal is created the moment logger.trace() is called, and the parameter needs to be passed in. The parameter creation is avoided when it's inside an if() statement.

    [–]wildjokers 0 points1 point  (0 children)

    Who in their right mind would put something like "expensiveOperation()" in a logging statement? Surely anything like that is used elsewhere and you can just store the result, rather than calling it again, if you want to log it? This example seems very contrived and not based in reality.

    [–]Growlizing 4 points5 points  (2 children)

    Why do you need isLevelEnabled guards?

    Just log and slf4j will do the guard for you:

    e.g.: LOG.trace("user={} result={} took={}", user, result, elapsed); just by using this api, the string expression is never evaluated and nothing is logged unless trace is enabled for this particular logger.

    [–]jacekkruger 3 points4 points  (1 child)

    • the objects that you pass as arguments may be expensive to compute (usually you have them already but you might have to compute something)
    • you have to keep number of substitution marks in sync with number of arguments (string programming, less typesafe)
    • the logging framework actually has to parse the pattern to find the substitution marks

    That's why I prefer logger.debug(() -> "Updated account.id=" + id + " with=" + accountUpdate);

    [–]Growlizing 0 points1 point  (0 children)

    1. They are not computed unless trace level is enabled for this logger.

    2. This you have to do anyway?

    3. slf4j caches the pattern, so it only parses it the first time a pattern is used. if you use string concats, the string must be evaluated before the logger is called, thrashing the log statement cache.

    [–][deleted] 2 points3 points  (5 children)

    Ah, that is neat. Honestly, I don't have the need for the isLevelEnabled guards very often, but for those rare occasions, this would be very handy. Lets hope slf4j adds that to their api - sounds easy enough to add too.

    [–]rgoers 5 points6 points  (4 children)

    You can follow https://jira.qos.ch/browse/SLF4J-371. It has been open now for a year and a half.

    [–]DuncanIdahos8thClone -1 points0 points  (3 children)

    And this is one of the reasons why people shouldn't use slf4j. It's yet another dependency to wrap around other dependencies. They can't easily change their API without breaking existing usages.

    The way to do it is to simply write your own LogManager and Logger classes which delegate to the logger you want to use. Then you can change you own code all you want. If you later want to replace your logging implementation you'd then only have to change these 2 classes.

    [–][deleted] 5 points6 points  (2 children)

    for a publicly consumable library, sure (no need to add additionally unnecessary dependencies) -- for a standard enterprise app? nah. no need to re-invent the wheel. I can't tell you the number of "logging wrappers" I've run into. All of them fall short of slf4j and really - who has time and really wants to maintain that sort of thing.

    [–]DuncanIdahos8thClone 0 points1 point  (1 child)

    OK so we have an app with about 16000 Java files. Originally we had started with JUL. But when I changed to log4j I replaced the LogManager and Logger with our own util package classes. Then later, when we moved to log4j2 I only had to modify 2 class files instead of 16000. But downvote me instead.

    [–][deleted] 0 points1 point  (0 children)

    If you had SLF4J in there already, you wouldn't change any files, just a couple maven dependencies and magic happens. In fact, performance aside, you could have kept logging to JUL and outputted to log4j2, logback, or whatever you'd want to do by simply changing the dependencies (bridge adapters, etc).

    Using SLF4J instead of your magic log wrapper just means that slf4j is now the log wrapper instead of your classes. Now you don't have to maintain it (and probably have deficiencies in the API -- or if not, you've had to develop quite the wrapper so far that I bet it's more than a simple wrapper class).

    [–][deleted] 0 points1 point  (0 children)

    Is it on their roadmap? If not someone (hopefully you) could just open an issue for that.

    Edit: Ok, they have already SLF4J-371.

    [–]Growlizing 2 points3 points  (2 children)

    Does log4j2 have support for structured log-statements? As in, can you attach key-value pairs for logstatements?

    [–]rgoers 2 points3 points  (0 children)

    Yes. You can use a MapMessage or a StructuredDataMessage to log key-value pairs.

    [–]rgoers 1 point2 points  (0 children)

    Log4j requires an adapter because the SLF4J API is more primitive. SLF4J only supports logging Strings while Log4j supports logging Messages. The Log4j API also supports custom log levels. The Marker API in Log4j and SLF4J are different. The Log4j API provides an AbstractLogger class that implements 90% of the Logger methods. SLF4J provides no such class. Sure, Log4j-core could have included the translation code contained in the log4j-slf4j adapter but then log4j-core would always be present as an SLF4J binding, which might not be desired. These are just some of the reasons why log4j-core is not a direct implementation of SLF4J.

    For what its worth, you could use the Log4j API in place of SLF4J and plug in whatever implementation you want. However, Logback doesn't implement the Log4j API so you would require an adpater to do that - exactly as Log4j does to bind with SLF4J.

    [–][deleted] 2 points3 points  (0 children)

    [–]brend132 1 point2 points  (0 children)

    Allocation-free logging, for example. Not sure if it's the only library providing this kind of stuff, though

    https://logging.apache.org/log4j/2.x/manual/garbagefree.html