Log4j 2 API

Messages

尽管 Log4j 2 提供了接受字符串和对象的 Logger 方法,但最终所有这些方法都将在 Message 对象中捕获,然后与日志事件相关联。应用程序可以自由构造自己的消息并将其传递给 Logger。尽管看起来比直接将消息格式和参数传递给事件更昂贵,但是测试表明,使用现代 JVM,创建和销毁事件的成本很小,尤其是当复杂的任务封装在消息而不是应用程序中时。此外,在使用接受字符串和参数的方法时,仅当任何已配置的全局过滤器或 Logger 的日志级别允许处理消息时,才会创建基础 Message 对象。

考虑一个应用程序,该应用程序的 Map 对象包含\ {"Name" = "John Doe", "Address" = "123 Main St.", "Phone" = "(999) 555-1212"},而 User 对象的 getId 方法返回“ jdoe”。开发人员希望添加一条参考消息,该消息返回“用户 John Doe 已使用 ID jdoe 登录”。可以通过以下方式完成此操作:

logger.info("User {} has logged in using id {}", map.get("Name"), user.getId());

尽管这没有内在的错误,但是随着对象的复杂性和所需输出的增加,此技术变得更加难以使用。作为替代,使用消息可以:

logger.info(new LoggedInMessage(map, user));

在此替代方法中,将格式委派给 LoggedInMessage 对象的 getFormattedMessage 方法。尽管在此替代方法中,将创建一个新对象,但是在格式化 LoggedInMessage 之前,不会调用传递给 LoggedInMessage 的对象上的任何方法。当对象的 toString 方法未生成您希望在日志中显示的信息时,此功能特别有用。

消息的另一个优点是它们简化了布局的编写。在其他日志记录框架中,布局必须逐个循环遍历参数,并根据遇到的对象确定要执行的操作。使用消息时,布局可以选择将格式委派给消息,也可以根据遇到的消息类型执行格式化。

从前面的示例中借用了标记来标识要记录的 SQL 语句的示例,还可以利用消息。首先,定义消息。

public class SQLMessage implements Message {
  public enum SQLType {
      UPDATE,
      QUERY
  };

  private final SQLType type;
  private final String table;
  private final Map<String, String> cols;

  public SQLMessage(SQLType type, String table) {
      this(type, table, null);
  }

  public SQLMessage(SQLType type, String table, Map<String, String> cols) {
      this.type = type;
      this.table = table;
      this.cols = cols;
  }

  public String getFormattedMessage() {
      switch (type) {
          case UPDATE:
            return createUpdateString();
            break;
          case QUERY:
            return createQueryString();
            break;
          default;
      }
  }

  public String getMessageFormat() {
      return type + " " + table;
  }

  public Object getParameters() {
      return cols;
  }

  private String createUpdateString() {
  }

  private String createQueryString() {
  }

  private String formatCols(Map<String, String> cols) {
      StringBuilder sb = new StringBuilder();
      boolean first = true;
      for (Map.Entry<String, String> entry : cols.entrySet()) {
          if (!first) {
              sb.append(", ");
          }
          sb.append(entry.getKey()).append("=").append(entry.getValue());
          first = false;
      }
      return sb.toString();
  }
}

接下来,我们可以在应用程序中使用该消息。

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.util.Map;

public class MyApp {

    private Logger logger = LogManager.getLogger(MyApp.class.getName());
    private static final Marker SQL_MARKER = MarkerManager.getMarker("SQL");
    private static final Marker UPDATE_MARKER = MarkerManager.getMarker("SQL_UPDATE", SQL_MARKER);
    private static final Marker QUERY_MARKER = MarkerManager.getMarker("SQL_QUERY", SQL_MARKER);

    public String doQuery(String table) {
        logger.entry(param);

        logger.debug(QUERY_MARKER, new SQLMessage(SQLMessage.SQLType.QUERY, table));

        return logger.exit();
    }

    public String doUpdate(String table, Map<String, String> params) {
        logger.entry(param);

        logger.debug(UPDATE_MARKER, new SQLMessage(SQLMessage.SQLType.UPDATE, table, parmas);

        return logger.exit();
    }
}

请注意,与本示例的先前版本相比,doUpdate 中的 logger.debug 不再需要包装在 isDebugEnabled 调用中,因为 SQLMessage 的创建与执行该检查的数量级相同。此外,SQL 列的所有格式现在都隐藏在 SQLMessage 中,而不必在业务逻辑中进行。最后,如果需要,可以编写过滤器和/或布局以在遇到 SQLMessage 时采取特殊措施。

FormattedMessage

首先检查传递给FormattedMessage的消息模式,以查看它是否为有效的 java.text.MessageFormat 模式。如果是,则使用 MessageFormatMessage 对其进行格式化。如果不是,则接下来检查它是否包含任何标记,这些标记是 String.format()的有效格式说明符。如果是这样,则使用 StringFormattedMessage 对其进行格式化。最后,如果该模式与任何一个都不匹配,则使用 ParameterizedMessage 对其进行格式化。

LocalizedMessage

提供LocalizedMessage主要是为了与 Log4j 1.x 兼容。通常,最佳的本地化方法是让 Client 端 UI 在 Client 端的语言环境中呈现事件。

LocalizedMessage 合并了 ResourceBundle,并允许消息模式参数成为分发包中消息模式的关键。如果未指定 Binding 包,LocalizedMessage 将尝试使用用于记录事件的 Logger 名称查找 Binding 包。从包中检索的消息将使用 FormattedMessage 进行格式化。

LoggerNameAwareMessage

LoggerNameAwareMessage 是具有 setLoggerName 方法的接口。在事件构造期间将调用此方法,以便消息具有在格式化消息时用于记录事件的 Logger 的名称。

MapMessage

MapMessage 包含字符串键和值的 Map。 MapMessage 实现 FormattedMessage 并接受“ XML”,“ JSON”或“ JAVA”的格式说明符,在这种情况下,Map 将被格式化为 XML,JSON 或java.util.AbstractMap.toString()记录。否则,Map 将被格式化为“ key1 = value1 key2 = value2 ...”。

一些 Appender 专门使用 MapMessage 对象:

  • JMS Appender配置有 MessageLayout 时,它将 Log4j MapMessage 转换为 JMS javax.jms.MapMessage。

  • JDBC Appender配置有 MessageLayout 时,它将 Log4j MapMessage 转换为 SQL INSERT 语句中的值。

  • 当使用 MessageLayout 配置MongoDB2 AppenderMongoDB3 Appender时,它将 Log4j MapMessage 转换为 MongoDB 对象中的字段。

当 Appender 支持 MessageLayout 时,Log4j 发送到目标的对象不是 Log4j 日志事件,而是自定义对象。

MessageFormatMessage

MessageFormatMessage处理使用conversion format的邮件。尽管此消息比 ParameterizedMessage 具有更大的灵 Active,但它也慢了大约两倍。

MultiformatMessage

MultiformatMessage 将具有一个 getFormats 方法和一个 getFormattedMessage 方法,该方法接受格式为 String 的数组。布局可以调用 getFormats 方法,以向其提供有关消息支持哪些格式选项的信息。然后,布局可以使用一种或多种格式调用 getFormattedMessage。如果消息无法识别格式名称,它将仅使用其默认格式来格式化数据。例如,StructuredDataMessage 接受格式为“ XML”的字符串,这将使它将事件数据格式化为 XML 而不是 RFC 5424 格式。

ObjectMessage

通过调用对象的 toString 方法设置其格式。从 Log4j 2.6 开始,尝试低垃圾或无垃圾的布局将改为调用 formatTo(StringBuilder)方法。

ParameterizedMessage

ParameterizedMessage处理包含格式为“{}”的消息,以表示可替换标记和替换参数。

ReusableObjectMessage

在无垃圾模式下,此消息用于将已记录的对象传递到布局和追加器。功能上等效于ObjectMessage

ReusableParameterizedMessage

在无垃圾模式下,此消息用于处理包含“{}”的消息,该消息的格式表示可替换的令牌和替换参数。功能上等效于ParameterizedMessage

ReusableSimpleMessage

在无垃圾模式下,此消息用于将记录的字符串和 CharSequences 传递给 Layout 和 Appender。功能上等效于SimpleMessage

SimpleMessage

SimpleMessage 包含不需要格式的 String 或 CharSequence。

StringFormattedMessage

StringFormattedMessage处理使用与[java.lang.String.format()](https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)兼容的conversion format的消息)。尽管此消息比 ParameterizedMessage 具有更大的灵 Active,但它也慢了 5 到 10 倍。

StructuredDataMessage

StructuredDataMessage允许应用程序向 Map 添加项目,并设置 ID 以允许根据RFC 5424将消息格式化为结构化数据元素。

ThreadDumpMessage

如果记录了 ThreadDumpMessage,它将为所有线程生成堆栈跟踪。堆栈跟踪将包括所有持有的锁。

TimestampMessage

TimestampMessage 将提供在事件构造期间调用的 getTimestamp 方法。消息中的时间戳将代替当前时间戳。