Java logging framework which does not require LOGGER declaration on every class
Asked Answered
M

2

7

I tried the following loggers

  • Java Logging API
  • Log4j
  • slf4j

All of these requires a LOGGER declaration at the class level, like the ones below

private final static java.util.logging.Logger.Logger LOGGER = java.util.logging.Logger.Logger.getLogger(MyClass.class.getName());
private final Logger slf4jLogger = LoggerFactory.getLogger(SLF4JHello.class);
private final static Logger log4jLogger = Logger.getLogger(Log4jHello.class);

This looks hideous to me, is there an logger framework in java that does not require this declaration?

What I'm looking for is, I can have a global declaration like

private final static Logger Logger = Logger.getLogger(MyApp.class);

But when I call Logger.log(..) from class XXX.class then the Logger should make use of the XXX.class name.

Massenet answered 24/2, 2016 at 3:56 Comment(9)
It is completely up to you (in any of these frameworks) how you define and name your loggers. You could just have one for your whole application, or call getLogger on any call.Comras
Related: #26601257 #27039194Comras
@Comras having one for the whole application will make all the logs with the same name. Which is not I'm looking for. Instead the framework has to display the class name of the caller.Massenet
The name of the logger is not necessarily tied to what it displays, either. All this can be configured.Comras
you can configure the caller class and method name and source code file name and line number to be displayed in most logging frameworks. But there is some runtime cost to it.Comras
The main impact of the logger name is that it allows the hierarchical application of rules (such as raise log level to DEBUG for org.apache.commons.*). I'd just stick to the established pattern. Most flexible, easy, fast (and a little ugliness that could only be resolved by macros or preprocessors or other nastiness).Comras
All I am saying is "a) that all logging frameworks present this pattern is a clue that it works very well b) they don't require you to follow this pattern"Comras
The modern way is to use Dependency Injection to set dependencies, such as the logging interface to use, which this pattern supports. So no modern logging framework is likely to do things differently.Kidderminster
@Kidderminster I don't know. Logging seems to be one of those things where "dependency lookup" is still widely accepted. And you can only inject a logging implementation, the interface is still fixed at compile-time (even with DI). And that implementation can be configured independent of the code that uses it, all that would be fixed without DI is the name of the logger.Comras
D
2

Your problem is more than likely not with the logging framework but with the layout.

Concrete example

so35592962/App.java

package so35592962;
import org.apache.logging.log4j.*;
import so35592962.sub.OtherClass;
public class App {
  public static final Logger logger = LogManager.getLogger();
  public static void main(String[] args) {
    logger.error("in App.main");
    OtherClass.act();
  }
}

so35592962/sub/OtherClass.java

package so35592962.sub;
import static so35592962.App.logger;

public class OtherClass {
  public static void act() {
    logger.error("OtherClass.act");
  }
}

So you can see this is totally what you want: classes that use a single logger. So good point, Log4J2 can be used for that.

Now I add the magic file log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %C{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

Running this will print:

12:05:28.834 [main] ERROR so35592962.App - in App.main
12:05:28.836 [main] ERROR so35592962.sub.OtherClass - OtherClass.act

Look, there are different class names here! Yet I used Log4J2.

What happened here?

Note the pattern used in the PatternLayout tag:

%d{HH:mm:ss.SSS} [%t] %-5level %C{36} - %msg%n

The standard examples and what you usually see on the Internet all use the %L pattern. This pattern is to show the logger name. But you said you don't want it. Fortunately, other patterns exist. %C will show the class name instead of the logger name. This is the pattern that is used here.

According to the PatternLayout documentation, the %C pattern does the following:

Outputs the fully qualified class name of the caller issuing the logging request.

Important note, also mentioned in the documentation:

Generating the class name of the caller (location information) is an expensive operation and may impact performance. Use with caution.

Despotic answered 24/2, 2016 at 11:2 Comment(3)
But if you only have a single Logger, you cannot assign different log levels (or different formatters, or different appenders) to different Loggers anymore.Comras
Correct, but this answers the initial question, I believe. One unique logger, using the class name.Diuretic
Also, this answer is not a "yes, by all means, do this", but a "your request can be done, here's how". I personally don't endorse this, but if ti works for the OP, I'm glad I helped!Diuretic
M
1

You could change both of these to class annotations with Lombok.

Microgram answered 24/2, 2016 at 4:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.