All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.filter.AbstractRequestLoggingFilter Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed 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.
 */

package org.springframework.web.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;

/**
 * Base class for {@code Filter}s that perform logging operations before and after a request
 * is processed.
 *
 * 

Subclasses should override the {@code beforeRequest(HttpServletRequest, String)} and * {@code afterRequest(HttpServletRequest, String)} methods to perform the actual logging * around the request. * *

Subclasses are passed the message to write to the log in the {@code beforeRequest} and * {@code afterRequest} methods. By default, only the URI of the request is logged. However, * setting the {@code includeQueryString} property to {@code true} will cause the query string of * the request to be included also; this can be further extended through {@code includeClientInfo} * and {@code includeHeaders}. The payload (body content) of the request can be logged via the * {@code includePayload} flag: Note that this will only log the part of the payload which has * actually been read, not necessarily the entire body of the request. * *

Prefixes and suffixes for the before and after messages can be configured using the * {@code beforeMessagePrefix}, {@code afterMessagePrefix}, {@code beforeMessageSuffix} and * {@code afterMessageSuffix} properties. * * @author Rob Harrop * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 1.2.5 * @see #beforeRequest * @see #afterRequest */ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter { /** * The default value prepended to the log message written before a request is * processed. */ public static final String DEFAULT_BEFORE_MESSAGE_PREFIX = "Before request ["; /** * The default value appended to the log message written before a request is * processed. */ public static final String DEFAULT_BEFORE_MESSAGE_SUFFIX = "]"; /** * The default value prepended to the log message written after a request is * processed. */ public static final String DEFAULT_AFTER_MESSAGE_PREFIX = "After request ["; /** * The default value appended to the log message written after a request is * processed. */ public static final String DEFAULT_AFTER_MESSAGE_SUFFIX = "]"; private static final int DEFAULT_MAX_PAYLOAD_LENGTH = 50; private boolean includeQueryString = false; private boolean includeClientInfo = false; private boolean includeHeaders = false; private boolean includePayload = false; private int maxPayloadLength = DEFAULT_MAX_PAYLOAD_LENGTH; private String beforeMessagePrefix = DEFAULT_BEFORE_MESSAGE_PREFIX; private String beforeMessageSuffix = DEFAULT_BEFORE_MESSAGE_SUFFIX; private String afterMessagePrefix = DEFAULT_AFTER_MESSAGE_PREFIX; private String afterMessageSuffix = DEFAULT_AFTER_MESSAGE_SUFFIX; /** * Set whether the query string should be included in the log message. *

Should be configured using an {@code } for parameter name * "includeQueryString" in the filter definition in {@code web.xml}. */ public void setIncludeQueryString(boolean includeQueryString) { this.includeQueryString = includeQueryString; } /** * Return whether the query string should be included in the log message. */ protected boolean isIncludeQueryString() { return this.includeQueryString; } /** * Set whether the client address and session id should be included in the * log message. *

Should be configured using an {@code } for parameter name * "includeClientInfo" in the filter definition in {@code web.xml}. */ public void setIncludeClientInfo(boolean includeClientInfo) { this.includeClientInfo = includeClientInfo; } /** * Return whether the client address and session id should be included in the * log message. */ protected boolean isIncludeClientInfo() { return this.includeClientInfo; } /** * Set whether the request headers should be included in the log message. *

Should be configured using an {@code } for parameter name * "includeHeaders" in the filter definition in {@code web.xml}. * @since 4.3 */ public void setIncludeHeaders(boolean includeHeaders) { this.includeHeaders = includeHeaders; } /** * Return whether the request headers should be included in the log message. * @since 4.3 */ protected boolean isIncludeHeaders() { return this.includeHeaders; } /** * Set whether the request payload (body) should be included in the log message. *

Should be configured using an {@code } for parameter name * "includePayload" in the filter definition in {@code web.xml}. * @since 3.0 */ public void setIncludePayload(boolean includePayload) { this.includePayload = includePayload; } /** * Return whether the request payload (body) should be included in the log message. * @since 3.0 */ protected boolean isIncludePayload() { return this.includePayload; } /** * Set the maximum length of the payload body to be included in the log message. * Default is 50 characters. * @since 3.0 */ public void setMaxPayloadLength(int maxPayloadLength) { Assert.isTrue(maxPayloadLength >= 0, "'maxPayloadLength' should be larger than or equal to 0"); this.maxPayloadLength = maxPayloadLength; } /** * Return the maximum length of the payload body to be included in the log message. * @since 3.0 */ protected int getMaxPayloadLength() { return this.maxPayloadLength; } /** * Set the value that should be prepended to the log message written * before a request is processed. */ public void setBeforeMessagePrefix(String beforeMessagePrefix) { this.beforeMessagePrefix = beforeMessagePrefix; } /** * Set the value that should be appended to the log message written * before a request is processed. */ public void setBeforeMessageSuffix(String beforeMessageSuffix) { this.beforeMessageSuffix = beforeMessageSuffix; } /** * Set the value that should be prepended to the log message written * after a request is processed. */ public void setAfterMessagePrefix(String afterMessagePrefix) { this.afterMessagePrefix = afterMessagePrefix; } /** * Set the value that should be appended to the log message written * after a request is processed. */ public void setAfterMessageSuffix(String afterMessageSuffix) { this.afterMessageSuffix = afterMessageSuffix; } /** * The default value is "false" so that the filter may log a "before" message * at the start of request processing and an "after" message at the end from * when the last asynchronously dispatched thread is exiting. */ @Override protected boolean shouldNotFilterAsyncDispatch() { return false; } /** * Forwards the request to the next filter in the chain and delegates down to the subclasses * to perform the actual request logging both before and after the request is processed. * @see #beforeRequest * @see #afterRequest */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { boolean isFirstRequest = !isAsyncDispatch(request); HttpServletRequest requestToUse = request; if (isIncludePayload() && isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) { requestToUse = new ContentCachingRequestWrapper(request, getMaxPayloadLength()); } boolean shouldLog = shouldLog(requestToUse); if (shouldLog && isFirstRequest) { beforeRequest(requestToUse, getBeforeMessage(requestToUse)); } try { filterChain.doFilter(requestToUse, response); } finally { if (shouldLog && !isAsyncStarted(requestToUse)) { afterRequest(requestToUse, getAfterMessage(requestToUse)); } } } /** * Get the message to write to the log before the request. * @see #createMessage */ private String getBeforeMessage(HttpServletRequest request) { return createMessage(request, this.beforeMessagePrefix, this.beforeMessageSuffix); } /** * Get the message to write to the log after the request. * @see #createMessage */ private String getAfterMessage(HttpServletRequest request) { return createMessage(request, this.afterMessagePrefix, this.afterMessageSuffix); } /** * Create a log message for the given request, prefix and suffix. *

If {@code includeQueryString} is {@code true}, then the inner part * of the log message will take the form {@code request_uri?query_string}; * otherwise the message will simply be of the form {@code request_uri}. *

The final message is composed of the inner part as described and * the supplied prefix and suffix. */ protected String createMessage(HttpServletRequest request, String prefix, String suffix) { StringBuilder msg = new StringBuilder(); msg.append(prefix); msg.append("uri=").append(request.getRequestURI()); if (isIncludeQueryString()) { String queryString = request.getQueryString(); if (queryString != null) { msg.append('?').append(queryString); } } if (isIncludeClientInfo()) { String client = request.getRemoteAddr(); if (StringUtils.hasLength(client)) { msg.append(";client=").append(client); } HttpSession session = request.getSession(false); if (session != null) { msg.append(";session=").append(session.getId()); } String user = request.getRemoteUser(); if (user != null) { msg.append(";user=").append(user); } } if (isIncludeHeaders()) { msg.append(";headers=").append(new ServletServerHttpRequest(request).getHeaders()); } if (isIncludePayload()) { String payload = getMessagePayload(request); if (payload != null) { msg.append(";payload=").append(payload); } } msg.append(suffix); return msg.toString(); } /** * Extracts the message payload portion of the message created by * {@link #createMessage(HttpServletRequest, String, String)} when * {@link #isIncludePayload()} returns true. * @since 5.0.3 */ @Nullable protected String getMessagePayload(HttpServletRequest request) { ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); if (wrapper != null) { byte[] buf = wrapper.getContentAsByteArray(); if (buf.length > 0) { int length = Math.min(buf.length, getMaxPayloadLength()); try { return new String(buf, 0, length, wrapper.getCharacterEncoding()); } catch (UnsupportedEncodingException ex) { return "[unknown]"; } } } return null; } /** * Determine whether to call the {@link #beforeRequest}/{@link #afterRequest} * methods for the current request, i.e. whether logging is currently active * (and the log message is worth building). *

The default implementation always returns {@code true}. Subclasses may * override this with a log level check. * @param request current HTTP request * @return {@code true} if the before/after method should get called; * {@code false} otherwise * @since 4.1.5 */ protected boolean shouldLog(HttpServletRequest request) { return true; } /** * Concrete subclasses should implement this method to write a log message * before the request is processed. * @param request current HTTP request * @param message the message to log */ protected abstract void beforeRequest(HttpServletRequest request, String message); /** * Concrete subclasses should implement this method to write a log message * after the request is processed. * @param request current HTTP request * @param message the message to log */ protected abstract void afterRequest(HttpServletRequest request, String message); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy