在 Serializable Java 类中使用 Logger 的正确方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2806550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the proper way to use a Logger in a Serializable Java class?
提问by Tim Visher
I have the following (doctored) class in a system I'm working on and Findbugsis generating a SE_BAD_FIELDwarning and I'm trying to understand why it would say that before I fix it in the way that I thought I would. The reason I'm confused is because the description would seem to indicate that I had used no other non-serializable instance fields in the class but bar.model.Foo is also not serializable and used in the exact same way (as far as I can tell) but Findbugs generates no warning for it.
我在我正在处理的系统中有以下(篡改)类,Findbugs正在生成SE_BAD_FIELD警告,我试图理解为什么它会在我按照我认为的方式修复它之前这么说。我感到困惑的原因是因为描述似乎表明我没有在类中使用其他不可序列化的实例字段,但是 bar.model.Foo 也不是可序列化的并且以完全相同的方式使用(就我而言可以告诉)但 Findbugs 不会为它生成警告。
import bar.model.Foo;
import java.io.File;
import java.io.Serializable;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Demo implements Serializable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final File file;
private final List<Foo> originalFoos;
private Integer count;
private int primitive = 0;
public Demo() {
for (Foo foo : originalFoos) {
this.logger.debug(...);
}
}
...
}
My initial blush at a solution is to get a logger reference from the factory right as I use it:
我最初对解决方案感到脸红是在我使用它时从工厂获得记录器参考:
public DispositionFile() {
Logger logger = LoggerFactory.getLogger(this.getClass());
for (Foo foo : originalFoos) {
this.logger.debug(...);
}
}
That doesn't seem particularly efficient, though.
不过,这似乎不是特别有效。
Thoughts?
想法?
回答by skaffman
Firstly, don't optimize prematurely. It may be that LoggerFactory.getLogger()is fast enough, and contributes no significant overhead to execution time. If in doubt, profile it.
首先,不要过早优化。这可能LoggerFactory.getLogger()足够快,并且不会对执行时间造成显着的开销。如果有疑问,请对其进行概要分析。
Secondly, the reason that findbugs isn't complaining about the use of Foois because the class doesn't have a field of type Foo, it has a field of type List. The generics are erased at compile time, there is no actual reference to Fooin the class, as far as the field definition is concerned. At runtime, the fact that Foois non-serializable would cause an exception if you tried to serialize an instance of the Democlass, but findbugs can't know this.
其次,findbugs 没有抱怨使用的Foo原因是因为该类没有 type 字段Foo,它有一个 type 字段List。泛型在编译时被删除,就Foo字段定义而言,类中没有实际引用。在运行时,Foo如果您尝试序列化Demo类的实例,则不可序列化的事实会导致异常,但 findbugs 无法知道这一点。
My first reaction would be to make the Loggera static field, rather than an instance field. Should work fine in this situation.
我的第一反应是创建Logger一个静态字段,而不是一个实例字段。在这种情况下应该可以正常工作。
public class Demo implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(Demo.class);
// .. other stuff
}
回答by erickson
I don't want things to take off on a tangent, but have you considered the conventional initialization of loggers?
我不希望事情发生在切线上,但是您是否考虑过记录器的常规初始化?
private static final Logger logger = LoggerFactory.getLogger(Demo.class);
If you don't really need different loggers for each instance (which is unusual), the problem would go away.
如果您真的不需要为每个实例使用不同的记录器(这是不寻常的),那么问题就会消失。
By the way, the authorof SL4J said (in a critiqueof Log4J wrappers like commons-logging),
顺便说一句,SL4J的作者说(在批评像 commons-logging 这样的 Log4J 包装器时),
More often than not, these wrappers are of doubtful quality such that the cost of inactive (or disabled) logging statements is multiplied by a factor of 1'000 (one thousand) compared to direct log4j usage. The most common error in wrapper classes is the invocation of the Logger.getLogger method on each log request. This is guaranteed to wreak havoc on your application's performance. Really!!!
通常情况下,这些包装器的质量令人怀疑,与直接使用 log4j 相比,不活动(或禁用)日志记录语句的成本乘以 1'000(一千)倍。包装类中最常见的错误是对每个日志请求调用 Logger.getLogger 方法。这肯定会对您的应用程序的性能造成严重破坏。真的!!!
That would suggest that your alternative idea of getting the logger each time you need it is not recommended.
这表明不建议您每次需要时获取记录器的替代想法。
回答by Ceki
FindBugs is misleading you in this particular case because the org.slf4j.Logger interface is notmarked as java.io.Serializable. However, SLF4J logger implementations that ship with SLF4J all support serialization out-of-the-box. Try it. You'll see that it works.
FindBugs 在这种特殊情况下会误导您,因为 org.slf4j.Logger 接口没有标记为 java.io.Serializable。但是,SLF4J 附带的 SLF4J 记录器实现都支持开箱即用的序列化。试试看。你会看到它有效。
Here is an excerpt from the SLF4j FAQ:
以下是 SLF4j 常见问题解答的摘录:
Contrary to static variables, instance variables are serialized by default. As of SLF4J version 1.5.3, logger instances survive serialization. Thus, serialization of the host class no longer requires any special action, even when loggers are declared as instance variables. In previous versions, logger instances needed to be declared as transient in the host class.
与静态变量相反,实例变量默认是序列化的。从 SLF4J 1.5.3 版开始,记录器实例在序列化后仍然存在。因此,宿主类的序列化不再需要任何特殊操作,即使记录器被声明为实例变量。在以前的版本中,记录器实例需要在宿主类中声明为瞬态。
回答by Ophidian
My initial reaction is to wonder if it even makes sense to serialize a Logger instance in your object. When you deserialize it later, is it really all that fair to expect the Logger's environment to be correct? I think I would rather just go with this and call it a day:
我最初的反应是想知道在你的对象中序列化一个 Logger 实例是否有意义。当您稍后对其进行反序列化时,期望 Logger 的环境正确是否真的那么公平?我想我宁愿选择这个并结束它:
private transient Logger logger = LoggerFactory.getLogger(this.getClass());

