我准备开一个系列,就是写一些在简要的学习项目中可能会用到的奇奇怪怪的功能,比如线程池或者统一异常处理类
取名为【项目demo】系列
然后将会同步到GitHub中:https://github.com/Livorth/FunctionalLearning
AOP日志管理
AOP是个好东西,用的地方很多,但是这里仅介绍作为日志管理的使用
总的来说,使用AOP的思路是差不多的
- 使用自定义注解
- 使用AOP环绕通知代理有对应注解的方法,然后在环绕的时候进行对应操作
- 在需要使用日志方法上加上注解,进行测试
在这里我并不会将日志信息入库,而是直接打印在控制台(因为入库麻烦但是容易实现)
我主要参考的 SpringBoot整合aop日志管理
使用自定义注解注解一般相当于一个标记,一个传递信息的作用
LogAnnotation.java
package cn.livorth.functionallearning.common.log;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface LogAnnotation {
// 操作模块
String logModule() default "";
// 操作类型
String logType() default "";
// 操作信息
String logDescription() default "";
}
AOP环绕通知类
其实除了环绕@Around:,还可以选择@Before、@After、@AfterRunning、@AfterThrowing
比较推荐用@AfterThrowing,对异常进行特殊处理
在我的日志方法中,我并没有采用将日志记录进数据库,但是可以加上,比较容易写
package cn.livorth.functionallearning.common.log;
import cn.livorth.functionallearning.utils.HttpContextUtils;
import cn.livorth.functionallearning.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class LogAspect {
@Pointcut("@annotation(cn.livorth.functionallearning.common.log.LogAnnotation)")
public void logPointCut(){}
@Around("logPointCut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = joinPoint.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
recordLog(joinPoint, JSON.toJSONString(result), time);
return result;
}
private void recordLog(ProceedingJoinPoint joinPoint, String result, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
// 日志记录
log.info("=====================log start================================");
// 日志基础信息
log.info("module:{}",logAnnotation.logModule());
log.info("type:{}",logAnnotation.logType());
log.info("description:{}",logAnnotation.logDescription());
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
log.info("request method:{}",className + "." + methodName + "()");
// 请求的参数
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
log.info("params:{}",params);
// 返回的结果
log.info("result:{}",result);
// 获取request 设置IP地址
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.info("ip:{}", IpUtils.getIpAddress(request));
log.info("execution time : {} ms",time);
log.info("=====================log end================================");
// 这里可以将相关信息提取出来进行一个入库操作
}
}
涉及到IP的处理
使用RequestContextHolder可以在Controller中获取request对象
然后可以使用request在请求头中找到对应的IP信息
这个部分是直接抄的,看起来大家都是通用的
HttpContextUtils.java
package cn.livorth.functionallearning.utils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtils {
// 在Controller中获取request对象
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
IpUtils.java
package cn.livorth.functionallearning.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
@Slf4j
public class IpUtils {
public static String getIpAddress(HttpServletRequest request) {
String ip = null;
String unknown = "unknown";
String seperator = ",";
int maxLength = 15;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
log.error("IpUtils ERROR ", e);
}
// 使用代理,则获取第一个IP地址
if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {
int idx = ip.indexOf(seperator);
if (idx > 0) {
ip = ip.substring(0, idx);
}
}
return ip;
}
public static String getIpAddress() {
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
return getIpAddress(request);
}
}
测试使用
分别在 getAllUser() 与 getAllUserByPage() 上加上 @LogAnnotation,并进行测试
@LogAnnotation(logModule = "user", logType = "select", logDescription = "获取所有用户信息") @GetMapping public ListgetAllUser(){ return userService.getAllUser(); } @LogAnnotation(logModule = "user", logType = "select", logDescription = "通过分页获取所有用户信息") @GetMapping("page/{thePage}/{pageSize}") public List getAllUserByPage(@PathVariable("thePage") int thePage, @PathVariable("pageSize") int pageSize){ Page page = new Page<>(thePage, pageSize); return userService.getAllUserByPage(page); }
写在后面
日志这种东西需要根据项目进行调整,毕竟很多的日志信息其实是无效信息,如果将这些东西存入数据库,只会占用服务器的资源,所以根据需求来,进行动态的调整才是最合适的
我的项目demo系列都会同步到GitHub上,欢迎围观
https://github.com/Livorth/FunctionalLearning



