printstream 本身无重定向功能,但可通过 system.setout()/seterr() 将输出转至文件,适用于轻量级脚手架;需手动处理路径创建、自动刷新、jvm 退出前 flush 及文件命名策略;当需分级过滤、mdc、轮转归档或远程上报时,应切换至 slf4j+logback。

PrintStream 本身没有“重定向功能”,但你可以用它把原本输出到控制台的内容,转而写入本地文件——这对轻量级脚手架(比如命令行工具、单机小服务、原型验证程序)来说,够用、可控、不依赖日志框架。
直接替换 System.out / System.err
这是最轻量的落地方式:调用 System.setOut() 或 System.setErr(),传入一个指向日志文件的 PrintStream 实例。所有后续的 System.out.println() 或未捕获异常的 e.printStackTrace() 都会自动落盘。
- 用
new PrintStream(new FileOutputStream("app.log", true))创建追加模式的流(true表示不覆盖) - 建议在应用启动早期执行,避免遗漏初始化阶段的输出
- 注意:
System.err和System.out是独立流,需分别重定向 - 若脚手架可能并发输出,PrintStream 是线程安全的,但内容混排仍可能发生,可加简单前缀区分来源(如
[OUT]/[ERR])
带时间戳和上下文的简易封装
原生 PrintStream 不带格式,但你可以包装一层,每次输出前自动补上时间与简要标识,提升可读性:
- 继承
PrintStream,重写println(String)等方法,在写入前拼接LocalDateTime.now() + " [INFO] " - 或更简单:用
PrintWriter替代,配合String.format("%s [INFO] %s%n", now(), msg)手动格式化后写入 - 避免在封装中做耗时操作(如频繁解析时间),可用
Instant.now().toString()快速取值
兼顾可靠性和资源释放
轻量 ≠ 随意。几个关键细节决定日志是否真能“平滑记录”:
- 文件路径要确保父目录存在,否则
FileOutputStream构造会抛FileNotFoundException;可提前new File("logs").mkdirs() - PrintStream 默认不自动刷新缓冲区(尤其在重定向后),程序异常退出时最后一段输出易丢失;推荐构造时启用自动 flush:
new PrintStream(out, true) - 不必手动 close —— 因为它是长期持有的全局流;但要在 JVM 退出前(如注册
Runtime.getRuntime().addShutdownHook())显式flush()一次,保底落盘 - 单文件日志长期运行会膨胀,可在脚手架启动时按日期生成文件名(如
app-20260605.log),无需滚动逻辑也能缓解问题
什么时候该停手,换日志框架?
当出现以下任一情况,说明已超出 PrintStream 的舒适区:
- 需要按级别过滤(只保留 ERROR)、异步写入防阻塞
- 要关联请求 ID、用户 ID 等 MDC 上下文字段
- 日志要压缩归档、按大小轮转、远程上报
- 团队协作中要求统一日志格式或对接 ELK/Splunk
那时再引入 SLF4J + Logback,几行配置就能接管全部输出,过渡也自然。











