Day36 - 1. 日志
1.1 作用:
跟输出语句一样,可以把程序在运行过程中的详细信息都打印在控制台上。
利用log日志还可以把这些详细信息保存到文件和数据库中。
🧠 理论理解:
日志的本质是程序运行状态的一种“记录手段”。它可以实时输出程序执行的详细信息,便于开发者调试错误、跟踪程序执行过程、定位问题。和简单的 System.out.println()
不同,日志可以按不同级别分类(比如 info、error)、可以定制输出格式,还支持输出到文件、数据库、远程服务器等多个媒介,从而实现专业化的运维监控。在我看来就是比如不当操作导致的bug能记录追踪。
🏢 企业实战理解:
在大厂(如阿里、字节跳动、Google 等),日志不仅是“打印”这么简单,而是系统级的监控手段:
-
会按模块、级别、节点统一采集。
-
日志会被采集到 ELK(Elasticsearch+Logstash+Kibana)、Prometheus 或字节自研的 APOLLO 等系统中,形成实时日志流,用于报警、追踪、甚至作为大数据分析的一部分。
-
例如字节跳动的在线广告推荐系统,日志会细粒度记录推荐链路的每一步执行细节,用来做A/B测试与效果归因。
1️⃣ 问题:请你详细说说 Java 日志框架的体系?它们之间的关系是什么?
✅ 答案:
Java 日志框架体系主要包含三个层次:
1️⃣ 日志门面(Facade)层:
-
代表:SLF4J、JCL(Jakarta Commons Logging)、Log4j2 API
-
作用:定义标准接口,屏蔽底层实现的差异,让上层代码不直接依赖某个具体实现。
2️⃣ 日志实现层:
-
代表:Logback、Log4j2 Core、JUL(Java Util Logging)
-
作用:真正负责日志的格式化、输出、轮转、持久化等操作。
3️⃣ 桥接层(Adapter):
-
比如:slf4j-log4j12、jul-to-slf4j
-
作用:打通门面和实现层,比如用 SLF4J + Logback 时,SLF4J 是门面,Logback 是实现。
🔎 它们的关系:
-
大型企业一般推荐 “门面 + 实现”分离。业务代码依赖 SLF4J(解耦),底层通过配置切换 Logback、Log4j2 等实现。这样即便未来要替换日志实现,也无需改动业务代码。
-
Spring Boot 默认:内部使用 SLF4J 门面 + Logback 实现。
场景题 1️⃣:
你在字节跳动负责一套实时推荐系统,突然监控发现日志写入速率异常飙升,磁盘 I/O 利用率接近 100%,服务响应时间显著上升。请问你会如何排查并解决这个问题?
✅ 答案:
1️⃣ 第一步:快速定位原因
-
登录服务器,查看磁盘空间和 I/O 情况,验证是否是日志文件导致磁盘占用。
-
用
lsof | grep log
查看哪些日志文件写入最频繁。 -
检查线上日志配置,重点排查是否误开启
DEBUG
/TRACE
级别。
2️⃣ 第二步:短期缓解措施
-
动态修改日志级别(Logback 支持热加载配置),将日志级别提高到
WARN
或ERROR
,立刻减少输出量。 -
判断日志是否采用异步输出,如果是同步日志,立即切换为
AsyncAppender
。 -
考虑将热点日志路径暂时挂载到独立磁盘,避免影响主业务盘。
3️⃣ 第三步:深度分析
-
查看最近代码变更,是否引入了新的日志点或循环输出场景。
-
检查是否有异常大对象序列化到日志(如完整的请求 JSON、堆栈等)。
-
结合监控(如 ELK、Prometheus)统计日志量的变化趋势,找出“爆炸点”。
4️⃣ 长期优化
-
增加日志采样机制,对高频请求日志采样输出(比如只打 1% 请求日志)。
-
对日志 SDK 做封装,加入日志内容大小限制和输出频率保护。
-
建立日志输出规范 + 发布审核机制,防止 DEBUG 日志上线。
🚩 总结:大厂线上日志事故不罕见,字节跳动、阿里巴巴都经历过“日志风暴”。最佳实践是:日志实时监控 + 动态调级 + 采样限流 三位一体,既满足调试需求,又保障系统稳定。
1.2 使用步骤:
不是java的,也不是自己写的,是第三方提供的代码,所以我们要导入jar包。
-
把第三方的代码导入到当前的项目当中
新建lib文件夹,把jar粘贴到lib文件夹当中,全选后右键点击选择add as a ....
检测导入成功:导入成功后jar包可以展开。在项目重构界面可以看到导入的内容。
-
把配置文件粘贴到src文件夹下。
-
在代码中获取日志对象。
-
调用方法打印日志。
🧠 理论理解:
因为日志不是 JDK 自带的功能(虽然 JDK 里也有 java.util.logging
,但应用场景比较弱),所以工业界常用的是第三方库,比如 Log4j、SLF4J、Logback。它们提供了专业的 API 和丰富的功能,需要通过导入 jar 包 + 配置文件集成到项目中,通常通过日志对象获取方法,调用 debug() / info() / error()
等方法打印日志。
🏢 企业实战理解:
-
大厂内部使用时会封装一层“日志SDK”,统一集成日志格式、异常链路ID、上下文信息(比如机器 IP、线程 ID、用户 ID)。
-
例如阿里巴巴内部会强制要求所有接口日志必须有 traceId 追踪号,方便定位一次调用的完整链路。
-
实际操作中很少“手动导包”,而是通过 Maven/Gradle 自动管理依赖,并结合 Spring Boot 的自动配置机制实现“开箱即用”。
2️⃣ 问题:日志级别 TRACE、DEBUG、INFO、WARN、ERROR 各自适合什么场景?为什么要分级?
✅ 答案:
-
TRACE:最细粒度的日志,适合用在开发调试阶段跟踪方法调用、循环内部状态变化,生产环境几乎不启用。
-
DEBUG:用于开发环境调试输出,记录接口请求参数、SQL 执行语句、缓存命中情况等。线上环境通常关闭,以避免日志膨胀。
-
INFO:业务流程正常日志,适合打印关键节点信息(如:服务启动、任务完成、支付成功等)。线上环境默认开启。
-
WARN:提示潜在问题,比如缓存未命中、接口响应时间超标等,但程序仍能正常运行。
-
ERROR:记录程序异常、关键故障,比如数据库连接失败、空指针异常。必须开启,且配合报警系统。
🚩 为什么要分级?
1️⃣ 减少无用日志污染,帮助开发者/运维精准定位问题。
2️⃣ 日志分级能动态调整策略,比如线上开启 INFO/WARN/ERROR,开发环境开启 DEBUG/TRACE,提高开发效率。
3️⃣ 对接日志系统时,支持按级别筛选和汇总,便于数据统计、监控和报警。
场景题 2️⃣:
你在 Google Cloud 团队工作,客户反馈他们希望查看 90 天内所有 ERROR 级别的访问日志,并且需要支持关键字搜索和接口响应时间筛选。请问你的设计方案是什么?
✅ 答案:
1️⃣ 数据采集方案:
-
日志通过 Logback +
AsyncAppender
异步采集,输出到本地文件并实时推送至 Google Cloud Logging(前身 Stackdriver)。 -
配置多级日志文件轮转,所有
ERROR
日志单独标记 tag,方便索引。
2️⃣ 存储方案:
-
日志数据进入 Cloud Logging 后,接入 BigQuery 或 Elasticsearch,支持大规模存储和高效检索。
-
保证 90 天数据可查,配置冷热分层存储:前 30 天热数据、后 60 天冷数据。
3️⃣ 检索功能实现:
-
基于 Elasticsearch 提供多条件查询:
-
日志级别 = ERROR
-
按接口路径、时间区间、关键字过滤
-
支持响应时间筛选(比如
responseTime > 1000ms
)
-
-
提供 Web UI(如 Kibana、Cloud Console)和 API 接口,满足可视化与程序化访问。
4️⃣ 性能与安全保障:
-
日志索引采用分片策略,优化高并发检索性能。
-
对客户敏感数据做脱敏处理(如 IP、账号)。
-
提供审计功能,记录日志访问操作。
🚩 总结:类似的企业需求在 Google、AWS 都很常见。核心是日志采集、传输、存储、检索 4 个环节要打通,并且必须有强大的日志查询平台支撑高可用检索。
1.3 日志级别
TRACE, DEBUG, INFO, WARN, ERROR
还有两个特殊的:
ALL:输出所有日志
OFF:关闭所有日志
日志级别从小到大的关系:
TRACE < DEBUG < INFO < WARN < ERROR
🧠 理论理解:
日志分级的核心目的是分层次、分关注点输出,避免信息泛滥:
-
TRACE:追踪级别,输出非常细节的信息(比如内部循环状态)。
-
DEBUG:调试级别,开发阶段常用。
-
INFO:一般性的信息,比如接口正常响应、服务启动成功等。
-
WARN:警告,不影响业务但存在潜在风险。
-
ERROR:错误信息,通常是程序异常或失败时输出。
ALL 表示全都开;OFF 表示全都关。不同的级别能按实际场景调整,比如线上环境通常不打印 DEBUG,以减少日志量。
🏢 企业实战理解:
-
字节跳动、腾讯、阿里等公司都有严格的“日志输出规范”。比如线上只允许 INFO 及以上日志输出,DEBUG 级别必须关闭,以避免泄露敏感数据或日志过载。
-
在 Google 的微服务体系中,日志不仅要打“级别”,还要打“模块标签”(如 auth、payment、push),方便后期筛选。
-
大厂常见的“安全审计”日志也是利用日志级别管理的,所有涉及到安全(登录、权限变更)操作必须打日志,并标记为高优先级收集。
3️⃣ 问题:Logback 的日志轮转策略是怎么做的?请举例说明大文件如何拆分归档?
✅ 答案:
Logback 提供多种轮转策略,最常用的是 SizeAndTimeBasedRollingPolicy
,结合“文件大小 + 时间”控制轮转:
🔨 主要策略参数:
-
fileNamePattern
:拆分后的文件名模板,支持日期%d
和序号%i
。 -
maxFileSize
:每个文件的最大体积(如 1MB)。 -
maxHistory
:保留历史文件数量。
✅ 举例:
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/logs/app-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
解释:
-
每天新建一个日志文件(按日期命名)。
-
当当天日志超过 100MB 时,自动编号切割为
app-2025-05-05.1.log.gz
、app-2025-05-05.2.log.gz
... -
历史日志最多保留 30 天,超过会自动删除。
🚩 大厂场景:
-
字节跳动在其日志平台上要求:单文件不得超过 200MB,单服务每日归档不少于 7 天,重要服务归档至少 30 天。
1.4 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/code/itheima-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/code/itheima-data2-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
🧠 理论理解:
配置文件是日志库的“控制中心”,决定了日志:
1️⃣ 输出位置:比如控制台、文件、远程服务器等。
2️⃣ 格式:通过 pattern 定义日期、线程、类名、消息等的展示形式。
3️⃣ 文件轮转机制:比如设置 1MB 自动拆分/压缩,避免单个文件过大。
4️⃣ 输出级别:决定什么级别的日志会输出。
日志库(如 Logback)的配置文件一般用 XML 编写,也支持动态热更新,修改配置无需重启。
🏢 企业实战理解:
-
企业内部通常会预置一份“统一日志配置模板”,项目接入时直接复用。
-
比如字节跳动的日志配置,会自动按机器 IP、业务 ID 分目录存储,保证跨服务查询时一目了然。
-
大型集群会通过远程日志收集器(如阿里云 SLS、ELK)结合配置文件,实现“本地输出 + 异步推送”,既满足开发调试,也方便全局运维追踪。
-
有些大厂还会在配置文件中加“报警配置”,比如 ERROR 日志量超过阈值就触发钉钉/飞书报警。
4️⃣ 问题:Spring Boot 为什么默认选择 Logback?你知道它和 Log4j2 的差异吗?
✅ 答案:
-
Spring Boot 默认 Logback,原因:
-
Logback 是 SLF4J 作者出的“亲儿子”,天然和 SLF4J 完美结合。
-
功能全面,支持高性能异步日志、动态调整日志级别、丰富的配置能力。
-
相比 Log4j(旧版)更安全,Logback 从设计上规避了早期 Log4j 的线程安全问题。
-
-
Logback vs Log4j2:
-
性能:Log4j2 异步日志采用 LMAX Disruptor 模型,在高并发下性能优于 Logback。
-
灵活性:Log4j2 支持 YAML/JSON/XML 多种配置格式,Logback 主要用 XML。
-
动态热加载:两者都支持,但 Log4j2 更强大,比如可在配置变更时动态刷新不重启。
-
🚩 大厂实践:
-
字节跳动在大数据服务中采用 Log4j2 + KafkaAppender,实现日志异步采集 + 实时入库。
-
美团 & 阿里云的 Java 平台则普遍仍用 Logback + ELK 组合。
5️⃣ 问题:线上日志暴增怎么办?怎么预防日志写入对业务性能的影响?
✅ 答案:
🔍 排查:
1️⃣ 检查是否误开 DEBUG/TRACE 级别。
2️⃣ 查看是否有异常循环或日志爆炸点(如死循环输出)。
3️⃣ 检查日志异步队列积压、I/O 堵塞问题。
🛡 预防措施:
-
✅ 使用异步日志(Logback AsyncAppender、Log4j2 AsyncLogger),减少业务线程阻塞。
-
✅ 对热点接口加日志采样/限流,比如只记录 1% 的请求。
-
✅ 严格区分日志级别,线上禁用 DEBUG。
-
✅ 设置日志轮转与自动归档策略,防止磁盘打爆。
🚩 企业案例:
-
在字节跳动,曾因一处 DEBUG 级别日志忘关,导致线上服务一天打出 600G 日志,直接打满磁盘,引发大面积故障。后续字节推行“日志级别审批机制”,要求线上发布前必须审核日志配置。
场景题 3️⃣:
你参与阿里巴巴“双11”项目,如何确保日志系统在高并发场景下既稳定又不会丢失关键日志?说说你的设计策略。
✅ 答案:
1️⃣ 高并发写入保障:
-
全面采用 异步日志(Logback
AsyncAppender
+ Disruptor 队列)。 -
设置大容量日志缓冲池,避免生产速度快于消费速度时丢日志。
-
重要日志(如交易日志)双写:一份到文件,一份实时推送到 Kafka/Flume。
2️⃣ 高可用架构:
-
日志系统独立部署,集群化设计,采用主从复制和负载均衡机制。
-
日志文件按机器自动分区 + 多副本备份,保证节点异常时不丢日志。
3️⃣ 实时监控 + 报警:
-
接入 Prometheus/Grafana,监控日志采集速率、错误率、I/O 使用率。
-
设置日志丢失/积压阈值,一旦触发立即报警(钉钉、飞书)。
4️⃣ 优化轮转与清理:
-
配置文件轮转策略,避免单文件过大;自动归档并压缩历史文件。
-
动态调级:高峰期只打必要的
INFO/WARN/ERROR
日志,降低系统负载。
5️⃣ 应急预案:
-
提前设计“降级机制”:日志采集节点异常时,服务仍然可用但开启最小日志输出。
-
关键业务链路打埋点,必要时通过埋点还原交易过程。
🚩 总结:阿里巴巴“双11”背后的日志系统是“隐形基建”。设计重点是:异步高性能、集群化容错、实时监控、应急预案。日志既是排障工具,也是风控保障的一部分。