0

I have the following codes:

MySuperClass

package com.mypackage;

public abstract class MySuperClass {
    private static final Logger LOGGER = LogManager.getLogger();

    protected void demoLog() {
        LOGGER.debug("My message")
    }
}

MyClass

package com.mypackage;

public class MyClass extends MySuperClass {

    public void executeLog() {
        demoLog();
    }
}

When com.mypackage.MyClass#executeLog method executes it prints:

2018-07-24 10:39:04,964 : DEBUG : : com.mypackage.MySuperClass : demoLog : My message

In the log message, the Emitter is com.mypackage.MySuperClass, but I would like to have com.mypackage.MyClass as the Emitter.

  • com.mypackage.MySuperClass is extended by multiple class
  • I cannot pass Class as argument to the LogManager.getLogger() by calling getClass() from MySuperClass since it is static. To me using static is a good idea, as the actual implementation is in JSF Managed Bean.

How can I achieve this?

There is a similar question was asked here: Java Logging With Abstract Classes, but it is not providing me with a solution.

Update - 1

  • If the Logger in MySuperClass is defined as private final Logger logger = LogManager.getLogger(getClass());, still the Emitter is com.mypackage.MySuperClass.
  • If I define

    protected abstract Logger getLogger();
    

    in MySuperClass and implement it as

    private final Logger logger = LogManager.getLogger(getClass());
    
    @Override
    protected Logger getLogger() {
        return logger;
    }
    

    in MyClass the Emitter is the same, i.e., com.mypackage.MySuperClass.

Update - 2

If I define

protected abstract Logger getLogger();

in MySuperClass and implement it as

private final Logger logger = LogManager.getLogger(MyClass.class);

@Override
protected Logger getLogger() {
    return logger;
}

in MyClass the Emitter is the same, i.e., com.mypackage.MySuperClass.

Tapas Bose
  • 28,796
  • 74
  • 215
  • 331

2 Answers2

3

One way to do it would be to migrate the LOGGER constant to all the leaf subclasses, and then make it accessible through a protected abstract method, like below:

public abstract class MySuperClass {

    protected void demoLog() {
        logger().debug("My message");
    }

    protected abstract Logger logger();
}

public class MyClass extends MySuperClass {

    private static final Logger LOGGER = LogManager.getLogger();

    public void executeLog() {
        demoLog();
    }

    @Override
    protected Logger logger() {
        return LOGGER;
    }
}

EDIT: I didn't look at the link you provided, and now that I did I noticed that my answer is an analog of this answer, so - as far as I understand - it's not a solution you look for.


The Actual Answer

OK, so what this question is about (and what you mean by "emitter") is the Log4j2 "class" conversion pattern (obtained using %C or %class), and not the Logger-related pattern (obtained using %c or %logger).

Consider this log4j2.xml configuration file related to your example:

<Appenders>
    <Console name="loggerVsClassAppender" target="SYSTEM_OUT">
        <PatternLayout pattern="Logger=%logger | Class: %class | %msg%n"/>
    </Console>
</Appenders>

<Loggers>
    <Logger name="com.mypackage" level="trace">
        <AppenderRef ref="loggerVsClassAppender"/>
    </Logger>
</Loggers>

When you run your code using this configuration, the output you get is:

Logger=com.mypackage.MySuperClass | Class: com.mypackage.MySuperClass | My message

When you run my code using this configuration, the output you get is:

Logger=com.mypackage.MyClass | Class: com.mypackage.MySuperClass | My message

As you can see, appropriate Logger is being used in my code, but Class remains the same.

Now here's an excerpt from the description of the %class conversion pattern:

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

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

As you can see, this is the class name as retrieved from the stack trace at call site of the Logger.debug method.

To conclude, you cannot change this particular value because MySuperClass is where the call actually happens. However, what you can do is replacing the %class conversion pattern with the %logger conversion pattern to get the expected result.

Tomasz Linkowski
  • 4,386
  • 23
  • 38
  • Hi, thank you for your response, though please check my update. – Tapas Bose Jul 24 '18 at 13:52
  • @TapasBose Thanks! I updated my answer - I hope it helps. – Tomasz Linkowski Jul 24 '18 at 16:52
  • @TomaszLinkowski You have this backwards. The name of the logger is calculated when the logger is instantiated. Typically it is the name of the class the logger resides in. On the other hand, the class will be the name of the class that called the logger method. It will change depending on which class made the call. So if you want to always print the name of the class that called the log method you must configure the layout to print the class, not the logger. – rgoers Jul 27 '18 at 23:52
  • @rgoers Your description is right, and I'm pretty sure I got things right :) Perhaps you didn't read the question carefully enough, or perhaps you understand "class that called the logger method" differently. The OP complains that executing `MyClass.executeLog` -> `MySuperClass.demoLog` -> `MySuperClass.LOGGER.debug` prints `MySuperClass` (as it should). Even after changing the last part to `MySuperClass.logger().debug` (where now `Logger` = `MyClass`), the OP still sees `MySuperClass` (as `debug` **is** called in `MySuperClass`). Hence my conclusion that he uses `%class` instead of `%logger`. – Tomasz Linkowski Jul 28 '18 at 07:12
  • 1
    @TomaszLinkowski, defining the instance of `Logger` in the superclass as: `private final Logger logger = LogManager.getLogger(getClass());` and changing the patter layout to `%c` from `%C` did the trick. Thanks for the pointer. – Tapas Bose Jul 28 '18 at 08:31
  • @TomaszLinkowski - Your solution finished off with "what you can do is replacing the %class conversion pattern with the %logger conversion pattern to get the expected result". That is exactly backwards. He needed to change from %logger (or %c) to %Class (or %C). – rgoers Jul 29 '18 at 16:43
  • @rgoers You're right about what my solution finished off with. And that's precisely what the OP did, as he himself stated in [the comment above yours](https://stackoverflow.com/questions/51496770/log4j2-logging-with-abstract-classes?noredirect=1#comment90105159_51498806): "changing the pattern layout **to** `%c` **from** `%C` did the trick" :) – Tomasz Linkowski Jul 29 '18 at 18:13
  • @TomaszLinkowski I am not sure how we can be reading the same thing and not seeing it the same way. %c is the logger name. %C is the class name. You told him to replace the class conversion character (%C) to the logger conversion pattern (%c). He got it working by doing the exact opposite. – rgoers Jul 29 '18 at 18:17
  • @rgoers I am not sure either :) Have you tested this? Because I have. Do you at least agree that `%c` (`%logger`) prints the `Logger` name (which can generally be **anything**), and `%C` (`%Class`) prints the class name (which is the name of the class in which the called `log` method *physically* resides, and not the name of the *instantiated* class). In my answer, I've shown that `%C` always prints `MySuperClass` (because `debug` is *physically* there), even though it's called on an instance of `MyClass`. If I got something wrong, please show me where. – Tomasz Linkowski Jul 29 '18 at 18:27
  • @TomaszLinkowski What you have said above is correct, with one correction. If MySuperClass contains the Logger then demoLog(), as shown in the question will always reflect that class, regardless of %c or %C, because the method resides there. If the executeLog method had a call to LOGGER.debug() instead of a call to demoLog() then %c would print the logger name (MySuperClass) but %C would reflect MyClass, because that is the class containing the call to logger.debug. I am not sure what you mean by the instantiated class, as MyClass is both the physical and instantiated class. – rgoers Jul 29 '18 at 18:37
  • @rgoers Well, then it seems we agree as to the principles, and yet we see the implications differently :) To clarify: 1) By "If the `executeLog` method had a call to `LOGGER.debug()`" you implicitly mean "and if `LOGGER` were `protected static` in `MySuperClass`", right? I agree. 2) Do you agree, then, that in the question and in all the answers `%C` always prints `MySuperClass`? 3) By *physically* I meant where the method is *defined*: `demoLog` is called *on an instance* of `MyClass`, yet it's a method *inherited* from (and *defined in*) `MySuperClass` (hence `%C` -> `MySuperClass`). – Tomasz Linkowski Jul 29 '18 at 19:25
  • @TomaszLinkowski StackOverflow is complaining about the length of this discussion. Instead of following up here I will find a place to clarify the difference between %c and %C, with examples, in the Log4j documentation. – rgoers Jul 29 '18 at 22:24
  • @TomaszLinkowski Please see http://logging.apache.org/log4j/2.x/manual/usage.html – rgoers Aug 04 '18 at 06:42
  • @rgoers I really appreciate your preparing such a detailed manual page! You'll find my suggestions regarding this manual page in [this chat room](https://chat.stackoverflow.com/rooms/177406/static-vs-non-static-log4j2-loggers). As to our discussion, the OP wanted the call to `MyClass.demoLog` to print `MyClass` (this corresponds to your points 1-2 and 7-8 in the manual). As you can see, the **only** time `MyClass` is printed there is point **7**, which corresponds to using the **`%logger`** pattern, and which proves my point. I hope it is convincing for you this time :) – Tomasz Linkowski Aug 04 '18 at 08:42
-1

Dont make it static. Loggers are shared among the "same key" so they will be shared between instances of the same class.

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • Making a logger static has nothing to do with the question. – rgoers Jul 27 '18 at 23:56
  • 1
    @rgoers ofc it does as non static context would allow you to `this.getClass()` creating 1 logger instance per inheriting class in result. Besides, as you can see, accepted resolutio is based on making loggers non-static (see comments). – Antoniossss Jul 29 '18 at 09:25
  • The accepted answer has nothing to do with the logger being static. It has to do with whether the format uses %c (the name of the logger) or %C (the name of the class containing the method calling the logging method). It is irrelevant whether the Logger is declared static or not. – rgoers Jul 29 '18 at 16:40
  • 1
    Cant you see that OP had to change to nonstatic logger? Check comments. – Antoniossss Jul 30 '18 at 06:34
  • I will say it one last time. Whether the logger is static or not is irrelevant. All that matters is %C is used. I have a good idea how this works since I wrote the code. – rgoers Jul 30 '18 at 06:40
  • @rgoers Im glad that was your last comment. Answer is more than valid. – Antoniossss Jul 30 '18 at 06:41