概要
Springには、AOP(Aspect Oriented Programming)という共通的な処理を外部へ切り出すことができる機能があります。外部へ切り出す機能として、「ログ出力」、「トランザクション」、「例外ハンドリング」などがあります。この記事ではログ出力の方法を紹介します。
実装
API
ログ出力するために簡単なAPIを作成します。Controllerクラスののせます。
2つメソッドがあり、これらを実行するとログ出力されるようにします。
package com.example.demo.controller;
import java.util.Collections;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.service.SpringLogService;
@RestController
@RequestMapping("log")
public class SpringLogController {
private final SpringLogService service;
public SpringLogController(SpringLogService service) {
this.service = service;
}
@GetMapping
public Map<Integer, String> getList() {
return service.findList();
}
@PostMapping
public void insert() {
Map<Integer, String> map = getList();
int maxKey = Collections.max(map.keySet());
service.insert(maxKey + 1, "追加");
}
}
ログ出力クラス
AOPを利用するために、以下を追加します。
(今回はgradleで実装しているため「build.gradle」に追加)
implementation 'org.springframework.boot:spring-boot-starter-aop'
ログ出力するためのクラスを作成します。
package com.example.demo.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SpringLoggingAspect {
Logger logger = LoggerFactory.getLogger(this.getClass());
@After("within(com.example.demo.controller.SpringLogController)")
public void commonLog(JoinPoint jp) {
logger.info("メソッド実行");
logger.info("メソッド名" + jp.getSignature().toString());
logger.warn("warnログ");
}
@Before("execution(* com.example.demo.controller.SpringLogController.insert*(..))")
public void postLog(JoinPoint jp) {
logger.info("これからinsert実行します");
}
}
ログ出力するために「Logback(log4jの後継とされている)」を利用しています。
いつどのメソッドが実行されたタイミングでログを出力するかは以下で制御しています。
いつはAdvice(特定のタイミング)で制御しています。
種類 | いつ |
---|---|
@Before | 実行前 |
@After | 実行後 |
@AfterReturning | 正常終了した後 |
@AfterThrowing | 例外がスローされた後 |
@Around | 実行前後指定可能で対象メソッドの実行自体も行う |
どのメソッドかはJoint Point、Pointcutで制御しています。(Adviceの後ろで指定)
様々な指定の方法がありますが、今回2パターンで実施してます。
within(Joint Point)
特定のクラスやパッケージの配下のクラス、継承しているクラスを指定することができます。
指定されたクラス配下のメソッドが実行された場合にはログが出力されます。
今回は「com.example.demo.controller.SpringLogController」内のメソッドが実行された場合にログが出力されます。
execution(Pointcut)
withinより細かい指定が可能です。
特定のメソッドや任意の文字から始まるメソッド、特定のクラスなど指定することができます。
「* com.example.demo.controller.SpringLogController.insert*(..)」
戻り値「任意」 パッケージ「controller配下」.クラス「SpringLogController」.メソッド「insertから始まる」()「引数が任意の0以上」 と指定してます。
なので今回はinsert()メソッドが実行する前に出力されます。
実施
getList()実行
実行すると、以下ログが表示されます(実施時間省略)
commonLogメソッド内に記載しているログのみ出力されています。
ログメソッドの引数に「JoinPoint」を指定することで実行メソッド名やクラス名などを出力することが可能です。
postLog実行
実行すると、以下ログが表示されます(実施時間省略)
commonLogメソッドとpostLogメソッド内に記載しているログのみ出力されています。
またpostLogメソッドの方が「@Before」で記載しているため、先に出力されていることがわかります。
ログレベル
本来はlog出力用の設定ファイルを作成すべきですが、簡単にログレベルを変更することが可能です。
「application.properties」に以下を指定するとwarn以上のログのみ出力されうようになります。
logging.level.root=warn
postLog実行後