Apache Log4j2 API官方使用指南(五) —— Messages

Although Log4j 2 provides Logger methods that accept Strings and Objects, all of these are ultimately captured in Message objects that are then associated with the log event. Applications are free to construct Messages of their own and pass them to the Logger. Although it may seem more expensive than passing the message format and parameters directly to the event, testing has shown that with modern JVMs the cost of creating and destroying events is minor, especially when complex tasks are encapsulated in the Message instead of the application. In addition, when using the methods that accept Strings and parameters, the underlying Message object will only be created if any configured global filters or the Logger’s log level allow the message to be processed.
尽管Log4j 2提供了接受字符串和对象的Logger方法,但最终所有这些方法都将在Message对象中捕获,然后与日志事件相关联。应用程序可以自由构造自己的消息并将其传递给Logger。尽管看起来比直接将消息格式和参数传递给事件更昂贵,但测试表明,使用现代JVM,创建和销毁事件的成本很小,尤其是当复杂的任务封装在消息而不是应用程序中时。另外,当使用接受字符串和参数的方法时,仅当任何已配置的全局过滤器或Logger的日志级别允许该消息被处理时,才会创建基础Message对象。

Consider an application that has a Map object containing {“Name” = “John Doe”, “Address” = “123 Main St.”, “Phone” = “(999) 555-1212”}and a User object that has a getId method that returns “jdoe”. The developer would like to add an informational message that returns “User John Doe has logged in using id jdoe”. The way this could be accomplished is by doing:
考虑一个应用程序,该应用程序的Map对象包含 {“Name” = “John Doe”, “Address” = “123 Main St.”, “Phone” = “(999) 555-1212”} ,而User对象包含一个返回“ jdoe”的getId方法。开发人员希望添加一条提示消息,该消息返回“用户John Doe已使用ID jdoe登录”。可以通过以下方式完成此操作:

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

While there is nothing inherently wrong with this, as the complexity of the objects and desired output increases this technique becomes harder to use. As an alternative, using Messages allows:
尽管这没有内在的错误,但是随着对象的复杂性和所需输出的增加,此技术变得更难使用。作为替代,使用Messages可以:

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

In this alternative the formatting is delegated to the LoggedInMessage object’s getFormattedMessage method. Although in this alternative a new object is created, none of the methods on the objects passed to the LoggedInMessage are invoked until the LoggedInMessage is formatted. This is especially useful when an Object’s toString method does not produce the information you would like to appear in the log.
在此替代方法中,将格式委派给LoggedInMessage对象的getFormattedMessage方法。尽管在此替代方法中,将创建一个新对象,但是在格式化LoggedInMessage之前,不会调用传递给LoggedInMessage的对象上的任何方法。当Object的toString方法未产生您希望在日志中显示的信息时,此功能特别有用。

Another advantage to Messages is that they simplify writing Layouts. In other logging frameworks the Layout must loop through the parameters individually and determine what to do based on what objects are encountered. With Messages the Layout has the option of delegating the formatting to the Message or performing its formatting based on the type of Message encountered.
Messages的另一个优点是它们简化了Layouts 的编写。在其他日志记录框架中,布局必须逐个循环遍历参数,并根据遇到的对象确定要执行的操作。使用Messages时,布局可以选择将格式委派给Message,也可以根据遇到的Message的类型执行格式化。

Borrowing from the earlier example illustrating Markers to identify SQL statements being logged, Messages can also be leveraged. First, the Message is defined.
从前面的示例中借用了Markers来标识要记录的SQL语句的示例,我们还可以用Messages来做。首先,定义消息。

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();
  }
}
Next we can use the message in our application.

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

Notice that in contrast to the prior version of this example, the logger.debug in doUpdate no longer needs to be wrapped in an isDebugEnabled call as creation of the SQLMessage is on the same order of magnitude of performing that check. Furthermore, all the formatting of the SQL columns is now hidden in the SQLMessage instead of having to take place in the business logic. Finally, if desired, Filters and/or Layouts can be written to take special action when an SQLMessage is encountered.
请注意,与该示例的先前版本相比,doUpdate中的logger.debug不再需要包装在isDebugEnabled调用中,因为创建SQLMessage与执行该检查的数量级相同。此外,SQL列的所有格式现在都隐藏在SQLMessage中,而不必在业务逻辑中进行。最后,如果需要,可以编写 Filters 和/或 Layouts 以在遇到SQLMessage时采取特殊措施。

FormattedMessage 格式化消息
The message pattern passed to a FormattedMessage is first checked to see if it is a valid java.text.MessageFormat pattern. If it is, a MessageFormatMessage is used to format it. If not it is next checked to see if it contains any tokens that are valid format specifiers for String.format(). If so, a StringFormattedMessage is used to format it. Finally, if the pattern doesn’t match either of those then a ParameterizedMessage is used to format it.
首先检查传递给FormattedMessage的消息模式,以查看它是否为有效的java.text.MessageFormat模式。如果是,则使用MessageFormatMessage对其进行格式化。如果不是,则接下来检查它是否包含任何标记,这些标记是 String.format() 的有效格式说明符。如果是这样,则使用StringFormattedMessage对其进行格式化。最后,如果该模式与任何一个都不匹配,则使用ParameterizedMessage对其进行格式化。

LocalizedMessage
LocalizedMessage is provided primarily to provide compatibility with Log4j 1.x. Generally, the best approach to localization is to have the client UI render the events in the client’s locale.
提供LocalizedMessage主要是为了提供与Log4j 1.x的兼容性。通常,最佳的本地化方法是让客户端UI在客户端的语言环境中渲染事件。

LocalizedMessage incorporates a ResourceBundle and allows the message pattern parameter to be the key to the message pattern in the bundle. If no bundle is specified, LocalizedMessage will attempt to locate a bundle with the name of the Logger used to log the event. The message retrieved from the bundle will be formatted using a FormattedMessage.
LocalizedMessage包含一个ResourceBundle,并允许消息模式参数成为分发包中消息模式的关键。如果未指定捆绑包,则LocalizedMessage将尝试使用用于记录事件的Logger名称查找捆绑包。从包中检索的消息将使用FormattedMessage进行格式化。

LoggerNameAwareMessage Logger名称支持消息
LoggerNameAwareMessage is an interface with a setLoggerName method. This method will be called during event construction so that the Message has the name of the Logger used to log the event when the message is being formatted.
LoggerNameAwareMessage是带有setLoggerName方法的接口。在事件构造期间将调用此方法,以便消息具有在格式化消息时用于记录事件的记录器的名称。

MapMessage 映射消息
A MapMessage contains a Map of String keys and values. MapMessage implements FormattedMessage and accepts format specifiers of “XML”, “JSON” or “JAVA”, in which case the Map will be formatted as XML, JSON or as documented by java.util.AbstractMap.toString(). Otherwise, the Map will be formatted as “key1=value1 key2=value2 …”.
MapMessage包含字符串键和值的映射。 MapMessage实现FormattedMessage并接受“ XML”,“ JSON”或“ JAVA”的格式说明符,在这种情况下,Map将被格式化为XML,JSON或由java.util.AbstractMap.toString()记录。否则,Map将被格式化为“ key1 = value1 key2 = value2 …”。

Some Appenders make special use of MapMessage objects:
一些Appender会对MapMessage对象特殊使用:

When a JMS Appender is configured with a MessageLayout, it converts a Log4j MapMessage to a JMS javax.jms.MapMessage.
当使用 MessageLayout 配置JMS Appender时,它将Log4j MapMessage转换为JMS javax.jms.MapMessage。
When a JDBC Appender is configured with a MessageLayout, it converts a Log4j MapMessage to values in a SQL INSERT statement.
当JDBC Appender配置有MessageLayout时,它将Log4j MapMessage转换为SQL INSERT语句中的值。
When a MongoDB2 Appender or MongoDB3 Appender is configured with a MessageLayout, it converts a Log4j MapMessage to fields in a MongoDB object.
当MongoDB2 Appender或MongoDB3 Appender配置有MessageLayout时,会将Log4j MapMessage转换为MongoDB对象中的字段。
When an Appender is MessageLayout-aware, the object Log4j sends to target is not a Log4j Log Event but a custom object.
当Appender支持MessageLayout时,Log4j发送到目标的对象不是Log4j日志事件,而是自定义对象。

篇幅有点长。。。待续。。。

MessageFormatMessage
MessageFormatMessage handles messages that use a conversion format. While this Message has more flexibility than ParameterizedMessage, it is also about two times slower.

MultiformatMessage
A MultiformatMessage will have a getFormats method and a getFormattedMessage method that accepts and array of format Strings. The getFormats method may be called by a Layout to provide it information on what formatting options the Message supports. The Layout may then call getFormattedMessage with one or more for the formats. If the Message doesn’t recognize the format name it will simply format the data using its default format. An example of this is the StructuredDataMessage which accepts a format String of “XML” which will cause it to format the event data as XML instead of the RFC 5424 format.

ObjectMessage
Formats an Object by calling its toString method. Since Log4j 2.6, Layouts trying to be low-garbage or garbage-free will call the formatTo(StringBuilder) method instead.

ParameterizedMessage
ParameterizedMessage handles messages that contain “{}” in the format to represent replaceable tokens and the replacement parameters.

ReusableObjectMessage
In garbage-free mode, this message is used to pass logged Objects to the Layout and Appenders. Functionally equivalent to ObjectMessage.

ReusableParameterizedMessage
In garbage-free mode, this message is used to handle messages that contain “{}” in the format to represent replaceable tokens and the replacement parameters. Functionally equivalent to ParameterizedMessage.

ReusableSimpleMessage
In garbage-free mode, this message is used to pass logged Strings and CharSequences to the Layout and Appenders. Functionally equivalent to SimpleMessage.

SimpleMessage
SimpleMessage contains a String or CharSequence that requires no formatting.

StringFormattedMessage
StringFormattedMessage handles messages that use a conversion format that is compliant with java.lang.String.format(). While this Message has more flexibility than ParameterizedMessage, it is also 5 to 10 times slower.

StructuredDataMessage
StructuredDataMessage allows applications to add items to a Map as well as set the id to allow a message to be formatted as a Structured Data element in accordance with RFC 5424.

ThreadDumpMessage
A ThreadDumpMessage, if logged, will generate stack traces for all threads. The stack traces will include any locks that are held.

TimestampMessage
A TimestampMessage will provide a getTimestamp method that is called during event construction. The timestamp in the Message will be used in lieu of the current timestamp.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章