2024年Clojure状态调查中分享您的想法!

欢迎!请查看关于页面获取更多关于这个平台的信息。

0
tools.logging

当使用tools.logging和java.util.logging(简称JUL)时,默认情况下我们得到的日志前缀如下

Oct 23, 2023 2:14:47 PM clojure.tools.logging$eval3668$fn__3671 invoke

请注意,记录的类和方法名称并没有什么帮助。

tools.logging库推荐解决方案是,用户应该将日志后端程序的输出格式更改,不再打印类和方法,而是打印日志记录器名称,即Clojure命名空间名称。

当执行日志记录和配置JUL的代码作者相同,即日志是和应用级别配置和执行时,这是一个可行的解决方案。

用例

我会说,tools.logging更加常见的用例是库作者使用,他们不知道最终用户使用的是哪种日志解决方案。同时,最终用户也不知道这个日志记录存在他们的依赖树中。在这种情况下,有人构建了一个Clojure应用,他们可能会有100个依赖项。突然,他们在stderr中收到了描述如上的消息。

他们可能想
- 将其重定向到文件
- 抑制这些消息
- 想知道哪个依赖产生了这些消息

所有这些任务都需要用户识别他们正在查看的是JUL日志记录,然后配置JUL以输出日志记录器名称,以找出进行日志记录的命名空间。 他们可能还希望保留与Java代码中的方法名称的格式。

方法

使用LogRecord类强制将类名转换为Clojure命名空间。示例代码

(cond-> (doto (LogRecord. level# ~msg)
                      (.setLoggerName ~ns)
                      (.setMessage ~msg)
                      (.setParameters (object-array ~params))
                      (.setSourceClassName ~ns))
              ~e (doto (.setThrown ~e)))

优点

  1. clojure.tools.logging$eval3668$fn__3671 invoke替换输出中的字符串为my.lib.namespace。前者没有信息价值,后者使我们能够知道在哪里查找执行日志语句的位置。

  2. 提高性能。如果调用.setSourceClassName,则日志系统不会使用反射来确定调用者类和方法。这涉及到堆栈跟踪,并且很慢。

  3. 即使在默认/无配置的JUL下,它也提供了关于在stderr中产生所有这些消息的人的位置的提示,如果用户不了解这个日志库/机制。

如果需要,我可以提交一个PR以影响这项更改。

1 个答案

0

我怀疑你的设置出了问题。我在项目中使用Logback并通过SLF4J,格式中的`logger`密钥始终输出了正确的命名空间。c.t.l logger的JUL实现与此并不太不同。

为了绝对确信,我创建了一个只有一个命名空间的最简单的项目

(ns app.core
  (:require [clojure.tools.logging :as log]
            [clojure.tools.logging.impl :as log-impl]))

(defn -main []
  (System/setProperty "java.util.logging.SimpleFormatter.format"
                      "[%1$tF %1$tT] [%4$-7s] [%3$s] %5$s %n")
  (binding [log/*logger-factory* (log-impl/jul-factory)]
    (log/info "Hello")))

它只依赖于org.clojure/tools.logging {:mvn/version "1.2.4"}
当使用clj -M -m app.core运行时,我在控制台看到这个

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[2023-11-05 16:57:41] [INFO   ] [app.core] Hello

如你所见,可读的命名空间名称在那里。

(这只是快速测试,当然不应该像这样使用`c.t.l.impl`。并且可能应该通过实际属性或属性文件设置格式。)

关键是调用System/setProperty tho'。如果你省略了这一步,打算依赖用户提供的任何配置,你会得到原帖中抱怨的原输出。

Nov 05, 2023 10:35:35 AM user$eval464$fn__467 invoke
INFO: Hello

设置了该属性后,我得到与你相同的结果,但没有SLF4J的抱怨

[2023-11-05 10:37:35] [INFO   ] [app.core] Hello
是的,但那是JUL的默认格式。其他日志库有其他默认格式,也许对于其他配置参数,还有不同的默认值。在我看来,这不是c.t.l应该关心的事情,因为这只是底层实现的非常薄的门面。
作者:
我完全同意,这不是c.t.l应该关心或解决的问题!我只是想指出,你的“重现”案例并不完全是OP问题的一个重现——因为你用那个setProperty调用“解决了”格式问题。

如果一个库选择使用c.t.l,而使用这个库的用户不进行任何日志操作,他们将得到一个难看的打印输出。如果该库的用户正在明确地进行日志操作,那么他们可能已经控制了那个输出。

在我们的工作中,我们使用log4j2,并让其与所有内容桥接,我们使用c.t.l并强制c.t.l使用log4j2——因此,如果库选择使用c.t.l,我们不会关心,因为一切都是一致的。

我想,这背后存在一个哲学问题,就是“(Clojure)库是否应首先使用c.t.l”或者它们应该“无日志”?(然后这将是什么样子)?

clojure.java.data依赖于它,但只是条件性地使用它——而next.jdbc依赖于它,这使得日志引入了许多人的程序中。但java.data在默认情况下并不进行日志记录...
作者:
正如所说的那样,问题是当随机库用户开始在他们自己的std.err中收到这些奇怪的日志语句,他们开始寻找原因的依据非常少。是的,显然你可以修复格式以省略这些信息。

我明白有些人认为这不是c.t.l应该解决的问题,但我认为打印输出是由c.t.l宏引起的,更重要的是,这个问题主要是可以通过修改c.t.l来解决的。

我支持这个变化,原因如下
- 没有负面影响
- 代码复杂度没有实际增加
- 提高性能(JUL不需要执行反射来确定调用类和方法)

我真的看不到这个变化的缺点。
...