我目前正在用java编写一个大项目,有很多类,有些类很小,只用很少的方法代表对象。 我的主类中有一个记录器,它工作正常。 我希望能够只使用一个记录器(带有一个控制台appender)和所有类。 我试图将对记录器的引用传递给不同的类,但它看起来不正确。 此外,有时我在没有运行main的情况下对类进行测试,因此没有为其他类初始化记录器。
实现这一目标的最佳方法是什么,我的意思是,如何从不同的类登录到一个日志,类之间没有硬依赖关系,并且能够独立地使用每个类的日志?
答案 0 :(得分:19)
如果我理解正确的话,你现在所拥有的是:
public class Main {
public static final Logger LOGGER = Logger.getLogger(Main.class);
}
public class AnotherClass {
public void doSomething() {
Main.LOGGER.debug("value=" + value);
}
}
或者,将对记录器的引用传递给类的构造函数。
首先,您可以使用传递给Logger.getLogger的相同值来使用一个全局记录器,例如:
public class Main {
private static final Logger LOGGER = Logger.getLogger("GLOBAL");
}
public class AnotherClass {
private final Logger LOGGER = Logger.getLogger("GLOBAL");
public void doSomething() {
LOGGER.debug("value=" + value);
}
}
这使用完全相同的记录器,Logger.getLogger在两个调用中返回相同的对象。您不再在类之间存在依赖关系,这将起作用。
我从你的评论中收集到的另一件事是你正在手工配置(使用BasicConfigurator.configure
。大多数情况下这不是必需的,你应该通过简单地添加log4j.properties来进行配置或者log4j.xml到你的类路径。在Eclipse中,这是通过将它添加到src /(或src / main / resources,如果你正在使用maven)来完成的。如果你正在使用junit,那么将它添加到test / source目录(或使用maven的src / test / resources)。这是配置log4j的更好的长期方法,因为您不必在类之间传递信息。
此外,使用记录器的推荐方法是将类传递给Logger.getLogger()。通过这种方式,您可以根据类名过滤输出,这通常比只有一个全局记录器更有用:
public class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class);
public static final main(String[] args) {
LOGGER.debug("started");
}
}
public class AnotherClass {
private final Logger LOGGER = Logger.getLogger(this.getClass());
public void doSomething() {
LOGGER.debug("value=" + value);
}
}
然后在log4j.properties中,您可以将单个appender配置为一个文件。
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
最后,没有必要将所有记录器声明为静态。如果你正在进行 lot [*]的对象创建,这只会产生显着的差异。将记录器声明为非静态字段允许您使用Logger.getLogger(this.getClass());
,在这种情况下,向类添加记录器将成为单行的剪切和粘贴。请参阅Should I declare Log references static or not?(遗憾的是,维基页面的链接已损坏),但slf4j page也包含了一个很好的解释。所以使用非静态字段除非你有充分的理由不这样做。
Cameron说他应该尝试使用slf4j,如果可能,它有一个杀手级功能,你可以使用多个日志框架。
[*]我的意思很多。
答案 1 :(得分:5)
Your logger instances should typically be private
, static
and final
。通过这样做,每个类将拥有它自己的记录器实例(在加载类后创建),这样您就可以识别创建日志记录的类,并且您不再需要跨类传递记录器实例。
答案 2 :(得分:4)
最好的方法是让每个类都有自己的记录器(以类命名),然后设置你的配置,使它们都附加到同一个appender。
例如:
class A {
private static final Logger log = Logger.getLogger(A.class);
}
class B {
private static final Logger log = Logger.getLogger(B.class);
}
然后你的log4j.properties看起来像log4j文档中的例子:
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
A
和B
都会记录到根记录器,因此会记录到同一个appender(在本例中为控制台)。
这将为您提供所需的内容:每个类都是独立的,但它们都写入同一个日志。您还可以获得奖励功能,您可以在log4j配置中更改每个类的日志记录级别。
顺便说一句,如果项目仍处于早期开发阶段,您可能需要考虑转移到slf4j。 slf4j比log4j有一些改进,使它更容易使用。
答案 3 :(得分:1)
您有多个记录器实例的原因是因为您希望它们在记录时表现不同(通常通过打印出配置的类名)。如果您不关心这一点,您可以在类中创建一个静态记录器实例,并在整个地方使用它。
要创建单个记录器,您只需创建一个静态实用程序日志记录类作为单点记录器,因此如果我们需要更改记录器包,您将只更新此类。
final public class Logger {
private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("Log");
enum Level {Error, Warn, Fatal, Info, Debug}
private Logger() {/* do nothing */};
public static void logError(Class clazz, String msg) {
log(Level.Error, clazz, msg, null);
}
public static void logWarn(Class clazz, String msg) {
log(Level.Warn, clazz, msg, null);
}
public static void logFatal(Class clazz, String msg) {
log(Level.Fatal, clazz, msg, null);
}
public static void logInfo(Class clazz, String msg) {
log(Level.Info, clazz, msg, null);
}
public static void logDebug(Class clazz, String msg) {
log(Level.Debug, clazz, msg, null);
}
public static void logError(Class clazz, String msg, Throwable throwable) {
log(Level.Error, clazz, msg, throwable);
}
public static void logWarn(Class clazz, String msg, Throwable throwable) {
log(Level.Warn, clazz, msg, throwable);
}
public static void logFatal(Class clazz, String msg, Throwable throwable) {
log(Level.Fatal, clazz, msg, throwable);
}
public static void logInfo(Class clazz, String msg, Throwable throwable) {
log(Level.Info, clazz, msg, throwable);
}
public static void logDebug(Class clazz, String msg, Throwable throwable) {
log(Level.Debug, clazz, msg, throwable);
}
private static void log(Level level, Class clazz, String msg, Throwable throwable) {
String message = String.format("[%s] : %s", clazz, msg);
switch (level) {
case Info:
logger.info(message, throwable);
break;
case Warn:
logger.warn(message, throwable);
break;
case Error:
logger.error(message, throwable);
break;
case Fatal:
logger.fatal(message, throwable);
break;
default:
case Debug:
logger.debug(message, throwable);
}
}
}
答案 4 :(得分:0)
我最近发现了这个解决方案。
在您希望使用记录器的所有类中使用@Log4j
批注。
好处:
该怎么做?
前提条件:在编辑器中安装Lombok插件,在项目中添加lombok依赖项。
现在定义部分:
@Log4j2
public class TestClass {
TestClass() {
log.info("Default Constructor");
//With the help of lombok plugin, you'll be able to see the suggestions
}
<Data members and data methods>
}