2017-03-16 1 views
1

Ich habe einen EnvironmentPostProcessor in SpringBoot erstellt, um Eigenschaften aus der Datenbank abzurufen und an die Spring Environment als PropertySource angefügt.Wie protokolliert man Fehler in einer EnvironmentPostProcessor-Ausführung

Dies ist der Code, den ich habe:

@Override 
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { 
    Map<String, Object> propertySource = new HashMap<>(); 
    // LOG SOMETHING HERE ******************* 
    logger.error("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); 
    String[] activeProfiles = environment.getActiveProfiles(); 
    String[] defaultProfiles = environment.getDefaultProfiles(); 

    // Do not pull db configuration when 'default' profile (used by Jenkins only) is run 
    if (activeProfiles.length == 0 && defaultProfiles[0] == "default") { 
     return; 
    } 

    // Load properties for Config schema 
    String dataSourceUrl = environment.getProperty("service.datasource.url"); 
    String username = environment.getProperty("service.datasource.username"); 
    String password = environment.getProperty("service.datasource.password"); 
    String driver = environment.getProperty("service.datasource.driverClassName"); 

    try { 
     // Build manually datasource to Config 
     DataSource ds = DataSourceBuilder 
       .create() 
       .username(username) 
       .password(password) 
       .url(dataSourceUrl) 
       .driverClassName(driver) 
       .build(); 

     // Fetch all properties 
     PreparedStatement preparedStatement = ds.getConnection().prepareStatement("SELECT name, value FROM propertyConfig WHERE service = ?"); 
     preparedStatement.setString(1, APP_NAME); 

     ResultSet rs = preparedStatement.executeQuery(); 

     // Populate all properties into the property source 
     while (rs.next()) { 
      String propName = rs.getString("name"); 
      propertySource.put(propName, rs.getString("value")); 
     } 

     // Create a custom property source with the highest precedence and add it to Spring Environment 
     environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource)); 

    } catch (Exception e) { 
     throw new Exception("Error fetching properties from ServiceConfig"); 
    } 
} 

Und das ist die main/META-INF/spring-factories Datei erstellt werden musste:

# Environment Post Processor 
org.springframework.boot.env.EnvironmentPostProcessor=com.blabla.config.ReadDbPropertiesPostProcessor 

Der Code funktioniert gut, es holt aus dem db, was ich brauche. Ich möchte jedoch Informationen darüber protokollieren, falls etwas nicht in Ordnung ist, zum Beispiel, wenn db heruntergefahren ist. Ich möchte einen Fehler protokollieren und den Start der App stoppen. Meine App ist so konfiguriert, dass sie den Logger und nicht die Konsole verwendet.

Ich habe versucht, den Fehler zu protokollieren, Ausnahmen zu werfen, auch etwas auszudrucken, aber mein Protokoll protokolliert diese Informationen nie.

Wie kann ich den Logger während dieser frühen Frühlingsphase benutzen? Ist es möglich, dies überhaupt zu tun? Benutze ich EnvironmentPostProcessor falsch?

Antwort

1

Das Problem hier ist, dass das Protokollierungssystem erst initialisiert wird, nachdem der Federkontext initialisiert wurde. Wenn die Protokollmethode aufgerufen wird, weiß das Protokollsystem nicht, was mit den Informationen zu tun ist, und es tut nichts.

Es gibt keine elegante Möglichkeit, dieses Problem zu lösen. Sie werden entweder das Spring-Managed-Log-System los oder benutzen Deferred-Log-Mechanismen (genau wie der Spring intern).

Um DeferredLog verwenden zu können, müssen Sie sicherstellen, dass das System nach der Kontextinitialisierung die Wiederholung der Protokolle anfordert.

Dies ist eine der Möglichkeiten, wie sie erreicht werden könnte:

@Component 
public class MyEnvironmentPostProcessor implements 
     EnvironmentPostProcessor, ApplicationListener<ApplicationEvent> { 

    private static final DeferredLog log = new DeferredLog(); 

    @Override 
    public void postProcessEnvironment(
      ConfigurableEnvironment env, SpringApplication app) { 
     log.error("This should be printed"); 
    } 

    @Override 
    public void onApplicationEvent(ApplicationEvent event) { 
     log.replayTo(MyEnvironmentPostProcessor.class); 
    } 
} 

In diesem Beispiel jede Protokollmeldung in den DeferredLog zwischengespeichert. Und sobald der Kontext initialisiert ist, ruft das System onApplicationEvent auf. Diese Methode wird alle zwischengespeicherten Protokollereignisse für den Standardprotokollierer wiedergeben.

HINWEIS: Ich habe ApplicationListener hier verwendet, aber Sie können jede bequeme Art und Weise verwenden. Die Idee ist, DeferredLog.replayTo() einmal Context initialisiert und es ist egal, von welchem ​​Ort Sie es nennen.

PS: Die Position von spring.factories sollte src/main/resources/META-INF sein, ansonsten postProcessEnvironment wird möglicherweise nicht aufgerufen.

+0

danke für die Antwort. Es ist sehr hilfreich ... werde das gerade jetzt testen. Möchten Sie etwas klären, wie binde ich das ApplicationEvent? Gibt es einen Platz, um sich für diese Events zu registrieren/registrieren oder wird dies automatisch von Springboot erkannt? –

+1

Sobald Sie "ApplicationListener" implementieren und Ihre Klasse mit "@ Component" annotieren, wird Spring automatisch "ApplicationEvent" abonnieren. –

+0

eine letzte Frage, wenn Sie meinen Code sehen ... es hat einen catch-Block und löst eine Laufzeitausnahme aus. Wenn die Ausnahme ausgelöst wird, wird das Protokoll nie konfiguriert. Wissen Sie, wie dieser Fall behandelt wird? –

Verwandte Themen