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.

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