前言
前面我们实现了AOP切面来拦截方法调用,本章将深入日志处理器的核心逻辑,学习如何将原始的方法调用信息转换为结构化、智能化的日志输出。这是框架价值体现的关键环节。
日志处理器的设计架构
核心处理流程
graph LR
A[方法调用信息] --> B[LogMethodProcessor]
B --> C[参数序列化]
B --> D[返回值处理]
B --> E[异常信息提取]
B --> F[执行时间计算]
C --> G[敏感信息脱敏]
D --> G
E --> G
F --> G
G --> H[消息格式化]
H --> I[日志输出]
处理器的核心职责:
- 🔄 数据转换:将Java对象转换为可读的日志格式
- 🛡️ 安全处理:对敏感信息进行脱敏
- ⏱️ 性能统计:精确计算方法执行时间
- 📝 消息格式化:生成结构化的日志消息
- 🎯 上下文管理:整合请求上下文信息
LogMethodProcessor - 核心处理器实现
java
package com.simpleflow.log.processor;
import com.simpleflow.log.annotation.LogLevel;
import com.simpleflow.log.config.LogConfig;
import com.simpleflow.log.context.LogContext;
import com.simpleflow.log.context.ThreadLocalTraceHolder;
import com.simpleflow.log.formatter.LogFormatter;
import com.simpleflow.log.util.JsonUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 日志方法处理器
*
* 负责处理方法执行的日志记录,包括参数序列化、返回值处理、
* 异常信息提取、执行时间计算等核心功能
*/
@Component
public class LogMethodProcessor {
private static final Logger logger = LoggerFactory.getLogger(LogMethodProcessor.class);
private final LogFormatter logFormatter;
public LogMethodProcessor(LogFormatter logFormatter) {
this.logFormatter = logFormatter;
}
/**
* 处理方法执行的完整流程
*/
public Object processMethodExecution(ProceedingJoinPoint joinPoint,
Method method,
Object[] args,
LogConfig config) throws Throwable {
String methodName = getMethodFullName(method);
long startTime = System.currentTimeMillis();
// 创建日志上下文
LogContext logContext = createLogContext(method, args, config);
try {
// 记录方法开始日志
logMethodStart(logContext, config);
// 执行目标方法
Object result = joinPoint.proceed();
// 计算执行时间
long executionTime = System.currentTimeMillis() - startTime;
logContext.setExecutionTime(executionTime);
logContext.setResult(result);
// 记录方法成功日志
logMethodSuccess(logContext, config);
return result;
} catch (Throwable throwable) {
// 计算执行时间
long executionTime = System.currentTimeMillis() - startTime;
logContext.setExecutionTime(executionTime);
logContext.setThrowable(throwable);
// 记录方法异常日志
logMethodError(logContext, config);
// 重新抛出异常
throw throwable;
}
}
/**
* 创建日志上下文
*/
private LogContext createLogContext(Method method, Object[] args, LogConfig config) {
LogContext context = new LogContext();
// 基础信息
context.setMethodName(getMethodFullName(method));
context.setClassName(method.getDeclaringClass().getSimpleName());
context.setStartTime(System.currentTimeMillis());
// 处理方法参数
if (config.getLogArgs() && args != null && args.length > 0) {
Object[] processedArgs = processMethodArgs(args, config);
context.setArgs(processedArgs);
}
// 整合请求上下文
context.setTraceContext(ThreadLocalTraceHolder.getCurrentTrace());
return context;
}
/**
* 处理方法参数
*/
private Object[] processMethodArgs(Object[] args, LogConfig config) {
if (args == null || args.length == 0) {
return new Object[0];
}
Object[] processedArgs = new Object[args.length];
Set<Integer> excludeIndexes = getExcludeIndexes(config);
for (int i = 0; i < args.length; i++) {
if (excludeIndexes.contains(i)) {
processedArgs[i] = "***EXCLUDED***";
} else {
processedArgs[i] = processArgument(args[i], config);
}
}
return processedArgs;
}
/**
* 处理单个参数
*/
private Object processArgument(Object arg, LogConfig config) {
if (arg == null) {
return null;
}
try {
// 对于简单类型,直接返回
if (isSimpleType(arg)) {
return arg;
}
// 对于复杂对象,序列化为JSON并进行脱敏
String jsonStr = JsonUtils.toJson(arg);
return applySensitiveFieldsMasking(jsonStr, config);
} catch (Exception e) {
logger.warn("处理参数时发生异常: {}", e.getMessage());
return arg.toString();
}
}
/**
* 记录方法开始日志
*/
private void logMethodStart(LogContext context, LogConfig config) {
if (!isLogEnabled(config.getLevel())) {
return;
}
try {
String message = logFormatter.formatStartMessage(context, config);
writeLog(config.getLevel(), message);
} catch (Exception e) {
logger.warn("记录方法开始日志失败: {}", e.getMessage());
}
}
/**
* 记录方法成功日志
*/
private void logMethodSuccess(LogContext context, LogConfig config) {
if (!isLogEnabled(config.getLevel())) {
return;
}
try {
String message = logFormatter.formatSuccessMessage(context, config);
writeLog(config.getLevel(), message);
} catch (Exception e) {
logger.warn("记录方法成功日志失败: {}", e.getMessage());
}
}
/**
* 记录方法异常日志
*/
private void logMethodError(LogContext context, LogConfig config) {
// 异常日志使用ERROR级别
LogLevel logLevel = config.getLogException() ? LogLevel.ERROR : config.getLevel();
if (!isLogEnabled(logLevel)) {
return;
}
try {
String message = logFormatter.formatErrorMessage(context, config);
writeLog(logLevel, message, context.getThrowable());
} catch (Exception e) {
logger.warn("记录方法异常日志失败: {}", e.getMessage());
}
}
// ========== 工具方法 ==========
/**
* 获取方法全名
*/
private String getMethodFullName(Method method) {
return method.getDeclaringClass().getSimpleName() + "." + method.getName();
}
/**
* 获取排除的参数索引
*/
private Set<Integer> getExcludeIndexes(LogConfig config) {
Set<Integer> excludeIndexes = new HashSet<>();
if (config.getExcludeArgs() != null) {
for (int index : config.getExcludeArgs()) {
excludeIndexes.add(index);
}
}
return excludeIndexes;
}
/**
* 判断是否为简单类型
*/
private boolean isSimpleType(Object obj) {
return obj instanceof String ||
obj instanceof Number ||
obj instanceof Boolean ||
obj instanceof Character ||
obj.getClass().isPrimitive();
}
/**
* 应用敏感字段脱敏
*/
private String applySensitiveFieldsMasking(String jsonStr, LogConfig config) {
if (config.getSensitiveFields() == null || config.getSensitiveFields().length == 0) {
return jsonStr;
}
String maskedJson = jsonStr;
for (String sensitiveField : config.getSensitiveFields()) {
maskedJson = maskSensitiveField(maskedJson, sensitiveField);
}
return maskedJson;
}
/**
* 脱敏单个敏感字段
*/
private String maskSensitiveField(String jsonStr, String fieldName) {
// 使用正则表达式匹配并替换敏感字段值
String pattern = "\"" + fieldName + "\"\\s*:\\s*\"[^\"]*\"";
String replacement = "\"" + fieldName + "\":\"******\"";
return jsonStr.replaceAll(pattern, replacement);
}
/**
* 检查日志级别是否启用
*/
private boolean isLogEnabled(LogLevel level) {
switch (level) {
case TRACE: return logger.isTraceEnabled();
case DEBUG: return logger.isDebugEnabled();
case INFO: return logger.isInfoEnabled();
case WARN: return logger.isWarnEnabled();
case ERROR: return logger.isErrorEnabled();
default: return false;
}
}
/**
* 写入日志
*/
private void writeLog(LogLevel level, String message) {
writeLog(level, message, null);
}
/**
* 写入日志(带异常)
*/
private void writeLog(LogLevel level, String message, Throwable throwable) {
switch (level) {
case TRACE:
if (throwable != null) {
logger.trace(message, throwable);
} else {
logger.trace(message);
}
break;
case DEBUG:
if (throwable != null) {
logger.debug(message, throwable);
} else {
logger.debug(message);
}
break;
case INFO:
if (throwable != null) {
logger.info(message, throwable);
} else {
logger.info(message);
}
break;
case WARN:
if (throwable != null) {
logger.warn(message, throwable);
} else {
logger.warn(message);
}
break;
case ERROR:
if (throwable != null) {
logger.error(message, throwable);
} else {
logger.error(message);
}
break;
}
}
}
JsonUtils - JSON序列化工具
java
package com.simpleflow.log.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JSON工具类
*
* 提供对象序列化和反序列化功能,专门优化用于日志记录场景
*/
public class JsonUtils {
private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
private static final ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper();
// 配置序列化选项
OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
OBJECT_MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 注册Java 8时间模块
OBJECT_MAPPER.registerModule(new JavaTimeModule());
}
/**
* 对象转JSON字符串
*/
public static String toJson(Object obj) {
if (obj == null) {
return "null";
}
try {
return OBJECT_MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.warn("对象序列化为JSON失败: {}", e.getMessage());
return obj.toString();
} catch (Exception e) {
logger.warn("对象序列化时发生未知异常: {}", e.getMessage());
return "\"<序列化失败>\"";
}
}
/**
* 对象转格式化的JSON字符串(用于调试)
*/
public static String toPrettyJson(Object obj) {
if (obj == null) {
return "null";
}
try {
return OBJECT_MAPPER.writerWithDefaultPrettyPrinter()
.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.warn("对象序列化为格式化JSON失败: {}", e.getMessage());
return toJson(obj);
}
}
/**
* JSON字符串转对象
*/
public static <T> T fromJson(String json, Class<T> clazz) {
if (json == null || json.trim().isEmpty()) {
return null;
}
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (JsonProcessingException e) {
logger.warn("JSON反序列化失败: {}", e.getMessage());
return null;
}
}
/**
* 安全的JSON序列化,限制输出长度
*/
public static String toJsonSafely(Object obj, int maxLength) {
String json = toJson(obj);
if (json.length() > maxLength) {
return json.substring(0, maxLength - 3) + "...";
}
return json;
}
}
LogContext - 日志上下文
java
package com.simpleflow.log.context;
import com.simpleflow.log.context.TraceContext;
/**
* 日志上下文
*
* 封装单次方法调用的所有日志相关信息
*/
public class LogContext {
/**
* 方法名(包含类名)
*/
private String methodName;
/**
* 类名
*/
private String className;
/**
* 方法参数
*/
private Object[] args;
/**
* 方法返回值
*/
private Object result;
/**
* 异常信息
*/
private Throwable throwable;
/**
* 开始时间
*/
private long startTime;
/**
* 执行时间(毫秒)
*/
private long executionTime;
/**
* 请求追踪上下文
*/
private TraceContext traceContext;
// ========== 构造方法 ==========
public LogContext() {
}
public LogContext(String methodName, Object[] args) {
this.methodName = methodName;
this.args = args;
this.startTime = System.currentTimeMillis();
}
// ========== 便利方法 ==========
/**
* 是否有参数
*/
public boolean hasArgs() {
return args != null && args.length > 0;
}
/**
* 是否有返回值
*/
public boolean hasResult() {
return result != null;
}
/**
* 是否有异常
*/
public boolean hasException() {
return throwable != null;
}
/**
* 获取请求ID
*/
public String getRequestId() {
return traceContext != null ? traceContext.getRequestId() : null;
}
/**
* 获取用户ID
*/
public String getUserId() {
return traceContext != null ? traceContext.getUserId() : null;
}
// ========== Getters and Setters ==========
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getExecutionTime() {
return executionTime;
}
public void setExecutionTime(long executionTime) {
this.executionTime = executionTime;
}
public TraceContext getTraceContext() {
return traceContext;
}
public void setTraceContext(TraceContext traceContext) {
this.traceContext = traceContext;
}
@Override
public String toString() {
return "LogContext{" +
"methodName='" + methodName + '\'' +
", className='" + className + '\'' +
", executionTime=" + executionTime +
", hasException=" + hasException() +
'}';
}
}
敏感信息脱敏策略
1. 字段级脱敏
java
/**
* 高级敏感信息脱敏处理器
*/
@Component
public class SensitiveDataMasker {
private static final Logger logger = LoggerFactory.getLogger(SensitiveDataMasker.class);
// 预定义的敏感字段模式
private static final Set<String> DEFAULT_SENSITIVE_PATTERNS = Set.of(
"password", "pwd", "secret", "token", "key",
"idCard", "phone", "mobile", "email", "address"
);
/**
* 智能脱敏处理
*/
public String maskSensitiveData(String jsonStr, String[] configuredFields) {
if (jsonStr == null || jsonStr.isEmpty()) {
return jsonStr;
}
Set<String> sensitiveFields = new HashSet<>();
// 添加配置的敏感字段
if (configuredFields != null) {
sensitiveFields.addAll(Arrays.asList(configuredFields));
}
// 添加默认敏感字段模式
sensitiveFields.addAll(DEFAULT_SENSITIVE_PATTERNS);
String maskedJson = jsonStr;
for (String field : sensitiveFields) {
maskedJson = maskField(maskedJson, field);
}
return maskedJson;
}
/**
* 脱敏单个字段
*/
private String maskField(String jsonStr, String fieldName) {
// 匹配不同格式的JSON字段
String[] patterns = {
// 字符串值: "field":"value"
"\"" + fieldName + "\"\\s*:\\s*\"[^\"]*\"",
// 数字值: "field":123
"\"" + fieldName + "\"\\s*:\\s*\\d+",
// 布尔值: "field":true
"\"" + fieldName + "\"\\s*:\\s*(true|false)"
};
String result = jsonStr;
for (String pattern : patterns) {
String replacement = "\"" + fieldName + "\":\"******\"";
result = result.replaceAll(pattern, replacement);
}
return result;
}
/**
* 根据字段类型进行不同的脱敏策略
*/
public String maskByType(String value, String fieldName) {
if (value == null || value.isEmpty()) {
return value;
}
// 手机号脱敏:保留前3位和后4位
if (fieldName.toLowerCase().contains("phone") ||
fieldName.toLowerCase().contains("mobile")) {
return maskPhone(value);
}
// 身份证脱敏:保留前6位和后4位
if (fieldName.toLowerCase().contains("idcard")) {
return maskIdCard(value);
}
// 邮箱脱敏:保留@前的第一个字符和@后的域名
if (fieldName.toLowerCase().contains("email")) {
return maskEmail(value);
}
// 默认脱敏:全部替换为星号
return "******";
}
private String maskPhone(String phone) {
if (phone.length() <= 7) {
return "******";
}
return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
}
private String maskIdCard(String idCard) {
if (idCard.length() <= 10) {
return "******";
}
return idCard.substring(0, 6) + "********" + idCard.substring(idCard.length() - 4);
}
private String maskEmail(String email) {
int atIndex = email.indexOf("@");
if (atIndex <= 0) {
return "******";
}
return email.charAt(0) + "****" + email.substring(atIndex);
}
}
2. 脱敏效果示例
java
// 原始数据
{
"username": "zhangsan",
"password": "123456",
"phone": "13812345678",
"email": "zhangsan@example.com",
"idCard": "110101199001011234"
}
// 脱敏后
{
"username": "zhangsan",
"password": "******",
"phone": "138****5678",
"email": "z****@example.com",
"idCard": "110101********1234"
}
性能优化技巧
1. 延迟序列化
java
/**
* 延迟序列化参数处理器
*/
public class LazyArgProcessor {
/**
* 创建延迟序列化的参数包装器
*/
public Object[] createLazyArgs(Object[] args, LogConfig config) {
if (!config.getLogArgs() || args == null) {
return null;
}
Object[] lazyArgs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
lazyArgs[i] = new LazySerializableArg(args[i], config);
}
return lazyArgs;
}
/**
* 延迟序列化的参数包装器
*/
private static class LazySerializableArg {
private final Object arg;
private final LogConfig config;
private String serialized;
public LazySerializableArg(Object arg, LogConfig config) {
this.arg = arg;
this.config = config;
}
@Override
public String toString() {
if (serialized == null) {
// 只有在真正需要时才进行序列化
if (arg == null) {
serialized = "null";
} else if (isSimpleType(arg)) {
serialized = arg.toString();
} else {
serialized = JsonUtils.toJsonSafely(arg, 1000);
// 应用脱敏
serialized = applySensitiveFieldsMasking(serialized, config);
}
}
return serialized;
}
private boolean isSimpleType(Object obj) {
return obj instanceof String || obj instanceof Number ||
obj instanceof Boolean || obj instanceof Character;
}
}
}
2. 异步日志处理
java
/**
* 异步日志处理器
*/
@Component
public class AsyncLogProcessor {
private final ExecutorService logExecutor;
private final BlockingQueue<LogTask> logQueue;
public AsyncLogProcessor() {
this.logQueue = new LinkedBlockingQueue<>(1000);
this.logExecutor = Executors.newFixedThreadPool(2,
new ThreadFactoryBuilder()
.setNameFormat("async-log-%d")
.setDaemon(true)
.build());
// 启动日志处理线程
startLogProcessor();
}
/**
* 异步处理日志
*/
public void processAsync(LogContext context, LogConfig config, LogType logType) {
LogTask task = new LogTask(context, config, logType);
if (!logQueue.offer(task)) {
// 队列满了,同步处理
processLogSync(task);
}
}
private void startLogProcessor() {
logExecutor.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
LogTask task = logQueue.take();
processLogSync(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} catch (Exception e) {
logger.error("异步日志处理异常", e);
}
}
});
}
private void processLogSync(LogTask task) {
// 具体的日志处理逻辑
}
enum LogType {
START, SUCCESS, ERROR
}
static class LogTask {
final LogContext context;
final LogConfig config;
final LogType logType;
LogTask(LogContext context, LogConfig config, LogType logType) {
this.context = context;
this.config = config;
this.logType = logType;
}
}
}
本章小结
✅ 完成的任务
- 核心处理器:实现了LogMethodProcessor的完整逻辑
- JSON序列化:创建了优化的JsonUtils工具类
- 敏感信息脱敏:实现了智能的脱敏策略
- 日志上下文:设计了LogContext承载调用信息
- 性能优化:实现了延迟序列化和异步处理
🎯 学习要点
- 数据转换的安全性和性能考虑
- 敏感信息脱敏的多种策略
- 异步处理在日志场景中的应用
- 上下文管理的重要性
- 错误处理不能影响业务逻辑
💡 思考题
- 如何平衡日志详细程度和性能开销?
- 敏感信息脱敏还有哪些改进空间?
- 异步日志处理的风险和应对策略?
🚀 下章预告
下一章我们将实现分布式链路追踪系统,学习如何在分布式环境下跟踪请求的完整生命周期,包括TraceContext设计、ThreadLocal管理、跨线程传递等核心技术。
💡 设计原则 : 日志处理器是框架的核心大脑,需要在功能完整性、性能效率、数据安全之间找到最佳平衡点。