欢迎光临
我们一直在努力

Spring-AOP接口日志怎么统一-入参返回值耗时与脱敏

Spring AOP 接口日志怎么统一?入参、返回值、耗时与脱敏

接口日志看似只是打印请求参数、返回值和耗时,进入生产环境后却会同时遇到敏感字段、超大对象、异常路径、TraceId 和日志成本等问题。日志越“完整”,泄露和噪声风险反而可能越高。

关键风险: 如果每个 Controller 自己记录,字段和格式无法统一;如果一个 AOP 无差别序列化所有对象,文件、密码、Token 与大列表又可能直接进入日志。企业级接口日志需要的是稳定事件模型,而不是更多 println。

MetaLite 用 BaseAspectLogger 及相关处理链统一采集入口、结果、耗时和异常,并给脱敏与失败补偿留下扩展位置。下文先定义一条可运营的接口日志应包含什么,再用源码说明这些字段在什么时机产生。

一、先统一“哪些调用需要被观察”

MetaLite 的 AspectTypeEnum 把调用分为八类:

类型场景是否为入口
API_RECEIVE 接收 HTTP 请求
JOB_RUN 执行定时任务
MQ_CONSUME 消费消息
DAO_CALL 访问数据库或搜索引擎
API_CALL 调用内部 HTTP 服务
MQ_PRODUCE 发送消息
REDIS_CALL 调用 Redis
CAFFEINE_CALL 调用本地缓存

入口负责建立 TraceId,调用层沿用当前上下文。这样一条请求可以在 API、RPC、缓存和 DAO 日志中使用同一 TraceId 关联,而不是靠开发者在每个方法里手工拼接。

这也是统一日志与普通 HandlerInterceptor 的区别:拦截器主要看到 HTTP 入口,无法天然覆盖定时任务、缓存、DAO 和服务调用。

二、为什么不是每类切面各写一套 Logger

MetaLite 为 API、RPC、Redis、Caffeine 和任务等场景保留各自的 Logger,例如:

public class ApiReceiveLogger extends BaseAspectLogger {
@Override
public AspectTypeEnum aspectType() {
return AspectTypeEnum.API_RECEIVE;
}
}

子类只声明自己属于哪种切面,真正的日志模板都由 BaseAspectLogger 完成。它在调用前记录:

开始时间 | 应用名 | 远端IP | 本机IP | 切面类型 | URI或方法 | 入参 |

调用完成后追加:

返回结果 | 耗时毫秒

因此一条日志可以形成稳定结构:

2026-08-07 10:20:30.123|admin|10.0.0.8|10.0.0.12|API_RECEIVE|/api/order/list|{"param":{…}}|{"return":{…}}|36

固定分隔字段比随意自然语言更适合检索和聚合,也让不同调用类型可以共用分析规则。

但这里的输出仍是普通文本日志,并没有自动变成 Elasticsearch 索引、Micrometer 指标或 OpenTelemetry Span;采集、解析、留存和告警仍需部署侧完成。

三、接口名称为什么不能一律写成类名加方法名

BaseAspectLogger.fetchName 对入口和内部调用做了不同处理:

  • API_RECEIVE 使用当前请求 URI;
  • 其他类型使用 类名.方法名。

用户报告 /api/order/list 出错时,直接按 URI 搜索比先猜 Controller 方法更快;排查 Redis 或 RPC 调用时,类名与方法名又比 HTTP 路径更准确。

远端 IP 也只在 API_RECEIVE 中读取。内部方法调用没有伪造一个“远端 IP”,避免字段看似完整、语义却是错误的。

四、如何防止入参和返回值把日志撑爆

MetaLite 用 PrintControl.printLength 为每种切面控制输出:

private int printLength;

当前语义是:

0 → 不输出该切面日志
-1 → 完整输出
>0 → JSON 序列化后按字符长度截断

这使 API 与 DAO 可以采用不同策略。例如 API 在排障期打印有限长度,DAO 默认不打印大结果,缓存调用只保留足够识别 key 和结果的片段。

需要注意,当前实现是在对象完整序列化之后再截取字符串。它能减少最终日志体积,却不能避免大对象序列化本身的 CPU 和内存成本;截断后的 JSON 也可能不是合法完整 JSON。因此超大列表、文件内容和二进制对象更应该在序列化前按字段或类型忽略,而不是只依赖长度。

五、忽略字段、忽略类型与脱敏应该分开

PrintControl 提供两类忽略规则:

private String ignoreFieldName;
private String ignoreFieldClass;

AspectLogProperties 还维护全局 DesensitizeRule:

private List<DesensitizeRule> globalDesensitizeControls;

三者分别解决不同问题:

  • 密码、私钥、Token:按字段直接不输出;
  • 文件、请求对象等大类型:按类型忽略;
  • 手机号、身份证等仍有排障价值的数据:保留部分字符并脱敏。

日志安全的原则不是“所有内容都打星号”,而是先做数据最小化,再决定哪些字段需要有限展示。

规则按字段名称或类型匹配,字段改名、同名异义和嵌套对象变化都可能造成漏网。因此配置存在不代表治理完成,仍应通过请求、返回值、集合和异常场景的测试验证。

六、日志序列化失败为什么不能拖垮业务

入参或返回值可能包含 FastJson2 无法正常处理的对象。BaseAspectLogger 在 fetchParam 和 fetchResult 中捕获序列化异常:

try {
return FastJson.obj2Json(...);
} catch (Exception e) {
log.error("fetchResult error", e);
return "{\\"return\\":\\"serialize error\\"}";
}

准确的源码行为是:记录序列化失败并用错误摘要占位,避免为了写日志改变主业务的成功或失败结果。

这体现了基础设施的降级原则:可观测性很重要,但日志组件不应成为业务接口的新故障源。

七、鉴权或校验提前失败时,日志会不会消失

切面处理器链通常采用 fail-fast:参数校验、认证或限流失败后,不再执行后续业务处理器。

如果日志处理器排在链尾,最直接的实现会导致“成功请求有日志,拒绝请求没日志”。MetaLite 的 AspectHandlerChain 在前置处理失败时,会从链尾寻找 BaseAspectLogger 并补执行日志前置逻辑,然后返回失败响应。

这样认证失败、限流和参数错误仍能获得基本调用上下文。

需要区分的是,前置日志补偿不代表所有处理器都会继续执行,也不代表错误响应一定走成功日志的完整后置流程。异常路径最终还会进入 errorHandle,业务异常只记录消息,其他异常附带堆栈。

八、DAO 的 SQL 日志为什么需要单独开关

PrintControl 还有一个仅针对 DAO_CALL 的配置:

private boolean printSql;

SQL 或 Elasticsearch DSL 对慢查询排查很有价值,但它们也可能包含大量参数、敏感条件和长文本。把 SQL 输出与普通方法参数分开控制,可以让生产环境默认克制,在受控诊断期按需打开。

开关本身不解决慢 SQL、执行计划和数据库指标问题。DAO 日志能告诉我们“调用了什么”,数据库监控才能解释“为什么慢”。

九、统一日志方案仍有哪些边界

结合当前源码,至少要公开以下限制:

  • 日志长度是序列化后按字符截断,截断结果不保证是完整 JSON;
  • 大对象仍会先序列化,长度限制不是性能隔离;
  • 字段忽略和脱敏依赖配置命中,需要持续测试;
  • BaseAspectLogger 输出的是结构化文本,不是自动建好的日志平台;
  • 耗时是当前切面包围的方法耗时,异步返回 Future 时不等于任务最终完成耗时;
  • TraceId 是轻量日志关联,不包含 Span、父子关系和调用拓扑;
  • 开启完整入参、结果或 SQL 会增加泄露与存储风险。
  • 这些边界不削弱统一日志的价值,反而决定了它应该处在什么位置:负责建立一致、可配置、可降级的观测入口,不伪装成完整 APM。

    十、从散落的 log.info 到可治理的调用日志

    Spring Boot 接口日志真正难的不是打印一行 JSON,而是统一回答:

    • 哪些入口和调用需要观察;
    • 每条日志有哪些稳定字段;
    • 大对象输出到什么程度;
    • 哪些字段必须忽略或脱敏;
    • 失败、短路和序列化异常如何降级;
    • 日志耗时代表同步方法,还是异步任务最终结果。

    MetaLite 用 AspectTypeEnum、AspectHandlerChain、BaseAspectLogger、PrintControl 和 AspectLogProperties 把这些决策集中到工程基座中。业务代码因此少写日志模板,团队也获得了一套可以统一审计和演进的规则。


    框架简介
    MetaLite 是面向企业生产环境的新一代 Java 微服务技术底座。系列文章重点分享代码背后的设计思路、技术取舍与工程实践。

    源码基线
    JDK 21、Spring Boot 3.2.9、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.3,具体组件版本以项目 backend-bom 为准。

    作者简介
    15 年 Spring 体系企业级开发经验,专注于 Java 微服务架构、工程治理与生产实践。

    持续更新
    MetaLite 系列内容将持续更新,围绕核心设计、源码链路、技术取舍与生产实践展开。欢迎关注作者,及时获取后续内容。

    在线演示
    演示地址: https://admin.metalite.top/
    演示账号: guess
    演示密码: admin@2026

    赞(0)
    未经允许不得转载:171主机测评 » Spring-AOP接口日志怎么统一-入参返回值耗时与脱敏
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址