|
17 | 17 |
|
18 | 18 | = Event Logger
|
19 | 19 |
|
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`]. |
23 | 22 |
|
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 | +==== |
27 | 29 |
|
28 |
| -== Advantages of Structured Logging |
| 30 | +Compared to using link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[a plain `Logger`], `EventLogger` |
29 | 31 |
|
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. |
33 | 34 |
|
34 |
| -== Integration with Syslog |
| 35 | +That is, following `EventLogger` usages: |
35 | 36 |
|
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] |
183 | 38 | ----
|
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); |
202 | 41 | ----
|
203 |
| -<1> The `RequestFilter` is mapped to all requests. |
204 | 42 |
|
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: |
207 | 44 |
|
208 |
| -[source, java] |
| 45 | +[source,java] |
209 | 46 | ----
|
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(); |
221 | 49 |
|
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(...)); |
236 | 52 | ----
|
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 |
| -
|
0 commit comments