日志
Laravel 用
Log::info(...)全局函数;Java 世界有标准日志门面 SLF4J,Spring Boot 默认集成 Logback。你用@Slf4j(Lombok)或LoggerFactory拿到 Logger,然后log.info(...)。
最小示例
java
@Service
public class PostService {
private static final org.slf4j.Logger log =
org.slf4j.LoggerFactory.getLogger(PostService.class);
public Post create(PostDto dto) {
log.info("创建帖子: {}", dto.getTitle());
return repository.save(dto.toEntity());
}
}更简洁的姿势:Lombok 的 @Slf4j,直接有 log 变量:
java
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class PostService {
public Post create(PostDto dto) {
log.info("创建帖子: {}", dto.getTitle()); // {} 占位符,等价于字符串拼接
log.warn("库存不足: {}", dto.getId());
return repository.save(dto.toEntity());
}
}为什么用 {} 占位而不是 +?
log.info("创建帖子: {}", title) 只有真正要输出时才拼接字符串,性能好;"..." + title 无论如何都会拼接。占位符是 Java 日志的最佳实践。
日志级别
| 级别 | 用途 | Laravel 对应 |
|---|---|---|
TRACE | 最细粒度 | Log::debug(更细) |
DEBUG | 调试 | Log::debug |
INFO | 常规信息 | Log::info |
WARN | 潜在问题 | Log::warning |
ERROR | 错误 | Log::error |
配置:application.yml
yaml
logging:
level:
root: info # 全局级别
com.example.demo.post: debug # 指定包更细
file:
name: logs/app.log # 输出到文件(默认只打控制台)
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"类比 Laravel
Laravel 的 LOG_CHANNEL、LOG_LEVEL 在 .env;Spring Boot 在 application.yml 的 logging.*,同样支持按环境覆盖。
日志里的上下文信息
Laravel 有 Log::withContext([...]);Spring Boot 3.x 用 MDC:
java
import org.slf4j.MDC;
// 入口(Filter/Interceptor)里写入
MDC.put("requestId", requestId);
MDC.put("userId", String.valueOf(userId));
// 整个请求生命周期内,任何日志自动带上这两个字段
log.info("查询帖子");
// 请求结束时清理(记得在 finally 里)
MDC.clear();配合日志 pattern 输出:
yaml
logging:
pattern:
console: "%d{...} [%X{requestId}] [%X{userId}] %-5level %logger{36} - %msg%n"TIP
结合 请求生命周期 里讲的 Filter,给每个请求生成 requestId 写入 MDC,是排查分布式问题的最强利器 —— 一条请求链路的所有日志都能串起来。
生产实践
| 建议 | 原因 |
|---|---|
生产级别设为 info | debug 会刷爆磁盘 |
业务关键节点打 info,异常打 error(带堆栈) | 可追溯、可告警 |
| 别打敏感信息(密码、token) | 日志会进日志系统,等于泄露 |
| 日志文件滚动 | logging.file.name 默认 10MB 滚动,够用 |
| 结构化日志(JSON) | 云平台/ELK 采集时更友好,可用 logstash-logback-encoder |
常见坑
- 忘写堆栈:
log.error("出错了 {}", e.getMessage())只打了消息没有堆栈。要log.error("出错了", e),把异常对象传进去。 - 日志没级别:全用
log.info,生产排障困难。 - 用 System.out:
System.out.println不走日志框架,没级别、没时间戳、性能差。永远用 Logger。