Log4j 2 API

Message

原文地址

虽然Log4j 2提供了Logger的方法来接收String和Object,但它们最终都将在Message对象中被捕获并与日志事件相关联。应用程序可以自由定义它们自己的Message并传递给Logger。尽管这可能看上去比将消息格式和参数传递给事件要成本更高,但测试显示在现代的JVM中,创建和销毁事件的成本很低,特别是在Message中概括复杂的任务,而不是在应用中。另外,当使用接受String和Object的方法时,所基于的Message对象只有在配置的全局Filter或Logger的日志级别允许消息被处理时才会被创建。

考虑应用程序有一个Map对象包含{"Name" = "John Doe", "Address" = "123 Main St.", "Phone" = "(999) 555-1212"},还有一个User对象含有一个返回“jdoe”的getId方法。开发者希望添加一条信息化的消息来返回“User John Doe has logged in using id jdoe”。做法是:

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

虽然这样做没有什么错,但随着对象的复杂程度提高,输出会变得越来越困难。作为替代,可以使用Message:

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

在这种替代做法中,格式化被委托给LoggedInMessage对象的getFormattedMessage方法。尽管在这里创建了一个新对象,但在被进行格式化之前不会调用LoggedInMessage中的任何方法。这特别适用于当对象的toString方法不能提供你希望在日志中展示的信息的场景。

Message的另一个优点是它简化了写Layout。在其他的日志框架中,Layout必须通过单独的参数循环,并且明确在遇到的对象上面要做什么。而通过Message,Layout能够可选地将格式化委托给Message,或者基于对应的Message自己做格式化。

借鉴此前说明Marker来记录SQL语句的例子,Message也可以起作用。首先,定义Message:

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();
  }
}

下面我们可以在应用中使用Message:

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时使用Filter和/或Layout执行特殊动作。

FormattedMessage

传递给FormattedMessage的消息模式会被首先检查是否为一个正确的java.text.MessageFormat模式。如果是,会用一个MessageFormatMessage来格式化。如果不是,会继而检查是否包含合法的String.format()规范模式。如果有,会用一个StringFormattedMessage来格式化。最后,如果这些都不匹配,就用一个ParameterizedMessage来格式化。

LocalizedMessage

LocalizedMessage主要提供与Log4j 1.x的兼容性。一般来讲,最好的本地化是让客户端UI将事件交给客户端本地。

LocalizedMessage合并了一个ResourceBundle并且允许用消息模式参数作为消息模式在bundle中的key。如果没有声明bundle,LocalizedMessage会尝试用记录事件的Logger名称来定位一个bundle。从bundle中获取的消息会用FormattedMessage进行格式化。

LoggerNameAwareMessage

LoggerNameAwareMessage是一个带有setLoggerName方法的接口。该方法将在事件构造期间被调用,这样Message就会在被格式化时持有用来记录事件的Logger名称。

MapMessage

一个MapMessage包含一个String的key和value的Map。MapMessage实现了FormattedMessage并接受一个“XML”的格式声明,以便让Map作为XML被格式化。否则,Map将会以“key1=value1 key2=value2...”的方式被格式化。

MessageFormatMessage

MessageFormatMessage接管使用转换格式的消息。尽管这个Message比ParameterizedMessage更加灵活,但它也同样更慢两倍。

MultiformatMessage

一个MultiformatMessage会有一个getFormats方法和一个接收格式字符串数组的getFormattedMessage方法。Layout会调用getFormats方法来得知该Message支持什么样子的格式。然后Layout可以通过一种或多种格式来调用getFormattedMessage方法。如果Message没有识别格式名称,则会用简单的默认格式来格式化数据。StructuredDataMessage就是个例子,它接收一个“XML”的格式字符串来让它将事件数据作为XML格式化,而替换掉RFC 5424格式。

ObjectMessage

调用toString方法来格式化对象。从Log4j 2.6开始,Layout尝试在调用formatTo(StringBuilder)方法时成为低垃圾或无垃圾的。

ParameterizedMessage

ParameterizedMessage处理格式中包含“{}”的消息,作为一个可替换的占位符,并接收用来替换的参数。

ReusableObjectMessage

在无垃圾模式中,这个Message用来将日志记录的对象传递给Layout和Appender。功能上与ObjectMessage相同。

ReusableParameterizedMessage

在无垃圾模式中,这个Message用来处理格式中包含“{}”的消息,作为一个可替换的占位符,并接收用来替换的参数。功能上与ParameterizedMessage相同。

ReusableSimpleMessage

在无垃圾模式中,这个Message用来将记录的String和CharSequence传递给Layout和Appender。功能上与SimpleMessage相同。

SimpleMessage

SimpleMessage包含一个无需格式化的String或CharSequence。

StringFormattedMessage

StringFormattedMessage处理包含适用于java.lang.String.format()转换格式。尽管它比ParameterizedMessage要更灵活,但却要更慢5到10倍。

StructuredDataMessage

StructuredDataMessage允许应用程序将信息添加到Map中并设置一个ID以让消息作为结构化数据进行格式化,依照RFC 5424

ThreadDumpMessage

一个ThreadDumpMessage,如果记录的话,会为所有线程生成栈追踪信息。如果运行在Java 6以上的环境,站信息还将包含持有的所有锁。

TimestampMessage

一个TimestampMessage会提供在事件构造期间被调用的getTimestamp方法。Message中的时间戳会被作为当前时间戳而替换。

results matching ""

    No results matching ""