2010-04-12 3 views
72

При использовании log4j доступен метод Logger.log(Priority p, Object message) и может использоваться для регистрации сообщения на уровне журнала, определенном во время выполнения. Мы используем этот факт и this tip перенаправляют stderr на регистратор на определенном уровне журнала.Настройка уровня журнала сообщений во время выполнения в slf4j

slf4j не имеет общего метода log(), который я могу найти. Означает ли это, что нет возможности реализовать вышеизложенное?

+4

Похоже, есть некоторая дискуссия добавив в SLF4J 2.0 на Dev список рассылки: http://www.qos.ch/pipermail/slf4j-dev/2010-March/002865.html –

+0

Размещена issue: http://bugzilla.slf4j.org/show_bug.cgi?id=206 – ripper234

+1

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

ответ

33

Нет способа сделать это с помощью slf4j.

Я полагаю, что причина, по которой эта функция отсутствует, что это почти невозможно построить Level типа для slf4j, которые могут быть эффективно отображенной на Level (или эквивалент) типа используется во всех возможных реализаций лесозаготовительных позади фасад. В качестве альтернативы дизайнеры решили, что your use-case is too unusual оправдывают накладные расходы на его поддержку.

Что касается @ripper234 «ы use-case (модульного тестирования), я думаю, что прагматичное решение изменить модульный тест (ы) с трудом проволочного знания о том, что система регистрации находится за SLF4J фасада ... при выполнении модульных тестов.

+5

На самом деле никакого отображения не требуется. Существует пять уровней, которые неявно определяются методами в 'org.slf4j.Logger': debug, error, info, trace, warn. –

+0

Это сопоставление, и для его поддержки стоит процессорный цикл. –

+57

WTF? SLF4J объявляется будущим протоколом java, и этот пример использования не поддерживается? (Первое, что я попытался сделать после интеграции SLF4J ..., чтобы уменьшить беспорядок в модульных тестах). – ripper234

-4

нет, он имеет целый ряд методов, информация(), отлаживать(), предупредить() и т.д. (это заменяет поле приоритета)

посмотреть на http://www.slf4j.org/api/org/slf4j/Logger.html для полного Logger API.

+0

Извините, я вижу, что вы сейчас спрашиваете. Нет, нет универсального способа изменить уровень журнала во время выполнения, но вы можете легко реализовать вспомогательный метод с статусом переключения. – oedo

+0

Да, но тогда вы должны сделать это один раз для каждой перегруженной версии метода «log». –

6

Это может быть сделано с enum и вспомогательным методом:

enum LogLevel { 
    TRACE, 
    DEBUG, 
    INFO, 
    WARN, 
    ERROR, 
} 

public static void log(Logger logger, LogLevel level, String format, Object[] argArray) { 
    switch (level) { 
     case TRACE: 
      logger.trace(format, argArray); 
      break; 
     case DEBUG: 
      logger.debug(format, argArray); 
      break; 
     case INFO: 
      logger.info(format, argArray); 
      break; 
     case WARN: 
      logger.warn(format, argArray); 
      break; 
     case ERROR: 
      logger.error(format, argArray); 
      break; 
    } 
} 

// example usage: 
private static final Logger logger = ... 
final LogLevel level = ... 
log(logger, level, "Something bad happened", ...); 

Вы можете добавить другие варианты log, скажем, если вы хотите, общие эквиваленты SLF4J-х 1-параметр или 2-параметр warn/error/и т.п. методы.

+3

Правда, но slf4j не нужно писать лог-обертки. – djjeck

+3

Целью SLF4J является предоставление абстракции для различных фреймворков регистрации. Если эта абстракция не обеспечивает именно то, что вам нужно, у вас нет выбора, кроме как написать вспомогательный метод. Единственная альтернатива - это способ, подобный тому, который был в моем ответе проекту SLF4J. –

+0

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

20

У Ричарда Ферна есть правильная идея, поэтому я написал полный класс, основанный на его скелетном коде. Надеюсь, он будет достаточно коротким, чтобы публиковать здесь. Скопировать & паста для наслаждения. Я, вероятно, следует добавить некоторые магические заклинания, тоже: «Этот код выпущен в общественное достояние»

import org.slf4j.Logger; 

public class LogLevel { 

    /** 
    * Allowed levels, as an enum. Import using "import [package].LogLevel.Level" 
    * Every logging implementation has something like this except SLF4J. 
    */ 

    public static enum Level { 
     TRACE, DEBUG, INFO, WARN, ERROR 
    } 

    /** 
    * This class cannot be instantiated, why would you want to? 
    */ 

    private LogLevel() { 
     // Unreachable 
    } 

    /** 
    * Log at the specified level. If the "logger" is null, nothing is logged. 
    * If the "level" is null, nothing is logged. If the "txt" is null, 
    * behaviour depends on the SLF4J implementation. 
    */ 

    public static void log(Logger logger, Level level, String txt) { 
     if (logger != null && level != null) { 
      switch (level) { 
      case TRACE: 
       logger.trace(txt); 
       break; 
      case DEBUG: 
       logger.debug(txt); 
       break; 
      case INFO: 
       logger.info(txt); 
       break; 
      case WARN: 
       logger.warn(txt); 
       break; 
      case ERROR: 
       logger.error(txt); 
       break; 
      } 
     } 
    } 

    /** 
    * Log at the specified level. If the "logger" is null, nothing is logged. 
    * If the "level" is null, nothing is logged. If the "format" or the "argArray" 
    * are null, behaviour depends on the SLF4J-backing implementation. 
    */ 

    public static void log(Logger logger, Level level, String format, Object[] argArray) { 
     if (logger != null && level != null) { 
      switch (level) { 
      case TRACE: 
       logger.trace(format, argArray); 
       break; 
      case DEBUG: 
       logger.debug(format, argArray); 
       break; 
      case INFO: 
       logger.info(format, argArray); 
       break; 
      case WARN: 
       logger.warn(format, argArray); 
       break; 
      case ERROR: 
       logger.error(format, argArray); 
       break; 
      } 
     } 
    } 

    /** 
    * Log at the specified level, with a Throwable on top. If the "logger" is null, 
    * nothing is logged. If the "level" is null, nothing is logged. If the "format" or 
    * the "argArray" or the "throwable" are null, behaviour depends on the SLF4J-backing 
    * implementation. 
    */ 

    public static void log(Logger logger, Level level, String txt, Throwable throwable) { 
     if (logger != null && level != null) { 
      switch (level) { 
      case TRACE: 
       logger.trace(txt, throwable); 
       break; 
      case DEBUG: 
       logger.debug(txt, throwable); 
       break; 
      case INFO: 
       logger.info(txt, throwable); 
       break; 
      case WARN: 
       logger.warn(txt, throwable); 
       break; 
      case ERROR: 
       logger.error(txt, throwable); 
       break; 
      } 
     } 
    } 

    /** 
    * Check whether a SLF4J logger is enabled for a certain loglevel. 
    * If the "logger" or the "level" is null, false is returned. 
    */ 

    public static boolean isEnabledFor(Logger logger, Level level) { 
     boolean res = false; 
     if (logger != null && level != null) { 
      switch (level) { 
      case TRACE: 
       res = logger.isTraceEnabled(); 
       break; 
      case DEBUG: 
       res = logger.isDebugEnabled(); 
       break; 
      case INFO: 
       res = logger.isInfoEnabled(); 
       break; 
      case WARN: 
       res = logger.isWarnEnabled(); 
       break; 
      case ERROR: 
       res = logger.isErrorEnabled(); 
       break; 
      } 
     } 
     return res; 
    } 
} 
+0

Это было бы проще в использовании с переменным параметром args (Object ...). – Anonymoose

+0

«org.slf4j.Logger» имеет довольно много сигнатур метода регистрации, которые не обрабатываются в вышеуказанном классе, поэтому расширение, вероятно, оправдано: http://www.slf4j.org/api/org/slf4j/Logger.html –

+0

Я думаю, что эта реализация добавит ненужное изменение. Когда вы используете вызов logger.info (...), регистратор имеет доступ к классу и методу вызывающего абонента, и он может быть автоматически добавлен в запись журнала. Теперь, с этой реализацией, журнал вызовов (logger, level, txt) создаст запись в журнале, в которой всегда будет один и тот же вызывающий: Loglevel.log. Я прав? – Domin

-1

с помощью Java самоанализа вы можете сделать это, например:

private void changeRootLoggerLevel(int level) { 

    if (logger instanceof org.slf4j.impl.Log4jLoggerAdapter) { 
     try { 
      Class loggerIntrospected = logger.getClass(); 
      Field fields[] = loggerIntrospected.getDeclaredFields(); 
      for (int i = 0; i < fields.length; i++) { 
       String fieldName = fields[i].getName(); 
       if (fieldName.equals("logger")) { 
        fields[i].setAccessible(true); 
        org.apache.log4j.Logger loggerImpl = (org.apache.log4j.Logger) fields[i] 
          .get(logger); 

        if (level == DIAGNOSTIC_LEVEL) { 
         loggerImpl.setLevel(Level.DEBUG); 
        } else { 
         loggerImpl.setLevel(org.apache.log4j.Logger.getRootLogger().getLevel()); 
        } 

        // fields[i].setAccessible(false); 
       } 
      } 
     } catch (Exception e) { 
      org.apache.log4j.Logger.getLogger(LoggerSLF4JImpl.class).error("An error was thrown while changing the Logger level", e); 
     } 
    } 

} 
+4

Это явно относится к log4j, а не к slf4j в общем –

8

Попробуйте переключиться на Logback и использование

ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME); 
rootLogger.setLevel(Level.toLevel("info")); 

Я считаю, что это будет единственный звонок для входа в систему, а остальная часть вашего кода останется без изменений. Logback использует SLF4J, и миграция будет безболезненной, необходимо будет изменить только файлы конфигурации xml.

Не забудьте установить уровень журнала после завершения.

+0

Я уже пользовался поддержкой slf4j с поддержкой журнала, и это мгновенно позволило мне очистить мои модульные тесты. Благодаря! – Lambart

+2

-1 Это не тот вопрос, о котором идет речь. – jan

+2

Это был мой первый -1, спасибо. Я считаю, что вы ошибаетесь. Logback использует SLF4J, поэтому ответ имеет значение. –

4

Любой, кто хочет получить полностью совместимое с SLF4J решение этой проблемы, возможно, захочет проверить Lidalia SLF4J Extensions - он находится на Maven Central.

1

Я только что столкнулся с подобной необходимостью. В моем случае slf4j настроен с помощью java logging adapter (jdk14 one). Используя следующий фрагмент кода, мне удалось изменить уровень отладки во время выполнения:

Logger logger = LoggerFactory.getLogger("testing"); 
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("testing"); 
julLogger.setLevel(java.util.logging.Level.FINE); 
logger.debug("hello world"); 
+1

Как и другие ответы, это не касается исходного вопроса, это другая проблема. –

0

На основании ответа Massimo Virgilio, я также удалось сделать это с SLF4J-log4j с помощью самоанализа. НТН.

Logger LOG = LoggerFactory.getLogger(MyOwnClass.class); 

org.apache.logging.slf4j.Log4jLogger LOGGER = (org.apache.logging.slf4j.Log4jLogger) LOG; 

try { 
    Class loggerIntrospected = LOGGER.getClass(); 
    Field fields[] = loggerIntrospected.getDeclaredFields(); 
    for (int i = 0; i < fields.length; i++) { 
     String fieldName = fields[i].getName(); 
     if (fieldName.equals("logger")) { 
      fields[i].setAccessible(true); 
      org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) fields[i].get(LOGGER); 
      loggerImpl.setLevel(Level.DEBUG); 
     } 
    } 
} catch (Exception e) { 
    System.out.println("ERROR :" + e.getMessage()); 
} 
7

Вы можете реализовать это, используя Java 8 lambdas.

import java.util.HashMap; 
import java.util.Map; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.slf4j.event.Level; 

public class LevelLogger { 
    private static final Logger LOGGER = LoggerFactory.getLogger(LevelLogger.class); 
    private static final Map<Level, LoggingFunction> map; 

    static { 
     map = new HashMap<>(); 
     map.put(Level.TRACE, (o) -> LOGGER.trace(o)); 
     map.put(Level.DEBUG, (o) -> LOGGER.debug(o)); 
     map.put(Level.INFO, (o) -> LOGGER.info(o)); 
     map.put(Level.WARN, (o) -> LOGGER.warn(o)); 
     map.put(Level.ERROR, (o) -> LOGGER.error(o)); 
    } 

    public static void log(Level level, String s) { 
     map.get(level).log(s); 
    } 

    @FunctionalInterface 
    private interface LoggingFunction { 
     public void log(String arg); 
    } 
} 
0

Вот лямбда решение не так удобно, как @Paul Croarkin-х в одну сторону (уровень эффективно передается дважды). Но я думаю: (а) пользователь должен пройти Logger; и (b) AFAIU исходный вопрос не требует удобного способа для повсеместно в приложении, а только ситуации с несколькими обычаями внутри библиотеки.

package test.lambda; 
import java.util.function.*; 
import org.slf4j.*; 

public class LoggerLambda { 
    private static final Logger LOG = LoggerFactory.getLogger(LoggerLambda.class); 

    private LoggerLambda() {} 

    public static void log(BiConsumer<? super String, ? super Object[]> logFunc, Supplier<Boolean> logEnabledPredicate, 
      String format, Object... args) { 
     if (logEnabledPredicate.get()) { 
      logFunc.accept(format, args); 
     } 
    } 

    public static void main(String[] args) { 
     int a = 1, b = 2, c = 3; 
     Throwable e = new Exception("something went wrong", new IllegalArgumentException()); 
     log(LOG::info, LOG::isInfoEnabled, "a = {}, b = {}, c = {}", a, b, c); 

     // warn(String, Object...) instead of warn(String, Throwable), but prints stacktrace nevertheless 
     log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e); 
    } 
} 

С SLF4J allows a Throwable (whose stack trace should be logged) inside the varargs param, я думаю, что нет никакой необходимости перегрузки методы log помощника для других потребителей, чем (String, Object[]).

0

Я смог сделать это для привязки JDK14, сначала запросив экземпляр Logger SLF4J и , затем, установив уровень на привязке - вы можете попробовать это для привязки Log4J.

private void setLevel(Class loggerClass, java.util.logging.Level level) { 
    org.slf4j.LoggerFactory.getLogger(loggerClass); 
    java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level); 
} 
Смежные вопросы