Log4j 2
  1. Log4j 2
  2. LOG4J2-138

org.apache.logging.log4j.Logger lacks some log(Level, String, ...) method

    Details

    • Type: Wish Wish
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0-beta3
    • Fix Version/s: None
    • Component/s: API

      Description

      Hello,
      I dont know if Im not alone with this problem but I encountered blocker in Log4j2 usage (blocker for me personally of course ). Im posting it as Wish issue as it is not a bug.

      Im trying to switch from Log4j 1.2 to 2 and it looked like piece of cake but - I have developped some logging facade or wrapper solution around log4j 1.2, it's something like simple SLF4J, it was necessary because my project logging needs are pretty special and also I wanted to have the option to easilly switch logging frameworks.

      Problem is that most of my logging code is in own abstract class that is common for all implementations, concrete implementations are extending this base and have to implement only few methods like log(Level, String) and isLevelLoggable(Level) etc.. The advantage is that implementations are very simple to write with minimal amount of copy/paste programming.

      Problem is that the new org.apache.logging.log4j.Logger iface does not contain any log(...) method and Im stuck.

      I have only 2 options - I can use org.apache.logging.log4j.core.Logger class without using iface but I dont want that for obvious reasons - I want to do it properly. Second option is to implement all info(), debug(),trace(),.... methods in my implementation class but I dont wanna do that because that is exactly what I wanted to avoid in my facade solution.

      I know it's my wish only, I dont know if this change had some good reasons but Im voting for adding some log() method to Logger iface The same thing applies for missing getLevel() method but I already solved this one easilly so it's not my priority.

      Thanks,
      Pavel

      1. customFacade.zip
        6 kB
        Pavel Trka
      2. custom.zip
        15 kB
        Ralph Goers

        Activity

        Hide
        Ralph Goers added a comment -

        If I understand correctly what you want is already supported. AbstractLogger contains a log method. AbstractLoggerWrapper explicitly makes some of the protected methods public just to do what you are asking for. I would suggest looking at SLF4JLogger.java, EventLogger.java and Log4jLog.java for examples of using AbstractLoggerWrapper.

        Show
        Ralph Goers added a comment - If I understand correctly what you want is already supported. AbstractLogger contains a log method. AbstractLoggerWrapper explicitly makes some of the protected methods public just to do what you are asking for. I would suggest looking at SLF4JLogger.java, EventLogger.java and Log4jLog.java for examples of using AbstractLoggerWrapper.
        Hide
        Gary Gregory added a comment -

        Is there a design reason we do not have log methods that take a log level in the Logger interface? Seems pretty useful. I can see that it would add more methods, and make the interface look more complicated, but log APIs are nice ways to generically implement some features for some clients.

        Show
        Gary Gregory added a comment - Is there a design reason we do not have log methods that take a log level in the Logger interface? Seems pretty useful. I can see that it would add more methods, and make the interface look more complicated, but log APIs are nice ways to generically implement some features for some clients.
        Hide
        Ralph Goers added a comment -

        How would it be more useful in the interface? Why would a user want to code logger.log(Level.WARN, ...) instead of logger.warn(...)? It is necessary for those implementing a custom logger to have that, which is why it is available in AbstractLogger and AbstractLoggerWrapper, but the idea of the interface is to separate the user from the implementation, which was one of the major problems in Log4j 1.x.

        Show
        Ralph Goers added a comment - How would it be more useful in the interface? Why would a user want to code logger.log(Level.WARN, ...) instead of logger.warn(...)? It is necessary for those implementing a custom logger to have that, which is why it is available in AbstractLogger and AbstractLoggerWrapper, but the idea of the interface is to separate the user from the implementation, which was one of the major problems in Log4j 1.x.
        Hide
        Gary Gregory added a comment -

        1. An app may want to make the Level configurable for certain errors (warn vs err vs fatal). I'm not sure how realistic that is.

        2. The API already offers Level as a parameter for some APIs (throwing() and catching() instead of warnCatching() and so on.) If you get rid of throwing() and catching(), then you can hide the Level enum, which goes in the direction of simplifying the public API. I am sure sure that this is a design goal for 2.0 though.

        Show
        Gary Gregory added a comment - 1. An app may want to make the Level configurable for certain errors (warn vs err vs fatal). I'm not sure how realistic that is. 2. The API already offers Level as a parameter for some APIs (throwing() and catching() instead of warnCatching() and so on.) If you get rid of throwing() and catching(), then you can hide the Level enum, which goes in the direction of simplifying the public API. I am sure sure that this is a design goal for 2.0 though.
        Hide
        Pavel Trka added a comment -

        Ralph: I think I described the reason why it would be useful (to me) to have such method in the interface - I need precisely the code you described - logger.log(Level.WARN,...), if I have to use logger.info(), logger.warn() etc my implementations get cluttered with methods and copy/paste, loggerl.log() method is ideal single "entry point" trough which I can implement all trace/debug/... functionality.

        If I undestand well, using AbstractLogger or AbstractLoggerWrapper are intended for someone who is implementing own logger and who are extending Log4j2 classes but IM NOT extending, Im only using Log4j - I simply want to get the Logger iface trough LogManager.getLogger() and then use the iface.

        I know that I can cast returned iface to AbstractLogger or create new AbstractLoggerWrapper around returned iface I simply wanted to know if there is a chance to push log() method to the interface as it would be simpliest straitforward solution hardened against possible inner implementation details changes in the future as the iface is hiding AbstractLogger details. Im not an Log4j API expert but my personal opinion is that exposing Level is not big implementation detail, correct me if Im wrong but as I was using old Log4j for looong time, I guess Level is the thing which gets hardly changed in the future. But I dont want to be a smartass, as I said Im not the expert.

        To demonstrate what Im doing and why I see it as useful Im attaching my log package code (is pretty simple so I think it should be clear) -

        Logger.java - my log interface defining our logging methods
        AbstractLogger.java - common ancestor implementing most of the Logger interface methods. The goal is to have as many as possible implementation code here, not in the subclasses.
        Log4jLogger.java - old Log4j logger implementation
        Log4j2Logger.java - new Log4j2 implementation, there is a "NOT POSSIBLE NOW" comment on the critical line
        JDKLogger.java - JDK logger implementation

        As you can see, as most of the code is in AbstractLogger, implementation classes (Log4j, JDK...) are pretty simple and thats a good thing. But now with Log4J2 it is a problem - if I will be exactly clean and use Log4j2 Logger interface, I have to override all debug/trace/... methods in Log4j2Logger.java implementation and that the thing I dont want to do.

        Of course for now i will use AbstratLoggerWrapper or casting in my implementation but the iface modification would be best for me

        Sorry for such long post,
        thanks

        Show
        Pavel Trka added a comment - Ralph: I think I described the reason why it would be useful (to me) to have such method in the interface - I need precisely the code you described - logger.log(Level.WARN,...), if I have to use logger.info(), logger.warn() etc my implementations get cluttered with methods and copy/paste, loggerl.log() method is ideal single "entry point" trough which I can implement all trace/debug/... functionality. If I undestand well, using AbstractLogger or AbstractLoggerWrapper are intended for someone who is implementing own logger and who are extending Log4j2 classes but IM NOT extending, Im only using Log4j - I simply want to get the Logger iface trough LogManager.getLogger() and then use the iface. I know that I can cast returned iface to AbstractLogger or create new AbstractLoggerWrapper around returned iface I simply wanted to know if there is a chance to push log() method to the interface as it would be simpliest straitforward solution hardened against possible inner implementation details changes in the future as the iface is hiding AbstractLogger details. Im not an Log4j API expert but my personal opinion is that exposing Level is not big implementation detail, correct me if Im wrong but as I was using old Log4j for looong time, I guess Level is the thing which gets hardly changed in the future. But I dont want to be a smartass, as I said Im not the expert. To demonstrate what Im doing and why I see it as useful Im attaching my log package code (is pretty simple so I think it should be clear) - Logger.java - my log interface defining our logging methods AbstractLogger.java - common ancestor implementing most of the Logger interface methods. The goal is to have as many as possible implementation code here, not in the subclasses. Log4jLogger.java - old Log4j logger implementation Log4j2Logger.java - new Log4j2 implementation, there is a "NOT POSSIBLE NOW" comment on the critical line JDKLogger.java - JDK logger implementation As you can see, as most of the code is in AbstractLogger, implementation classes (Log4j, JDK...) are pretty simple and thats a good thing. But now with Log4J2 it is a problem - if I will be exactly clean and use Log4j2 Logger interface, I have to override all debug/trace/... methods in Log4j2Logger.java implementation and that the thing I dont want to do. Of course for now i will use AbstratLoggerWrapper or casting in my implementation but the iface modification would be best for me Sorry for such long post, thanks
        Hide
        Pavel Trka added a comment -

        custom log facade code

        Show
        Pavel Trka added a comment - custom log facade code
        Hide
        Pavel Trka added a comment -

        small correction - I can only use AbstractLoggerWrapper, not casting, as Im not extending Log4j classes so I cant use protected methods. My code in Log4j2Logger would look like this (although Im not sure with FQCN usage)

        public void log(String message, LogLevel level)

        { AbstractLoggerWrapper wrapper = new AbstractLoggerWrapper((org.apache.logging.log4j.spi.AbstractLogger) logImpl, logImpl.getName()); wrapper.log(null, Log4j2Logger.class.getName(), toImplLevel(level), new SimpleMessage(createMessage(message)), null); }
        Show
        Pavel Trka added a comment - small correction - I can only use AbstractLoggerWrapper, not casting, as Im not extending Log4j classes so I cant use protected methods. My code in Log4j2Logger would look like this (although Im not sure with FQCN usage) public void log(String message, LogLevel level) { AbstractLoggerWrapper wrapper = new AbstractLoggerWrapper((org.apache.logging.log4j.spi.AbstractLogger) logImpl, logImpl.getName()); wrapper.log(null, Log4j2Logger.class.getName(), toImplLevel(level), new SimpleMessage(createMessage(message)), null); }
        Hide
        Ralph Goers added a comment -

        I've looked at your code. What you are doing is precisely why there is an SPI with AbstractLoggerWrapper that does contain the log method. You are not a simple user of the Log4j API but are creating your own logging framework. If you were to simply use the Logger interface you would not be able to properly use the ClassLoaderContextSelector. Using the AbstractLoggerWrapper you can. You are also not taking advantage of features in Log4j 2.

        I have uploaded an implementation of Log4j2Logger that makes better use of what Log4j 2 offers. However, since you did not provide all the relevant helper clases I can't be sure you won't have to make modifications to it. Note that I didn't have to override all your debug and trace methods, although I did override some of the log methods to take advantage of how Log4j 2 can better support your use of DataObjects.

        Show
        Ralph Goers added a comment - I've looked at your code. What you are doing is precisely why there is an SPI with AbstractLoggerWrapper that does contain the log method. You are not a simple user of the Log4j API but are creating your own logging framework. If you were to simply use the Logger interface you would not be able to properly use the ClassLoaderContextSelector. Using the AbstractLoggerWrapper you can. You are also not taking advantage of features in Log4j 2. I have uploaded an implementation of Log4j2Logger that makes better use of what Log4j 2 offers. However, since you did not provide all the relevant helper clases I can't be sure you won't have to make modifications to it. Note that I didn't have to override all your debug and trace methods, although I did override some of the log methods to take advantage of how Log4j 2 can better support your use of DataObjects.
        Hide
        Pavel Trka added a comment -

        Thank you very much for your effort and tips. Although now I dont have much time to study it in detail, I think it's not necessary to implement more log() methods in Log4j2Logger, I only needed some confirmation that this is the right usecase for using AbstractLoggerWrapper But I wiil definitelly take some inspiration from custom message class sample

        I dont use much of the features now but Im doing major update of our logging layer and this was only beginning where I wanted to plug Log4j2 in. In few days I will leverage some more functionalities like thread context etc. Also maybee I will not use many log4j 2 feature at the code layer, but the reasons to switch are also performance and some filtering features we will implement simply by configuration.

        Thank you again, I will close this issue.
        Pavel Trka

        Show
        Pavel Trka added a comment - Thank you very much for your effort and tips. Although now I dont have much time to study it in detail, I think it's not necessary to implement more log() methods in Log4j2Logger, I only needed some confirmation that this is the right usecase for using AbstractLoggerWrapper But I wiil definitelly take some inspiration from custom message class sample I dont use much of the features now but Im doing major update of our logging layer and this was only beginning where I wanted to plug Log4j2 in. In few days I will leverage some more functionalities like thread context etc. Also maybee I will not use many log4j 2 feature at the code layer, but the reasons to switch are also performance and some filtering features we will implement simply by configuration. Thank you again, I will close this issue. Pavel Trka
        Hide
        Pavel Trka added a comment -

        Clarified

        Show
        Pavel Trka added a comment - Clarified

          People

          • Assignee:
            Unassigned
            Reporter:
            Pavel Trka
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development