java - pattern - Configurar programáticamente LogBack appender
logback pattern (5)
Tengo un apéndice de inicio de sesión definido en logback.xml, es un appender de base de datos, pero tengo curiosidad de saber si hay alguna forma de configurar el appender en java utilizando mi propio grupo de conexiones definido como un bean.
Encuentro cosas similares, pero nunca la respuesta real.
Aquí un ejemplo simple que funciona para mí (tenga en cuenta que utilizo FileAppender en este ejemplo)
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;
}
}
Como referencia, cuando intenta modificar el código responsable de crear registradores, hay un conjunto de reglas que deben cumplirse para que funcione un registrador.
Estas reglas se describieron en un artículo excelente y útil . Configuración programática de slf4j / logback :
Ahora tengo experiencia con la configuración programática de slf4j / logback.
Tarea
Un programa debe abrir un archivo de registro separado para cada archivo de entrada procesado.
Solución para la tarea
En lugar de configurar logback vía xml, se necesita instanciar "manualmente" encoders, appenders y loggers, luego configurarlos y vincularlos.
Advertencia 1
El logback se vuelve loco al intentar compartir el codificador (es decir, PatternLayoutEncoder) entre los appenders.
Solución para la advertencia 1
Crea un codificador separado para cada appender.
Advertencia 2
Logback se niega a registrar nada, si los codificadores y anexos no están asociados con el contexto de registro.
Solución para la advertencia 2
Llame a setContext en cada codificador y appender, pasando LoggerFactory como parámetro.
Advertencia 3
Logback se niega a registrar nada, si los codificadores y los appenders no se inician.
Solución para la advertencia 3
los codificadores y anexos deben iniciarse en el orden correcto, es decir, primero codificadores y anexos.
Advertencia 4
Los objetos RollingPolicy (es decir, TimeBasedRollingPolicy) producen mensajes de error extraños como "formato de fecha no reconocido", cuando no están adjuntos al mismo contexto que appender.
Solución para caveat 4
llame a setContext en RollingPolicy de la misma manera que en codificadores y anexos.
Aquí está el ejemplo de trabajo de la configuración de logback "manual":
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)
}
}
No puedo comentar (¿todavía?), Me gustaría agregar tres consejos;
con respecto a las advertencias anteriores, si tiene problemas, simplemente agregue una llamada a
StatusPrinter.print(context);
después de que todo ha sido configurado, es decir, después de haber agregado sus apéndices al apilador raíz / "Principal": le dirá qué está mal.
Me gusta mucho separar los niveles de registro en diferentes archivos; cuando busco errores, empiezo buscando en el archivo de error, etc., configurándolos como
tot_[app name].log : Level.INFO deb_[app name].log : Level.DEBUG err_[app name].log : Level.ERROR
enrutamiento por medio de una clase de filtro privada simple como
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;
}
}
}
y luego simplemente llame a myFilter.start()
y myAppender.addFilter(myFilter);
.
Por último, al unirlo, generalmente quiero poder cambiar los niveles de registro dinámicamente teniendo la configuración implementar una interfaz simple como
public interface LoggingService { void setRootLogLevel(Level level); }
manteniendo el nivel de registro de raíz en algún archivo de propiedad que se monitorea para que siempre que haya alguna entrada válida allí, simplemente llame a este servicio implementado como
@Override
public void setRootLogLevel(Level level) {
if (context != null && context.isStarted()) {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(level);
}
}
con mi nuevo nivel de registrador de raíz.
Puede configurar appenders mediante programación. Casi todos los anexos se prueban usando la configuración programática. Se deduce que hay muchos ejemplos de configuración de agregador programático en el código fuente del proyecto de logback. Para un apilador logback-core, busque en logback-core/src/test/java
, y para un apéndice logback-classic en logback-classic/src/test/java
.
Simplemente, si alguien buscara un ejemplo concreto de configuración programática.
Aquí configuro el juego de caracteres de 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"));
Y mi 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>
¿Por qué necesito configurar programáticamente un registrador? Porque empaqueté mi aplicación (Spring Boot) en un archivo jar. En consecuencia, parece que el archivo Logback.xml se oculta dentro de un jar. Sin embargo, no es conveniente desempaquetar y cambiarlo. Y no necesito ningún archivo logback.xml junto a mi app.jar. Solo tengo el archivo app.yaml que contiene todas las propiedades de configuración para la aplicación.