Как использовать log4j с несколькими классами? - PullRequest
20 голосов
/ 02 октября 2011

В настоящее время я пишу большой проект на Java, с многочисленными классами, некоторые классы очень маленькие, которые просто представляют объекты только несколькими методами. У меня в главном классе установлен логгер, и он отлично работает. Я хочу иметь возможность использовать только один регистратор (с одним консольным приложением) со всеми классами. Я пытался передать ссылку на регистратор в разные классы, но это выглядит неправильно. кроме того, иногда я запускаю тесты на классах без запуска main, и, следовательно, регистратор не инициализируется для других классов.

Какой наилучший подход для достижения этого, я имею в виду, как вести журнал из разных классов в один журнал, без жесткой зависимости между классами и с возможностью использовать журнал независимо с каждым классом?

Ответы [ 4 ]

19 голосов
/ 02 октября 2011

Если я правильно понимаю, то, что вы имеете в данный момент:

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 к вашему classpath. В Eclipse это делается путем добавления его в src / (или src / main / resources, если вы используете maven). Если вы используете junit, то добавьте его в каталог test / source (или src / test / resources с maven). Это гораздо лучший долгосрочный способ настройки 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 вы можете настроить один аппендер на один файл.

# 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

Наконец, нет необходимости объявлять все ваши логгеры как статические. Это заметно только в том случае, если вы делаете лотов [*] создания объекта. Объявление ваших регистраторов как нестатических полей позволяет вам использовать Logger.getLogger(this.getClass());, и в этом случае добавление регистратора в класс становится вырезкой и вставкой одной строки. См. Должен ли я объявить ссылки в журнале статическими или нет? (к сожалению, ссылка на вики-страницу не работает), но страница slf4j также содержит хорошее объяснение. Поэтому используйте нестатические поля, если у вас нет веских причин не делать этого.

Кэмерон прав, когда говорит, что вы должны попытаться использовать slf4j , если это возможно, у него есть одна убийственная особенность, вы можете использовать несколько каркасов ведения журналов с ним.

[*] и я имею в виду много.

5 голосов
/ 02 октября 2011

Ваши экземпляры регистратора обычно должны быть private, static и final. Таким образом, у каждого класса будет свой экземпляр журнала (который создается после загрузки класса), чтобы вы могли определить класс, в котором была создана запись журнала, а также вам больше не нужно передавать экземпляры регистратора между классами.

4 голосов
/ 02 октября 2011

Лучший способ сделать это - иметь у каждого класса свой собственный регистратор (названный в честь класса), а затем настроить свою конфигурацию так, чтобы они все добавлялись к одному и тому же приложению.

Например:

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 будут подключаться к корневому логгеру и, следовательно, к одному и тому же приложению (в данном случае консоли).

Это даст вам то, что вы хотите: каждый класс независим, но все они пишут в один и тот же журнал. Вы также получаете бонусную функцию, которая позволяет изменять уровень ведения журнала для каждого класса в конфигурации log4j.

Кроме того, вы можете рассмотреть возможность перехода на slf4j, если проект все еще находится на ранней стадии разработки. У slf4j есть некоторые улучшения по сравнению с log4j, с которыми немного проще работать.

1 голос
/ 21 октября 2017

Причина, по которой у вас есть несколько экземпляров регистратора, заключается в том, что вы хотите, чтобы они вели себя по-разному при ведении журнала (обычно путем распечатки имени класса, с которым они настроены).Если вас это не волнует, вы можете просто создать один экземпляр статического регистратора в классе и использовать его повсеместно.

Чтобы создать один регистратор, вы можете просто создать класс журналирования статической утилиты, который будетрегистратор одной точки, поэтому если нам нужно изменить пакет регистратора, вы обновите только этот класс.

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);
        }
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...