Programmare appender LogBack a livello di programmazione

Ho un app di logback definito nel logback.xml, è un appender del DB, ma sono curioso di sapere se c’è un modo per configurare l’appender in java usando il mio pool di connessioni definito come bean.

Trovo cose simili, ma mai la risposta vera.

Ecco un semplice esempio che funziona per me (nota che io uso FileAppender in questo esempio)

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 fileAppender = new FileAppender(); 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; } } 

È ansible configurare le appendici a livello di codice. Quasi tutte le appendici vengono testate utilizzando la configurazione programmatica. Ne consegue che esistono molti esempi di configurazione dell’appender programmatico nel codice sorgente del progetto di logback. Per un appender di logback-core, guarda sotto logback-core/src/test/java e per un look appender di logback-classico sotto logback-classic/src/test/java .

Come riferimento, quando si tenta di modificare il codice responsabile della creazione dei logger, vi è un certo numero di regole che devono essere soddisfatte affinché il logger funzioni.

Queste regole sono state descritte in un grande e utile articolo Configurazione programmatica di slf4j / logback :

Ora ho esperienza con la configurazione programmatica di slf4j / logback.

Compito

Un programma deve aprire un file di registro separato per ogni file di input elaborato.

Soluzione per compito

Invece di configurare il logback tramite xml, è necessario “manualmente” istanziare encoder, appendici e logger, quindi configurarli e collegarli insieme.

Avvertenza 1

Il logback impazzisce nel tentativo di condividere il codificatore (ad esempio PatternLayoutEncoder) tra gli appendici.

Soluzione per l’avvertimento 1

Crea un codificatore separato per ogni appender.

Avvertenza 2

Logback rifiuta di registrare qualsiasi cosa, se encoders e appenders non sono associati al contesto di logging.

Soluzione per l’avvertimento 2

Chiama setContext su ogni codificatore e appender, passando LoggerFactory come parametro.

Avvertenza 3

Logback rifiuta di registrare qualsiasi cosa, se non sono stati avviati codificatori e appendici.

Soluzione per l’avvertenza 3

gli encoder e gli appendici devono essere avviati nell’ordine corretto, ovvero primi encoder, quindi appendici.

Avvertenza 4

Gli oggetti RollingPolicy (ad esempio TimeBasedRollingPolicy) producono strani messaggi di errore come “formato data non riconosciuto”, quando non sono collegati allo stesso contesto dell’appender.

Soluzione per l’avvertimento 4

chiama setContext su RollingPolicy allo stesso modo degli encoder e degli appendici.

Ecco un esempio funzionante di configurazione di logback “manuale”:

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

Solo, se qualcuno cercasse un esempio concreto di configurazione programmatica.

Qui ho impostato il set di caratteri di ConsoleAppender:

 LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); ConsoleAppender appender = (ConsoleAppender) lc.getLogger("appconsole").getAppender("STDOUT"); LayoutWrappingEncoder enc = (LayoutWrappingEncoder) appender.getEncoder(); enc.setCharset(Charset.forName("utf-8")); 

E il mio logback.xml:

   866 [%level] %msg%n      

Perché ho bisogno di programmare un logger? Perché, impacchetta la mia app (Spring Boot) in un file jar. Di conseguenza, sembra che il file Logback.xml sia nascosto all’interno di un jar. Tuttavia, non è conveniente per decomprimere e cambiarlo. E non ho bisogno di alcun file logback.xml accanto alla mia app.jar. Ho solo il file app.yaml che contiene tutte le proprietà di configurazione per l’app.

Non è permesso commentare (ancora?), Vorrei solo aggiungere tre suggerimenti;

  • per quanto riguarda le avvertenze sopra, se hai problemi, aggiungi una chiamata a

     StatusPrinter.print(context); 

    dopo che tutto è stato configurato, cioè dopo aver aggiunto gli appendici alla radice / appender “Principale”: ti dirà cosa è sbagliato.

  • Mi piace molto separare i livelli di registrazione in diversi file; quando cerco errori, comincio a cercare nel file di errore e così via, impostandoli come

 tot_[app name].log : Level.INFO deb_[app name].log : Level.DEBUG err_[app name].log : Level.ERROR 

routing mediante una semplice class di filtro privata come

  private static class ThresholdLoggerFilter extends Filter { 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; } } } 

e quindi chiama semplicemente myFilter.start() e myAppender.addFilter(myFilter); .

  • Infine, mettendo insieme, di solito voglio essere in grado di modificare i livelli di registro in modo dinamico con l’installazione implementare alcune semplici interfacce come

     public interface LoggingService { void setRootLogLevel(Level level); } 

mantenendo il livello di registrazione di root in qualche file di proprietà che viene monitorato in modo che ogni volta che ci sia qualche input valido, io chiamo solo questo servizio implementato come

  @Override public void setRootLogLevel(Level level) { if (context != null && context.isStarted()) { ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level); } } 

con il mio nuovo livello di root logger.