August 29, 2018

Micronaut Mastery: Adding Custom Info To Info Endpoint

In a previous post we learned how to add build information to the /info endpoint in our application. We can add custom information to the /info endpoint via our application configuration or via a bean by implementing the InfoSource interface. Remember we need to add the io.micronaut:management dependency to our application for this feature.

Any configuration property that is prefixed with info will be exposed via the /info endpoint. We can define the properties in configuration files, using Java system properties or system environment variables. In the following example configuration file we add the property info.sample.message:

// File: src/main/resources/application.yml
...
info:
  sample:
    message: Micronaut is awesome

Another option is to create a new class that implements the InfoSource interface. We need to override the getSource method to return a Publisher with a PropertySource. In our example application we have a ConferenceRepository bean that can access a database with data about conferences. In the following example InfoSource implementation we want to return the number of conferences that are stored in the database:

package mrhaki.micronaut;

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.MapPropertySource;
import io.micronaut.context.env.PropertySource;
import io.micronaut.core.async.SupplierUtil;
import io.micronaut.management.endpoint.info.InfoEndpoint;
import io.micronaut.management.endpoint.info.InfoSource;
import io.micronaut.runtime.context.scope.Refreshable;
import io.reactivex.Flowable;
import org.reactivestreams.Publisher;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

// Component is refreshable via a refresh event
@Refreshable
// Only create this component when the following 
// requirements are met:
@Requires(beans = InfoEndpoint.class)
@Requires(property = "endpoints.info.config.enabled", notEquals = "false")
public class DatabaseInfoSource implements InfoSource {

    /**
     * Repository to fetch count of conferences in the database.
     */
    private final ConferenceRepository conferenceRepository;

    /**
     * Supplier for returning result.
     */
    private final Supplier<PropertySource> supplier;

    public DatabaseInfoSource(final ConferenceRepository conferenceRepository) {
        this.conferenceRepository = conferenceRepository;
        
        // Cache result from database.
        // This component is refreshable via 
        // a refresh event to get the latest value
        // from the database.
        this.supplier = SupplierUtil.memoized(this::retrieveDatabaseInfo);
    }

    /**
     * Return information.
     * 
     * @return Information about the database.
     */
    @Override
    public Publisher<PropertySource> getSource() {
        return Flowable.just(supplier.get());
    }

    /**
     * Get information from the database via the {@link #conferenceRepository} instance.
     * 
     * @return Number of conferences in the database.
     */
    private MapPropertySource retrieveDatabaseInfo() {
        final Map<String, Long> databaseInfo = new HashMap<>();
        databaseInfo.put("db.conferences.count", conferenceRepository.count());
        return new MapPropertySource("database", databaseInfo);
    }

}

Let's run our Micronaut application and invoke the /info endpoint. We see the following response if we have 42 conference records in our database:

{
    "db": {
        "conferences": {
            "count": 42
        }
    },
    "sample": {
        "message": "Micronaut is awesome"
    }
}

Written with Micronaut 1.0.0.M4.