Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for dynamic MDC with jakarta (modern javax)
- Loading branch information
Showing
11 changed files
with
1,235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
logevents/src/main/java/org/logevents/optional/jakarta/HttpServletMDC.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.logevents.optional.jakarta; | ||
|
||
import org.logevents.config.MdcFilter; | ||
import org.logevents.formatters.exceptions.ExceptionFormatter; | ||
import org.logevents.mdc.DynamicMDC; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Populates MDC or JSON format according to <a href="https://www.elastic.co/guide/en/ecs/current/">Elastic | ||
* Common Schema</a> guidelines: | ||
* | ||
* <ul> | ||
* <li><strong>url.original:</strong> The URL as the user entered it</li> | ||
* <li><strong>http.request.method:</strong> GET, PUT, POST, DELETE etc</li> | ||
* <li><strong>user.name:</strong> {@link HttpServletRequest#getRemoteUser()}</li> | ||
* <li><strong>client.address:</strong> {@link HttpServletRequest#getRemoteAddr()}</li> | ||
* <li><strong>event.time:</strong> The number of seconds since the request started</li> | ||
* <li> | ||
* <strong>error.{class, message, stack_trace} (only JSON, not MDC)</strong>: | ||
* The exception in <code>"javax.servlet.error.exception"</code> (if any) | ||
* </li> | ||
* <li><strong>http.response.status_code</strong>: {@link HttpServletResponse#getStatus()}</li> | ||
* <li><strong>http.response.mime_type</strong>: {@link HttpServletResponse#getContentType()}</li> | ||
* <li><strong>http.response.redirect</strong>: "Location" response header</li> | ||
* </ul> | ||
*/ | ||
public class HttpServletMDC implements DynamicMDC { | ||
|
||
private final HttpServletRequest request; | ||
private final HttpServletResponse response; | ||
private final long duration; | ||
|
||
public HttpServletMDC(HttpServletRequest request, HttpServletResponse response, long duration) { | ||
this.request = request; | ||
this.response = response; | ||
this.duration = duration; | ||
} | ||
|
||
public static Supplier<DynamicMDC> supplier(HttpServletRequest request, HttpServletResponse response) { | ||
long start = System.currentTimeMillis(); | ||
return () -> new HttpServletMDC(request, response, System.currentTimeMillis() - start); | ||
} | ||
|
||
@Override | ||
public Iterable<? extends Map.Entry<String, String>> entrySet() { | ||
Map<String, String> result = new java.util.LinkedHashMap<>(); | ||
HttpServletResponseMDC.addMdcVariables(result, response); | ||
HttpServletRequestMDC.addMdcVariables(result, request); | ||
result.put("event.time", String.format("%.04f", duration / 1000.0)); | ||
return result.entrySet(); | ||
} | ||
|
||
@Override | ||
public void populateJsonEvent(Map<String, Object> jsonPayload, MdcFilter mdcFilter, ExceptionFormatter exceptionFormatter) { | ||
HttpServletResponseMDC.populateJson(jsonPayload, response); | ||
HttpServletRequestMDC.populateJson(jsonPayload, exceptionFormatter, request); | ||
jsonPayload.put("event.time", String.format("%.04f", duration / 1000.0)); | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
logevents/src/main/java/org/logevents/optional/jakarta/HttpServletRequestMDC.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package org.logevents.optional.jakarta; | ||
|
||
import org.logevents.config.MdcFilter; | ||
import org.logevents.formatters.JsonLogEventFormatter; | ||
import org.logevents.formatters.exceptions.ExceptionFormatter; | ||
import org.logevents.mdc.DynamicMDC; | ||
import org.logevents.mdc.DynamicMDCAdapter; | ||
import org.logevents.util.JsonUtil; | ||
|
||
import jakarta.servlet.ServletRequest; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Populates MDC or JSON format according to <a href="https://www.elastic.co/guide/en/ecs/current/">Elastic | ||
* Common Schema</a> guidelines: | ||
* | ||
* <ul> | ||
* <li><strong>url.original:</strong> The URL as the user entered it</li> | ||
* <li><strong>http.request.method:</strong> GET, PUT, POST, DELETE etc</li> | ||
* <li><strong>user.name:</strong> {@link HttpServletRequest#getRemoteUser()}</li> | ||
* <li><strong>client.address:</strong> {@link HttpServletRequest#getRemoteAddr()}</li> | ||
* <li><strong>event.time:</strong> The number of seconds since the request started</li> | ||
* <li> | ||
* <strong>error.{class, message, stack_trace} (only JSON, not MDC)</strong>: | ||
* The exception in <code>"javax.servlet.error.exception"</code> (if any) | ||
* </li> | ||
* </ul> | ||
*/ | ||
public class HttpServletRequestMDC implements DynamicMDC { | ||
|
||
public static void addMdcVariables(Map<String, String> result, HttpServletRequest request) { | ||
result.put("http.request.method", request.getMethod()); | ||
result.put("url.original", request.getRequestURL().toString()); | ||
result.put("user.name", request.getRemoteUser()); | ||
result.put("client.address", request.getRemoteHost()); | ||
} | ||
|
||
public static void populateJson(Map<String, Object> jsonPayload, ExceptionFormatter exceptionFormatter, HttpServletRequest request) { | ||
jsonPayload.put("url.original", request.getRequestURL().toString()); | ||
jsonPayload.put("user.name", request.getRemoteUser()); | ||
jsonPayload.put("client.address", request.getRemoteHost()); | ||
|
||
if (jsonPayload.containsKey("http")) { | ||
JsonUtil.getObject(jsonPayload, "http").put("request.method", request.getMethod()); | ||
} else { | ||
jsonPayload.put("http.request.method", request.getMethod()); | ||
} | ||
|
||
if (request.getAttribute("javax.servlet.error.exception") instanceof Throwable) { | ||
Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception"); | ||
jsonPayload.put("error", JsonLogEventFormatter.toJsonObject(exception, exceptionFormatter)); | ||
} | ||
} | ||
|
||
private final HttpServletRequest request; | ||
private final long duration; | ||
|
||
private HttpServletRequestMDC(HttpServletRequest request, long duration) { | ||
this.request = request; | ||
this.duration = duration; | ||
} | ||
|
||
public static Supplier<DynamicMDC> supplier(HttpServletRequest request) { | ||
long start = System.currentTimeMillis(); | ||
return () -> new HttpServletRequestMDC(request, System.currentTimeMillis() - start); | ||
} | ||
|
||
public static DynamicMDCAdapter.Cleanup put(ServletRequest request) { | ||
return DynamicMDC.putDynamic("request", supplier((HttpServletRequest) request)); | ||
} | ||
|
||
@Override | ||
public Iterable<? extends Map.Entry<String, String>> entrySet() { | ||
Map<String, String> result = new java.util.LinkedHashMap<>(); | ||
addMdcVariables(result, request); | ||
result.put("event.time", String.format("%.04f", duration / 1000.0)); | ||
return result.entrySet(); | ||
} | ||
|
||
@Override | ||
public void populateJsonEvent(Map<String, Object> jsonPayload, MdcFilter mdcFilter, ExceptionFormatter exceptionFormatter) { | ||
populateJson(jsonPayload, exceptionFormatter, request); | ||
jsonPayload.put("event.time", String.format("%.04f", duration / 1000.0)); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
logevents/src/main/java/org/logevents/optional/jakarta/HttpServletResponseMDC.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.logevents.optional.jakarta; | ||
|
||
import org.logevents.config.MdcFilter; | ||
import org.logevents.formatters.exceptions.ExceptionFormatter; | ||
import org.logevents.mdc.DynamicMDC; | ||
|
||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Populates MDC or JSON format according to <a href="https://www.elastic.co/guide/en/ecs/current/">Elastic | ||
* Common Schema</a> guidelines: | ||
* | ||
* <ul> | ||
* <li><strong>http.response.status_code</strong>: {@link HttpServletResponse#getStatus()}</li> | ||
* <li><strong>http.response.mime_type</strong>: {@link HttpServletResponse#getContentType()}</li> | ||
* <li><strong>http.response.redirect</strong>: "Location" response header</li> | ||
* </ul> | ||
*/ | ||
public class HttpServletResponseMDC implements DynamicMDC { | ||
|
||
public static void populateJson(Map<String, Object> jsonPayload, HttpServletResponse response) { | ||
Map<String, Object> http = new HashMap<>(); | ||
|
||
Map<String, Object> httpResponse = new HashMap<>(); | ||
httpResponse.put("status_code", response.getStatus()); | ||
httpResponse.put("mime_type", response.getContentType()); | ||
httpResponse.put("redirect", response.getHeader("Location")); | ||
|
||
http.put("response", httpResponse); | ||
jsonPayload.put("http", http); | ||
} | ||
|
||
public static void addMdcVariables(Map<String, String> result, HttpServletResponse response) { | ||
result.put("http.response.status_code", String.valueOf(response.getStatus())); | ||
result.put("http.response.mime_type", response.getContentType()); | ||
result.put("http.response.redirect", response.getHeader("Location")); | ||
} | ||
|
||
private final HttpServletResponse response; | ||
|
||
public HttpServletResponseMDC(HttpServletResponse response) { | ||
this.response = response; | ||
} | ||
|
||
public static Supplier<DynamicMDC> supplier(HttpServletResponse response) { | ||
return () -> new HttpServletResponseMDC(response); | ||
} | ||
|
||
@Override | ||
public Iterable<? extends Map.Entry<String, String>> entrySet() { | ||
Map<String, String> result = new java.util.LinkedHashMap<>(); | ||
addMdcVariables(result, response); | ||
return result.entrySet(); | ||
} | ||
|
||
@Override | ||
public void populateJsonEvent(Map<String, Object> jsonPayload, MdcFilter mdcFilter, ExceptionFormatter exceptionFormatter) { | ||
populateJson(jsonPayload, response); | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
logevents/src/main/java/org/logevents/optional/jakarta/LogEventsConfigurationServlet.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package org.logevents.optional.jakarta; | ||
|
||
import org.logevents.LogEventFactory; | ||
import org.logevents.LogEventLogger; | ||
import org.logevents.core.LogEventFilter; | ||
import org.logevents.util.JsonUtil; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.slf4j.event.Level; | ||
|
||
import jakarta.servlet.http.HttpServlet; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class LogEventsConfigurationServlet extends HttpServlet { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(LogEventsConfigurationServlet.class); | ||
|
||
@Override | ||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { | ||
Map<String, Object> configuration = logConfigurationToJson(LogEventFactory.getInstance()); | ||
resp.setContentType("application/json"); | ||
resp.getWriter().println(JsonUtil.toIndentedJson(configuration)); | ||
} | ||
|
||
Map<String, Object> logConfigurationToJson(LogEventFactory logEventFactory) { | ||
Map<String, LogEventLogger> loggers = logEventFactory.getLoggers(); | ||
|
||
List<String> loggerNames = new ArrayList<>(loggers.keySet()); | ||
Collections.sort(loggerNames); | ||
|
||
Map<String, String> logLevels = new LinkedHashMap<>(); | ||
// TODO: null check? | ||
logLevels.put("/", logEventFactory.getRootLogger().getOwnFilter().toString()); | ||
for (String loggerName : loggerNames) { | ||
LogEventFilter threshold = logEventFactory.getLogger(loggerName).getOwnFilter(); | ||
logLevels.put(loggerName, threshold != null ? threshold.toString() : "<inherited>"); | ||
} | ||
|
||
Map<String, String> observers = new LinkedHashMap<>(); | ||
observers.put("/", logEventFactory.getRootLogger().getObserver()); | ||
for (String loggerName : loggerNames) { | ||
observers.put(loggerName, logEventFactory.getLogger(loggerName).getObserver()); | ||
} | ||
|
||
Map<String, Object> configuration = new LinkedHashMap<>(); | ||
configuration.put("logLevels", logLevels); | ||
configuration.put("observers", observers); | ||
return configuration; | ||
} | ||
|
||
@Override | ||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { | ||
setLogLevel(req.getParameter("loggerName"), req.getParameter("level")); | ||
resp.sendRedirect(req.getContextPath() + req.getServletPath() + req.getPathInfo()); | ||
} | ||
|
||
void setLogLevel(String loggerName, String levelName) { | ||
Level level = levelName == null || levelName.equals("null") ? null : Level.valueOf(levelName); | ||
logger.info("Changing log level for {} to {}", loggerName, level); | ||
|
||
LogEventFactory.getInstance().setLevel(loggerName, level); | ||
} | ||
|
||
} |
Oops, something went wrong.