blob: bf4f8d6ce7de5bbf02b35152d3c0fce87db50fe6 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
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
http://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.
-->
<!-- Generated by Apache Maven Doxia at 2018-12-02 -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Log4j_Audit &#x2013; RequestContext - Apache Log4j Audit</title>
<link rel="stylesheet" href="./css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="./css/site.css" type="text/css" />
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script type="text/javascript" src="./js/bootstrap.min.js"></script>
<script type="text/javascript" src="./js/prettify.min.js"></script>
<script type="text/javascript" src="./js/site.js"></script>
<meta name="Date-Revision-yyyymmdd" content="20181202" />
<meta http-equiv="Content-Language" content="en" />
</head>
<body class="composite">
<a href="http://www.apache.org/events/current-event.html">
<img class=logo-left src="http://www.apache.org/events/current-event-234x60.png"/>
</a>
<img class="logo-right" src="./images/logo.png" alt="Apache log4j logo" />
<a href="https://logging.apache.org/">
<img class="logo-center" src="./images/ls-logo.jpg" alt="Apache logging services logo" />
</a>
<div class="clear"></div>
<div class="navbar">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="http://logging.apache.org/log4j-audit">Apache Log4j Audit &trade;</a>
<ul class="nav">
<li>
<a href="https://cwiki.apache.org/confluence/display/LOGGING/Home" class="external" target="_blank" title="Logging Wiki">Logging Wiki</a>
</li>
<li>
<a href="https://www.apache.org/" class="external" target="_blank" title="Apache">Apache</a>
</li>
<li>
<a href="https://logging.apache.org/" class="external" target="_blank" title="Logging Services">Logging Services</a>
</li>
<li>
<a href="https://github.com/apache/logging-log4j-audit" class="external" target="_blank" title="GitHub">GitHub</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<table class="layout-table">
<tr>
<td class="sidebar">
<div class="well sidebar-nav">
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-home"></i>Apache Log4j Auditâ„¢</li>
<li class="none">
<a href="index.html" title="About">About</a>
</li>
<li class="none">
<a href="download.html" title="Download">Download</a>
</li>
<li class="none">
<a href="gettingStarted.html" title="Getting Started">Getting Started</a>
</li>
<li class="none">
<a href="catalog.html" title="Audit Catalog">Audit Catalog</a>
</li>
<li class="none active">
<a href="requestContext.html" title="RequestContext">RequestContext</a>
</li>
<li class="none">
<a href="sample.html" title="Sample Project">Sample Project</a>
</li>
<li class="none">
<a href="changelog.html" title="Changelog">Changelog</a>
</li>
<li class="none">
<a href="apidocs/index.html" title="Javadoc">Javadoc</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-pencil"></i>For Contributors</li>
<li class="none">
<a href="build.html" title="Building Log4j Audit from Source">Building Log4j Audit from Source</a>
</li>
<li class="none">
<a href="guidelines.html" title="Guidelines">Guidelines</a>
</li>
<li class="none">
<a href="javastyle.html" title="Style Guide">Style Guide</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-cog"></i>Component Reports</li>
<li class="none">
<a href="log4j-audit/log4j-audit-api/index.html" title="Audit API">Audit API</a>
</li>
<li class="none">
<a href="log4j-audit/log4j-audit-war/index.html" title="Audit Service">Audit Service</a>
</li>
<li class="none">
<a href="log4j-audit/log4j-audit-maven-plugin/index.html" title="Maven Plugin">Maven Plugin</a>
</li>
<li class="none">
<a href="log4j-catalog/log4j-catalog-api/index.html" title="Catalog API">Catalog API</a>
</li>
<li class="none">
<a href="log4j-catalog/log4j-catalog-git/index.html" title="Git Catalog Access">Git Catalog Access</a>
</li>
<li class="none">
<a href="log4j-catalog/log4j-catalog-jpa/index.html" title="JPA Catalog Access">JPA Catalog Access</a>
</li>
<li class="none">
<a href="log4j-catalog/log4j-catalog-war/index.html" title="Catalog Editor">Catalog Editor</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-info-sign"></i>Project Information</li>
<li class="none">
<a href="dependency-convergence.html" title="Dependency Convergence">Dependency Convergence</a>
</li>
<li class="none">
<a href="dependency-management.html" title="Dependency Management">Dependency Management</a>
</li>
<li class="none">
<a href="team-list.html" title="Project Team">Project Team</a>
</li>
<li class="none">
<a href="mail-lists.html" title="Mailing Lists">Mailing Lists</a>
</li>
<li class="none">
<a href="issue-tracking.html" title="Issue Tracking">Issue Tracking</a>
</li>
<li class="none">
<a href="license.html" title="Project License">Project License</a>
</li>
<li class="none">
<a href="source-repository.html" title="Source Repository">Source Repository</a>
</li>
<li class="none">
<a href="project-summary.html" title="Project Summary">Project Summary</a>
</li>
</ul>
<ul class="nav nav-list">
<li class="nav-header"><i class="icon-cog"></i>Project Reports</li>
<li class="none">
<a href="changes-report.html" title="Changes Report">Changes Report</a>
</li>
<li class="none">
<a href="jira-report.html" title="JIRA Report">JIRA Report</a>
</li>
<li class="none">
<a href="rat-report.html" title="RAT Report">RAT Report</a>
</li>
</ul>
</div>
<div id="poweredBy">
<a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy">
<img class="poweredBy" alt="Built by Maven" src="./images/maven-feather.png" />
</a>
</div>
</td>
<td class="content">
<!-- vim: set syn=markdown : -->
<!-- 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
http://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. --><h1>RequestContext</h1>
<p>Log4j-Audit incorporates Log4j ThreadContext variables as a way of including information into audit events that is common throughout the application. While usage of a RequestContext is not required, the use of one should be considered a best practice.</p>
<p>In a typical application that might use a ThreadContext Map, it will place variables in the Map so that they will be included in all Log4j log events. Log4j Audit takes advantage of this so that variables placed into the ThreadContext Map will be automatically be available in audit events as well. In addition, through the use of the RequestContextFilter and RequestContextHeaderInterceptor, these variable will automatically be passed from a client application to a REST service.</p>
<p>A common use case is to include a request id and/or a session id in the RequestContext. By generating these variables when a user first logs in and at the start of every action a user takes all log events, including audit events, can be correlated in the application and through all the microservices the application might use. This could also include things like a client account number (especially useful in multi-tenant applications), the user&#x2019;s login id, and the ip address of the user. In a services-based application these values need to be populated at the point of entry to the application and then passed to all the service endpoints. It is especially useful to create artificial request and session ids so that all the activities across all the servers can be correlated to the user&#x2019;s request and/or session.</p>
<p>When defining audit events there are generally two types of data included in the event. The first are data elements that are specific to a particular or small set of log events. The second are data elements that would be expected to be included in all audit events. The second type are normally included in the RequestContext. Although they do not have to be placed in the RequestContext doing so reduces the effort require to create an audit event since only the data specific to that event needs to be added by the application.</p>
<p>Another use case for RequestContext or ThreadContext Map variables is when lower level components need access to data elements that the components calling them have no use for. For example, an application may shard its database based on a customer&#x2019;s account number. A particular component may not need the account number in any of its operations but would be forced to pass it through so the database access component could use it to determine which database to use. By using the RequestContext the intermediate component no longer has to be aware of what customer it is performing work for.</p>
<p>One aspect of the Log4j ThreadContext should be noted. When an application directly creates a thread ThreadContext variables are NOT automatically provided to that thread. Applications may make use of the RequestContext when determining which variables in the ThreadContext Map should be passed to the child thread.</p>
<p>Log4j provides a convenient way to identify the RequestContext variables such that they can be reliably accessed by the application and passed to the target services. To do this the application should create a RequestContext class that resembles <a class="externalLink" href="https://github.com/apache/logging-log4j-audit-sample/blob/master/audit-service-war/src/main/java/org/apache/logging/log4j/audit/service/RequestContext.java">RequestContext.java</a> in the log4j-audit-sample project. A portion of that is shown here:</p>
<div class="source">
<div class="source">
<pre>/**
* Defines all the variables that an application needs to be available in the ThreadContext for audit logging and
* general application usage.
*/
@HeaderPrefix(&quot;mycorp-context-&quot;)
public final class RequestContext {
@ClientServer
public static final String REQUEST_ID = &quot;requestId&quot;;
@ClientServer
public static final String SESSION_ID = &quot;sessionId&quot;;
@Local
public static final String CALLING_HOST = &quot;callingHost&quot;;
public static final String HOST_NAME = &quot;hostName&quot;;
private static final String LOCAL_HOST_NAME = NetUtils.getLocalHostname();
/**
* The Supplier is used to populate the hostName key after the hostName value from the caller has been
* placed into the callingHost map entry.
*/
@Chained(fieldName = HOST_NAME, chainedFieldName = CALLING_HOST)
public static final Supplier&lt;String&gt; LOCAL_HOST_SUPPLIER = () -&gt; LOCAL_HOST_NAME;
}
</pre></div></div>
<div class="section">
<h2><a name="RequestContext_Annotations"></a>RequestContext Annotations</h2>
<div class="section">
<h3><a name="HeaderPrefix"></a>HeaderPrefix</h3>
<p>The <tt>@HeaderPrefix</tt> annotation is used to define the string to be prepended to the names of the ThreadContext variables when they are passed to a REST service. The default value is &#x201c;request-context-&#x201d;.</p></div>
<div class="section">
<h3><a name="Mapping_Annotations"></a>Mapping Annotations</h3>
<p>The <tt>@ClientServer</tt>, <tt>@Local</tt>, and <tt>@Chained</tt> annotations represent the 3 types of RequestContext variables. </p>
<ul>
<li><tt>@ClientServer</tt> represents a variable whose value should be passed to the target service using the same name.</li>
<li><tt>@Local</tt> represents a variable that is used in the local application and should not be passed to a client service.</li>
<li><tt>@Chained</tt> represents a variable that should be passed to the target service using a new name. A variable with the same named may be created in the target service using a different value. This is primarily used to pass the name of the current server to the target service.</li> </ul></div></div>
<div class="section">
<h2><a name="RequestContext_methods"></a>RequestContext methods</h2>
<p>The public static set and get methods shown in <a class="externalLink" href="https://github.com/apache/logging-log4j-audit-sample/blob/master/audit-service-war/src/main/java/org/apache/logging/log4j/audit/service/RequestContext.java">RequestContext.java</a> are optional but provide a convenient way to access the variables stored in Log4j&#x2019;s ThreadContext Map.</p>
<p>It should also be noted that it is possible to allow the RequestContext variables to be copied into an instance of the RequestContext so they can be passed to a child thread where the instance will be used to populate that thread&#x2019;s ThreadContext Map. See the save and restore methods in the sample <a class="externalLink" href="https://github.com/apache/logging-log4j-audit-sample/blob/master/audit-service-war/src/main/java/org/apache/logging/log4j/audit/service/RequestContext.java">RequestContext.java</a> for an example of how that can be done.</p></div>
<div class="section">
<h2><a name="Transporting_the_RequestContext"></a>Transporting the RequestContext</h2>
<p>This diagram shows how the RequestContext is created at the beginning of every request where it is used by the application (including audit logging). It is then converted to HTTP headers by a Spring Interceptor (provided by Log4j Audit) that are passed on a REST call to a service. The service then uses a servlet filter (provided by Log4j Audit) to create a RequestContext for use by the service.</p>
<p>This same mechanism can be applied to other kinds of distributed requests. For example, when using Spring AMQP the RequestContext fields can be captured as AMQP headers using a MessagePostProcessor and the RequestContext recreated in the consumer using an Interceptor.</p>
<p><img src="images/request-context.png" alt="RequestContext Flow" /></p>
<div class="section">
<h3><a name="Initializing_the_RequestContext"></a>Initializing the RequestContext</h3>
<p>The RequestContext should be initialized at the beginning of every request and cleared at the end of the request. In a web application, typically some data is captured during login and is captured in the HTTP session. In a web application that does not use an HTTP session this data would be stored wherever the equivalent of session data is stored (for example, the local storage of the browser). This session data is then copied into the RequestContext on each request in a ServletFilter or a Spring Interceptor similar to the example that follows. Although this example uses Spring it could be implemented as a Servlet Filter or something similar.</p>
<div class="source">
<div class="source">
<pre>public class RequestContextInterceptor implements HandlerInterceptor {
private static Logger logger = LogManager.getLogger(RequestContextInterceptor.class);
private ThreadLocal&lt;Long&gt; startTime = new ThreadLocal&lt;&gt;();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
boolean success = true;
String uri = request.getRequestURI();
String queryString = request.getQueryString();
HttpSession session = request.getSession(true);
if (!uri.contains(&quot;login&quot;) {
// SessionData will be populated during login.
SessionData sessionData = SessionData.getSessionData(session);
if (sessionData == null) {
logger.info(&quot;no account logged in - send to login page&quot;);
response.sendRedirect(&quot;/login&quot;);
success = false;
} else {
startTime.set(System.nanoTime());
long corpAcctNbr = sessionData.getCorporateAccountNumber();
RequestContext.getRequestId();
RequestContext.setCorpAcctNumber(corpAcctNbr);
RequestContext.setUserId(Long.toString(sessionData.getUserId()));
RequestContext.setIpAddress(request.getRemoteAddr());
RequestContext.setOnBehalfOf(sessionData.getOnBehalfOf());
RequestContext.setOnBehalfOfAccount(sessionData.getOnBehalfOfAccount());
RequestContext.setSessionId(sessionData.getSessionId());
RequestContext.setLoginId(sessionData.getLoginId());
String localHost = NetUtils.getLocalHostname();
if (localHost != null &amp;&amp; !localHost.equals(&quot;UNKNOWN_HOST&quot;)) {
RequestContext.setHostName(localHost);
}
RequestContext.setProductName(&quot;Application&quot;);
RequestContext.setProductVersion(&quot;3&quot;);
}
} else {
RequestContext.setIpAddress(request.getRemoteAddr());
startTime.set(System.nanoTime());
}
return success;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
Long start = startTime.get();
if (start != null) {
long elapsed = System.nanoTime() - start;
startTime.remove();
StringBuilder sb = new StringBuilder(&quot;Request &quot;).append(request.getRequestURI()).append(&quot; completed in &quot;);
ElapseUtil.addElapsed(elapsed, sb);
logger.info(sb.toString());
}
RequestContext.clear();
}
}
</pre></div></div></div>
<div class="section">
<h3><a name="Passing_the_RequestContext_to_a_Spring_REST_Service"></a>Passing the RequestContext to a Spring REST Service</h3>
<p>Log4j Audit provides an Interceptor that can be used with Spring web applications that will convert RequestContext variables to HTTP headers. The RequestContextHeaderInterceptor can be created by a java configuration method such as:</p>
<div class="source">
<div class="source">
<pre> @Bean
public List&lt;ClientHttpRequestInterceptor&gt; createInterceptor() {
List&lt;ClientHttpRequestInterceptor&gt; list = new ArrayList&lt;&gt;();
RequestContextMappings mappings = new RequestContextMappings(RequestContext.class));
list.add(new RequestContextHeaderInterceptor(mappings);
return list;
}
</pre></div></div>
<p>Assuming spring-web is being used, the returned list should then be added to the RestTemplate.</p>
<p>If Spring is not being used a component similar to the RequestContextHeaderInterceptor that is appropriate for the application could be used instead.</p>
<p>The target application then needs to convert the headers back into RequestContext variables by creating a class that extends WebApplicationInitializer that does:</p>
<div class="source">
<div class="source">
<pre> @Override
public void onStartup(ServletContext servletContext) throws ServletException {
RequestContextFilter filter = new RequestContextFilter(RequestContext.class);
servletContext.addFilter(&quot;requestContextFilter&quot;, filter).addMappingForUrlPatterns(null, true, &quot;/*&quot;);
}
</pre></div></div>
<p>As an added benefit, the RequestContextFilter will log the beginning and end of each request and log the elapsed time at the end of the request.</p>
<p>Although the preceding example uses Spring the code within the method will work for any Java web application that uses version 3.0 of the servlet spec or greater, provided it is called as the application is being initialized.</p></div></div>
<div class="section">
<h2><a name="Configuring_Logging_to_Include_RequestContext_Variables"></a>Configuring Logging to Include RequestContext Variables</h2>
<p>When logging RequestContext variables simply configure log4j2.xml to include specific ThreadContext variables or all of them using the %X pattern converter or by setting the includeMdc, includeThreadContext, or properties attribute to true on the desired Layout.</p></div>
</td>
</tr>
</table>
</div>
<div class="footer">
<p>Copyright &copy; 2016-2018 <a class="external" href="http://www.apache.org">Apache Software Foundation</a>. All Rights Reserved.</p>
<p>Apache Logging, Apache Log4j, Log4j, Apache Log4j Audit, Log4j Audit, Apache, the Apache feather logo, and the Apache Logging project logo are trademarks of The Apache Software Foundation.</p>
<p>Site powered by <a class="external" href="http://getbootstrap.com/">Twitter Bootstrap</a>. Icons from <a class="external" href="http://glyphicons.com/">Glyphicons Free</a>.</p>
</div>
</div>
</body>
</html>