23

In the process of converting some old loggers from String.format to the newer slf4j {} variant, I stumbled upon this case:

logger.error(String.format("%s ... %s ... %s", ...), e);

I would like to use only {} and remove the String format, however, the logger method signature which includes the throwable is:

error(String msg, Throwable t)

So I do have to keep the String.format in this case ?!

Why is there no:

error(Throwable t, String format, Object... arguments)

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
  • 2
    Hmmm actually there was a duplicate here: https://stackoverflow.com/questions/6371638/slf4j-how-to-log-formatted-message-object-array-exception, but it uses different keywords in the title ... maybe mine will help others googling – Christophe Roussy Jul 12 '17 at 10:10
  • @jmehrens exactly the link I posted in the comment above yours – Christophe Roussy Jul 13 '17 at 09:19
  • [A vote to close as duplicate will automatically post a comment with the duplicate link...](https://stackoverflow.com/help/duplicates) – jmehrens Jul 13 '17 at 14:32
  • 1
    @ChristopheRoussy this was a first item on Google for "org.slf4j logger how to format with throwable" search string, thanks! – Betlista Jan 23 '20 at 04:23

1 Answers1

39

As of SLF4J 1.6.0, in the presence of multiple parameters and if the last argument in a logging statement is an exception, then SLF4J will presume that the user wants the last argument to be treated as an exception and not a simple parameter.

So, writing (in SLF4J version 1.6.x and later)

logger.error("one two three: {} {} {}", "a", "b", 
          "c", new Exception("something went wrong"));

http://www.slf4j.org/faq.html#paramException:

"Yes, as of SLF4J 1.6.0, but not in previous versions. The SLF4J API supports parametrization in the presence of an exception, assuming the exception is the last parameter."

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
Mathias G.
  • 4,875
  • 3
  • 39
  • 60
  • Ok so it is smart behind the scenes :) – Christophe Roussy Jul 12 '17 at 10:08
  • I created [improvement request](https://jira.qos.ch/browse/SLF4J-482), you can vote for it if you like it. – Betlista Jan 23 '20 at 05:08
  • 5
    a completely wrong answer is upvoted. The described behavior has nothing to do with SLF4J, may be you got confused with your logger implementation. In reality, in SLF4J 1.7.x there Logger interface has method signature: `public void error(String format, Object... arguments);` which obviously has nothing to do with last exception being Throwable or not. It is left to actual logger implementation to take care of that. – Anton Pryamostanov Jun 13 '20 at 10:38
  • 2
    @AntonPryamostanov http://www.slf4j.org/faq.html#paramException – Christophe Roussy Nov 20 '20 at 08:39
  • This SLF4J documentation is also wrong. SLF4J does not restrict/process this "last" position parameter type for being Exception or anything else. Just see the SLF4J code. – Anton Pryamostanov Nov 21 '20 at 12:30
  • 6
    @AntonPryamostanov, this is a completely right answer. Yes, SLF4J chose to accept Throwable as the last argument even for formatting methods, which means the varargs overload can't statically type it as Throwable - a signature of `(String, Object..., Throwable)` is impossible, it's a compiler error, because varargs must be the last. So they obviously decided to not bother typing it even with non-varargs overloads. Nevertheless it's explicitly documented that if you pass Throwable as the last argument, it will be treated as such and not a message formatting parameter. – Vsevolod Golovanov Mar 12 '21 at 09:05
  • 2
    @AntonPryamostanov, I literally just explained that there is no such signature and why. – Vsevolod Golovanov Mar 14 '21 at 12:22
  • @VsevolodGolovanov `Nevertheless it's explicitly documented that if you pass Throwable as the last argument, it will be treated as such and not a message formatting parameter.` - it is not. It is giving an example of 2 arg method, not a vararg method. – Anton Pryamostanov Mar 14 '21 at 13:25
  • And secondly - even the 2 arg FAQ entry is wrong as I mentioned above. As there is no logic for second arg Throwable checking within SLF4J - it is done in the level of logger implementation - which depends on logging library and there is no such guarantee. – Anton Pryamostanov Mar 14 '21 at 13:29
  • AntonPryamostanov is wrong in this case. The SLF4J FAQ gives an example of a call to the var-arg version of `error`, where the last argument is treated specially being a `Throwable`. This is also mentioned in the Javadoc for [`Logger`](https://www.slf4j.org/apidocs/org/slf4j/Logger.html). – Lii Feb 04 '22 at 13:12
  • Kindly note that, `logger.error("string message", new Exception())` is the only signature in which Throwable t would be `NOT NULL`. In all the other cases, they get casted to Object array elements. Therefore, it is inaccurate to say that the last argument being an exception gets mapped to the Throwable argument of the overload signature. – Abhay Nagaraj Aug 11 '22 at 19:42
  • @Mathias G I have tried all the following overloads slf4j's logger ``` logger.error("string message", ex); logger.error("msg", new Object[]{"arg1", "arg2"}, new Exception("msg")); logger.error("msg",new Object[]{"arg1", new NullPointerException("msg"), new RuntimeException("rte"), new Exception("msg")}, new ArrayIndexOutOfBoundsException("msg")); logger.error("msg", new Exception("te"), new Exception("msg")); logger.error("1 2 3: {} {} {}", "a", new Exception("msg")); ``` Out of all the above, first overload signature is the only one where `t != null` – Abhay Nagaraj Aug 11 '22 at 19:46