簡介
SLF4J是Simple Logging Facade for Java的縮寫。它主要提供了Java目前現(xiàn)有日志框架的簡單抽象。它使用戶能夠使用單個依賴項(xiàng)處理任何日志框架,例如:Log4j,Logback和JUL。也可以在運(yùn)行/部署時遷移到對應(yīng)的日志記錄框架。
優(yōu)點(diǎn)
*可以在部署時遷移到所需的日志記錄框架。
*提供了對所有流行的日志框架的綁定。
*支持參數(shù)化日志記錄消息。
*程序和日志記錄框架分離。
*提供了一個簡單的日志遷移工具。
常規(guī)使用
參看slf4j官網(wǎng)網(wǎng)站
參看易佰教程
這個模塊需要的知識貯備
實(shí)際項(xiàng)目使用自定義注解和aop的方式來實(shí)現(xiàn)操作訪問記錄功能
自定義注解
參看Java的注解
AOP
Spring AOP簡單樣例
暫時不記錄數(shù)據(jù)庫,等學(xué)習(xí)完jpa后,在記錄到數(shù)據(jù)中。
實(shí)踐
maven文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
目錄結(jié)構(gòu)

image.png
源碼
定義記錄那些信息Log類(Log.java)
package com.springboot.action.saas.common.logging.domain;
import lombok.Data;
import java.sql.Timestamp;
@Data
public class Log {
//描述
private String description;
//方法名
private String method;
//參數(shù)
private String params;
//日志類型
private String logType;
//請求ip
private String requestIp;
//請求耗時
private Long time;
//異常詳細(xì)
private String exceptionDetail;
//請求實(shí)踐
private Timestamp createTime;
//構(gòu)造函數(shù)(代參)
public Log(String logType, Long time) {
this.logType = logType;
this.time = time;
}
}
日志記錄業(yè)務(wù)接口(LogService.java)
package com.springboot.action.saas.common.logging.service;
import com.springboot.action.saas.common.logging.domain.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.scheduling.annotation.Async;
/*
* 日志記錄業(yè)務(wù)
*
*/
public interface LogService {
/*
* 記錄日志,異步執(zhí)行,不影響正常業(yè)務(wù)流程
* 參數(shù) joinPoint 切面方法的信息,當(dāng)前切入點(diǎn)各種信息
* log 要記錄的日志信息有那些
* */
@Async
void save(ProceedingJoinPoint joinPoint, Log log);
}
日志記錄業(yè)務(wù)接口實(shí)現(xiàn)(LogServiceImpl.java)
package com.springboot.action.saas.common.logging.service.impl;
import com.springboot.action.saas.common.logging.domain.Log;
import com.springboot.action.saas.common.logging.service.LogService;
import com.springboot.action.saas.common.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Service
public class LogServiceImpl implements LogService {
/*
* 記錄日志接口實(shí)現(xiàn)
**/
@Override
public void save(ProceedingJoinPoint joinPoint, Log log) {
//獲取request 請求對象
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
.getRequest();
//getSignature獲取切面相關(guān)信息,比如方法名、目標(biāo)方法參數(shù)等信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//獲取抽象類(代理對象)方法
Method method = signature.getMethod();
//返回該元素的指定類型的注釋,這里是Log注解
com.springboot.action.saas.common.logging.annotation.Log aopLog = method.getAnnotation(com.springboot.action.saas.common.logging.annotation.Log.class);
//獲取注解傳遞的參數(shù)
if (log != null) {
log.setDescription(aopLog.value());
}
//通過最笨的反射方法,獲取方法路徑
String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()";
log.setMethod(methodName);
//參數(shù)處理
//獲取參數(shù)值
Object[] argValues = joinPoint.getArgs();
//獲取參數(shù)名
String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
//組織參數(shù)列表
String params = "{";
if(argValues != null){
for (int i = 0; i < argValues.length; i++) {
params += " " + argNames[i] + ": " + argValues[i];
}
}
log.setParams(params + " }");
//獲取IP地址
log.setRequestIp(StringUtils.getIP(request));
//輸出下日志到控制臺
System.out.println(log.toString());
}
}
配置切點(diǎn)和切點(diǎn)對應(yīng)的動作(好多文檔都說是通知)
LogAspect.java
package com.springboot.action.saas.common.logging.aspect;
import com.springboot.action.saas.common.logging.domain.Log;
import com.springboot.action.saas.common.logging.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogAspect {
//日志業(yè)務(wù)
@Autowired
private LogService logService;
//當(dāng)前時間
private long currentTime = 0L;
/**
* 配置切入點(diǎn), 匹配連接點(diǎn)的方法是否有Log注解,定義切點(diǎn)
*/
@Pointcut("@annotation(com.springboot.action.saas.common.logging.annotation.Log)")
public void logPointcut() {
// 該方法無方法體,主要為了讓同類中其他方法使用此切入點(diǎn)
}
/**
* 配置環(huán)繞通知,使用在方法logPointcut()上注冊的切入點(diǎn),具體要通知在什么條件下執(zhí)行和執(zhí)行什么動作
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint){
//返回值
Object result = null;
//獲取當(dāng)前時間
currentTime = System.currentTimeMillis();
try {
//執(zhí)行目標(biāo)方法
result = joinPoint.proceed();
} catch (Throwable e) {
//拋異常
throw new RuntimeException(e.getMessage());
}
//創(chuàng)建日志對象
Log log = new Log("INFO",System.currentTimeMillis() - currentTime);
//記錄日志
logService.save(joinPoint, log);
//返回目標(biāo)方法的返回值
return result;
}
/**
* 配置異常通知,異常的日志也要記錄
*
* @param joinPoint join point for advice
* @param e exception
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
//創(chuàng)建日志對象
Log log = new Log("ERROR",System.currentTimeMillis() - currentTime);
//異常設(shè)置
log.setExceptionDetail(e.getMessage());
//記錄異常
logService.save((ProceedingJoinPoint)joinPoint, log);
}
}
REST接口
/**
* 顯示所有Member,請求url:"http://xxx/member/v1/findall"
*
* @return List
*/
//這里增加了自定義日志注解
@Log("獲取全部用戶列表")
@RequestMapping(value = "/v1/findall")
public List<Member> findAllMember() {
return memberService.findAllMember();
}
實(shí)際運(yùn)行效果
請求
http://localhost:8080/member/v1/findall
瀏覽器
{"success":true,"code":0,"data":[{"id":1,"phone":null,"password":null,"nickname":"demo1","email":null,"ctime":null,"utime":null,"last_login_time":null,"last_login_ip":null,"invite_code":null,"is_active":null,"is_delete":null},{"id":2,"phone":null,"password":null,"nickname":"demo2","email":null,"ctime":null,"utime":null,"last_login_time":null,"last_login_ip":null,"invite_code":null,"is_active":null,"is_delete":null}],"message":"","currentTime":1564631416588}
控制臺
Log(description=獲取全部用戶列表, method=com.springboot.action.saas.modules.user.controller.MemberController.findAllMember(), params={ }, logType=INFO, requestIp=127.0.0.1, time=4, exceptionDetail=null, createTime=null)
打tag 1.0.4版本,提交代碼。
git tag -a v1.0.4 -m "實(shí)現(xiàn)AOP日志信息獲取"
git push origin v1.0.4