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

log4cl在SBCL多线程脚本中输出重复日志,是否为Bug或使用问题?

SBCL脚本模式下Log4CL线程日志重复问题分析与解决

问题现象

在SBCL 2.4.1环境中使用最新版Log4CL时,出现以下异常:

  • 将代码作为SBCL脚本运行时,线程函数内的日志会重复输出,每条日志打印两次
  • 但将代码加载到SBCL REPL中运行时,日志输出正常,无重复现象
  • 仅线程执行的代码段出现重复日志,主线程日志无此问题

原代码示例

#!/usr/bin/sbcl --script

(require :asdf)
(require :log4cl)

(log:config :debug)

(log:info "begin")

(defun worker ()
  (let ((name (sb-thread:thread-name sb-thread:*current-thread*)))
    (log:info "~a started" name)
    (log:debug "~a ~a" name (sb-ext:get-time-of-day))
    (log:info "~a stoped" name)))

(loop for thread in (loop for i below 2
                          collect (sb-thread:make-thread
                                   'worker
                                   :name (format nil "worker-~a" i)
                                   :arguments nil))
      do (sb-thread:join-thread thread))

(log:info "done")

脚本运行重复日志输出

$ ./foo.lisp
 <INFO> [17:40:25] cl-user foo.lisp () - begin
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-0 started
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-0 started
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-1 started
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-1 started
<DEBUG> [17:40:25] cl-user foo.lisp (worker) - worker-0 1713346825
<DEBUG> [17:40:25] cl-user foo.lisp (worker) - worker-0 1713346825
<DEBUG> [17:40:25] cl-user foo.lisp (worker) - worker-1 1713346825
<DEBUG> [17:40:25] cl-user foo.lisp (worker) - worker-1 1713346825
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-0 stoped
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-0 stoped
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-1 stoped
 <INFO> [17:40:25] cl-user foo.lisp (worker) - worker-1 stoped
 <INFO> [17:40:25] cl-user foo.lisp () - done

REPL加载正常输出

sbcl --noinform --no-userinit
* (load "foo.lisp")
 <INFO> [17:48:36] cl-user foo.lisp () - begin
 <INFO> [17:48:36] cl-user foo.lisp (worker) - worker-0 started
 <INFO> [17:48:36] cl-user foo.lisp (worker) - worker-1 started
<DEBUG> [17:48:36] cl-user foo.lisp (worker) - worker-1 1713347316
 <INFO> [17:48:36] cl-user foo.lisp (worker) - worker-1 stoped
<DEBUG> [17:48:36] cl-user foo.lisp (worker) - worker-0 1713347316
 <INFO> [17:48:36] cl-user foo.lisp (worker) - worker-0 stoped
 <INFO> [17:48:36] cl-user foo.lisp () - done
T

问题原因

这是SBCL脚本模式与Log4CL初始化逻辑的兼容性问题:

  • SBCL脚本模式下,(require :log4cl)会初始化一次默认控制台appender
  • 当新线程创建时,脚本环境的特性会触发Log4CL内部的初始化逻辑再次执行,导致同一个控制台appender被重复注册
  • 每条日志会被多个相同的appender输出,最终造成重复打印

REPL模式下不存在此问题,因为REPL环境是单初始化流程,appender仅被注册一次。

解决方案

显式管理Log4CL的appender,在配置日志前清除所有默认appender,再手动添加一次控制台appender,避免重复注册:

修改后的代码

#!/usr/bin/sbcl --script

(require :asdf)
(require :log4cl)

// 清除所有已存在的appender,避免重复注册
(log:remove-all-appenders)
// 手动添加控制台appender到根日志器
(log:add-appender (log:console-appender) log:*root-logger*)

(log:config :debug)

(log:info "begin")

(defun worker ()
  (let ((name (sb-thread:thread-name sb-thread:*current-thread*)))
    (log:info "~a started" name)
    (log:debug "~a ~a" name (sb-ext:get-time-of-day))
    (log:info "~a stoped" name)))

(loop for thread in (loop for i below 2
                          collect (sb-thread:make-thread
                                   'worker
                                   :name (format nil "worker-~a" i)
                                   :arguments nil))
      do (sb-thread:join-thread thread))

(log:info "done")

验证效果

运行修改后的脚本,日志输出不再重复,与REPL模式下表现一致:

$ ./foo.lisp
 <INFO> [xx:xx:xx] cl-user foo.lisp () - begin
 <INFO> [xx:xx:xx] cl-user foo.lisp (worker) - worker-0 started
 <INFO> [xx:xx:xx] cl-user foo.lisp (worker) - worker-1 started
<DEBUG> [xx:xx:xx] cl-user foo.lisp (worker) - worker-0 171xxxxxxx
 <INFO> [xx:xx:xx] cl-user foo.lisp (worker) - worker-0 stoped
<DEBUG> [xx:xx:xx] cl-user foo.lisp (worker) - worker-1 171xxxxxxx
 <INFO> [xx:xx:xx] cl-user foo.lisp (worker) - worker-1 stoped
 <INFO> [xx:xx:xx] cl-user foo.lisp () - done

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:37:05