2014-12-15 12 views
6

ho un app dropwizard, dove ho configurato appenders logger di file come segue:Dropwizard non registra logger personalizzati per archiviare

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, logger creato 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); 

Effettuare alcune attività di registrazione principale():

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

L'applicazione viene avviata 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

+0

Si dispone già del livello di registrazione predefinito INFO, l'assegnazione di INFO a determinate classi è ridondante a questo punto. Solo FYI. – Natan

risposta

7

UPDATE L'ultima versione di dropwizard supporta configurazioni di registrazione fuori dalla scatola

Ho incontrato lo stesso problema cercando di creare Dropwizard (0.8.4) con un file separati. 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 utente alla radice. Questo non è molto ideale quindi era necessario sovrascrivere. Quello che ho fatto è stato:

  1. Sovrascrivere LoggingFactory.

Questo è un po 'disordinato poiché non v'è alcune cose che devono essere copiati purtroppo :(Qui è la mia realizzazione:

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.MalformedObjectNameException; 
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<String, String> 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<String, Level> 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<ILoggingEvent> 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 (MalformedObjectNameException | 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 potete sé, ho purtroppo dovuto copiare/incollare un paio membri privati ​​e metodi per rendere le cose funzionano come previsto

ho aggiunto un nuovo campo:.

@JsonProperty("loggerMapping") 
private ImmutableMap<String, String> loggerMappings; 

Questo mi permette di configurare un ma pping per ogni logger. Questo non era fuori dalla scatola permesso come non riesco a ottenere un nome (dropwizard imposta i nomi dell'appender, molto scomodo ...)

Così ho aggiunto un nuovo Logger che nel mio caso fa anche la sostituzione del nome host che necessario per diversi motivi. Per questo sovrascrivo il buon vecchio FileAppenderFactory e implementa la mia interfaccia MappedLogger. Attuazione 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; 
    } 
} 

prega di notare che, al fine di aggiungere un nuovo tipo JSON, si dovrà seguire la JavaDoc in AppenderFactory (Aggiungi 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 di configurazione ora legare questi due insieme:

for (AppenderFactory output : getAppenders()) { 
     Appender<ILoggingEvent> 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à all'indietro ho osservato 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 potete vedere sto mappando i fatti appender al eventi logger. In questo modo tutti i miei eventi finiscono nel file A, mentre le altre informazioni finiscono altrove.

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

+0

Ehi - sarebbe possibile includere anche l'importazione del tuo esempio? Grazie. – Ztyx

+0

Sicuro. L'ho appena aggiornato. – pandaadb

+0

Fantastico. Grazie! – Ztyx

5

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

1.Configura il logger nella classe Application (cioè il punto di inizio dell'applicazione con il metodo principale) come di seguito.

2. Configurare logback.xml come di seguito.

<?xml version="1.0" encoding="UTF-8"?> 
<configuration> 
<appender name="OpLogger " class="ch.qos.logback.core.FileAppender"> 
    <file>/var/log/applicationname-mylogger.log</file> 
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
     <!-- daily rollover --> 
     <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> 
     <!-- keep 30 days' worth of history --> 
     <maxHistory>30</maxHistory> 
    </rollingPolicy> 
    <append>false</append> 
    <encoder> 
     <pattern>%-5relative %-5level %logger{35} - %msg%n</pattern> 
    </encoder> 
</appender> 
<appender name="classLogger" class="ch.qos.logback.core.FileAppender"> 
    <file>/var/log/applicationame-com.path.to.class.log</file> 
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> 
     <!-- daily rollover --> 
     <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> 
     <!-- keep 30 days' worth of history --> 
     <maxHistory>30</maxHistory> 
    </rollingPolicy> 
    <append>false</append> 
    <encoder> 
     <pattern>%-5relative %-5level %logger{35} - %msg%n</pattern> 
    </encoder> 
</appender> 
<logger name="mylogger"> 
    <level value="INFO" /> 
    <appender-ref ref="OpLogger" /> 
</logger> 
<logger name="com.path.to.class"> 
    <level value="INFO" /> 
    <appender-ref ref="classLogger" /> 
</logger> 
</configuration> 

uso 3.Now logger

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

EDIT:

ho cercare di implementare la stessa data logger nel mio progetto di esempio. Funziona bene nel mio caso. Non è possibile utilizzare LOGGER prima dell'inizializzazione dell'applicazione Dropwizard. L'Dropwizard inizializzato solo quando si chiama

new ExampleApplication().run(args); 

Quindi, se logger viene utilizzato prima Dropwizard inizializzato, il registro non ci sarà. 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.

+0

Grazie per la risposta; ma ho già Dropwizard con Logback incorporato; non dovrei configurare solo il file configuration.yml? –

+0

Sì, Dropwizard utilizza il logback internamente. Potete per favore pubblicare il vostro codice metodo principale dove provate ad usare il logger e chiamare a nuovo ExampleApplication(). Run (args); ? Vedi anche la mia risposta modificata. –