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
- Aver installato una jdk (useremo la versione 8 ma va bene anche una successiva).
- 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.
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');
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.
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.
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:
Cliccando sul nome dell'app, si aprirà il dettaglio del monitoring:
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:
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:
- Cloud Native Spring in Action: https://amzn.to/3xZFg1S
- 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