DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • Upcoming DZone Events
  • The Technology Powering Trading Signals in Binary Options: A Deep Dive
  • Using NLP To Uncover Truth in the Age of Fake News and Bots

Trending

  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • A Modern Stack for Building Scalable Systems
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot

Micronaut Mastery: Add Custom Health Indicators

Let's see how we can set up custom app health metrics for our microservices applications in Micronaut quickly and easily.

By 
Hubert Klein Ikkink user avatar
Hubert Klein Ikkink
·
Aug. 23, 18 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
8.3K Views

Join the DZone community and get the full member experience.

Join For Free

When we add the io.micronaut:management dependency to our Micronaut application, we get, among other things, a /health endpoint. We must enable it in our application configuration, where we can also configure how much information is shown and if we want to secure the endpoint. Micronaut has some built-in health indicators, some of which are only available based on certain conditions. For example, there is a disk space health indicator that will return a status of DOWN when the free disk space is less than a (configurable) threshold. If we would have one or more DataSource beans for database access in our application context, a health indicator is added to show if the database(s) are available or not.

We can also add our own health indicator that will show up in the /health endpoint. We must write a class that implements the HealthIndicator interface and add it to the application context. We could add some conditions to make sure the bean is loaded when needed. Micronaut also has the abstract AbstractHealthIndicator class, which can be used as base class for writing custom health indicators.

First, we must add the io.micronaut:management dependency to our compile classpath like in the following example Gradle build file:

// File: build.gradle
...
dependencies {
    ...
    compile "io.micronaut:management"
    ...
}
...

Next, we write a class that implements HealthIndicator or extends AbstractHealthIndicator. In the following example, we implement HealthIndicator and the method getResult. This health indicator will try to access a remote URL and will return the status UP when the URL is reachable and DOWN when the status code is invalid or an exception occurs. We also use the @Requires annotation to make sure the indicator is only loaded when the correct value is set for a configuration property and when the HealthPoint bean is available.

package mrhaki.micronaut;

import io.micronaut.health.HealthStatus;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.client.Client;
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.management.health.indicator.HealthIndicator;
import io.micronaut.management.health.indicator.HealthResult;
import org.reactivestreams.Publisher;

import javax.inject.Singleton;
import java.util.Collections;

@Singleton
// Only create bean when configuration property
// endpoints.health.url.enabled equals true,
// and HealthEndpoint bean to expose /health endpoint is available.
@Requires(property = HealthEndpoint.PREFIX + ".url.enabled", value = "true")
@Requires(beans = HealthEndpoint.class)
public class RemoteUrlHealthIndicator implements HealthIndicator {

    /**
     * Name for health indicator.
     */
    private static final String NAME = "remote-url-health";

    /**
     * URL to check.
     */
    private static final String URL = "http://www.mrhaki.com/";

    /**
     * We use {@link RxHttpClient} to check if
     * URL is reachable.
     */
    private RxHttpClient client;

    /**
     * Inject client with URL to check.
     * 
     * @param client Client to check if URl is reachable.
     */
    public RemoteUrlHealthIndicator(@Client(URL) final RxHttpClient client) {
        this.client = client;
    }

    /**
     * Implementaton of {@link HealthIndicator#getResult()} where we
     * check if the url is reachable and return result based
     * on the HTTP status code.
     * 
     * @return Contains {@link HealthResult} with status UP or DOWN.
     */
    @Override
    public Publisher<HealthResult> getResult() {
        return client.exchange(HttpRequest.HEAD("/"))
                     .map(this::checkStatusCode)
                     .onErrorReturn(this::statusException);
    }

    /**
     * Check response status code and return status UP when code is
     * between 200 and 399, otherwise return status DOWN.
     * 
     * @param response Reponse with status code.
     * @return Result with checked URL in the details and status UP or DOWN.
     */
    private HealthResult checkStatusCode(HttpResponse<?> response) {
        final int statusCode = response.getStatus().getCode();
        final boolean statusOk = statusCode >= 200 && statusCode < 400;
        final HealthStatus healthStatus = statusOk ? HealthStatus.UP : HealthStatus.DOWN;

        // We use the builder API of HealthResult to create 
        // the health result with a details section containing
        // the checked URL and the health status.
        return HealthResult.builder(NAME, healthStatus)
                           .details(Collections.singletonMap("url", URL))
                           .build();
    }

    /**
     * Set status is DOWN when exception occurs checking URL status.
     * 
     * @param exception Exception thrown when checking status.
     * @return Result with exception in details in status DOWN.
     */
    private HealthResult statusException(Throwable exception) {
        // We use the build API of HealthResult to include
        // the original exception in the details and 
        // status is DOWN.
        return HealthResult.builder(NAME, HealthStatus.DOWN)
                           .exception(exception)
                           .build();
    }

}

Finally, we add configuration properties to our application.yml file:

# File: src/main/resources/application.yml
...
endpoints:
  health:
    enabled: true
    sensitive: false # non-secured endpoint
    details-visible: ANONYMOUS # show details for everyone
    url:
      enabled: true
...

Let's run our Micronaut application and invoke the /health endpoint. In the JSON response, we see the output of our custom health indicator:

...
        "remote-url-health": {
            "details": {
                "url": "http://www.mrhaki.com/"
            },
            "name": "micronaut-sample",
            "status": "UP"
        },
...

When we use a URL that is not available, we get the following output:

...
        "remote-url-health": {
            "details": {
                "error": "io.micronaut.http.client.exceptions.HttpClientResponseException: Not Found"
            },
            "name": "micronaut-sample",
            "status": "DOWN"
        },
...

Written with Micronaut 1.0.0.M4.

Indicator (metadata) Health (Apple)

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • Upcoming DZone Events
  • The Technology Powering Trading Signals in Binary Options: A Deep Dive
  • Using NLP To Uncover Truth in the Age of Fake News and Bots

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!