Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

212
Vistas
How to log errors in a EnvironmentPostProcessor execution

I have created an EnvironmentPostProcessor in SpringBoot to fetch properties from database and attached it to the Spring's Environment as a PropertySource.

This is the code I have:

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

And this is the main/META-INF/spring-factories file had to be created:

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

The code works well, it fetches from the db what I need. However, I would like to log information about this in case something wrong occurs, for instance if db is down I want to log an error and stop the app to start. My app is configured to use logger and not the console.

I have tried logging the error, throwing exceptions, also printing out something but my log is never logging this information.

How can I do to use the logger during this early spring stage? Is it possible to do this in anyway? Am I using EnvironmentPostProcessor wrongly?

about 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

The problem here is that logging system initialized only after spring context is initialized. When the log method is invoked the log system does not know what to do with the information and it does nothing.

There is no elegant way to solve this issue. You either get rid of spring-managed log system or use deferred log mechanisms (just like spring does internally).

To be able to use DeferredLog you have to make sure that after context initialization the system will request to replay logs.

Here is one of the ways how it could be achieved:

@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 this example every log message is cached in the DeferredLog. And once the context initialized the system will call onApplicationEvent. This method will replay all the cached log-events to the standard logger.

NOTE: I used ApplicationListener here but you can use every convenient way. The idea is to call DeferredLog.replayTo() once context initialized and it does not matter from which place you call it.

PS: The location of spring.factories should be src/main/resources/META-INF otherwise postProcessEnvironment might not be invoked.

about 4 years ago · Santiago Trujillo Denunciar

0

As noted in the accepted answer, the problem is the logging system isn't initialized yet when EnvironmentPostProcessors are run.

However, using a mechanism like a static DeferredLog in EnvironmentPostProcessor to store the logs temporarily, then replay them in a ApplicationListener<ApplicationPreparedEvent> (once the logging system is initialized) does not work either because the EnvironmentPostProcessor and ApplicationListener are loaded and initialized by different class loaders.

Because of that, the instance of the Class used for the ApplicationListener has no visibility into the instance of the Class used as the EnvironmentPostProcessor (even if they are in fact the same class).

One hack would be to use System.setProperty(...) to set what you want to log out in the EnvironmentPostProcessor, and System.getProperty(...) in the ApplicationListener to log it out. This avoids the issue with Spring's class loaders. I definitely recommend against using this approach, but it does work.

YMMV, but in my case I found that moving the custom environment setup logic from an EnvironmentPostProcessor to an ApplicationListener<ApplicationPreparedEvent> worked just fine for me, logging included.

Spring Application Events reference: https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/spring-boot-features.html#boot-features-application-events-and-listeners


UPDATE: Based on shaohua-shi's answer, here is a simple working solution that uses an ApplicationContextInitializer to replay a DeferredLog after the logging system has been initialized:

public class MyEnvPostProcessor implements EnvironmentPostProcessor {

    private DeferredLog log = new DeferredLog();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
        app.addInitializers(ctx -> log.replayTo(MyEnvPostProcessor.class));

        log.warn("In Env Post Processor");
    }
}

Log:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

2020-06-25 20:51:15.118  WARN 7297 --- [  restartedMain] c.e.testenvpostproc.MyEnvPostProcessor   : In Env Post Processor
about 4 years ago · Santiago Trujillo Denunciar

0

I found! invoke addInitializers when you execut postProcessEnvironment.

public class MyEnvironmentProcessor implements EnvironmentPostProcessor, Ordered {

    public static final int ORDER = Ordered.LOWEST_PRECEDENCE - 6;

    private DeferredLog logger = new DeferredLog();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
        app.addInitializers(new MyContextInitializer(this));
        logger.error("---------------MyEnvironmentProcessor---------------");
    }
    public DeferredLog getLogger() {
        return logger;
    }
}
public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    private final Logger logger = LoggerFactory.getLogger(I3keContextInitializer.class);

    MyEnvironmentProcessor processor;

    public MyContextInitializer(MyEnvironmentProcessor processor) {
        this.processor = processor;
    }

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        logger.warn("------------> DeferredLog:");
        processor.getLogger().replayTo(MyEnvironmentProcessor.class);
    }
}

show log

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

06-10 15:48:59.874  WARN 38518 --- [restartedMain] c.s.e.init.MyContextInitializer       |21 : ------------> DeferredLog:
06-10 15:48:59.885 ERROR 38518 --- [restartedMain] c.s.e.e.MyEnvironmentProcessor        |231 : ---------------MyEnvironmentProcessor---------------
about 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda