Log4j 2.8似乎迭代了collection参数而不是调用toString

时间:2018-02-16 10:14:48

标签: log4j2

在我看来,当集合作为参数传递而不是简单地调用toString()时,Log4j正试图做一些智能。

请注意以下代码,例如

public static void main(String[] args) {
    Collection<String> x = TreeMultiset.create(); //From Guava
    x.add("John");
    x.add("John");
    x.add("John");
    x.add("Dummy");
    System.out.println(x);
    LOG.info("People {}", x);
    LOG.info("People {}", x.toString());
}

这里LOG基本上是log4j LogManager的记录器。这给出了以下输出。

[Dummy, John x 3]
2018-02-16 09:57:42,804 INFO main [MyClass] People [Dummy, John, John, John]
2018-02-16 09:57:42,806 INFO main [MyClass] People [Dummy, John x 3]

我期待日志行中的最后一行输出,但我现在在我的日志文件中有[Dummy,John,John,John ....]。

仔细检查后,罪魁祸首似乎是org.apache.logging.log4j.message.ParameterFormatter#recursiveDeepToString,其代码为

private static void recursiveDeepToString(final Object o, final StringBuilder str, final Set<String> dejaVu) {
        if (appendSpecialTypes(o, str)) {
            return;
        }
        if (isMaybeRecursive(o)) {
            appendPotentiallyRecursiveValue(o, str, dejaVu);
        } else {
            tryObjectToString(o, str);
        }
    }

appendPotentiallyRecursiveValue只是遍历Collection而不是调用.toString()

我的问题是:这真的有必要吗? (甚至危险吗?)

1 个答案:

答案 0 :(得分:1)

很好的发现。这种行为实际上并不是特定于Log4j 2.8,它一直存在于自Log4j2的alpha版本以来的代码库中。

那么,为什么Log4j这样做呢?简短的回答是,这是一项安全措施。想象一下这段代码:

List<Object> list = new ArrayList<>();
list.add(list); // add list to itself 

// option 1: what does this print?
System.out.println(list);

// option 2: what does this print/log?
logger.info(“list contents: {}”, list);

第一个示例(内部调用列表中的toString)将一直挂起,直到它出现堆栈溢出错误。

第二个例子,使用Log4j记录相同的列表将打印出合理的内容。而且,更重要的是,它不会死。

这可能是一个简单的例子,但在实际应用中,自我引用并不总是容易预防。用户喜欢它,如果记录对象,即使他们犯了错误,也不会在生产中杀死他们的应用程序。 : - )