Log4j 2
  1. Log4j 2
  2. LOG4J2-710

Documentation for Custom Levels and Custom Loggers

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0
    • Fix Version/s: 2.0.1
    • Component/s: Documentation
    • Labels:
      None

      Description

      Documentation preview. Feedback welcome.
      Side-nav menu:

      Custom Loggers page:


      Custom Log Levels
      Log4J 2 supports custom log levels. It is possible to log messages at any self-defined custom level by passing this level to the Logger.log() method:

      final Logger logger = LogManager.getLogger();
      final Level VERBOSE = Level.forName("VERBOSE", 550);
      
      logger.log(VERBOSE, "a verbose message");
      logger.log(VERBOSE, "another message");
      

      When defining a custom log level, the intLevel parameter (550 in the example above) determines where the custom level exists in relation to the standard levels built in to Log4J 2. For reference, the table below shows the intLevel of the built-in log levels.

      Standard Level intLevel
      OFF 0
      FATAL 100
      ERROR 200
      WARN 300
      INFO 400
      DEBUG 500
      TRACE 600
      ALL Integer.MAX_VALUE


      The Standard Logger Interface
      The built-in levels have a set of convenience methods on the Logger interface that makes them easier to use. For example, the Logger interface has 14 debug methods that support the DEBUG level:

      // convenience methods for the built-in DEBUG level
      debug(Marker, Message)
      debug(Marker, Message, Throwable)
      debug(Marker, Object)
      debug(Marker, Object, Throwable)
      debug(Marker, String)
      debug(Marker, String, Object...)
      debug(Marker, String, Throwable)
      debug(Message)
      debug(Message, Throwable)
      debug(Object)
      debug(Object, Throwable)
      debug(String)
      debug(String, Object...)
      debug(String, Throwable)
      

      Similar method sets exist for the other built-in levels.


      Adding or Replacing Levels

      It would be nice to have the same ease of use with custom levels, so that after declaring a custom VERBOSE level, we would be able to use code like this:

      logger.verbose("a verbose message"); // no need to pass the VERBOSE level as a parameter
      logger.verbose("another message");
      

      In the above example, a convenience method was added to the Logger interface, in addition to the existing trace(), debug(), info(), ... methods for the built-in log levels.

      There is another use case, Domain Specific Language loggers, where we want to replace the existing trace(), debug(), info(), ... methods with all-custom methods.

      For example, for medical devices we could have only critical(), warning(), and advisory() methods. Another example could be a game that has only defcon1(), defcon2(), and defcon3() levels.

      If it were possible to hide existing log levels, users could customize the Logger interface to match their requirements. Some people may not want to have a FATAL or a TRACE level, for example. They would like to be able to create a custom Logger that only has debug(), info(), warn() and error() methods.


      Generating source code for a Logger wrapper
      Common Log4J usage is to get an instance of the Logger interface from the LogManager and call the methods on this interface. However, the custom log Levels are not known in advance, so Log4J cannot provide an interface with convenience methods for these custom log Levels.

      To solve this, Log4J ships with a tool that generates source code for a Logger wrapper. The generated wrapper class has convenience methods for each custom log level, making custom levels just as easy to use as the built-in levels.

      There are two flavors of wrappers: ones that extend the Logger API (adding methods to the built-in levels) and ones that customize the Logger API (replacing the built-in methods).

      When generating the source code for a wrapper class, you need to specify:

      • the fully qualified name of the class to generate
      • the list of custom levels to support and their intLevel relative strength
      • whether to extend Logger (and keep the existing built-in methods) or have only methods for the custom log levels

      You would then include the generated source code in the project where you want to use custom log levels.


      Example Usage of a Generated Logger Wrapper
      Here is an example of how one would use a generated logger wrapper for custom levels DIAG, NOTICE and VERBOSE:

      // ExtLogger is a generated logger wrapper
      import com.mycompany.myproject.ExtLogger;
      
      public class MyService {
          // instead of Logger logger = LogManager.getLogger(MyService.class):
          private static final ExtLogger logger = ExtLogger.create(MyService.class);
          
          public void someMethod() {
              // ...
              logger.trace("the built-in TRACE level");
              logger.verbose("a custom level: a VERBOSE message");
              logger.debug("the built-in DEBUG level");
              logger.notice("a custom level: a NOTICE message");
              logger.info("the built-in INFO level");
              logger.diag("a custom level: a DIAG message");
              logger.warn("the built-in WARN level");
              logger.error("the built-in ERROR level");
              logger.fatal("the built-in FATAL level");
              // ...
          }
          ...
      }
      


      Generating Extended Loggers

      Use the following command to generate a wrapper that adds methods to the built-in ones:

      java -cp log4j-core-2.0.jar org.apache.logging.log4j.core.tools.Generate$ExtendedLogger com.mycomp.ExtLogger DIAG=350 NOTICE=450 VERBOSE=550 > com/mycomp/ExtLogger.java
      

      This will generate source code for a logger wrapper that has the convenience methods for the built-in levels as well as the specified custom levels. The tool sends the generated source code to the console, so we redirected the output to a file.


      Generating Custom Loggers

      Use the following command to generate a wrapper that hides the built-in levels and has only custom levels:

      java -cp log4j-core-2.0.jar org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java
      

      This will generate source code for a logger wrapper that has only convenience methods for the specified custom levels, not for the built-in levels. The tool sends the generated source code to the console, so we redirected the output to a file.

        Issue Links

          Activity

          Hide
          Remko Popma added a comment -

          Added in revision 1611386.

          Show
          Remko Popma added a comment - Added in revision 1611386.
          Hide
          Gary Gregory added a comment -

          The class is generated in the default package? Is there an option to pick the package name and a destination root folder? Such that if I pass com.foo as the package and c:/mysources as the folder, the tool generates the class in c:\mysources\com\foo?

          Show
          Gary Gregory added a comment - The class is generated in the default package? Is there an option to pick the package name and a destination root folder? Such that if I pass com.foo as the package and c:/mysources as the folder, the tool generates the class in c:\mysources\com\foo?
          Hide
          Gary Gregory added a comment -

          Docs look good BTW!

          Show
          Gary Gregory added a comment - Docs look good BTW!
          Hide
          Remko Popma added a comment -

          You need to specify the fully qualified class name, so you specify the package.
          The source code is printed to the console.
          For convenience you can redirect to a file. At that point you can specify the directory structure required for the package as well, for example:

          java Generate$CustomLogger com.mycomp.myproject.MyLogger ABC=123 XYZ=456 > com/mycomp/myproject/MyLogger.java
          

          Glad you like the docs!

          Show
          Remko Popma added a comment - You need to specify the fully qualified class name, so you specify the package. The source code is printed to the console. For convenience you can redirect to a file. At that point you can specify the directory structure required for the package as well, for example: java Generate$CustomLogger com.mycomp.myproject.MyLogger ABC=123 XYZ=456 > com/mycomp/myproject/MyLogger.java Glad you like the docs!
          Hide
          Gary Gregory added a comment -

          When you say ...com/mycomp/myproject/MyLogger.java on the CLI, how does it know to create the right package statement?

          Show
          Gary Gregory added a comment - When you say ...com/mycomp/myproject/MyLogger.java on the CLI, how does it know to create the right package statement?
          Hide
          Gary Gregory added a comment -

          Weird, I cannot edit my own comments!

          Show
          Gary Gregory added a comment - Weird, I cannot edit my own comments!
          Hide
          Remko Popma added a comment -

          Changing status from Closed to Resolved so that comments can be edited.

          Show
          Remko Popma added a comment - Changing status from Closed to Resolved so that comments can be edited.
          Hide
          Remko Popma added a comment - - edited

          It gets the package from the first argument: com.mycomp.myproject.MyLogger.
          If the first argument does not contain any dot '.' characters, the package declaration is omitted (so then it becomes the default package).

          I did consider taking the output folder as an additional arguments, but the command line redirection works just as well and keeps the tool implementation simple.

          Show
          Remko Popma added a comment - - edited It gets the package from the first argument: com.mycomp.myproject.MyLogger . If the first argument does not contain any dot '.' characters, the package declaration is omitted (so then it becomes the default package). I did consider taking the output folder as an additional arguments, but the command line redirection works just as well and keeps the tool implementation simple.
          Hide
          Nelson MELINA added a comment - - edited

          From where do you use that command ?

          java org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java
          

          I tried from the root of the project, the log4j\log4j-core\src\main\java\org\apache\logging\log4j\core\tools, log4j\log4j-core\src\main\, log4j\log4j-core\src\main\java\ and i got an error saying it could not find or load the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger.

          Tried also

          C:\Users\nelson\Documents\NetBeansProjects\log4j\log4j-core\src\main\java\org\apache\logging\log4j\core\tools>java -cp . org.apache.logging.log4j.core.tools.Generate$ExtendedLogger
          Error: no se ha encontrado o cargado la clase principal org.apache.logging.log4j.core.tools.Generate$ExtendedLogger
          
          Show
          Nelson MELINA added a comment - - edited From where do you use that command ? java org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java I tried from the root of the project, the log4j\log4j-core\src\main\java\org\apache\logging\log4j\core\tools, log4j\log4j-core\src\main\, log4j\log4j-core\src\main\java\ and i got an error saying it could not find or load the org.apache.logging.log4j.core.tools.Generate$ExtendedLogger. Tried also C:\Users\nelson\Documents\NetBeansProjects\log4j\log4j-core\src\main\java\org\apache\logging\log4j\core\tools>java -cp . org.apache.logging.log4j.core.tools.Generate$ExtendedLogger Error: no se ha encontrado o cargado la clase principal org.apache.logging.log4j.core.tools.Generate$ExtendedLogger
          Hide
          Remko Popma added a comment - - edited

          From the command line, use

          java -cp log4j-core-2.0.jar org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java
          
          Show
          Remko Popma added a comment - - edited From the command line, use java -cp log4j-core-2.0.jar org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java
          Hide
          Nelson MELINA added a comment -

          Ok, it works from the jar. thanks.

          PS Any idea what i was doing wrong before ?

          Show
          Nelson MELINA added a comment - Ok, it works from the jar. thanks. PS Any idea what i was doing wrong before ?
          Hide
          Remko Popma added a comment -

          Classpath issue? Looks like you were trying to run from the source directory instead of the classes directory?

          Show
          Remko Popma added a comment - Classpath issue? Looks like you were trying to run from the source directory instead of the classes directory?
          Hide
          Nelson MELINA added a comment -
          Show
          Nelson MELINA added a comment - True Remko Popma .

            People

            • Assignee:
              Remko Popma
              Reporter:
              Remko Popma
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development