springboot + AOP 日誌
阿新 • • 發佈:2019-01-03
一. 簡介
記錄日誌方便排錯,記錄操作記錄。通常有倆層需要加日誌:controller層和service層。
controller層的日誌使用Log列印資訊,service層的日誌使用資料庫記錄操作日誌。
Controller層 |
通過新增一個AOP切面,監控controller層的所有方法: 呼叫之前列印請求資訊,包含URL,HTTP_METHOD,IP,CLASS_METHOD,ARGS; 在方法完成後,列印返回的請求結果,包含code, msg。 |
ServiceImpl層 |
建立一張日誌表sys_log, 建立一個SysLogService介面和一個SysLogServiceImpl實現類。裡面包含一個saveSysLog(...)方法,該方法加@Transactional註解,內建的傳播行為為PROPAGATION_REQUIRES_NEW。 在每個其他的service層中注入SysLogService,在每個方法最後加一個插入日誌表的操作。 此處不展示。 |
二. 程式碼
2.1 切面類
package com.yzx.operateplatform.aspect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.yzx.operateplatform.vo.ResultVO; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * 日誌切面類 */ @Aspect @Component public class LogAspect { private final static Logger logger = LoggerFactory.getLogger(LogAspect.class); // ..表示包及子包 該方法代表controller層的所有方法 @Pointcut("execution(public * com.yzx.operateplatform.controller..*.*(..))") public void controllerMethod() { } @Before("controllerMethod()") public void LogRequestInfo(JoinPoint joinPoint) throws Exception { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); StringBuffer requestLog = new StringBuffer(); requestLog.append("請求資訊:") .append("URL = {" + request.getRequestURI() + "},\t") .append("HTTP_METHOD = {" + request.getMethod() + "},\t") .append("IP = {" + request.getRemoteAddr() + "},\t") .append("CLASS_METHOD = {" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "},\t"); if(joinPoint.getArgs().length == 0) { requestLog.append("ARGS = {} "); } else { requestLog.append("ARGS = " + new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL) .writeValueAsString(joinPoint.getArgs()[0]) + ""); } logger.info(requestLog.toString()); } @AfterReturning(returning = "resultVO", pointcut = "controllerMethod()") public void logResultVOInfo(ResultVO resultVO) throws Exception { logger.info("請求結果:" + resultVO.getCode() + "\t" + resultVO.getMsg()); } }
注意:
如果有BaseController的話,不需要被日誌掃描,可以通過新增不掃描路徑即可。如下:
2.2 ResultVO類
package com.yzx.codedemo.vo; import io.swagger.annotations.ApiModelProperty; /** * 返回結果類 * * @param <T> 型別 */ public class ResultVO<T> { @ApiModelProperty("狀態碼 0失敗 1成功 ") private Integer code; @ApiModelProperty("返回資訊") private String msg; @ApiModelProperty("返回資料") private T data; public ResultVO(Integer code, String msg) { this.code = code; this.msg = msg; } public ResultVO(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } /** * 請求成功 狀態碼 1 * * @param msg 返回資訊 * @param <T> 型別 * @return ResultVO */ public static <T> ResultVO getSuccess(String msg) { return new ResultVO(1, msg); } /** * 請求成功 狀態碼 1 * * @param msg 返回資訊 * @param data 返回物件 * @param <T> 型別 * @return ResultVO */ public static <T> ResultVO getSuccess(String msg, T data) { return new ResultVO(1, msg, data); } /** * 請求失敗 狀態碼 0 * * @param msg 返回資訊 * @param <T> 型別 * @return ResultVO */ public static <T> ResultVO getFailed(String msg) { return new ResultVO(0, msg); } /** * 請求失敗 狀態 0 * * @param msg 返回資訊 * @param data 返回資料 * @param <T> 型別 * @return ResultVO */ public static <T> ResultVO getFailed(String msg, T data) { return new ResultVO(0, msg, data); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
2.3 Controller類
package com.yzx.codedemo.controller.user;
import com.yzx.codedemo.entity.user.SysUser;
import com.yzx.codedemo.service.user.SysUserService;
import com.yzx.codedemo.vo.PageVO;
import com.yzx.codedemo.vo.ResultVO;
import com.yzx.codedemo.vo.user.SysUserVO;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import java.util.List;
@Api(tags = {"SysUserController"}, description = "使用者Controller")
@RestController
@RequestMapping(value = "/sysuser")
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@ApiOperation("查詢使用者分頁列表")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", dataType = "Long", name = "currentPage", value = "當前頁碼", required = true),
@ApiImplicitParam(paramType = "query", dataType = "Long", name = "pageSize", value = "每頁記錄數", required = true),
@ApiImplicitParam(paramType = "query", dataType = "String", name = "userName", value = "使用者名稱稱", required = false),
@ApiImplicitParam(paramType = "query", dataType = "String", name = "phone", value = "手機號", required = false),
@ApiImplicitParam(paramType = "query", dataType = "Long", name = "userState", value = "使用者狀態: 0刪除 1正常", required = false),
@ApiImplicitParam(paramType = "query", dataType = "String", name = "starttime", value = "開始時間 格式:yyyy-MM-dd HH:mm:ss", required = false),
@ApiImplicitParam(paramType = "query", dataType = "String", name = "endtime", value = "結束時間 格式:yyyy-MM-dd HH:mm:ss", required = false)
})
@GetMapping(value = "/page")
public ResultVO<PageVO<SysUserVO>> selectSysUserPage(@ApiIgnore SysUserVO sysUserVO) {
try {
return this.sysUserService.selectSysUserPage(sysUserVO);
} catch (Exception e) {
e.printStackTrace();
return ResultVO.getSuccess("查詢使用者分頁列表失敗");
}
}
}
2.3 測試
每次呼叫controller層的程式碼的方法,都會列印日誌資訊:
2018-08-22 16:17:12.251 INFO 12200 --- [nio-8080-exec-1] com.yzx.codedemo.aspect.LogAspect : 請求資訊:URL = {/sysuser/page}, HTTP_METHOD = {GET}, IP = {0:0:0:0:0:0:0:1}, CLASS_METHOD = {com.yzx.codedemo.controller.user.SysUserController.selectSysUserPage}, ARGS = {"currentPage":1,"pageSize":10}
2018-08-22 16:17:12.319 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage_COUNT : ==> Preparing: SELECT count(0) FROM sys_user WHERE 1 = 1
2018-08-22 16:17:12.336 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage_COUNT : ==> Parameters:
2018-08-22 16:17:12.351 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage_COUNT : <== Total: 1
2018-08-22 16:17:12.356 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage : ==> Preparing: SELECT id AS user_id, user_name, password, phone, createtime FROM sys_user WHERE 1=1 ORDER BY createtime DESC LIMIT ?
2018-08-22 16:17:12.357 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage : ==> Parameters: 10(Integer)
2018-08-22 16:17:12.363 DEBUG 12200 --- [nio-8080-exec-1] c.y.c.m.u.S.selectSysUserPage : <== Total: 10
2018-08-22 16:17:12.365 INFO 12200 --- [nio-8080-exec-1] com.yzx.codedemo.aspect.LogAspect : 請求結果:1 查詢分頁列表成功