blob: cf0e069e8f6420b66eb2a22ca8efe6da21348e22 [file] [log] [blame]
////
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
////
= Log4j 2 API
Ralph Goers <rgoers@apache.org>
[#Messages]
== 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.
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:
[source,java]
----
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:
[source,java]
----
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.
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.
Borrowing from the earlier example illustrating Markers to identify SQL
statements being logged, Messages can also be leveraged. First, the
Message is defined.
[source,java]
----
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.
[source,java]
----
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.
[#FormattedMessage]
=== FormattedMessage
The message pattern passed to a
link:../log4j-api/apidocs/org/apache/logging/log4j/message/FormattedMessage.html[`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.
[#LocalizedMessage]
=== LocalizedMessage
link:../log4j-api/apidocs/org/apache/logging/log4j/message/LocalizedMessage.html[`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` 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.
[#LoggerNameAwareMessage]
=== LoggerNameAwareMessage
`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.
[#MapMessage]
=== 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
https://docs.oracle.com/javase/7/docs/api/java/util/AbstractMap.html#toString()[`java.util.AbstractMap.toString()`].
Otherwise, the Map will be formatted as `"key1=value1 key2=value2 ..."`.
Some Appenders make special use of `MapMessage` objects:
* When a link:appenders.html#JMSAppender[JMS Appender] is configured
with a `MessageLayout`, it converts a Log4j `MapMessage` to a JMS
`javax.jms.MapMessage`.
* When a link:appenders.html#JDBCAppender[JDBC Appender] is configured
with a `MessageLayout`, it converts a Log4j `MapMessage` to values in a
SQL INSERT statement.
* When a link:appenders.html#NoSQLAppenderMongoDB3[MongoDB3 Appender] or
link:appenders.html#NoSQLAppenderMongoDB4[MongoDB4 Appender] is
configured with a `MessageLayout`, it converts a Log4j `MapMessage` to
fields in a MongoDB object.
When an Appender is `MessageLayout`-aware, the object Log4j sends to
target is not a Log4j Log Event but a custom object.
[#MessageFormatMessage]
=== MessageFormatMessage
link:../log4j-api/apidocs/org/apache/logging/log4j/message/MessageFormatMessage.html[`MessageFormatMessage`]
handles messages that use a
https://docs.oracle.com/javase/7/docs/api/java/text/MessageFormat.html[conversion
format]. While this `Message` has more flexibility than
`ParameterizedMessage`, it is also about two times slower.
[#MultiformatMessage]
=== 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 `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]
=== 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
link:../log4j-api/apidocs/org/apache/logging/log4j/message/ParameterizedMessage.html[`ParameterizedMessage`]
handles messages that contain "\{}" in the format to represent
replaceable tokens and the replacement parameters.
[#ReusableObjectMessage]
=== ReusableObjectMessage
In garbage-free mode, this message is used to pass logged Objects to the
Layout and Appenders. Functionally equivalent to
<<ObjectMessage>>.
[#ReusableParameterizedMessage]
=== 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]
=== ReusableSimpleMessage
In garbage-free mode, this message is used to pass logged `String`s and
`CharSequence`s to the Layout and Appenders. Functionally equivalent to
<<SimpleMessage>>.
[#SimpleMessage]
=== SimpleMessage
`SimpleMessage` contains a `String` or `CharSequence` that requires no
formatting.
[#StringFormattedMessage]
=== StringFormattedMessage
link:../log4j-api/apidocs/org/apache/logging/log4j/message/StringFormattedMessage.html[`StringFormattedMessage`]
handles messages that use a
https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax[conversion
format] that is compliant with
https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#format(java.lang.String,%20java.lang.Object...)[java.lang.String.format()].
While this Message has more flexibility than `ParameterizedMessage`, it is
also 5 to 10 times slower.
[#StructuredDataMessage]
=== StructuredDataMessage
link:../log4j-api/apidocs/org/apache/logging/log4j/message/StructuredDataMessage.html[`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 http://tools.ietf.org/html/rfc5424[RFC 5424].
[#ThreadDumpMessage]
=== ThreadDumpMessage
A ThreadDumpMessage, if logged, will generate stack traces for all
threads. The stack traces will include any locks that are held.
[#TimestampMessage]
=== 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.