Java开发中,日志记录是调试程序、监控运行状态和排查问题的关键环节,以下是几种主流的日志打印方式及其详细实现方法:
基础方案:标准输出流
最简单直接的方式是通过System.out或System.err进行控制台输出。
System.out.println("普通信息"); // 标准输出(通常为绿色文本)
System.err.println("错误警告"); // 错误流(红色高亮显示)
这种方式无需额外依赖库,适合临时调试,但缺乏分级管理和持久化能力,例如在循环中批量打印数据时可用格式化字符串增强可读性:
for (int i = 0; i < 5; i++) {
System.out.printf("第%d次迭代 值=%.2f%n", i, Math.random());
}
JDK自带框架:java.util.logging
自JDK 1.4起内置的日志体系提供了基础架构,核心步骤包括获取Logger实例、设置级别和输出格式,典型用法如下:
import java.util.logging.;
public class JULExample {
private static final Logger logger = Logger.getLogger(JULExample.class.getName());
public static void main(String[] args) {
logger.severe("严重错误发生!"); // SEVERE级别
logger.warning("潜在风险需要注意"); // WARNING级别
logger.info("系统启动成功"); // INFO级别
logger.config("配置参数已加载"); // CONFIG级别
logger.fine("细节调试信息"); // FINE级别(默认不启用)
logger.finest("极细粒度追踪"); // FINEST级别(需显式开启)
}
}
若要修改默认行为,可通过Handler配置处理器,如将日志写入文件并按大小滚动分割:
FileHandler fileHandler = new FileHandler("app.log", true); // 追加模式
fileHandler.setLevel(Level.ALL); // 接收所有级别日志
logger.addHandler(fileHandler);
SimpleFormatter formatter = new SimpleFormatter(); // 简单文本格式
fileHandler.setFormatter(formatter);
更复杂的场景可使用XML配置文件定义过滤器、格式化模板等高级特性。
第三方库首选:Log4j系列
作为事实上的行业标杆,Log4j家族(尤其是Log4j 2)具有高度可配置性和卓越性能,基本用法分为三步:引入依赖→获取Logger→记录不同等级的消息,以Maven项目为例,先添加依赖项:
<!-Log4j 2核心组件 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.x</version>
</dependency>
代码实现如下:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4jDemo {
private static final Logger log = LogManager.getLogger();
public static void main(String[] args) {
log.error("功能异常", new Exception("数据库连接失败")); // 带堆栈跟踪的错误日志
log.warn("资源即将耗尽"); // 预警提示
log.info("用户登录ID={}", "admin"); // 占位符自动填充参数
log.debug("解析JSON耗时{}毫秒", systemTime()); // 延迟加载的调试信息
log.trace("进入方法entry()"); // 最底层执行轨迹
}
}
其强大之处在于通过log4j2.xml配置文件实现灵活管控,例如下列配置可实现按日期归档、控制台+文件双输出、自定义图案布局:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} %msg%n"/>
</Console>
<RollingFile name="File" fileName="logs/app.log" filePattern="logs/archive/app-%d{MM-dd-yyyy}.gz">
<PatternLayout>
<pattern>%d{ISO8601} [%t] %p %c{1} %m%n</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
该配置会每天生成新的压缩日志文件,单个文件超过10MB时自动分割,同时保留最近30天的日志副本。
其他优秀替代方案对比表
| 特性 | java.util.logging | Log4j 2 | SLF4J+Logback |
|---|---|---|---|
| 性能开销 | 中等 | 极低 | 非常低 |
| 配置灵活性 | 有限(代码/XML) | 高度自由(XML) | 依赖实现框架 |
| 门面模式支持 | |||
| 异步写入能力 | |||
| MDC上下文传递 | |||
| 社区活跃度 | 较低 | 很高 | 极高 |
最佳实践建议
- 合理分级:ERROR仅用于系统级故障,INFO记录重要业务节点,DEBUG用于开发环境排错;
- 参数化消息:避免字符串拼接改用占位符,提升性能且支持国际化;
- 异常堆栈保留:捕获Throwable后作为第二个参数传入logging方法;
- 敏感信息过滤:生产环境关闭DEBUG级别,或使用MDC标记请求ID代替用户凭证打印;
- 资源释放:确保所有FileHandler/SocketHandler在使用完毕后正确关闭。
FAQs
Q1: 如何让日志同时输出到控制台和文件?
A: 在Log4j的配置中添加多个Appender即可实现多目标输出,例如上述XML示例中的<Console>和<RollingFile>组合,或者在代码中手动添加不同的Handler对象,注意不同Handler可以设置独立的过滤条件和格式样式。
Q2: 为什么有时候看不到预期的日志内容?
A: 常见原因包括:①日志级别设置过高(如仅开启INFO却试图打印DEBUG);②配置文件未被正确加载(检查路径拼写错误);③第三方库版本冲突导致静默失败,建议通过logger.isDebugEnabled()等方法预判是否应该产生该条日志,并在启动参数中显式指定日志级别(如-DlogLevel=TRACE)。
