-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
Description
背景
生产环境上出现了问题,但线上日志的等级一般是 INFO ,错误日志没有打印出来。
不知道内部是怎么调用的,这个时候,怎么办?
发个版本,把所有函数的入口加上日志,返回值也打印出来。
发布后,让测试再次测试,打开日志,发现问题,改完重新发布 ……
日志统一打印怎么解决,不可能要求项目里面所有人都在在函数的输入和返回值打印出来结果,怎么办?利用 Spring Aspect 即可。
依赖
先添加 Spring Aop 依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>代码
假设我们的包为 top.techial.knowledge,
我们需要查看这个包下加了 @Repository,@Service 和 @RestController 注解的类内函数 参数 和 返回值 。
使用 @Around("applicationPackagePointcut() && springBeanPointcut()") 进行环绕。
@Log4j2 是 lombok 中的一个注解。
设置打印出来参数的日志等级为 DEBUG。
import lombok.extern.log4j.Log4j2;
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.stereotype.Component;
import java.util.Arrays;
@Aspect
@Log4j2
@Component
public class LoggingAspect {
@Pointcut("within(@org.springframework.stereotype.Repository *)" +
" || within(@org.springframework.stereotype.Service *)" +
" || within(@org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
}
@Pointcut("within(top.techial.knowledge..*)")
public void applicationPackagePointcut() {
}
@AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
if (log.isErrorEnabled()) {
log.error(
"Exception in {}.{}() with cause = '{}' and exception = '{}'",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), e.getCause() != null ? e.getCause() : "NULL", e.getMessage(),
e
);
}
}
@Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
}
try {
Object result = joinPoint.proceed();
if (log.isDebugEnabled()) {
log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), result);
}
return result;
} catch (IllegalArgumentException e) {
if (log.isErrorEnabled()) {
log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
}
throw e;
}
}
}线上错误定位
有了这个 日志切面,我们再配合 Spring Actuator,actuator 可以在不重启的情况下修改日志的等级。
依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>application.yml 添加如下配置:
# spring actuator
management:
server:
port: 8080
endpoint:
health:
show-details: always
endpoints:
enabled-by-default: true
web:
base-path: /actuator
exposure:
include:
- loggers
- health
- info我们就可以通过发送请求 查看 / 修改 日志输出等级,以 curl 为例,其他的以此类推。
-
查看某个包的日志等级,包以
top.techial.knowledge为例。# curl http://localhost:8080/actuator/loggers/{package} curl http://localhost:8080/actuator/loggers/top.techial.knowledge -
修改某个包的日志等级,修改成
DEBUG等级。包以top.techial.knowledge为例。# curl -X POST http://localhost:8080/actuator/loggers/{package} \ # -H "Content-Type: application/json" \ # --data '{"configuredLevel":"{level}"}' curl -X POST http://localhost:8080/actuator/loggers/top.techial.knowledge \ -H "Content-Type: application/json" \ --data '{"configuredLevel":"debug"}
再也不用线上出问题,打包加日志!!!
真香。