Dropwizard non registra i logger personalizzati sul file

Ho un’app dropwizard, in cui ho configurato gli appendici logger per file come segue:

logging: level: INFO loggers: "mylogger": INFO "com.path.to.class": INFO appenders: - type: file currentLogFilename: .logs/mylogs.log archivedLogFilenamePattern: .logs/archive.%d.log.gz archivedFileCount: 14 

E, creato logger nella mia app:

 import org.slf4j.Logger; import org.slf4j.LoggerFactory; private final Logger OpLogger = LoggerFactory.getLogger("mylogger"); (and) private final Logger ClassLogger = LoggerFactory.getLogger(pathToClass.class); 

Effettua qualche registrazione di prova in main ():

 OpLogger.info("test 1"); ClassLogger.info("test 2); 

L’applicazione si avvia e viene eseguita senza problemi; ma non ottengo alcun registro (ad eccezione dei log di accesso al molo, ovviamente, che sono correttamente stampati su mylogs.log), né in stdout o nel file mylogs.log. Invece, se rimuovo la configurazione dei logger in configuration.yml, ottengo tutti i log stampati su stdout. Forse è un problema di dropwizard o devo aggiungere qualcosa a configuration.yml? Sto usando Dropwizard 0.8.0

AGGIORNAMENTO L’ultima versione di dropwizard supporta le configurazioni di registrazione fuori dalla scatola

Mi sono imbattuto nello stesso problema cercando di configurare Dropwizard (0.8.4) con un file separato. Mi sono imbattuto nello stesso problema. Così ho scavato un po ‘più a fondo e ho trovato una soluzione per me (non la più pulita ma non riuscivo a farlo funzionare in modo diverso).

Il problema è che LoggingFactory#configure aggiunge automaticamente ogni appender a root. Questo non è molto ideale quindi era necessario sovrascrivere. Quello che ho fatto è stato:

  1. Sovrascrivi LoggingFactory .

Questo è un po ‘caotico dato che ci sono alcune cose che devono essere copiate tristemente 🙁 Ecco la mia implementazione:

 import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.util.Map; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformsdObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.logback.InstrumentedAppender; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.jmx.JMXConfigurator; import ch.qos.logback.classic.jul.LevelChangePropagator; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.util.StatusPrinter; import io.dropwizard.logging.AppenderFactory; import io.dropwizard.logging.LoggingFactory; public class BetterDropWizardLoggingConfig extends LoggingFactory { @JsonIgnore final LoggerContext loggerContext; @JsonIgnore final PrintStream configurationErrorsStream; @JsonProperty("loggerMapping") private ImmutableMap loggerMappings; private static void hijackJDKLogging() { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); } public BetterDropWizardLoggingConfig() { PatternLayout.defaultConverterMap.put("h", HostNameConverter.class.getName()); this.loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); this.configurationErrorsStream = System.err; } private Logger configureLevels() { final Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); loggerContext.reset(); final LevelChangePropagator propagator = new LevelChangePropagator(); propagator.setContext(loggerContext); propagator.setResetJUL(true); loggerContext.addListener(propagator); root.setLevel(getLevel()); for (Map.Entry entry : getLoggers().entrySet()) { loggerContext.getLogger(entry.getKey()).setLevel(entry.getValue()); } return root; } @Override public void configure(MetricRegistry metricRegistry, String name) { hijackJDKLogging(); final Logger root = configureLevels(); for (AppenderFactory output : getAppenders()) { Appender build = output.build(loggerContext, name, null); if(output instanceof MappedLogger && ((MappedLogger) output).getLoggerName() != null) { String appenderName = ((MappedLogger) output).getLoggerName(); String loggerName = loggerMappings.get(appenderName); Logger logger = this.loggerContext.getLogger(loggerName); logger.addAppender(build); } else { root.addAppender(build); } } StatusPrinter.setPrintStream(configurationErrorsStream); try { StatusPrinter.printIfErrorsOccured(loggerContext); } finally { StatusPrinter.setPrintStream(System.out); } final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); try { final ObjectName objectName = new ObjectName("io.dropwizard:type=Logging"); if (!server.isRegistered(objectName)) { server.registerMBean(new JMXConfigurator(loggerContext, server, objectName), objectName); } } catch (MalformsdObjectNameException | InstanceAlreadyExistsException | NotCompliantMBeanException | MBeanRegistrationException e) { throw new RuntimeException(e); } configureInstrumentation(root, metricRegistry); } private void configureInstrumentation(Logger root, MetricRegistry metricRegistry) { final InstrumentedAppender appender = new InstrumentedAppender(metricRegistry); appender.setContext(loggerContext); appender.start(); root.addAppender(appender); } } 

Come puoi vedere, ho dovuto copiare / incollare alcuni membri e metodi privati ​​per far funzionare le cose come previsto.

Ho aggiunto un nuovo campo:

 @JsonProperty("loggerMapping") private ImmutableMap loggerMappings; 

Questo mi permette di configurare una mapping per ogni logger. Questo non era fuori dalla scatola permesso in quanto non riesco a ottenere un nome (dropwizard imposta i nomi dei nomi degli appender, molto scomodo …)

Così ho aggiunto un nuovo Logger che nel mio caso fa anche la sostituzione del nome host di cui avevo bisogno per motivi diversi. Per questo sovrascrivo il buon vecchio FileAppenderFactory e implemento la mia interfaccia MappedLogger . Implementazione qui:

 import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.rolling.RollingFileAppender; import io.dropwizard.logging.AppenderFactory; import io.dropwizard.logging.FileAppenderFactory; @JsonTypeName("hostnameFile") public class HostnameFileAppender extends FileAppenderFactory implements AppenderFactory, MappedLogger { private static String uuid = UUID.randomUUID().toString(); @JsonProperty private String name; public void setCurrentLogFilename(String currentLogFilename) { super.setCurrentLogFilename(substitute(currentLogFilename)); } private String substitute(final String pattern) { String substitute = null; try { substitute = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { System.err.println("Failed to get local hostname:"); e.printStackTrace(System.err); substitute = uuid; System.err.println("Using " + substitute + " as fallback."); } return pattern.replace("${HOSTNAME}", substitute); } @Override public void setArchivedLogFilenamePattern(String archivedLogFilenamePattern) { super.setArchivedLogFilenamePattern(substitute(archivedLogFilenamePattern)); } @Override public String getLoggerName() { return name; } } 

Si noti che per aggiungere un nuovo tipo JSON, si dovrà seguire il JavaDoc in AppenderFactory (aggiungere Meta-inf al classpath e rendere il nuovo appender rilevabile)

Fin qui tutto bene, ora abbiamo una configurazione che può raccogliere i mapping del logger, abbiamo un logger che può avere un nome opzionale.

Nel metodo configure ora li lego insieme:

 for (AppenderFactory output : getAppenders()) { Appender build = output.build(loggerContext, name, null); if(output instanceof MappedLogger && ((MappedLogger) output).getLoggerName() != null) { String appenderName = ((MappedLogger) output).getLoggerName(); String loggerName = loggerMappings.get(appenderName); Logger logger = this.loggerContext.getLogger(loggerName); logger.addAppender(build); } else { root.addAppender(build); } } 

Per compatibilità con le versioni precedenti ho mantenuto il comportamento predefinito. Se non è definito alcun nome, l’appender verrà aggiunto al logger principale. Altrimenti risolvo il logger digitato e aggiungo l’appender come desiderato.

E, ultimo ma non meno importante, il buon vecchio yaml config:

 logging: # The default level of all loggers. Can be OFF, ERROR, WARN, INFO, DEBUG, TRACE, or ALL. level: INFO loggers: "EVENT" : INFO loggerMapping: # for easier search this is defined as: appenderName -> loggerName rather than the other way around "eventLog" : "EVENT" appenders: - type: console threshold: ALL logFormat: "myformat" - type: hostnameFile # NOTE THE NEW TYPE WITH HOSTNAME RESOLVE currentLogFilename: /Users/artur/tmp/log/my-${HOSTNAME}.log threshold: ALL archive: true archivedLogFilenamePattern: mypattern archivedFileCount: 31 timeZone: UTC logFormat: "myFormat" - type: hostnameFile name: eventLog # NOTE THE APPENDER NAME currentLogFilename: something threshold: ALL archive: true archivedLogFilenamePattern: something archivedFileCount: 31 timeZone: UTC logFormat: "myFormat" - type: hostnameFile currentLogFilename: something threshold: ERROR archive: true archivedLogFilenamePattern: something archivedFileCount: 31 timeZone: UTC logFormat: "myFormat" 

Come puoi vedere sto mappando l’appender degli eventi al logger degli eventi. In questo modo tutti i miei eventi finiscono nel file A, mentre le altre informazioni finiscono altrove.

Spero che aiuti. Potrebbe non essere la soluzione più pulita, ma non credo che Dropwizard consenta attualmente questa funzione.

È ansible implementare il logger separato con il dropwizard utilizzando il logback.

1. Configurare il logger nella class Application (ovvero il punto iniziale dell’applicazione con il metodo principale) come di seguito.

 LoggerContext context = (LoggerContext)LoggerFactory.getILoggerFactory(); context.reset(); ContextInitializer initializer = new ContextInitializer(context); initializer.autoConfig(); 

2. Configurare logback.xml come di seguito.

    /var/log/applicationname-mylogger.log   logFile.%d{yyyy-MM-dd}.log  30  false  %-5relative %-5level %logger{35} - %msg%n    /var/log/applicationame-com.path.to.class.log   logFile.%d{yyyy-MM-dd}.log  30  false  %-5relative %-5level %logger{35} - %msg%n            

3. Ora utilizzare il logger

 static final Logger OpLogger = LoggerFactory.getLogger("mylogger"); static final Logger classLogger = LoggerFactory.getLogger("com.path.to.class"); 

MODIFICARE :

Ho provato a implementare lo stesso logger nel mio progetto di esempio. Funziona bene nel mio caso. Non è ansible utilizzare LOGGER prima dell’inizializzazione dell’applicazione Dropwizard. Il Dropwizard è stato inizializzato solo quando si chiama

  new ExampleApplication().run(args); 

Quindi, se il logger viene utilizzato prima dell’inizializzazione di Dropwizard, il log non sarà presente. Ho provato a implementare lo scenario con il metodo principale. La prima istruzione di registro non viene stampata poiché abbiamo utilizzato il logger prima dell’inizializzazione di Dropwizard, ma verrà stampata la seconda istruzione di registro.

  OpLogger.info("test 1"); new ExampleApplication().run(args); ClassLogger.info("test 2); 

Spero che questo ti aiuti a risolvere il tuo problema.