Установка уровня журнала сообщений во время выполнения в slf4j
при использовании log4j,Logger.log(Priority p, Object message) метод доступен и может использоваться для регистрации сообщения на уровне журнала, определенном во время выполнения. Мы используем этот факт и этот совет для перенаправления stderr на регистратор на определенном уровне журнала.
slf4j не имеет общего log() метод, который я могу найти. Означает ли это, что нет никакого способа реализовать вышеизложенное?
12 ответов:
нет никакого способа сделать это с помощью
slf4j.Я полагаю, что причина отсутствия этой функции заключается в том, что практически невозможно построить
Levelтипslf4jкоторые могут быть эффективно сопоставлены сLevel(или эквивалентный) тип, используемый во всех возможных реализациях ведения журнала за фасадом. В качестве альтернативы дизайнеры решили, что ваш случай слишком необычен для обоснования накладных расходов на поддержку оно.о @ripper234 ' s use-case (модульное тестирование), я думаю, что прагматическое решение-это изменить модульный тест(ы) на знание жесткого провода о том, какая система регистрации находится за фасадом slf4j ... при выполнении модульных тестов.
Ричард Фирн имеет правильную идею, поэтому я написал полный класс, основанный на его скелетном коде. Надеюсь, это достаточно коротко, чтобы опубликовать здесь. Скопируйте и вставьте для удовольствия. Вероятно, мне также следует добавить магическое заклинание: "этот код выпущен в общественное достояние"
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; } }
вы можете реализовать это с помощью 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); } }
попробуйте переключиться на 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 и остальная часть вашего кода останется неизменной. Logback использует SLF4J, и миграция будет безболезненной, просто файлы конфигурации xml должны быть изменены.
Не забудьте установить уровень журнала после того, как вы закончите.
Это можно сделать с помощью
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, скажем, если вам нужны общие эквиваленты 1-параметра SLF4J или 2-параметраwarn/error/ etc. методы.
любой, кто хочет полностью совместимое с SLF4J решение этой проблемы, может захотеть проверить Lidalia SLF4J расширения - это в Maven Центральной.
Я только что столкнулся с подобной необходимостью. В моем случае slf4j настроен с адаптером ведения журнала java (jdk14). Используя следующий фрагмент кода мне удалось изменить уровень отладки во время выполнения:
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");
основываясь на ответе Массимо Вирджилио, мне также удалось сделать это с помощью 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()); }
вот лямбда-решение не так удобно, как @Paul Croarkin в одном из способов (уровень эффективно передается дважды). Но я думаю, что (а) пользователь должен передать регистратор; и (Б) 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 позволяет Throwable (чья трассировка стека должна быть зарегистрирована) внутри varargs param, Я думаю, что нет необходимости для перегрузки
logвспомогательный метод для других потребителей, чем(String, Object[]).
я смог сделать это для привязки JDK14, сначала запросив экземпляр регистратора 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); }
используя Java introspection вы можете сделать это, например:
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); } } }
нет, он имеет ряд методов, info (), debug (), warn () и т. д. (Это заменяет поле приоритета)
взгляните на http://www.slf4j.org/api/org/slf4j/Logger.html для полного api регистратора.
Comments