You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何lein repl会阻塞STDERR?如何实现双端输出适配systemd日志

这个问题我之前也碰到过——Leiningen默认会把NREPL会话里的System/err输出绑定到当前REPL连接,而不是转发到启动进程的终端(也就是systemd监控的那个stderr流)。要实现两端同时输出,有几个实用的方案,根据你的场景选就行:

方案1:全局配置(所有项目生效)

如果你希望所有Leiningen项目都默认支持这个功能,可以修改你的全局Leiningen配置文件~/.lein/profiles.clj,添加:user配置段,在REPL初始化时包装System/err:

{:user {:repl-options {:init (do
                               ;; 导入Java的TeeOutputStream类,用于复制输出到两个流
                               (import [java.io TeeOutputStream])
                               ;; 获取启动终端的stderr流(Linux下是/dev/stderr)
                               (let [terminal-stderr (clojure.java.io/output-stream "/dev/stderr")
                                     ;; 当前REPL会话的stderr流
                                     repl-stderr System/err]
                                 ;; 把System/err设置为同时输出到两个流的TeeOutputStream
                                 (System/setErr (TeeOutputStream. repl-stderr terminal-stderr))))}}}

保存后,不管是在无项目环境下运行lein repl,还是在项目里启动REPL,只要执行(.println System/err "test"),内容会同时出现在REPL会话和启动终端里,systemd也能捕获到日志。

方案2:单个项目配置(仅当前项目生效)

如果只需要给特定项目配置,直接修改项目根目录下的project.clj,添加同样的REPL初始化逻辑:

(defproject your-app "0.1.0-SNAPSHOT"
  ;; 其他配置...
  :repl-options {:init (do
                         (import [java.io TeeOutputStream])
                         (let [terminal-stderr (clojure.java.io/output-stream "/dev/stderr")
                               repl-stderr System/err]
                           (System/setErr (TeeOutputStream. repl-stderr terminal-stderr))))})

方案3:针对lein run的应用代码修改

如果你的场景是用lein run启动应用,并且希望应用运行时(包括远程REPL连接后的输出)的stderr同时输出到终端和systemd,可以在应用的-main函数开头添加包装逻辑:

(ns your-app.core
  (:import [java.io TeeOutputStream])
  (:gen-class))

(defn -main [& args]
  ;; 包装System/err为双输出流
  (let [terminal-stderr (clojure.java.io/output-stream "/dev/stderr")
        current-err System/err]
    (System/setErr (TeeOutputStream. current-err terminal-stderr)))
  
  ;; 你的应用业务代码...
  (.println System/err "This will show up in both REPL and systemd logs!")
  )

关键原理说明

Java的TeeOutputStream会把所有写入它的字节同时复制到两个指定的输出流中——这里我们把它分别指向REPL会话的stderr和启动终端的/dev/stderr(也就是systemd监控的进程stderr)。这样不管是在本地REPL还是远程NREPL连接中输出的stderr内容,都会同时出现在两个地方。

注意:这个方案基于Linux环境(因为用到了/dev/stderr),如果你在Windows上使用,需要替换成对应的标准错误流路径,不过你提到了systemd,应该是Linux环境,所以没问题。

内容的提问来源于stack exchange,提问作者user12769468

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 19:13:15