Block Image

Spring Boot Actuator è un modulo di Spring che permette di monitorare una applicazione Spring Boot in modo facile e veloce.
Basterà infatti importare la dipendenza dello starter di actuator e si avranno subito a disposizione degli endpoint che forniranno informazioni sullo status dell'app, sulle variabili di ambiente e altro ancora.

Useremo come applicazione da monitorare quella del tutorial: Spring Boot REST, modificando per comodità il nome dell'app in spring-monitoring-client.

Prerequisiti

  1. Aver installato una jdk (useremo la versione 8 ma va bene anche una successiva).
  2. Aver installato maven (https://maven.apache.org/install.html).

Primo passo: importiamo la seguente dipendenza Maven

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

Secondo passo: modifichiamo l'application.properties

server.port=8081
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
spring.jmx.enabled=true
spring.application.name=spring-monitoring-client

info.app.name=@description@
info.app.description=Esempio di Spring Boot Actuator
info.app.version=@version@
info.author.name=Vincenzo
info.author.surname=Racca
info.author.website=https://vincenzoracca.com

Le properties col prefisso management servono per settare il monitoring di Spring Actuator.

In particolare con endpoints.web.exposure.include=* stiamo indicato ad actuator che vogliamo esporre tutti i suoi endpoint (di default alcuni sono nascosti) e con endpoint.health.show-details=always indichiamo che oltre allo status dell'app (UP/DOWN), vogliamo anche tutte le altre informazioni messe a disposizione dall'endpoint health, come lo status del database.

Le properties col suffisso info ci permettono di personalizzare la sezione info di actuator con qualsiasi informazione vogliamo.

Nota 1: Per usare all'interno dell'application.properties delle properties di Maven basta usare il simbolo @ prima e dopo il nome della property di Maven.

Terzo passo: creiamo un file data.sql

Creiamo un file denominato data.sql all'interno di src/main/resources con il seguente contenuto:

INSERT INTO user (name, surname, address) VALUES ('Vincenzo', 'Racca', 'via Roma');
INSERT INTO user (name, surname, address) VALUES ('Pippo', 'Pluto', 'via Chiaia');
Nota 2: Se usiamo H2 e inseriamo questo file del path indicato, Spring Boot automaticamente cercherà questo file ed eseguirà lo script SQL.

Quarto passo: avviamo l'applicazione

Avviamo l'applicazione e invochiamo l'endpoint: localhost:8081/actuator. Avremo come risposta degli endpoint:

{
    "_links": {
        "self": {
            "href": "http://localhost:8081/actuator",
            "templated": false
        },
        "beans": {
            "href": "http://localhost:8081/actuator/beans",
            "templated": false
        },
        "caches-cache": {
            "href": "http://localhost:8081/actuator/caches/{cache}",
            "templated": true
        },
        "caches": {
            "href": "http://localhost:8081/actuator/caches",
            "templated": false
        },
        "health": {
            "href": "http://localhost:8081/actuator/health",
            "templated": false
        }
},
       ...
}

Questi sono tutti gli endpoint messi a disposizione da Actuator. Invochiamo la resource health:
localhost:8081/actuator/health.

{
    "status": "UP",
    "components": {
        "db": {
            "status": "UP",
            "details": {
                "database": "H2",
                "validationQuery": "isValid()"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 210974404608,
                "free": 161783414784,
                "threshold": 10485760,
                "exists": true
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}

Avremo un JSON con le info sullo status dell'app, del db, del disco e sul ping. Se volessimo lo status di un particolare componente, come il db, basterebbe aggiungere al path il componente, come ad esempio: /actuator/health/db.

Con la chiamata localhost:8081/actuator/info avremo invece la seguente risposta:

{
    "app": {
        "name": "Spring Actuator Application",
        "description": "This is a very simple Spring Boot Application",
        "version": "0.0.1-SNAPSHOT"
    },
    "author": {
        "name": "Vincenzo",
        "surname": "Racca",
        "website": "https://vincenzoracca.com"
    }
}

Una API interessante è quella riguardante le metrics: invocando
localhost:8081/actuator/metrics
avremo come risposta le metrics messe a disposizione da Spring Actuator. Per conoscere il valore di una metric basta aggiungere come path param il nome della metric, come ad esempio
/actuator/metrics/system.cpu.usage
e avremo una risposta simile a questa:

{
    "name": "system.cpu.usage",
    "description": "The \"recent cpu usage\" for the whole system",
    "baseUnit": null,
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 0.513888888888889
        }
    ],
    "availableTags": []
}

Quinto passo: creiamo una health custom

Possiamo anche creare varie healths custom implementando l'interfaccia HealtIndicator.

@Component
public class UserHealthChecker implements HealthIndicator {

    private final UserRepository userRepository;

    public UserHealthChecker(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public Health health() {
        Map<String,Long> inits = new HashMap<>();
        long count = userRepository.count();
        if(count > 0) {
            inits.put("userInit",count);
            return Health.up().withDetails(inits).build();
        }
        else {
            return Health.down().withDetail("userInit", "FAILED").
                    build();
        }
    }
}

Questa semplice metric verifica la corretta inizializzazione della tabella user allo startup dell'app. Se il numero di user è maggiore di zero, la health avrà lo status UP col totale degli users, altrimenti lo status sarà DOWN. Gli status di default sono:

  • UP
  • DOWN
  • OUT_OF_SERVICE
  • UNKNOWN

È possibile comunque aggiungere degli stati custom.

Quinto passo: monitoring della chiamate http

Per monitorare le chiamate HTTP dobbiamo creare un bean di tipo HttpTraceRepository. Spring fornice un'implementazione di questa interfaccia con la classe InMemoryHttpTraceRepository che permette di tenere in memoria le ultime 100 chiamate HTTP fatte.

@Configuration
public class HttpTraceConfig {

    @Bean
    HttpTraceRepository httpTraceRepository() {
        return new InMemoryHttpTraceRepository();
    }
}

Chimando l'endpoint /actuator/httptrace dovremmo avere una risposta simile a questa:

    "traces": [
        {
            "timestamp": "2020-10-16T11:13:48.052Z",
            "principal": null,
            "session": null,
            "request": {
                "method": "GET",
                "uri": "http://localhost:8081/users",
                "headers": {
                  ...
                    "user-agent": [
                        "PostmanRuntime/7.26.5"
                    ]
                },
                "remoteAddress": null
            },
            "response": {
                "status": 200,
                "headers": {
                    "Keep-Alive": [
                   ...
            "timeTaken": 57
        }
    ]
}

dove timeTaken è il numero di millisecondi che sono serviti per gestire la richiesta.

ATTENZIONE: La documentazione di Spring Actuator suggerisce di usare questo trace solo in un ambiente di sviluppo. Per un ambiente di produzione è consigliabile utilizzare altri prodotti come Zipkin o quantomeno fornire una propria implementazione di HttpTraceConfig.

Come abbiamo potuto notare, Spring Actuator è molto facile da integrare nel nostro progetto Spring Boot e fornisce interessanti endpoint per monitorare l'app. Inoltre abbiamo esplorato solo alcune sue caratteristiche. Ad esempio è possibile fornire anche degli endpoint custom oppure modificare il level di un log a runtime tramite l'endpoint /loggers/{name}.

Un altro grande vantaggio è che questo modulo dalla versione 2 usa Micrometer che è un metrics facade open source che fornire un elenco di API di metrics vendor-neutral. Un po' come quando usiamo le api JPA e poi possiamo usare qualsiasi vendor come Hibernate o EclipseLink. Micrometer è facilmente integrabile con Prometheus che è un sistema di monitoring open source molto usato. A sua volta Prometheus è facilmente integrabile con Grafana, una applicazione open source che permette di visualizzare tramite grafici le metrics di una applicazione. Praticamente con Spring Actuator si apre un mondo di possibili integrazioni!

Ma se volessimo monitorare una applicazione fatta da microservizi? Ogni microservizio avrebbe il suo Spring Actuator rendendo un po` confusionario il monitoring dell'intera app. Oppure se volessimo mostrare le metrics fornite da Spring Actuator in modo più pulito senza aver bisogno di installare altre applicazioni come Prometheus e Grafana?
Possiamo usare Spring Boot Admin!

Spring Boot Admin è un progetto nato per gestire e monitorare delle Spring Boot Applications in maniera centralizzata e grafica.
In particolare ci sarà una app Spring Boot Admin Server che potrà gestire un elenco di web service (applicazioni client).
Le applicazioni client possono registrarsi all'applicazione server (Spring Boot Admin Server) con Spring Boot Admin Client (una dipendenza maven) oppure se si usa un Service Discovery come Eureka basta aggiungere l'app Spring Boot Admin Server come Discovery Client. Il frontend di Spring Boot Admin Server è scritto in vue.js.

Nota 3: Spring Boot Admin è facilmente integrabile anche con applicazioni Python con Flask o FastAPI usando il progetto Pyctuator.

Sesto passo: creiamo una app Spring Boot Monitoring Server

Questa sarà la nostra Spring Boot Admin Server, che potrà gestire diversi web service insieme (quindi particolarmente indicato per i microservizi). Aggiungiamo queste dipendenze:

 <dependencies>
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
</dependencies>

Aggiungiamo la classe:

@SpringBootApplication
@EnableAdminServer
public class SpringMonitoringServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringMonitoringServerApplication.class, args);
	}

}

Infine modifichiamo l'application.properties

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.username=mail
spring.mail.password=password
spring.boot.admin.notify.mail.to=mail

Queste properties servono per ricevere notifiche via mail quando cambia lo status di una delle app che gestisce Spring Boot Admin Server.
Ovviamente non è obbligatorio; in tal caso possiamo omettere queste properties. Inoltre Spring Boot Admin supporta molti altri sistemi di notifica come Slack, Telegram e Microsoft Teams.

Avviamo ora l'applicazione.

Settimo passo: associamo l'app Spring Monitoring Client con la Spring Monitoring Server

Aggiungiamo questa dipendenza nel pom della nostra app client:

<properties>
    <spring-boot-admin.version>2.3.0</spring-boot-admin.version>
</properties>
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-dependencies</artifactId>
            <version>${spring-boot-admin.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Aggiungiamo poi la property nell'application.properties:

spring.boot.admin.client.url=http://localhost:8080

Avviamo anche l'app client. Da browser andiamo su localhost:8080. Avremo una schermata simile a questa:

Block Image

Cliccando sul nome dell'app, si aprirà il dettaglio del monitoring:

Block Image

Block Image

Abbiamo praticamente tutte le api di Spring Boot Actuator in formato visuale invece che JSON!

Se abbiamo configurato anche il sistema di notifica mail, e proviamo a spegnere l'applicazione client, riceveremo una mail di questo tipo:

Block Image

Conclusioni

Abbiamo visto il modulo Spring Boot Actuator che ci permette con semplici passi di monitorare la nostra applicazione Spring Boot.
Abbiamo inoltre creato una app Spring Boot Admin e associato quest'ultima all'app Spring Boot Actuator avendo così la possibilità di avere le informazioni sul monitoring dell'app in formato grafico invece che JSON.

Abbiamo visto solo una piccola parte di Spring Boot Admin; è possibile ad esempio integrare il modulo security oppure aggiungere nuove pagine alla schermata di default.

Potete trovare il progetto completo (modulo client e server) sul mio github a questo link:
Spring Boot Monitoring

Articoli su Spring: Spring

Documentazione di Spring Boot Actuator: Spring Boot Actuator

Documentazione di Spring Boot Admin: Spring Boot Admin

Libri consigliati su Spring:

  • Pro Spring 5 (Spring da zero a hero): https://amzn.to/3KvfWWO
  • Pivotal Certified Professional Core Spring 5 Developer Exam: A Study Guide Using Spring Framework 5 (per certificazione Spring): https://amzn.to/3KxbJSC
  • Pro Spring Boot 2: An Authoritative Guide to Building Microservices, Web and Enterprise Applications, and Best Practices (Spring Boot del dettaglio): https://amzn.to/3TrIZic