8. Health Monitoring - pnguye35/pal-tracker GitHub Wiki

Purpose This lab introduces Spring Boot Actuator, a library that adds many production-ready monitoring features to an app.

Learning Outcomes After completing the lab, you will be able to:

Explain how to set up Actuator for a Spring app Describe how to record custom metrics Outline how to create a custom health indicator Recall use cases for custom health checks and metrics Get started Before starting the lab, pull in some failing tests using Git:

git cherry-pick actuator-start Our goal is to get our test suite passing by the end of the lab.

Actuator Add the actuator dependency to your build.gradle file:

implementation 'org.springframework.boot:spring-boot-starter-actuator' Expose all actuator endpoints by adding the following to the bootRun and test environments in your build.gradle file.

"MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE": "*", Actuator endpoints Spring Boot Actuator exposes several endpoints that show useful information about your application. This lab will walk you through the more interesting and important ones.

Visit the /actuator/mappings endpoint.

Here you will see all the controller endpoints that you have defined, as well as any endpoints that are brought in from dependencies.

Visit the /actuator/env endpoint.

This endpoint shows the state of the environment that your application is running in, including environment variables and system properties.

Visit the /actuator/beans endpoint.

This endpoint shows the beans that are being used within your application.

Visit the /actuator/metrics endpoint.

This endpoint shows all the metrics that actuator has collected by default, such as system information like uptime, load, memory, etc. Custom metrics will also be shown here.

There are several types of metrics that can be collected:

Gauge - gives a real time measurement of a metric. Counter - counts the number of times an event has occurred. DistributionSummary - tracks distribution of events Timer - tracks large number of short running events Visit the /actuator/health endpoint.

This endpoint shows the application status (up or down) for anonymous users.

Add the following to the bootRun and test environments in your build.gradle file.

"MANAGEMENT_ENDPOINT_HEALTH_SHOWDETAILS": "always", It provides additional information such as disk space and database connection health status.

Add the following to your build.gradle file:

springBoot { buildInfo() } This adds a META-INF/build-info.properties file to your jar. When actuator sees this file it exposes a BuildInfoContributor bean which adds build information to the /info endpoint. Visit the /actuator/info endpoint to see this information.

You can add your own information to this endpoint by creating a bean that implements InfoContributor.

Customize actuator data Create a custom Counter metric that tracks the total number of times:

A time entry is created A time entry is deleted A time entry is updated A time entry is read Time entries are listed Create a custom DistributionSummary metric in the TimeEntryController that shows the distribution of the current number of time entries recorded:

Hide TimeEntryController.java pal-tracker/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java package io.pivotal.pal.tracker;

import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController @RequestMapping("/time-entries") public class TimeEntryController {

private TimeEntryRepository timeEntriesRepo;
private final DistributionSummary timeEntrySummary;
private final Counter actionCounter;

public TimeEntryController(
        TimeEntryRepository timeEntriesRepo,
        MeterRegistry meterRegistry
) {
    this.timeEntriesRepo = timeEntriesRepo;

    timeEntrySummary = meterRegistry.summary("timeEntry.summary");
    actionCounter = meterRegistry.counter("timeEntry.actionCounter");
}

@PostMapping
public ResponseEntity<TimeEntry> create(@RequestBody TimeEntry timeEntry) {
    TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry);
    actionCounter.increment();
    timeEntrySummary.record(timeEntriesRepo.list().size());

    return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED);
}

@GetMapping("{id}")
public ResponseEntity<TimeEntry> read(@PathVariable Long id) {
    TimeEntry timeEntry = timeEntriesRepo.find(id);
    if (timeEntry != null) {
        actionCounter.increment();
        return new ResponseEntity<>(timeEntry, HttpStatus.OK);
    } else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

@GetMapping
public ResponseEntity<List<TimeEntry>> list() {
    actionCounter.increment();
    return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK);
}

@PutMapping("{id}")
public ResponseEntity<TimeEntry> update(@PathVariable Long id, @RequestBody TimeEntry timeEntry) {
    TimeEntry updatedTimeEntry = timeEntriesRepo.update(id, timeEntry);
    if (updatedTimeEntry != null) {
        actionCounter.increment();
        return new ResponseEntity<>(updatedTimeEntry, HttpStatus.OK);
    } else {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
}

@DeleteMapping("{id}")
public ResponseEntity delete(@PathVariable Long id) {
    timeEntriesRepo.delete(id);
    actionCounter.increment();
    timeEntrySummary.record(timeEntriesRepo.list().size());

    return new ResponseEntity(HttpStatus.NO_CONTENT);
}

}

Update the TimeEntryControllerTest by passing in a mock for the MeterRegistry. Change the setUp function in the test to:

@BeforeEach public void setUp() throws Exception { timeEntryRepository = mock(TimeEntryRepository.class); MeterRegistry meterRegistry = mock(MeterRegistry.class);

doReturn(mock(DistributionSummary.class))
        .when(meterRegistry)
        .summary("timeEntry.summary");

doReturn(mock(Counter.class))
        .when(meterRegistry)
        .counter("timeEntry.actionCounter");

controller = new TimeEntryController(timeEntryRepository, meterRegistry);

} Create a custom health indicator called TimeEntryHealthIndicator that reports a status of up as long as the number of time entries in the database is less than five:

Hide TimeEntryHealthIndicator.java pal-tracker/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java package io.pivotal.pal.tracker;

import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component;

@Component public class TimeEntryHealthIndicator implements HealthIndicator {

private static final int MAX_TIME_ENTRIES = 5;
private final TimeEntryRepository timeEntryRepo;

public TimeEntryHealthIndicator(TimeEntryRepository timeEntryRepo) {
    this.timeEntryRepo = timeEntryRepo;
}

@Override
public Health health() {
    Health.Builder builder = new Health.Builder();

    if(timeEntryRepo.list().size() < MAX_TIME_ENTRIES) {
        builder.up();
    } else {
        builder.down();
    }

    return builder.build();
}

} Visit the /actuator/health endpoint again. You now see a field for timeEntry with a status of up or down. Test the health indicator by creating and deleting some time entries.

Run the build to make sure that the tests are passing.

Commit and push your changes.

Set the following environment variables on PAS. Use these with care in production environments because they expose sensitive information if not secured properly.

cf set-env pal-tracker MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE "*" cf set-env pal-tracker MANAGEMENT_ENDPOINT_HEALTH_SHOWDETAILS always cf restage pal-tracker Submit this assignment Submit the assignment using the cloudNativeDeveloperHealthMonitoring Gradle task. It requires you to provide the URL of your application. For example:

cd ~/workspace/assignment-submission ./gradlew cloudNativeDeveloperHealthMonitoring -PserverUrl=https://${APP_URL}/actuator Learning Outcomes Now that you have completed the lab, you should be able to:

Explain how to set up Actuator for a Spring app Describe how to record custom metrics Outline how to create a custom health indicator Recall use cases for custom health checks and metrics Extra If you have additional time, explore the many actuator endpoints. Expose your project's git information through the info endpoint.

⚠️ **GitHub.com Fallback** ⚠️