Программно настроить приложение LogBack
У меня есть приложение logback, определенное в logback.xml, это приложение DB, но мне любопытно, есть ли способ настроить appender в java, используя мой собственный пул соединений, определенный как bean.
Я нахожу похожие вещи, но не фактический ответ.
Ответы
Ответ 1
Вот простой пример, который работает для меня (обратите внимание, что я использую FileAppender в этом примере)
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
public class Loggerutils {
public static void main(String[] args) {
Logger foo = createLoggerFor("foo", "foo.log");
Logger bar = createLoggerFor("bar", "bar.log");
foo.info("test");
bar.info("bar");
}
private static Logger createLoggerFor(String string, String file) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n");
ple.setContext(lc);
ple.start();
FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
fileAppender.setFile(file);
fileAppender.setEncoder(ple);
fileAppender.setContext(lc);
fileAppender.start();
Logger logger = (Logger) LoggerFactory.getLogger(string);
logger.addAppender(fileAppender);
logger.setLevel(Level.DEBUG);
logger.setAdditive(false); /* set to true if root should log too */
return logger;
}
}
Ответ 2
Программные приложения можно настроить программно. Почти все приставки тестируются с использованием программной конфигурации. Из этого следует, что в исходном коде проекта logback имеется много примеров конфигурации программных приложений. Для приложения с поддержкой logback-core найдите под logback-core/src/test/java
, а для журнала-классического appender - под logback-classic/src/test/java
.
Ответ 3
Просто, если кто-то будет искать конкретный пример программной конфигурации.
Здесь я настраиваю кодировку ConsoleAppender:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ConsoleAppender<ILoggingEvent> appender =
(ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT");
LayoutWrappingEncoder<ILoggingEvent> enc =
(LayoutWrappingEncoder<ILoggingEvent>) appender.getEncoder();
enc.setCharset(Charset.forName("utf-8"));
И мой logback.xml:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>866</charset>
<pattern>[%level] %msg%n</pattern>
</encoder>
</appender>
<logger name="appconsole">
<appender-ref ref="STDOUT" />
</logger>
Почему мне нужно программировать конфигурацию регистратора? Потому что я упаковываю свое приложение (Spring Boot) в файл jar. Следовательно, файл Logback.xml кажется скрытым внутри банки. Хотя, неудобно распаковывать и изменять его. И мне не нужен любой файл logback.xml рядом с моим app.jar. У меня есть только файл app.yaml, который содержит все свойства конфигурации для приложения.
Ответ 4
Как ссылка, когда вы пытаетесь изменить код, ответственный за создание регистраторов, существует множество правил, которые должны выполняться для работы регистратора.
Эти правила были описаны в большой и полезной статье Программная конфигурация slf4j/logback:
Теперь у меня есть опыт программирования программной конфигурации slf4j/logback.
Task
Программа должна открыть отдельный файл журнала для каждого обработанного входного файла.
Решение задачи
Вместо того, чтобы настраивать logback через xml, нужно "вручную" создавать экземпляры кодировщиков, добавок и журналов, а затем настраивать и связывать их вместе.
Предостережение 1
Сбой при регистрации происходит при попытке совместного использования кодировщика (т.е. PatternLayoutEncoder) между приложением.
Решение для оговорки 1
Создайте отдельный кодер для каждого приложения.
Caveat 2
Резервирование отказывается записывать что-либо, если кодеры и приложения не связаны с протоколом ведения журнала.
Решение для оговорки 2
Вызов setContext для каждого кодировщика и приложения, проходящего через LoggerFactory как параметр.
Caveat 3
Отказ в регистрации не позволяет регистрировать что-либо, если энкодеры и приложения не запускаются.
Решение для оговорки 3
Кодеры и апплеты должны быть запущены в правильном порядке, то есть первые кодеры, а затем приложения.
Caveat 4
Объекты RollingPolicy (т.е. TimeBasedRollingPolicy) создают странные сообщения об ошибках, такие как "формат даты не распознается", если они не привязаны к тому же контексту, что и appender.
Решение для оговорки 4
вызов setContext на RollingPolicy так же, как и для кодеров и добавок.
Вот рабочий пример конфигурации "ручного" журнала:
package testpackage
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import org.slf4j.LoggerFactory
class TestLogConfig {
public static void main(String[] args) {
LoggerContext logCtx = LoggerFactory.getILoggerFactory()
PatternLayoutEncoder logEncoder = new PatternLayoutEncoder()
logEncoder.setContext(logCtx)
logEncoder.setPattern('%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n')
logEncoder.start()
ConsoleAppender logConsoleAppender = new ConsoleAppender()
logConsoleAppender.setContext(logCtx)
logConsoleAppender.setName('console')
logConsoleAppender.setEncoder(logEncoder)
logConsoleAppender.start()
logEncoder = new PatternLayoutEncoder()
logEncoder.setContext(logCtx)
logEncoder.setPattern('%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level - %msg%n')
logEncoder.start()
RollingFileAppender logFileAppender = new RollingFileAppender()
logFileAppender.setContext(logCtx)
logFileAppender.setName('logFile')
logFileAppender.setEncoder(logEncoder)
logFileAppender.setAppend(true)
logFileAppender.setFile('logs/logfile.log')
TimeBasedRollingPolicy logFilePolicy = new TimeBasedRollingPolicy()
logFilePolicy.setContext(logCtx)
logFilePolicy.setParent(logFileAppender)
logFilePolicy.setFileNamePattern('logs/logfile-%d{yyyy-MM-dd_HH}.log')
logFilePolicy.setMaxHistory(7)
logFilePolicy.start()
logFileAppender.setRollingPolicy(logFilePolicy)
logFileAppender.start()
Logger log = logCtx.getLogger("Main")
log.additive = false
log.level = Level.INFO
log.addAppender(logConsoleAppender)
log.addAppender(logFileAppender)
}
}
Ответ 5
Не разрешено комментировать (пока?), я просто хотел бы добавить три подсказки;
-
относительно предостережений выше, если у вас есть проблемы, просто добавьте вызов
StatusPrinter.print(context);
после того, как все настроено, то есть после добавления
добавляет root/ "Main" appender: он скажет вам, что не так.
-
Мне очень нравится разделять уровни ведения журналов в разных файлах; при поиске ошибок я начинаю с поиска в файле ошибки и т.д., установив их как
tot_[app name].log : Level.INFO
deb_[app name].log : Level.DEBUG
err_[app name].log : Level.ERROR
с помощью простого частного класса фильтра, такого как
private static class ThresholdLoggerFilter extends Filter<ILoggingEvent> {
private final Level level;
private ThresholdLoggerFilter(Level level){
this.level = level;
}
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getLevel().isGreaterOrEqual(level)) {
return FilterReply.NEUTRAL;
} else {
return FilterReply.DENY;
}
}
}
а затем просто вызовите myFilter.start()
и myAppender.addFilter(myFilter);
.
-
Наконец, я собираюсь собрать все вместе, я обычно хочу, чтобы динамически менял уровни журналов, имея в настройке какой-нибудь простой интерфейс, например
public interface LoggingService {
void setRootLogLevel(Level level);
}
сохранение уровня корневого ведения журналов в некотором файле свойств, который контролируется таким образом, что всякий раз, когда есть какой-то допустимый ввод, я просто вызываю эту службу, реализованную как
@Override
public void setRootLogLevel(Level level) {
if (context != null && context.isStarted()) {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
}
}
с моим новым уровнем корневого регистратора.