はじめに
静的ページ(JavaScriptやCSS等)は除いて、Spring Bootでリクエストを受けたものについて、以下をログに出力したい。
- 処理開始時刻、処理終了時刻、処理時間
- URL
- すべてのリクエストパラメータ
- レスポンスステータス
また、一つ一つのリクエストを一意に特定しやすくするため、ログインユーザ名とUUIDを組み合わせた文字列を、ログの先頭に識別子として必ず付与する。
検証version
- Spring Boot 1.3.5
- Selenium WebDriver 2.53.0
ソース
LoggingFilter.java
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = ((HttpServletRequest)request);
String uri = httpReq.getRequestURI();
if (isStatic(uri)) {
chain.doFilter(request, response);
return;
}
long start = System.currentTimeMillis();
UUID uuid = UUID.randomUUID();
String userName = httpReq.getRemoteUser();
String requestIdentifier = "[" + userName + "]" + "[" + uuid + "]";
log.info(String.format("%s start", requestIdentifier));
log.info(String.format("%s URI: %s", requestIdentifier, uri));
Map<String, String[]> params = httpReq.getParameterMap();
for (Entry<String, String[]> param : params.entrySet()) {
log.info(String.format("%s PARAM_KEY: %s, PARAM_VALUE: %s"
, requestIdentifier
, param.getKey()
, Arrays.toString(param.getValue())));
}
chain.doFilter(request, response);
int status = ((HttpServletResponse)response).getStatus();
log.info(String.format("%s end in %d millisec. STATUS %d", requestIdentifier, System.currentTimeMillis() - start, status));
}
private boolean isStatic(String uri) {
return uri.contains("/js/")
|| uri.contains("/css/")
|| uri.contains("/fonts/");
}
@Override
public void destroy() {
}
}
ソースコード解説
ログ項目の取得方法
処理開始時刻、処理終了時刻、処理時間は通常のJavaコードである。
URL、リクエストパラメータ、レスポンスステータスは、SpringのモジュールではなくJavaServletでおなじみのHttpServletRequest、HttpServletResponseを利用して取得する。
Loggerオブジェクト
Loggerオブジェクトを取得するためには、通常 private static final Logger log = LoggerFactory.getLogger(Sample.class);
のようなコードを書く必要があるが、Lombokを使用しているため、 @Slf4j
でクラスをアノテートすることで、logという名前の Loggerオブジェクトが使えるようなる。
Filter実装クラスのSpring Bootへの登録方法
@Component
をFilter実装クラスにつけるだけで、Spring BootがFilterとして扱ってくれる。
今回はFilterはただ一つLoggingFilterのみを登録すればよかったのでこの方法を採っているが、Filter実装クラスを複数登録し、さらにFilterの実行順序を制御するのであれば、FilterRegistrationBeanを使用する必要があるだろう。
FilterRegistrationBeanの使用方法はこちらを参考:Spring Boot で複数の Filter を定義する