import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Stopwatch;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import lombok.Generated;
import org.apache.commons.lang3.ArrayUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ResponseStatusException;
@EnableAspectJAutoProxy
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final List<Class<?>> IGNORED_CLASSES = List.of(
HttpServletRequest.class,
HttpServletResponse.class,
HttpSession.class,
Model.class
);
private static final String EMPTY_PARAMS = "empty";
static {
OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
JavaTimeModule module = new JavaTimeModule();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
OBJECT_MAPPER.registerModule(module);
}
@Pointcut("within(@org.springframework.stereotype.Controller *) || " +
"within(@org.springframework.web.bind.annotation.RestController *)")
public void logPointcut() {}
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
if (Objects.nonNull(AnnotationUtils.findAnnotation(signature.getMethod(), ResponseStatus.class))) {
return point.proceed(point.getArgs());
}
String baseLog = "empty";
Stopwatch sw = Stopwatch.createStarted();
try {
baseLog = OBJECT_MAPPER.writeValueAsString(getLogBody(point));
log.info("请求开始 ==> {}", baseLog);
} catch (Exception e) {
log.error("记录请求开始日志时出错", e);
}
Object proceedResult;
try {
proceedResult = point.proceed(point.getArgs());
} catch (ResponseStatusException e) {
log.error("业务异常 <== {}, 总耗时: {}, 错误信息: {}", baseLog, sw.stop(), e.getReason());
throw e;
} catch (Throwable e) {
log.error("请求异常 <== {}, 总耗时: {}", baseLog, sw.stop(), e);
throw e;
}
try {
log.info("请求结束 <== {}, 总耗时: {}", baseLog, sw.stop());
} catch (Exception e) {
log.error("记录请求结束日志时出错", e);
}
return proceedResult;
}
private LogPrintBody getLogBody(ProceedingJoinPoint joinPoint) throws Throwable {
LogPrintBody.LogPrintBodyBuilder builder = LogPrintBody.builder();
Object[] args = joinPoint.getArgs();
builder.params(ArrayUtils.isEmpty(args) ? EMPTY_PARAMS : Arrays.stream(args)
.filter(arg -> Objects.isNull(arg) || !IGNORED_CLASSES.contains(arg.getClass()))
.toList());
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (sra != null) {
HttpServletRequest request = sra.getRequest();
builder.uri(request.getRequestURI())
.host(request.getRemoteHost())
.currentUserId(request.getHeader("currentLoginUserId"))
.fromApplication(request.getHeader("originAppName"))
.regular(request.getHeader("regular"))
.brand(request.getHeader("tag"));
} else {
builder.uri("未找到请求");
}
return builder.build();
}
@Generated
public static class LogPrintBody {
private final String uri;
private final String currentUserId;
private final String fromApplication;
private final String host;
private final String brand;
private final String regular;
private final Object params;
private LogPrintBody(String uri, String currentUserId, String fromApplication,
String host, String brand, String regular, Object params) {
this.uri = uri;
this.currentUserId = currentUserId;
this.fromApplication = fromApplication;
this.host = host;
this.brand = brand;
this.regular = regular;
this.params = params;
}
public String uri() { return uri; }
public String currentUserId() { return currentUserId; }
public String fromApplication() { return fromApplication; }
public String host() { return host; }
public String brand() { return brand; }
public String regular() { return regular; }
public Object params() { return params; }
@Generated
public static class LogPrintBodyBuilder {
private String uri;
private String currentUserId;
private String fromApplication;
private String host;
private String brand;
private String regular;
private Object params;
public LogPrintBodyBuilder() {}
public LogPrintBodyBuilder uri(String uri) {
this.uri = uri;
return this;
}
public LogPrintBodyBuilder currentUserId(String currentUserId) {
this.currentUserId = currentUserId;
return this;
}
public LogPrintBodyBuilder fromApplication(String fromApplication) {
this.fromApplication = fromApplication;
return this;
}
public LogPrintBodyBuilder host(String host) {
this.host = host;
return this;
}
public LogPrintBodyBuilder brand(String brand) {
this.brand = brand;
return this;
}
public LogPrintBodyBuilder regular(String regular) {
this.regular = regular;
return this;
}
public LogPrintBodyBuilder params(Object params) {
this.params = params;
return this;
}
public LogPrintBody build() {
return new LogPrintBody(uri, currentUserId, fromApplication, host, brand, regular, params);
}
@Override
public String toString() {
return "LogPrintBodyBuilder(uri=" + uri +
", currentUserId=" + currentUserId +
", fromApplication=" + fromApplication +
", host=" + host +
", brand=" + brand +
", regular=" + regular +
", params=" + params + ")";
}
}
}
}