かなり前から稼働しているWebサービスをspringbootで再構築をしているところですが、リクエストとレスポンスをログに出力するという既存仕様の踏襲にAOPを使ってみました。
対象のWebサービスは、昔ながらのフォームのポストでリクエストを受け付ける仕様なので、application/x-www-form-urlencodedに対応する形になります。
AOPについては、こちらを参考にさせて頂きました。
リクエストとレスポンスなので、@Aroundでメソッドの前後にログ出力をします。
Controllerのメソッド引数にHttpServletRequestとHttpServletResponseを入れる事で、Advicer側でパラメータが取得出来ました。
@Controller
public class XXXContoller {
@RequestMapping(path={ "/online/XXX.do"}, method = RequestMethod.POST)
public void doPost(HttpServletRequest request, HttpServletResponse response) {
//サービス層で処理
}
}
Adviser側です。
@Aspect
@Component
public class ControllerAdviser {
private static final Logger logger= LogManager.getLogger();
/**
* Controllerメソッドの前後処理
* */
@Around("execution(* jp.esoro.application.controller..*.*(..)) && args(request,response,..)")
public void Around(ProceedingJoinPoint joinPoint, HttpServletRequest request, HttpServletResponse response){
try {
String logMessage = request.getRemoteAddr() + ":" +request.getRequestURI() + ":";
Map<String, String[]> map = request.getParameterMap();
if(map != null) {
boolean firstFlg = true;
for (Iterator<Map.Entry<String, String[]>> it = map.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, String[]> entry = it.next();
String[] value = entry.getValue();
if(value.length > 0){
if (firstFlg) {
logMessage = logMessage + entry.getKey() + "=" +value[0];
firstFlg = false;
}
else{
logMessage = logMessage +"&" + entry.getKey() + "=" +value[0];
}
}
}
}
logger.info(logMessage);
//実処理の実行
joinPoint.proceed();
//レスポンスログ(未実装)
} catch (Throwable e) {
logger.warn(e.getMessage());
}
}
}
下記のようにログに出力されました。
2019-05-21 11-38-39.002:0:0:0:0:0:0:0:1:/online/XXXX.do:Id=9999999&Password=ggggg&Action=regist
でも、今回の要件にはこれ以外にログ出力要件があり、Controllerの引数ではちょっと無理っぽい。。
他にも、同じクラスから呼ばれたメソッドには適用されないようなので、ビジネスロジック側の影響は避けられないようでした。
また、戻りがあるメソッドに適用するにはjoinPoint.proceed()の戻りを返す必要があったりと、色々ハマりどころがあるようです。