自定义日志级别

在代码中定义自定义日志级别

Log4J 2 支持自定义日志级别。自定义日志级别可以在代码或配置中定义。要在代码中定义自定义日志级别,请使用 Level.forName()方法。此方法为指定名称创建一个新级别。定义日志级别后,可以通过调用 Logger.log()方法并传递自定义日志级别来在此级别记录消息:

// This creates the "VERBOSE" level if it does not exist yet.
final Level VERBOSE = Level.forName("VERBOSE", 550);

final Logger logger = LogManager.getLogger();
logger.log(VERBOSE, "a verbose message"); // use the custom VERBOSE level

// Create and use a new custom level "DIAG".
logger.log(Level.forName("DIAG", 350), "a diagnostic message");

// Use (don't create) the "DIAG" custom level.
// Only do this *after* the custom level is created!
logger.log(Level.getLevel("DIAG"), "another diagnostic message");

// Using an undefined level results in an error: Level.getLevel() returns null,
// and logger.log(null, "message") throws an exception.
logger.log(Level.getLevel("FORGOT_TO_DEFINE"), "some message"); // throws exception!

定义自定义日志级别时,intLevel 参数(在上面的示例中为 550 和 350)确定与 Log4J 2 内置的标准级别相关的自定义级别的位置。作为参考,下表显示了已构建的日志级别。 -在日志级别。

  • Log4J 内置的标准日志级别*
Standard LevelintLevel
OFF0
FATAL100
ERROR200
WARN300
INFO400
DEBUG500
TRACE600
ALLInteger.MAX_VALUE

在配置中定义自定义日志级别

自定义日志级别也可以在配置中定义。这对于在 Logger 过滤器或追加器过滤器中使用自定义级别很方便。与在代码中定义日志级别类似,必须先定义一个自定义级别,然后才能使用它。如果 Logger 或附加器配置有未定义的级别,则该 Logger 或附加器将无效并且将不处理任何日志事件。

CustomLevel 配置元素创建一个自定义级别。在内部,它调用与上面讨论的相同的 Level.forName()方法。

CustomLevel Parameters

Parameter NameTypeDescription
nameString自定义级别的名称。请注意,级别名称区分大小写。惯例是使用所有大写名称。
intLevelinteger确定与 Log4J 2 内置的标准级别有关的自定义级别存在的位置(请参见上表)。

以下示例显示了一种配置,该配置定义了一些自定义日志级别并使用自定义日志级别来筛选发送到控制台的日志事件。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <!-- Define custom levels before using them for filtering below. -->
  <CustomLevels>
    <CustomLevel name="DIAG" intLevel="350" />
    <CustomLevel name="NOTICE" intLevel="450" />
    <CustomLevel name="VERBOSE" intLevel="550" />
  </CustomLevels>

  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
    </Console>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <!-- Only events at DIAG level or more specific are sent to the console. -->
      <AppenderRef ref="Console" level="diag" />
      <AppenderRef ref="MyFile" level="trace" />
    </Root>
  </Loggers>
</Configuration>

内置日志级别的便捷方法

内置日志级别在 Logger 界面上具有一组便捷方法,使它们更易于使用。例如,Logger 接口具有 24 个 debug()方法,它们支持 DEBUG 级别:

// 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)
// lambda support methods added in 2.4
debug(Marker, MessageSupplier)
debug(Marker, MessageSupplier, Throwable)
debug(Marker, String, Supplier<?>...)
debug(Marker, Supplier<?>)
debug(Marker, Supplier<?>, Throwable)
debug(MessageSupplier)
debug(MessageSupplier, Throwable)
debug(String, Supplier<?>...)
debug(Supplier<?>)
debug(Supplier<?>, Throwable)

对于其他内置级别也存在类似的方法。相反,自定义级别需要将日志级别作为附加参数传递。

// need to pass the custom level as a parameter
logger.log(VERBOSE, "a verbose message");
logger.log(Level.forName("DIAG", 350), "another message");

与自定义级别具有相同的易用性会很好,因此,在声明自定义 VERBOSE/DIAG 级别之后,我们可以使用如下代码:

// nice to have: descriptive methods and no need to pass the level as a parameter
logger.verbose("a verbose message");
logger.diag("another message");
logger.diag("java 8 lambda expression: {}", () -> someMethod());

标准的 Logger 界面无法提供用于自定义级别的便捷方法,但是接下来的几节介绍了一个代码生成工具来创建 Logger,旨在使自定义级别与内置级别一样易于使用。

添加或替换日志级别

我们假定除了内置日志级别的现有 trace(),debug(),info(),...方法之外,大多数用户都希望向 Logger 接口添加自定义级别的方法。

还有另一种用例,特定于域的语言 Logger,我们要用全定制方法替换现有的 trace(),debug(),info(),...方法。

例如,对于医疗设备,我们只能使用 critical(),warning()和 advisory()方法。另一个示例可能是仅具有 defcon1(),defcon2()和 defcon3()水平的游戏。

如果可以隐藏现有的日志级别,则用户可以自定义 Logger 界面以满足他们的要求。例如,某些人可能不希望具有致命或跟踪级别。他们希望能够创建仅具有 debug(),info(),warn()和 error()方法的自定义 Logger。

为自定义 Logger 包装器生成源代码

Log4J 的常见用法是从 LogManager 获取 Logger 接口的实例并在该接口上调用方法。但是,自定义日志级别不是预先已知的,因此 Log4J 无法为这些自定义日志级别提供具有便捷方法的接口。

为了解决这个问题,Log4J 附带了一个为 Logger 包装器生成源代码的工具。生成的包装器类具有用于每个自定义日志级别的便捷方法,使自定义级别与内置级别一样易于使用。

有两种包装器: 扩展 Logger API(将方法添加到内置级别)和 定制 Logger API(替代内置方法)。

在为包装器类生成源代码时,需要指定:

  • 要生成的类的完全限定名称

  • 要支持的自定义级别列表及其 intLevel 的相对强度

  • 是否扩展 Logger(并保留现有的内置方法)或仅对自定义日志级别使用方法

然后,您将在要使用自定义日志级别的项目中包含生成的源代码。

生成的 Logger 包装程序的示例用法

这是一个示例如何使用具有自定义级别 DIAG,NOTICE 和 VERBOSE 的生成的 Logger 包装器的示例:

// 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 demoExtendedLogger() {
        // ...
        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");
        logger.notice("java 8 lambda expression only executed if NOTICE is enabled: {}", () -> someMethod());
        // ...
    }
    ...
}

生成扩展日志 Logger

使用以下命令来生成 Logger 包装器,该包装器将方法添加到内置方法中:

java -cp log4j-core-2.13.3.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator \
        com.mycomp.ExtLogger DIAG=350 NOTICE=450 VERBOSE=550 > com/mycomp/ExtLogger.java

这将为 Logger 包装器生成源代码,该包装器具有内置级别和指定的自定义级别的便捷方法。该工具将生成的源代码打印到控制台。通过附加“>文件名”,可以将输出重定向到文件。

注意:在 log4j-2.9 之前,此工具是内部类 Generate $ ExtendedLogger。
在 Unix/Mac/Linux 上的 bash shell 下,需要转义美元字符$,因此类名应在单引号'org.apache.logging.log4j.core.tools.Generate $ ExtendedLogger'之间。

生成自定义日志 Logger

使用以下命令来生成 Logger 包装程序,该记录程序包装程序将隐藏内置级别并仅具有自定义级别:

java -cp log4j-core-2.13.3.jar org.apache.logging.log4j.core.tools.CustomLoggerGenerator \
        com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java

这将为 Logger 包装器生成源代码,该 Logger 包装器仅具有指定自定义级别的便利方法,而没有内置级别的便捷方法。该工具将生成的源代码打印到控制台。通过附加“>文件名”,可以将输出重定向到文件。

注意:在 log4j-2.9 之前,此工具是内部类 Generate $ ExtendedLogger。
在 Unix/Mac/Linux 上的 bash shell 下,需要转义美元字符$,因此类名应在单引号'org.apache.logging.log4j.core.tools.Generate $ CustomLogger'之间。