Log4j 2 API
消息
虽然 Log4j 2 提供了接受字符串和对象的记录器方法,但所有这些最终都会被捕获在消息对象中,然后与日志事件关联。应用程序可以自由地构建自己的消息并将其传递给记录器。虽然这可能看起来比直接将消息格式和参数传递给事件更昂贵,但测试表明,使用现代 JVM,创建和销毁事件的成本很小,尤其是在将复杂的任务封装在消息而不是应用程序中时。此外,当使用接受字符串和参数的方法时,只有在任何配置的全局过滤器或记录器的日志级别允许处理消息时,才会创建底层消息对象。
考虑一个应用程序,它有一个包含 {"Name" = "John Doe", "Address" = "123 Main St.", "Phone" = "(999) 555-1212"} 的 Map 对象,以及一个具有 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(table);
logger.debug(QUERY_MARKER, new SQLMessage(SQLMessage.SQLType.QUERY, table));
return logger.exit();
}
public String doUpdate(String table, Map<String, String> params) {
logger.entry(params);
logger.debug(UPDATE_MARKER, new SQLMessage(SQLMessage.SQLType.UPDATE, table, params);
return logger.exit();
}
}请注意,与该示例的先前版本相比,doUpdate 中的 logger.debug 不再需要包装在 isDebugEnabled 调用中,因为创建 SQLMessage 的顺序与执行该检查的顺序相同。此外,所有 SQL 列的格式化现在都隐藏在 SQLMessage 中,而不是必须在业务逻辑中进行。最后,如果需要,可以编写过滤器和/或布局,以便在遇到 SQLMessage 时采取特殊操作。
FormattedMessage
FormattedMessage 是一个支持多个模式格式化程序的消息
- 如果模式是有效的 java.text.MessageFormat 模式,则使用 MessageFormatMessage 来格式化它,
- 如果模式包含有效的未转义的
{}指定符,则使用 ParameterizedMessage 来格式化它, - 如果模式是有效的 java.uti.Formatter 模式,则使用 StringFormattedMessage 来格式化它。
LocalizedMessage
LocalizedMessage 主要用于提供与 Log4j 1.x 的兼容性。通常,本地化的最佳方法是让客户端 UI 以客户端的语言环境呈现事件。
LocalizedMessage 包含一个 ResourceBundle,并允许消息模式参数成为捆绑包中消息模式的键。如果未指定捆绑包,LocalizedMessage 将尝试找到一个名为用于记录事件的记录器的捆绑包。从捆绑包中检索到的消息将使用 FormattedMessage 进行格式化。
LoggerNameAwareMessage
LoggerNameAwareMessage 是一个具有 setLoggerName 方法的接口。此方法将在事件构造期间被调用,以便消息在格式化消息时具有用于记录事件的记录器的名称。
MapMessage
MapMessage 包含一个包含字符串键和值的 Map。MapMessage 实现 FormattedMessage 并接受 "XML"、"JSON" 或 "JAVA" 的格式指定符,在这种情况下,Map 将被格式化为 XML、JSON 或如 java.util.AbstractMap.toString() 中所述。否则,Map 将被格式化为 "key1=value1 key2=value2 ..."。
一些附加器对 MapMessage 对象进行了特殊使用
- 当 JMS 附加器 使用
MessageLayout配置时,它会将 Log4jMapMessage转换为 JMSjavax.jms.MapMessage。 - 当 JDBC 附加器 使用
MessageLayout配置时,它会将 Log4jMapMessage转换为 SQL INSERT 语句中的值。 - 当 MongoDB3 附加器 或 MongoDB4 附加器 使用
MessageLayout配置时,它会将 Log4jMapMessage转换为 MongoDB 对象中的字段。
当附加器是 MessageLayout 意识时,Log4j 发送到目标的对象不是 Log4j 日志事件,而是自定义对象。
MessageFormatMessage
MessageFormatMessage 处理使用 转换格式 的消息。虽然此消息比 ParameterizedMessage 具有更大的灵活性,但它也大约慢两倍。
MultiformatMessage
MultiformatMessage 将具有 getFormats 方法和 getFormattedMessage 方法,这些方法接受并数组格式字符串。getFormats 方法可以被布局调用以向其提供有关消息支持的格式选项的信息。然后,布局可以使用一个或多个格式调用 getFormattedMessage。如果消息不识别格式名称,它将简单地使用其默认格式格式化数据。一个例子是 StructuredDataMessage,它接受 "XML" 的格式字符串,这将导致它将事件数据格式化为 XML 而不是 RFC 5424 格式。
ObjectMessage
通过调用对象的 toString 方法来格式化对象。从 Log4j 2.6 开始,尝试保持低垃圾或无垃圾的布局将调用 formatTo(StringBuilder) 方法而不是此方法。
ParameterizedMessage
ParameterizedMessage 处理在格式中包含 "{}" 以表示可替换令牌和替换参数的消息。
ReusableObjectMessage
在无垃圾模式下,此消息用于将记录的对象传递给布局和附加器。在功能上等效于 ObjectMessage。
ReusableParameterizedMessage
在无垃圾模式下,此消息用于处理在格式中包含 "{}" 以表示可替换令牌和替换参数的消息。在功能上等效于 ParameterizedMessage。
ReusableSimpleMessage
在无垃圾模式下,此消息用于将记录的字符串和 CharSequence 传递给布局和附加器。在功能上等效于 SimpleMessage。
SimpleMessage
SimpleMessage 包含一个不需要格式化的 String 或 CharSequence。
StringFormattedMessage
StringFormattedMessage 处理使用与 java.lang.String.format() 兼容的 转换格式 的消息。虽然此 Message 比 ParameterizedMessage 更灵活,但它也慢 5 到 10 倍。
StructuredDataMessage
StructuredDataMessage 允许应用程序向 Map 添加项,并设置 id 以便根据 RFC 5424 将消息格式化为结构化数据元素。
ThreadDumpMessage
如果记录 ThreadDumpMessage,它将为所有线程生成堆栈跟踪。堆栈跟踪将包括任何持有的锁。
TimestampMessage
TimestampMessage 将提供一个在事件构建期间调用的 getTimestamp 方法。消息中的时间戳将用于代替当前时间戳。


