Skip to content

Commit aeb51c8

Browse files
committed
Improve documentation on structured logging (#2665)
1 parent d3d4e5b commit aeb51c8

File tree

10 files changed

+191
-283
lines changed

10 files changed

+191
-283
lines changed

log4j-api/src/main/java/org/apache/logging/log4j/EventLogger.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
import org.apache.logging.log4j.spi.ExtendedLogger;
2121

2222
/**
23-
* Logs "Events" that are represented as {@link StructuredDataMessage}.
23+
* Convenience to log {@link StructuredDataMessage}s.
24+
*
25+
* @deprecated {@link Logger} accepts {@link StructuredDataMessage}s, users should use to that instead.
2426
*/
27+
@Deprecated
2528
public final class EventLogger {
2629

2730
/**
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="https://logging.apache.org/xml/ns"
4+
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5+
type="deprecated">
6+
<description format="asciidoc">Deprecate `org.apache.logging.log4j.EventLogger` for removal</description>
7+
</entry>

src/changelog/.2.x.x/deprecate_duration.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xmlns="https://logging.apache.org/xml/ns"
44
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5-
type="changed">
6-
<description format="asciidoc">Deprecate `org.apache.logging.log4j.core.appender.rolling.action.Duration` class for removal.</description>
5+
type="deprecated">
6+
<description format="asciidoc">Deprecate `org.apache.logging.log4j.core.appender.rolling.action.Duration` class for removal</description>
77
</entry>

src/changelog/.2.x.x/deprecate_log4j2_default_status_level.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
xmlns="https://logging.apache.org/xml/ns"
44
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
55
type="changed">
6-
<description format="asciidoc">Deprecate `log4j2.defaultStatusLevel` property in Log4j Core in favor of `log4j2.statusLoggerLevel`.</description>
6+
<description format="asciidoc">Deprecate `log4j2.defaultStatusLevel` property in Log4j Core in favor of `log4j2.statusLoggerLevel`</description>
77
</entry>

src/changelog/.2.x.x/deprecate_log4j_mongodb4.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xmlns="https://logging.apache.org/xml/ns"
44
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
5-
type="updated">
6-
<description format="asciidoc">Deprecate the log4j-mongodb4 module in favor of log4j-mongodb.</description>
5+
type="deprecated">
6+
<description format="asciidoc">Deprecate the `log4j-mongodb4` module in favor of `log4j-mongodb`</description>
77
</entry>

src/site/antora/modules/ROOT/pages/manual/api.adoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Did you know that Log4j provides specialized APIs for Kotlin and Scala?
4646
Check out {logging-services-url}/log4j/kotlin[Log4j Kotlin] and {logging-services-url}/log4j/scala[Log4j Scala] projects for details.
4747
====
4848
49+
[#intro]
4950
== Introduction
5051
5152
include::partial$manual/api-intro.adoc[leveloffset=+1]
@@ -205,7 +206,13 @@ xref:manual/resource-logger.adoc[Read more on resource loggers...]
205206
[#event-logger]
206207
=== Event logger
207208
208-
link:../javadoc/log4j-api/org/apache/logging/log4j/EventLogger.html[`EventLogger`] provides a simple way to log structured events conforming with the `STRCUTURED-DATA` format defined in https://tools.ietf.org/html/rfc5424[RFC 5424 (The Syslog Protocol)].
209+
link:../javadoc/log4j-api/org/apache/logging/log4j/EventLogger.html[`EventLogger`] is a convenience to log xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`]s, which format their content in a way compliant with https://datatracker.ietf.org/doc/html/rfc5424#section-6[the Syslog message format described in RFC 5424].
210+
211+
[WARNING]
212+
====
213+
*Event Logger is deprecated for removal!*
214+
We advise users to switch to plain `Logger` instead.
215+
====
209216
210217
xref:manual/eventlogging.adoc[Read more on event loggers...]
211218

src/site/antora/modules/ROOT/pages/manual/eventlogging.adoc

Lines changed: 21 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -17,236 +17,36 @@
1717
1818
= Event Logger
1919
20-
The `EventLogger` class provides a mechanism for logging significant events
21-
in an application using structured data. This approach is beneficial for
22-
tracking user actions, monitoring system behavior, and debugging issues.
20+
link:../javadoc/log4j-api/org/apache/logging/log4j/EventLogger.html[`EventLogger`] is a convenience to log xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`]s, which format their content in a way compliant with https://datatracker.ietf.org/doc/html/rfc5424#section-6[the Syslog message format described in RFC 5424].
21+
Historically, `EventLogger` was added to help users migrate from SLF4J `EventLogger`, which was removed in https://www.slf4j.org/news.html#1.7.26[SLF4J version `1.7.26`].
2322
24-
Theoretically, every `Logger` can be used to perform this kind of action;
25-
however, the `EventLogger` class provides a shortcut to log events with structured data
26-
since it allows for a static method to log events.
23+
[WARNING]
24+
====
25+
*Event Logger is deprecated for removal!*
26+
We advise users to switch to plain `Logger` instead.
27+
Refer to xref:manual/api.adoc[] on how to use `Logger`.
28+
====
2729
28-
== Advantages of Structured Logging
30+
Compared to using link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[a plain `Logger`], `EventLogger`
2931
30-
Structured logging means events Log4j records events with detailed and structured information.
31-
That way, logs are easier to read and process. They provide better context and
32-
are also more consistent than plain text logs.
32+
* attaches an `EVENT` xref:manual/markers.adoc[marker], and
33+
* sets the xref:manual/customloglevels.adoc[level] to `OFF`, unless one is explicitly provided.
3334
34-
== Integration with Syslog
35+
That is, following `EventLogger` usages:
3536
36-
Log4j complies with Syslogs RFC5424 standard.
37-
This feature allows for easy integration with existing log management and monitoring systems.
38-
39-
== Example Configuration
40-
41-
To configure Log4j to output logs in Syslog (RFC5424) format, one needs to use the xref:manual/layouts.adoc#RFC5424Layout[`Rfc5424Layout`] layout.
42-
Developers can use the following configuration to log events to a local Syslog server:
43-
44-
[source, xml]
45-
----
46-
<Appenders>
47-
<Syslog name="Syslog" host="localhost" port="514"> <!--1-->
48-
<Rfc5424Layout/> <!--2-->
49-
</Syslog>
50-
</Appenders>
51-
52-
<Loggers>
53-
<Logger name="MyApp" level="info" additivity="false">
54-
<AppenderRef ref="Syslog"/>
55-
</Logger>
56-
</Loggers>
57-
----
58-
<1> The `Syslog` appender sends logs to a local Syslog server.
59-
<2> The `StructuredDataLayout` layout formats logs in RFC5424 format.
60-
61-
Of course, sending logs to a Syslog server is unnecessary.
62-
Developers can use the `StructuredDataLayout` layout with any other appender, such as `FileAppender` or `ConsoleAppender`.
63-
64-
As an example, the output could look like this:
65-
66-
[source, text]
67-
----
68-
<165>1 2024-05-16T12:34:56.789Z myapp MyApp - ID47 [transfer@12345 toAccount="123456" fromAccount="654321" amount="1000"] User 654321 has transferred 1000 to account 123456
69-
----
70-
71-
== Using the `EventLogger`
72-
73-
The `EventLogger` class provides a simple way to log structured events.
74-
It uses the `StructuredDataMessage` class to create structured log messages.
75-
76-
Assume a simple application that performs funds transfers between accounts.
77-
This application should send a certain amount of funds from one account to another and log the event.
78-
79-
The account class is defined as follows, with a unique ID and a balance:
80-
81-
[source, java]
82-
----
83-
class Account {
84-
private String id;
85-
private long balance;
86-
// Getters and setters omitted for brevity
87-
}
88-
----
89-
90-
The `transfer()` method transfers funds between two accounts and logs the event.
91-
It needs to take two accounts and the amount to transfer as parameters.
92-
93-
Apart from the key-value pairs provided in the map of the `StructuredDataMessage,`
94-
the `StructuredDataMessage` also takes a unique ID, a free-form message, and a type as parameters.
95-
96-
The free-form message is a human-readable description of the event.
97-
This message is good for operators who need to understand the event quickly,
98-
but not so much for automated processing.
99-
100-
[source, java]
101-
----
102-
public static String transferFunds(Account toAccount, Account fromAccount, long amount) {
103-
toAccount.deposit(amount);
104-
fromAccount.withdraw(amount);
105-
106-
// Create a unique ID for the transaction
107-
String confirm = UUID.randomUUID().toString();
108-
109-
String freeFormMessage = "User " + fromAccount.getId() + " has transferred " + amount + " to account " + toAccount.getId(); <1>
110-
111-
// Create the StructuredDataMessage
112-
StructuredDataMessage msg = new StructuredDataMessage(confirm, freeFormMessage, "transfer"); <2>
113-
msg.put("toAccount", toAccount.getId()); <3>
114-
msg.put("fromAccount", fromAccount.getId());
115-
msg.put("amount", amount);
116-
117-
// Log the event
118-
EventLogger.logEvent(msg); <4>
119-
120-
return confirm;
121-
}
122-
----
123-
<1> The free-form message is a human-readable description of the event.
124-
<2> The `StructuredDataMessage` constructor takes an ID, the free-form message, and a type.
125-
<3> Developers can add key-value pairs to the message.
126-
<4> The `EventLogger` logs the event.
127-
128-
That way, the `transferFunds()` method logs the event with structured data
129-
by using the `EventLogger`.
130-
131-
== Web Application Example
132-
133-
In a web application, developers can use a servlet filter to populate the
134-
`ThreadContext` map with data related to the request.
135-
136-
The following example demonstrates how a `Filter` could investigate the request
137-
and populate the `ThreadContext` map with data such as the user's IP address,
138-
the user's login ID, the server's hostname, the product name, the locale, and the timezone.
139-
140-
[source, java]
141-
----
142-
import org.apache.logging.log4j.ThreadContext;
143-
import org.apache.commons.lang.time.DateUtils;
144-
145-
import javax.servlet.*;
146-
import javax.servlet.http.*;
147-
import java.io.IOException;
148-
import java.util.TimeZone;
149-
150-
public class RequestFilter implements Filter {
151-
private FilterConfig filterConfig;
152-
private static String TZ_NAME = "timezoneOffset";
153-
154-
// Other methods ommitted for brevity
155-
156-
/**
157-
* Sample filter that populates the MDC on every request.
158-
*/
159-
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
160-
throws IOException, ServletException {
161-
HttpServletRequest request = (HttpServletRequest)servletRequest;
162-
163-
ThreadContext.put("ipAddress", request.getRemoteAddr()); <1>
164-
165-
HttpSession session = request.getSession(false);
166-
if (session != null) {
167-
// Assuming, an authentication filter has already populated the loginId in the session
168-
String loginId = (String)session.getAttribute("loginId");
169-
if (loginId != null) {
170-
ThreadContext.put("loginId", loginId);
171-
}
172-
}
173-
174-
ThreadContext.put("hostname", servletRequest.getServerName());
175-
ThreadContext.put("productName", filterConfig.getInitParameter("ProductName"));
176-
ThreadContext.put("locale", servletRequest.getLocale().getDisplayName());
177-
ThreadContext.put("timezone", TimeZone.getDefault().getDisplayName());
178-
179-
filterChain.doFilter(servletRequest, servletResponse);
180-
ThreadContext.clear();
181-
}
182-
}
37+
[source,java]
18338
----
184-
<1> The `doFilter()` method populates the `ThreadContext` map with data related to the request.
185-
186-
The `Filter` needs to be registered in your `web.xml` file:
187-
188-
[source, xml]
189-
----
190-
<filter>
191-
<filter-name>RequestFilter</filter-name>
192-
<filter-class>com.example.RequestFilter</filter-class>
193-
<init-param>
194-
<param-name>ProductName</param-name>
195-
<param-value>YourProductName</param-value>
196-
</init-param>
197-
</filter>
198-
<filter-mapping>
199-
<filter-name>RequestFilter</filter-name>
200-
<url-pattern>/*</url-pattern> <1>
201-
</filter-mapping>
39+
EventLogger.logEvent(new StructuredDataMessage(...));
40+
EventLogger.logEvent(new StructuredDataMessage(...), Level.INFO);
20241
----
203-
<1> The `RequestFilter` is mapped to all requests.
20442
205-
Eventually, a `Servlet` or any other related class, such as a Spring Controller, can be used to log events with structured data.
206-
The following example uses a `Servlet` to call the `EventLogger` and log a user action.
43+
are equivalent to the following plain `Logger` usages:
20744
208-
[source, java]
45+
[source,java]
20946
----
210-
import javax.servlet.*;
211-
import javax.servlet.http.*;
212-
import java.io.IOException;
213-
214-
public class UserActionServlet extends HttpServlet {
215-
216-
protected void doPost(HttpServletRequest request, HttpServletResponse response)
217-
throws ServletException, IOException {
218-
String userId = request.getParameter("userId");
219-
String action = request.getParameter("action");
220-
String details = request.getParameter("details");
47+
private static final Marker EVENT_MARKER = MarkerManager.getMarker("EVENT");
48+
private static final Logger LOGGER = LogManager.getLogger();
22149
222-
// Perform and log the user action
223-
String message = "User " + userId + " performed action: " + action;
224-
StructuredDataMessage msg = new StructuredDataMessage(UUID.randomUUID().toString(), message, "userAction"); <1>
225-
msg.put("userId", userId);
226-
msg.put("action", action);
227-
msg.put("details", details);
228-
229-
// Log the event
230-
EventLogger.logEvent(msg);
231-
232-
// Respond to the client
233-
response.getWriter().write("Action logged successfully");
234-
}
235-
}
50+
LOGGER.log(Level.OFF, EVENT_MARKER, new StructuredDataMessage(...));
51+
LOGGER.info(EVENT_MARKER, new StructuredDataMessage(...));
23652
----
237-
<1> `userAction` is the name of the current transaction
238-
239-
That way, not only the data provided to the `EventLogger` is used, but also all the
240-
data populated in the `ThreadContext` map is included in the log message.
241-
242-
== Benefits of Structured Logging
243-
244-
1. **Improved Readability and Context:**
245-
Structured logs include detailed information, making them easier to understand and analyze.
246-
2. **Better for Automated Processing:**
247-
Structured logs are easily parsed by existing log management tools.
248-
3. **Consistency:**
249-
Structured logging enforces a consistent format, helping to identify patterns in logs.
250-
4. **Performance Optimization:**
251-
Structured messages are - as all messages - only constructed when necessary, keeping overhead low.
252-

src/site/antora/modules/ROOT/pages/manual/layouts.adoc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,20 @@ This page will try to answer following questions:
2929
3030
This section introduces you to some common concerns shared by almost all <<collection,predefined layouts>> that you need to be aware of while using them.
3131
32+
[#structured-logging]
33+
=== Structured logging
34+
35+
include::partial$manual/structured-logging.adoc[]
36+
37+
Log4j strives to provide top of the class support for structured logging.
38+
To create an end-to-end experience, it provides several xref:manual/messages.adoc#collection-structured[structured message types] along with layouts supporting structured logging.
39+
40+
We recommend xref:manual/json-template-layout.adoc[] for structured logging purposes.
41+
3242
[#charset]
3343
=== Character encoding
3444
35-
All <<collection[predefined layouts] produce `String` that eventually get converted into a `byte>>` using the https://docs.oracle.com/javase/{java-target-version}/docs/api/java/nio/charset/Charset.html[`Charset`] configured.
45+
All <<collection,predefined layouts>> produce `String` that eventually get converted into a `byte` using the https://docs.oracle.com/javase/{java-target-version}/docs/api/java/nio/charset/Charset.html[`Charset`] configured.
3646
While doing so, unless an explicit encoding configuration is stated, they use `UTF-8` by default.
3747
If you want all your log events to be formatted in a certain character encoding that is different from what the employed layout defaults to, make sure to configure the layout's character encoding as needed.
3848
@@ -834,6 +844,9 @@ This attribute only applies to RFC 5424 Syslog records.
834844
|The string that should be used to replace newlines within the message text
835845
|===
836846
847+
RFC 5424 Layout has specialized handling for xref:manual/messages.adoc#StructuredDataMessage[`StructuredDataMessage`]s.
848+
By combining two, users can have complete control on how their message is encoded in a way compliant with RFC 5424, while RFC 5424 Layout will make sure the rest of the information attached to the log event is properly injected.
849+
837850
[#SerializedLayout]
838851
=== Serialized Layout
839852

0 commit comments

Comments
 (0)