大家好,我是小悟。
日志系统是什么?
想象一下,你的程序是个有点健忘的程序员同事(没错,就是那个总说"我本地是好的"的家伙)。日志系统就是给他配的贴身小秘书,每天拿着小本本记录:
- 🕒 几点几分干了啥(时间戳)
- 🤔 心里想什么(调试信息)
- 😊 今天工作顺利吗(INFO信息)
- 😨 卧槽出问题了(ERROR信息)
- 🔥 救火啊要炸了(FATAL信息)
没有日志系统?那就好比程序生病了,你问他"哪不舒服?",他只会回答"我挂了"。有了日志,他就能详细告诉你:"昨天下午3点,我在处理用户订单时,因为数据库连接断了,导致..."
好了,废话不多说,让我们给SpringBoot程序配个"贴心小秘书"!
第1步:SpringBoot的"天生丽质"
SpringBoot这家伙很贴心,已经内置了日志系统!就像你买手机,相机APP已经预装好了。
kotlin
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LogDemoController {
// 创建日志记录器,就像给这个类配了个专属记者
private static final Logger logger = LoggerFactory.getLogger(LogDemoController.class);
@GetMapping("/hello")
public String sayHello() {
// 不同的日志级别,就像不同的说话语气
logger.trace("这是最详细的跟踪信息 - 连我呼吸都要记录");
logger.debug("调试信息 - 我在想:用户到底点了啥按钮?");
logger.info("普通信息 - 用户访问了hello接口,一切正常");
logger.warn("警告信息 - 内存有点高,像吃了太多内存的胖子");
logger.error("错误信息 - 数据库连接失败!快来人啊!");
// 还可以带参数,像填空一样
String userName = "码农小张";
int userId = 123;
logger.info("用户 {} (ID: {}) 登录成功", userName, userId);
return "Hello World! 快去控制台看日志吧!";
}
@GetMapping("/oops")
public String makeMistake() {
try {
// 故意制造一个错误
int result = 10 / 0;
return "这行代码永远执行不到";
} catch (Exception e) {
// 记录异常信息,参数e会自动打印堆栈
logger.error("数学老师没教好,除零错误了!", e);
return "哎呀,出错了!详情请看日志";
}
}
}
第2步:配置文件 - 给秘书定规矩
在application.yml(或application.properties)中配置。这是告诉小秘书:"哪些话要记,哪些话不用记,记在哪里..."
yaml
# application.yml - 日志系统的"规章制度"
# 第一部分:全局日志级别设置
logging:
level:
root: INFO # 根日志级别:INFO及以上才记录
com.example.demo: DEBUG # 我们自己的包可以详细点
org.springframework.web: WARN # Spring的web包,只记录警告及以上
# 第二部分:输出到哪里(控制台和文件都记)
file:
name: logs/myapp.log # 日志文件路径
max-size: 10MB # 单个文件最大10MB,超过就切分
max-history: 30 # 保留最近30天的日志
# 第三部分:日志格式 - 给小秘书的"记录模板"
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 第四部分:按包或类单独设置(精细化管理)
group:
web: org.springframework.web, org.springframework.security
app: com.example.demo.controller, com.example.demo.service
level:
web: INFO
app: DEBUG
# 如果你想用logback的详细配置(高级玩法)
# 在resources目录下创建logback-spring.xml
第3步:高级玩法 - 自定义日志配置
在resources目录下创建logback-spring.xml,这是给小秘书的详细工作手册:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- 控制台输出 - 给开发人员看的 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 彩色日志,让控制台不再单调! -->
<pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按天滚动的文件输出 - 给运维人员看的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<!-- 每天一个文件,最多保存30天 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 错误日志单独文件 - 重要的事情说三遍,重要的错误单独记 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>90</maxHistory> <!-- 错误日志保留更久 -->
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 不同环境的配置 -->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.example" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
</springProfile>
<springProfile name="prod">
<root level="WARN">
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
<logger name="com.example" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
<appender-ref ref="ERROR_FILE"/>
</logger>
</springProfile>
</configuration>
第4步:AOP实现方法日志 - 自动记录每个方法
ini
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MethodLogAspect {
private static final Logger logger = LoggerFactory.getLogger(MethodLogAspect.class);
/**
* 自动记录Controller层每个方法的执行情况
* 就像给每个方法配了个贴身观察员
*/
@Around("execution(* com.example.demo.controller..*.*(..))")
public Object logControllerMethods(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
logger.info("方法开始执行: {},参数: {}", methodName, args);
try {
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
logger.info("方法执行成功: {},耗时: {}ms,返回值: {}",
methodName, executionTime, result);
return result;
} catch (Exception e) {
long executionTime = System.currentTimeMillis() - startTime;
logger.error("方法执行失败: {},耗时: {}ms,异常: {}",
methodName, executionTime, e.getMessage(), e);
throw e;
}
}
}
第5步:日志工具类 - 让日志更智能
typescript
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;
/**
* 日志工具类 - 给日志加点"智能"
*/
public class LogUtil {
/**
* 性能监控 - 记录代码块执行时间
*/
public static <T> T monitorPerformance(Logger logger, String taskName,
Supplier<T> task) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
logger.info("开始执行: {}", taskName);
try {
T result = task.get();
stopWatch.stop();
logger.info("执行完成: {},耗时: {}ms",
taskName, stopWatch.getTotalTimeMillis());
return result;
} catch (Exception e) {
stopWatch.stop();
logger.error("执行失败: {},耗时: {}ms,错误: {}",
taskName, stopWatch.getTotalTimeMillis(), e.getMessage(), e);
throw e;
}
}
/**
* 业务日志 - 记录关键业务操作
*/
public static void businessLog(Logger logger, String operation,
String userId, Object... details) {
// 这里可以扩展,比如记录到数据库或发送到消息队列
logger.info("业务操作 - 用户: {}, 操作: {}, 详情: {}",
userId, operation, details);
}
// 使用示例
public void exampleUsage() {
Logger logger = LoggerFactory.getLogger(this.getClass());
// 监控性能
String result = monitorPerformance(logger, "计算用户报表", () -> {
// 模拟耗时操作
Thread.sleep(1000);
return "报表数据";
});
// 记录业务日志
businessLog(logger, "用户登录", "user123", "IP: 192.168.1.1", "设备: Chrome");
}
}
第6步:与ELK等日志系统集成(高级玩法)
makefile
# application-prod.yml - 生产环境配置
logging:
# 输出JSON格式,方便ELK采集
pattern:
console: '{"timestamp":"%d{yyyy-MM-dd HH:mm:ss.SSS}", "level":"%level", "thread":"%thread", "logger":"%logger", "message":"%msg", "exception":"%ex"}'
# Logstash收集配置(如果需要)
logstash:
enabled: true
host: localhost
port: 5000
总结:日志系统的"生存指南"
经过这一番折腾,我们的SpringBoot程序终于有了一个称职的贴身秘书。让我们总结一下日志系统的几个关键点:
为什么要用日志系统?
- 故障排查:程序说"我挂了" → 日志说"3点15分数据库连接超时"
- 性能分析:用户说"好卡" → 日志说"这个接口平均响应2.3秒"
- 行为追踪:老板说"谁干的" → 日志说"用户admin在10点修改了配置"
- 数据统计:产品说"有多少人用" → 日志说"今天有15234次访问"
最佳实践:
- 级别要分明:DEBUG用于开发,INFO用于日常,ERROR用于异常
- 信息要详细:时间、线程、级别、类名、消息、异常,一个都不能少
- 性能要注意:日志IO是性能杀手,异步日志是个好选择
- 安全要牢记:密码、token等敏感信息别往日志里写
常见坑点:
- 日志太多:把DEBUG级别放到生产环境,日志文件瞬间爆炸
- 日志太少:出问题时,日志里只有"出错了",没有"为啥错"
- 格式混乱:今天用JSON,明天用文本,后天ELK不认了
- 忘记归档:日志文件把磁盘写满了,程序真挂了
最后:
给你的日志系统起个名字吧!比如叫"小日志"、"程序记录仪"、"代码摄像头"。毕竟,它要陪你度过无数个排查BUG的不眠之夜 ,是你在茫茫代码海洋中的灯塔 ,是你在程序崩溃时的救命稻草。
现在,去给你的SpringBoot程序配个"贴心小秘书"吧!当程序再次崩溃时,至少有人(日志)能告诉你:"亲,这次是因为..."

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海