日志组件
框架提供了一套统一的日志组件体系,封装在 host.springboot.framework3.core.logging 包下,帮助你:
- ✅ 使用统一格式输出业务日志与异常信息
- ✅ 自动格式化
ApplicationException,打印 errorCode / errorMessage / userTip - ✅ 为 RPC 调用追加请求参数与响应结果,便于排查问题
- ✅ 在组件类中通过
logInstance()一键获得带标签的日志器
适用场景:业务服务日志、RPC 调用日志、网关/中间层日志等。
核心结构
主要类与接口:
LoggingComponent:组件日志接口(通常由业务类实现)LoggingComponentProvider:在组件中使用的日志 API(trace/debug/info/warn/error)LoggingExecutor:获取日志实例的工厂LoggingProvider:通用日志提供器,面向底层封装场景BaseLogging:统一实现日志格式组装LoggingImpl:默认日志实现LoggingRpcExecutor:RPC 日志扩展实现
日志格式
BaseLogging 中会将日志格式化为:
- 普通日志:
[logTag] logDetailTag [k1: v1, k2: v2, ...]
- 当
throwable为ApplicationException时:[logTag] logDetailTag [k1: v1, ..., errorCode: xxx, errorMessage: xxx, userTip: xxx]
这样在一个日志行里就能同时看到:业务标签、详细标签、关键字段以及统一异常信息。
在组件中使用日志(推荐方式)
第一步:实现 LoggingComponent
import host.springboot.framework3.core.logging.LoggingComponent;
import host.springboot.framework3.core.logging.LoggingComponentProvider;
@Service
public class OrderService implements LoggingComponent {
@Override
public String logTag() {
// 建议使用模块级别 Tag,便于过滤
return "ORDER";
}
// 获取日志实例(懒加载)
private LoggingComponentProvider logger() {
return this.logInstance();
}
public void createOrder(CreateOrderRequest request) {
logger().info("接收创建订单请求", Pair.of("userId", request.getUserId()));
// ... 业务逻辑
logger().debug("订单创建成功", Pair.of("orderId", orderId));
}
}说明:
logTag():组件级别标签,例如ORDER、USER、PAYMENT等,用于快速搜索日志。logInstance():由LoggingComponent默认实现,内部通过LoggingExecutor.instance(this)创建LoggingComponentProvider。info/debug/warn/error/trace:均有多种重载,可带Throwable及多对key/value附加信息。
常用调用方式示例
// 不带附加信息
logger().info("开始处理用户下单请求");
// 带单个附加字段
logger().debug("加载用户信息", Pair.of("userId", userId));
// 带异常(例如调用下游失败)
try {
paymentClient.pay(order);
} catch (Exception e) {
logger().error("调用支付服务失败", e, Pair.of("orderId", orderId));
}在非组件类中使用日志
如果你在静态工具类或非 Spring 组件中,需要手动创建日志实例,可以使用 LoggingExecutor.instance():
import host.springboot.framework3.core.logging.LoggingExecutor;
import host.springboot.framework3.core.logging.LoggingProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImportTaskRunner {
private static final Logger LOG = LoggerFactory.getLogger(ImportTaskRunner.class);
private static final LoggingProvider LOGGING = LoggingExecutor.instance();
public void run() {
LOGGING.info(LOG, "IMPORT", "开始导入任务");
try {
// do import
LOGGING.info(LOG, "IMPORT", "导入成功", Pair.of("count", 100));
} catch (Exception e) {
LOGGING.error(LOG, "IMPORT", "导入失败", e, Pair.of("step", "parse"));
}
}
}要点:
- 手工维护一个
Logger(SLF4J 原生),再配合LoggingProvider输出统一格式日志。 - 适合框架内部、工具类、非 Spring Bean 场景。
RPC 调用日志
对于 RPC 场景,建议使用 LoggingExecutor.rpc(param, response),自动将请求参数与响应结果拼入日志中:
import host.springboot.framework3.core.logging.LoggingExecutor;
import host.springboot.framework3.core.logging.LoggingRpcExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserRpcClient {
private static final Logger LOG = LoggerFactory.getLogger(UserRpcClient.class);
public UserDTO getUser(Long userId) {
UserQueryRequest request = new UserQueryRequest(userId);
UserDTO response = null;
LoggingRpcExecutor rpcLogger = LoggingExecutor.rpc(request, null);
try {
// 调用远程服务
response = remoteClient.getUser(request);
rpcLogger = LoggingExecutor.rpc(request, response);
rpcLogger.info(LOG, "USER_RPC", "调用用户服务成功");
} catch (Exception e) {
rpcLogger.error(LOG, "USER_RPC", "调用用户服务异常", e);
throw e;
}
return response;
}
}LoggingRpcExecutor 会在日志中自动追加:
param: RPC 请求参数response: RPC 响应数据
示例日志结构:
[USER_RPC] 调用用户服务成功 [param: {...}, response: {...}]与异常体系的联动
当传入的 Throwable 为框架的 ApplicationException 时,BaseLogging 会自动拆解并格式化:
logger().error("业务处理失败", new ApplicationException(
ErrorCodeEnum.USER_ACCOUNT_NOT_EXIST,
"用户不存在"
));实际输出包含:
[ORDER] 业务处理失败 [errorCode: A0201, errorMessage: 用户账户不存在, userTip: 用户不存在]这样,无论是业务异常还是系统异常,日志中都能快速定位:
- 哪个模块(
logTag) - 哪个步骤(
logDetailTag) - 哪个错误码与用户提示
最佳实践
✅ 推荐做法
- 为每个组件实现
LoggingComponent并返回稳定的logTag - 在关键路径上使用
info+ 关键字段(订单号、用户ID、TraceId 等) - 在异常分支使用
error,并传入Throwable与业务关键字段 - RPC 调用使用
LoggingExecutor.rpc(param, response)记录请求与响应
❌ 不推荐做法
- 直接使用
System.out.println打印日志 - 混用多种格式,导致搜索困难
- 只打印异常栈、不打印业务上下文(userId、orderId 等)
常见问题 FAQ
Q: 必须实现 LoggingComponent 吗?
A: 不是必须。业务类推荐实现 LoggingComponent 以获得更简洁的 API;工具类或框架内部可以直接使用 LoggingExecutor.instance() 与 LoggingProvider。
Q: 日志级别如何控制?
A: LoggingComponent 中的 logLevel() 会根据当前 Logger 的配置动态返回最细日志级别;你只需按语义选择 trace/debug/info/warn/error,具体是否输出由日志配置(如 logback)控制。
Q: 可否自定义日志格式?
A: 当前版本日志格式由 BaseLogging 统一实现。如需深度自定义,可在你的项目中封装自己的 LoggingProvider 实现,复用 BaseLogging 的能力或参考其实现进行扩展。
