如何使用log4j与多个类?

时间:2011-10-02 06:07:42

标签: java log4j

我目前正在用java编写一个大项目,有很多类,有些类很小,只用很少的方法代表对象。 我的主类中有一个记录器,它工作正常。 我希望能够只使用一个记录器(带有一个控制台appender)和所有类。 我试图将对记录器的引用传递给不同的类,但它看起来不正确。 此外,有时我在没有运行main的情况下对类进行测试,因此没有为其他类初始化记录器。

实现这一目标的最佳方法是什么,我的意思是,如何从不同的类登录到一个日志,类之间没有硬依赖关系,并且能够独立地使用每个类的日志?

5 个答案:

答案 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

AB都会记录到根记录器,因此会记录到同一个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批注。

好处:

  1. 易于维护,易于跟踪。
  2. 仅创建记录器的一个对象,将一直使用。

该怎么做?

前提条件:在编辑器中安装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>
}