从P4小白到P7专家都是怎么打日志的?一文揭秘大厂日志规范
从P4小白到P7专家都是怎么打日志的?一文揭秘大厂日志规范
大家好,今天我们来聊聊一个让无数程序员又爱又恨的话题——日志打印。
你是否也遇到过这些场景:
- 线上出问题了,翻遍日志却找不到关键信息
- 日志文件几个G,根本没法看
- 用户投诉了,但日志里没有用户ID,无法定位问题
- 开发环境日志详细,生产环境却一片空白
- 想查问题,却被海量无用日志淹没了
别慌!今天我就把这套从P4小白到P7专家的日志打印规范全掏出来,手把手教你写出高质量的日志!
为什么日志这么重要?
在开始正题之前,先聊聊为什么日志如此重要:
- 问题排查:90%的线上问题都靠日志来定位
- 系统监控:通过日志可以监控系统运行状态
- 安全审计:记录用户操作,防范安全风险
- 性能分析:通过日志分析系统性能瓶颈
- 合规要求:很多行业对日志有明确要求
P4小白阶段:能跑就行
刚入行的程序员通常是这样的:
// P4小白写法 - 各种问题
@RestController
public class OrderController {
@PostMapping("/api/orders")
public Result createOrder(@RequestBody OrderRequest request) {
System.out.println("开始创建订单");
try {
Order order = orderService.create(request);
System.out.println("订单创建成功: " + order.getId());
return Result.success(order);
} catch (Exception e) {
System.out.println("订单创建失败: " + e.getMessage());
return Result.error("创建失败");
}
}
}
问题分析:
- 使用System.out.println,无法控制级别
- 没有结构化信息,难以解析
- 缺少关键上下文信息
- 生产环境可能看不到日志
P5进阶阶段:开始用日志框架
有经验的程序员会使用专业的日志框架:
// P5进阶写法 - 使用SLF4J
@RestController
@Slf4j
public class OrderController {
@PostMapping("/api/orders")
public Result createOrder(@RequestBody OrderRequest request) {
log.info("开始创建订单,用户ID: {}, 商品ID: {}",
request.getUserId(), request.getProductId());
try {
Order order = orderService.create(request);
log.info("订单创建成功,订单ID: {}", order.getId());
return Result.success(order);
} catch (Exception e) {
log.error("订单创建失败,用户ID: {}, 错误信息: {}",
request.getUserId(), e.getMessage());
return Result.error("创建失败");
}
}
}
改进点:
- 使用专业日志框架
- 添加了关键业务信息
- 区分了不同日志级别
P6高手阶段:规范化的日志体系
高级工程师会建立完整的日志规范:
// P6高手写法 - 规范化日志
@RestController
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private TraceContext traceContext;
@PostMapping("/api/orders")
public Result createOrder(@RequestBody @Valid OrderRequest request) {
// 生成追踪ID
String traceId = traceContext.getTraceId();
// 记录入口日志
log.info("[{}] [ORDER_CREATE] 开始创建订单 | userId={}, productId={}, quantity={}",
traceId, request.getUserId(), request.getProductId(), request.getQuantity());
long startTime = System.currentTimeMillis();
try {
// 业务处理
Order order = orderService.create(request);
// 记录成功日志
long cost = System.currentTimeMillis() - startTime;
log.info("[{}] [ORDER_CREATE] 订单创建成功 | orderId={}, cost={}ms",
traceId, order.getId(), cost);
return Result.success(order);
} catch (BusinessException e) {
// 业务异常
long cost = System.currentTimeMillis() - startTime;
log.warn("[{}] [ORDER_CREATE] 业务异常 | userId={}, code={}, message={}, cost={}ms",
traceId, request.getUserId(), e.getCode(), e.getMessage(), cost);
return Result.error(e.getCode(), e.getMessage());
} catch (Exception e) {
// 系统异常
long cost = System.currentTimeMillis() - startTime;
log.error("[{}] [ORDER_CREATE] 系统异常 | userId={}, error={}, cost={}ms",
traceId, request.getUserId(), e.getMessage(), cost, e);
return Result.error("系统繁忙,请稍后重试");
}
}
}
规范要点:
- 统一格式:[追踪ID] [业务标识] 日志内容
- 关键信息:用户ID、订单ID、耗时等
- 异常处理:区分业务异常和系统异常
- 性能监控:记录方法执行耗时
P7专家阶段:全链路追踪日志
架构师级别的日志体系会考虑全链路追踪:
// P7专家写法 - 全链路追踪
@RestController
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private TraceService traceService;
@PostMapping("/api/orders")
@Trace(operation = "订单创建", module = "订单系统")
public Result createOrder(@RequestBody @Valid OrderRequest request) {
// 获取追踪上下文
TraceContext context = traceService.getCurrentContext();
// 记录入口日志
log.info("[{}] [ORDER_CREATE] 接收到订单创建请求 | userId={}, clientIp={}, userAgent={}",
context.getTraceId(),
request.getUserId(),
context.getClientIp(),
context.getUserAgent());
// 参数校验日志
if (request.getQuantity() <= 0) {
log.warn("[{}] [ORDER_CREATE] 参数校验失败 | userId={}, quantity={}",
context.getTraceId(), request.getUserId(), request.getQuantity());
return Result.error("商品数量必须大于0");
}
long startTime = System.currentTimeMillis();
long dbStartTime, dbEndTime;
try {
// 调用库存服务前
log.debug("[{}] [INVENTORY_CHECK] 开始检查库存 | productId={}, quantity={}",
context.getTraceId(), request.getProductId(), request.getQuantity());
dbStartTime = System.currentTimeMillis();
boolean hasStock = inventoryService.checkStock(request.getProductId(), request.getQuantity());
dbEndTime = System.currentTimeMillis();
log.debug("[{}] [INVENTORY_CHECK] 库存检查完成 | productId={}, hasStock={}, dbCost={}ms",
context.getTraceId(), request.getProductId(), hasStock, dbEndTime - dbStartTime);
if (!hasStock) {
log.warn("[{}] [ORDER_CREATE] 库存不足 | userId={}, productId={}, quantity={}",
context.getTraceId(), request.getUserId(), request.getProductId(), request.getQuantity());
return Result.error("库存不足");
}
// 创建订单
dbStartTime = System.currentTimeMillis();
Order order = orderService.create(request);
dbEndTime = System.currentTimeMillis();
long totalCost = System.currentTimeMillis() - startTime;
log.info("[{}] [ORDER_CREATE] 订单创建成功 | userId={}, orderId={}, totalCost={}ms, dbCost={}ms",
context.getTraceId(), request.getUserId(), order.getId(), totalCost, dbEndTime - dbStartTime);
// 异步发送消息
messageService.sendOrderCreatedEvent(order);
return Result.success(order);
} catch (BusinessException e) {
long cost = System.currentTimeMillis() - startTime;
log.warn("[{}] [ORDER_CREATE] 业务异常 | userId={}, code={}, message={}, cost={}ms",
context.getTraceId(), request.getUserId(), e.getCode(), e.getMessage(), cost);
return Result.error(e.getCode(), e.getMessage());
} catch (Exception e) {
long cost = System.currentTimeMillis() - startTime;
log.error("[{}] [ORDER_CREATE] 系统异常 | userId={}, error={}, cost={}ms | stackTrace={}",
context.getTraceId(), request.getUserId(), e.getMessage(), cost,
ExceptionUtils.getStackTrace(e));
return Result.error("系统繁忙,请稍后重试");
}
}
}
专家级特性:
- 全链路追踪:统一的追踪ID贯穿整个调用链
- 详细监控:记录每个关键步骤的耗时
- 分类日志:不同业务模块使用不同标识
- 异常堆栈:关键异常记录完整堆栈信息
10个日志打印黄金法则
1. 使用占位符,避免字符串拼接
// 错误写法
log.info("用户" + userId + "的订单" + orderId + "创建成功");
// 正确写法
log.info("用户{}的订单{}创建成功", userId, orderId);
2. 合理使用日志级别
// ERROR - 系统错误、数据丢失等严重问题
log.error("数据库连接失败", e);
// WARN - 可预期的异常情况
log.warn("用户{}尝试访问无权限资源", userId);
// INFO - 重要的业务流程节点
log.info("订单{}支付成功", orderId);
// DEBUG - 详细的调试信息
log.debug("方法执行参数: {}", params);
3. 添加关键业务上下文
// 包含追踪ID、用户ID、业务ID等关键信息
log.info("[{}] [USER_LOGIN] 用户登录成功 | userId={}, loginTime={}",
traceId, userId, LocalDateTime.now());
4. 记录方法执行耗时
long startTime = System.currentTimeMillis();
try {
// 业务逻辑
doSomething();
} finally {
long cost = System.currentTimeMillis() - startTime;
log.info("方法执行完成,耗时: {}ms", cost);
}
5. 异常日志要记录堆栈
try {
// 业务逻辑
} catch (Exception e) {
log.error("业务处理异常 | userId={}", userId, e);
}
6. 避免在循环中打印日志
// 错误写法
for (Item item : items) {
log.info("处理商品: {}", item.getId()); // 性能问题
}
// 正确写法
log.info("开始处理{}个商品", items.size());
// 批量处理逻辑
log.info("商品处理完成");
7. 敏感信息要脱敏
// 错误写法
log.info("用户手机号: {}", phoneNumber);
// 正确写法
log.info("用户手机号: {}", maskPhone(phoneNumber));
private String maskPhone(String phone) {
if (phone == null || phone.length() < 7) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(7);
}
8. 日志格式要统一
// 推荐格式:[追踪ID] [业务标识] 日志内容 | key=value
log.info("[{}] [ORDER_PAY] 支付成功 | userId={}, orderId={}, amount={}",
traceId, userId, orderId, amount);
9. 避免重复日志
// 错误写法 - 在多层调用中重复记录相同信息
public void methodA() {
log.info("进入方法A");
methodB();
log.info("退出方法A");
}
public void methodB() {
log.info("进入方法B"); // 重复日志
// 业务逻辑
log.info("退出方法B"); // 重复日志
}
10. 生产环境日志级别要合理
# logback-spring.xml
<configuration>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</springProfile>
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
</configuration>
实战案例:某电商平台支付日志优化
优化前:混乱的日志
@Service
public class PaymentService {
public void processPayment(PaymentRequest request) {
System.out.println("开始支付处理");
try {
// 调用第三方支付
String result = thirdPartyPayService.pay(request.getAmount(), request.getCardNo());
System.out.println("支付结果: " + result);
} catch (Exception e) {
System.out.println("支付失败: " + e.getMessage());
}
}
}
优化后:规范的日志
@Service
@Slf4j
public class PaymentService {
@Autowired
private TraceService traceService;
@Autowired
private ThirdPartyPayService thirdPartyPayService;
@Trace(operation = "支付处理", module = "支付中心")
public PaymentResult processPayment(PaymentRequest request) {
TraceContext context = traceService.getCurrentContext();
// 入口日志
log.info("[{}] [PAYMENT_PROCESS] 开始支付处理 | userId={}, amount={}, paymentMethod={}",
context.getTraceId(), request.getUserId(), request.getAmount(), request.getPaymentMethod());
long startTime = System.currentTimeMillis();
try {
// 参数校验
validatePaymentRequest(request);
// 调用第三方支付前日志
log.debug("[{}] [THIRD_PARTY_PAY] 调用第三方支付 | amount={}, cardNo={}",
context.getTraceId(), request.getAmount(), maskCardNo(request.getCardNo()));
long payStartTime = System.currentTimeMillis();
ThirdPartyPayResult payResult = thirdPartyPayService.pay(request);
long payCost = System.currentTimeMillis() - payStartTime;
// 第三方支付结果日志
log.debug("[{}] [THIRD_PARTY_PAY] 第三方支付返回 | result={}, cost={}ms",
context.getTraceId(), payResult.isSuccess(), payCost);
if (!payResult.isSuccess()) {
log.warn("[{}] [PAYMENT_PROCESS] 第三方支付失败 | userId={}, errorCode={}, errorMsg={}",
context.getTraceId(), request.getUserId(),
payResult.getErrorCode(), payResult.getErrorMsg());
return PaymentResult.failure(payResult.getErrorCode(), payResult.getErrorMsg());
}
// 保存支付记录
long saveStartTime = System.currentTimeMillis();
PaymentRecord record = savePaymentRecord(request, payResult);
long saveCost = System.currentTimeMillis() - saveStartTime;
long totalCost = System.currentTimeMillis() - startTime;
log.info("[{}] [PAYMENT_PROCESS] 支付处理成功 | userId={}, paymentId={}, totalCost={}ms, payCost={}ms, saveCost={}ms",
context.getTraceId(), request.getUserId(), record.getId(),
totalCost, payCost, saveCost);
return PaymentResult.success(record);
} catch (ValidationException e) {
long cost = System.currentTimeMillis() - startTime;
log.warn("[{}] [PAYMENT_PROCESS] 参数校验失败 | userId={}, errors={}, cost={}ms",
context.getTraceId(), request.getUserId(), e.getErrors(), cost);
return PaymentResult.failure("PARAM_ERROR", e.getMessage());
} catch (BusinessException e) {
long cost = System.currentTimeMillis() - startTime;
log.warn("[{}] [PAYMENT_PROCESS] 业务异常 | userId={}, code={}, message={}, cost={}ms",
context.getTraceId(), request.getUserId(), e.getCode(), e.getMessage(), cost);
return PaymentResult.failure(e.getCode(), e.getMessage());
} catch (Exception e) {
long cost = System.currentTimeMillis() - startTime;
log.error("[{}] [PAYMENT_PROCESS] 系统异常 | userId={}, error={}, cost={}ms",
context.getTraceId(), request.getUserId(), e.getMessage(), cost, e);
return PaymentResult.failure("SYSTEM_ERROR", "系统繁忙");
}
}
private String maskCardNo(String cardNo) {
if (cardNo == null || cardNo.length() < 8) {
return "****";
}
return cardNo.substring(0, 4) + "****" + cardNo.substring(cardNo.length() - 4);
}
}
5个核心监控指标,让你的日志发挥最大价值
1. 错误率监控
@Component
public class LogMetricsCollector {
private final MeterRegistry meterRegistry;
public void recordError(String module, String operation) {
Counter.builder("log.errors")
.description("日志错误统计")
.tag("module", module)
.tag("operation", operation)
.register(meterRegistry)
.increment();
}
}
2. 响应时间监控
public void recordResponseTime(String module, String operation, long durationMs) {
Timer.builder("log.response.time")
.description("接口响应时间")
.tag("module", module)
.tag("operation", operation)
.register(meterRegistry)
.record(durationMs, TimeUnit.MILLISECONDS);
}
3. 日志量监控
public void recordLogCount(String level, String module) {
Counter.builder("log.count")
.description("日志数量统计")
.tag("level", level)
.tag("module", module)
.register(meterRegistry)
.increment();
}
4. 业务指标监控
public void recordBusinessMetric(String metricName, String businessType, long value) {
Gauge.builder("business." + metricName)
.description("业务指标")
.tag("type", businessType)
.register(meterRegistry, value);
}
5. 异常类型监控
public void recordException(String exceptionType, String module) {
Counter.builder("log.exceptions")
.description("异常类型统计")
.tag("type", exceptionType)
.tag("module", module)
.register(meterRegistry)
.increment();
}
结语
掌握日志打印规范,核心不是记住所有规则,而是理解每个规范背后的业务价值和技术考量:
- P4小白:能打印日志就行
- P5进阶:使用专业日志框架
- P6高手:建立规范化日志体系
- P7专家:构建全链路追踪日志
记住:好的日志不是一次到位的,而是在实践中不断优化的。从满足基本需求开始,根据实际情况逐步完善,最终你也能构建出高质量的日志体系!
日志规范只是系统稳定运行的一部分,后续我们还会分享更多监控和优化的实战技巧,记得关注我们的公众号"服务端技术精选"!
觉得这篇文章对你有帮助吗?欢迎点赞、在看、转发三连,你的支持是我们持续创作的最大动力!
服务端技术精选 | 专注分享实用的后端技术干货
标题:从P4小白到P7专家都是怎么打日志的?一文揭秘大厂日志规范
作者:jiangyi
地址:http://jiangyi.space/articles/2025/12/21/1766304291741.html
- 为什么日志这么重要?
- P4小白阶段:能跑就行
- P5进阶阶段:开始用日志框架
- P6高手阶段:规范化的日志体系
- P7专家阶段:全链路追踪日志
- 10个日志打印黄金法则
- 1. 使用占位符,避免字符串拼接
- 2. 合理使用日志级别
- 3. 添加关键业务上下文
- 4. 记录方法执行耗时
- 5. 异常日志要记录堆栈
- 6. 避免在循环中打印日志
- 7. 敏感信息要脱敏
- 8. 日志格式要统一
- 9. 避免重复日志
- 10. 生产环境日志级别要合理
- 实战案例:某电商平台支付日志优化
- 优化前:混乱的日志
- 优化后:规范的日志
- 5个核心监控指标,让你的日志发挥最大价值
- 1. 错误率监控
- 2. 响应时间监控
- 3. 日志量监控
- 4. 业务指标监控
- 5. 异常类型监控
- 结语
0 评论