Mastering Spring Boot’s Actuator
Madhura Bhave
@madhurabhave23
2
Actuator?
3
actuator
noun
mechanical device for moving or
controlling something
Spring Boot Actuator
noun
module for controlling Spring
Boot applications
Actuator?
Spring Boot Actuator Endpoints
Actuator endpoints
• Endpoints under /actuator
• management.endpoints.web.base-path
• management.endpoints.web.exposure.include

management.endpoints.web.exposure.exclude
• management.endpoint.shutdown.enabled
• management.endpoint.health.show-details
• EndpointRequest
• Jersey, WebFlux, or Spring MVC
5
6
{
"cron" : [ {
"runnable" : {
"target" : "com.example.Processor.processOrders"
},
"expression" : "0 0 0/3 1/1 * ?"
} ],
"fixedDelay" : [ {
"runnable" : {
"target" : "com.example.Processor.purge"
},
"initialDelay" : 5000,
"interval" : 5000
} ],
"fixedRate" : [ {
"runnable" : {
"target" : "com.example.Processor.retrieveIssues"
},
"initialDelay" : 10000,
"interval" : 3000
} ]
}
/actuator/scheduledtasks
Inspired by a proposal from
Huang YunKun
Builds on top of improvements in
Spring Framework 5.0
"cron" : [ {
"runnable" : {
"target" : "com.example.Processor.processOrders"
},
"expression" : "0 0 0/3 1/1 * ?"
}
"fixedDelay" : [ {
"runnable" : {
"target" : "com.example.Processor.purge"
},
"initialDelay" : 5000,
"interval" : 5000
}
7
{
"sessions" : [ {
"id" : "d66667e1-bcf1-453a-89f2-1d777439c4de",
"attributeNames" : [ ],
"creationTime" : "2018-05-09T11:28:40.594Z",
"lastAccessedTime" : "2018-05-09T13:28:28.594Z",
"maxInactiveInterval" : 1800,
"expired" : false
}, {
"id" : "85af4f77-319f-4912-8e81-f40c9de63735",
"attributeNames" : [ ],
"creationTime" : "2018-05-09T01:28:40.593Z",
"lastAccessedTime" : "2018-05-09T13:27:55.593Z",
"maxInactiveInterval" : 1800,
"expired" : false
}, {
"id" : "4db5efcc-99cb-4d05-a52c-b49acfbb7ea9",
"attributeNames" : [ ],
"creationTime" : "2018-05-09T08:28:40.594Z",
"lastAccessedTime" : "2018-05-09T13:28:03.594Z",
"maxInactiveInterval" : 1800,
"expired" : false
} ]
}
/actuator/sessions
Contributed by Vedran Pavic
{
"id" : "85af4f77-319f-4912-8e81-f40c9de63735",
"attributeNames" : [ ],
"creationTime" : "2018-05-09T01:28:40.593Z",
"lastAccessedTime" : "2018-05-09T13:27:55.593Z",
"maxInactiveInterval" : 1800,
"expired" : false
}
8
{
"cacheManagers" : {
"anotherCacheManager" : {
"caches" : {
"countries" : {
"target" :
"java.util.concurrent.ConcurrentHashMap"
}
}
},
"cacheManager" : {
"caches" : {
"cities" : {
"target" : "java.util.concurrent.ConcurrentHashMap"
},
"countries" : {
“target" : "java.util.concurrent.ConcurrentHashMap"
}
}
}
}
}
/actuator/caches
Contributed by
Johannes Edmeier
"cacheManager" : {
"caches" : {
"cities" : {
"target" : "java.util.concurrent.ConcurrentHashMap"
},
"countries" : {
"target" : "java.util.concurrent.ConcurrentHashMap"
}
}
}
{
"target" : "java.util.concurrent.ConcurrentHashMap",
"name" : "cities",
"cacheManager" : "cacheManager"
}
/{name}
9
{
"contentDescriptor" : {
"providerVersion" : “5.1.0.RC1”,
"providerFormatVersion" : 1.0,
"provider" : "spring-integration"
},
"nodes" : [ {
"nodeId" : 1,
"name" : "nullChannel",
"componentType" : "channel"
}, {
"nodeId" : 2,
"name" : "errorChannel",
"componentType" : "publish-subscribe-channel"
}, {
"nodeId" : 3,
"name" : "_errorLogger",
"componentType" : "logging-channel-adapter"
} ],
"links" : [ {
"from" : 2,
"to" : 3,
"type" : "input"
} ]
}
/actuator/integrationgraph
Contributed by Tim Ysewyn
"links" : [ {
"from" : 2,
"to" : 3,
"type" : "input"
} ]
"nodes" : [ {
"nodeId" : 1,
"name" : "nullChannel",
"componentType" : "channel"
} ]
10
{
"status" : "UP",
"details" : {
"db" : {
"status" : "UP",
"details" : {
"database" : "HSQL Database Engine",
"hello" : 1
}
},
"broker" : {
"status" : "UP",
"details" : {
"us1" : {
"status" : "UP",
"details" : {
"version" : "1.0.2"
}
},
"us2" : {
"status" : "UP",
"details" : {
"version" : "1.0.4"
}}}}}}
/actuator/health /{component}
{
"status" : "UP",
"details" : {
"us1" : {
"status" : "UP",
"details" : {
"version" : "1.0.2"
}
},
"us2" : {
"status" : "UP",
"details" : {
"version" : "1.0.4"
}}}}
{
"status" : "UP",
"details" : {
"version" : "1.0.2"
}
}
/{instance}
CloudFoundry Integration
CloudFoundry Integration
• Endpoints available under /cloudfoundryapplication
• Requires a valid UAA token
• management.cloudfoundry.enabled
• Jersey, WebFlux, or Spring MVC
12
Web API documentation
Writing your own endpoints
15
/**
* {@link Endpoint} to expose a user's {@link Session}s.
*
* @author Vedran Pavic
* @since 2.0.0
*/
@Endpoint(id = "sessions")
public class SessionsEndpoint {
// …
}
@Endpoint(id = "sessions")
http://example.com/actuator/
sessions
16
@ReadOperation
public SessionsReport sessionsForUsername(String username) {
Map<String, ? extends Session> sessions =
this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
return new SessionsReport(sessions);
}
@ReadOperation
(String username)SessionsReport
GET http://example.com/actuator/sessions
username
? =alice
17
@ReadOperation
public SessionDescriptor getSession(@Selector String sessionId) {
Session session = this.sessionRepository.findById(sessionId);
if (session == null) {
return null;
}
return new SessionDescriptor(session);
}
@ReadOperation
(@Selector String sessionId)SessionDescriptor
GET http://example.com/actuator/sessions
sessionId
/{ }
18
@DeleteOperation
public void deleteSession(@Selector String sessionId) {
this.sessionRepository.deleteById(sessionId);
}
@DeleteOperation
@Selector String sessionId
DELETE http://example.com/actuator/sessions
sessionId
/{ }
void
19
@Endpoint(id = "loggers")
public class LoggersEndpoint {
@WriteOperation
public void configureLogLevel(@Selector String name,
@Nullable LogLevel configuredLevel) {
Assert.notNull(name, "Name must not be empty");
this.loggingSystem.setLogLevel(name, configuredLevel);
}
}
@Endpoint(id = "loggers")
@WriteOperation
void @Selector String name
@Nullable LogLevel configuredLevel
POST http://example.com/actuator/loggers
name
/{ }
{
" ": "DEBUG"
}
configuredLevel
Writing your own endpoints
Beyond the basics
21
@EndpointWebExtension(endpoint = HealthEndpoint.class)
public class HealthEndpointWebExtension {
@ReadOperation
public WebEndpointResponse<Health> getHealth(
SecurityContext securityContext) {
return this.responseMapper.map(
this.delegate.health(), securityContext);
}
}
@EndpointWebExtension(endpoint = HealthEndpoint.class)
WebEndpointResponse<Health>
SecurityContext securityContext
22
@ServletEndpoint
@ControllerEndpoint
@RestControllerEndpoint
Thanks!
Madhura Bhave
@madhurabhave23

Mastering Spring Boot's Actuator with Madhura Bhave

  • 1.
    Mastering Spring Boot’sActuator Madhura Bhave @madhurabhave23
  • 2.
  • 3.
    3 actuator noun mechanical device formoving or controlling something Spring Boot Actuator noun module for controlling Spring Boot applications Actuator?
  • 4.
  • 5.
    Actuator endpoints • Endpointsunder /actuator • management.endpoints.web.base-path • management.endpoints.web.exposure.include
 management.endpoints.web.exposure.exclude • management.endpoint.shutdown.enabled • management.endpoint.health.show-details • EndpointRequest • Jersey, WebFlux, or Spring MVC 5
  • 6.
    6 { "cron" : [{ "runnable" : { "target" : "com.example.Processor.processOrders" }, "expression" : "0 0 0/3 1/1 * ?" } ], "fixedDelay" : [ { "runnable" : { "target" : "com.example.Processor.purge" }, "initialDelay" : 5000, "interval" : 5000 } ], "fixedRate" : [ { "runnable" : { "target" : "com.example.Processor.retrieveIssues" }, "initialDelay" : 10000, "interval" : 3000 } ] } /actuator/scheduledtasks Inspired by a proposal from Huang YunKun Builds on top of improvements in Spring Framework 5.0 "cron" : [ { "runnable" : { "target" : "com.example.Processor.processOrders" }, "expression" : "0 0 0/3 1/1 * ?" } "fixedDelay" : [ { "runnable" : { "target" : "com.example.Processor.purge" }, "initialDelay" : 5000, "interval" : 5000 }
  • 7.
    7 { "sessions" : [{ "id" : "d66667e1-bcf1-453a-89f2-1d777439c4de", "attributeNames" : [ ], "creationTime" : "2018-05-09T11:28:40.594Z", "lastAccessedTime" : "2018-05-09T13:28:28.594Z", "maxInactiveInterval" : 1800, "expired" : false }, { "id" : "85af4f77-319f-4912-8e81-f40c9de63735", "attributeNames" : [ ], "creationTime" : "2018-05-09T01:28:40.593Z", "lastAccessedTime" : "2018-05-09T13:27:55.593Z", "maxInactiveInterval" : 1800, "expired" : false }, { "id" : "4db5efcc-99cb-4d05-a52c-b49acfbb7ea9", "attributeNames" : [ ], "creationTime" : "2018-05-09T08:28:40.594Z", "lastAccessedTime" : "2018-05-09T13:28:03.594Z", "maxInactiveInterval" : 1800, "expired" : false } ] } /actuator/sessions Contributed by Vedran Pavic { "id" : "85af4f77-319f-4912-8e81-f40c9de63735", "attributeNames" : [ ], "creationTime" : "2018-05-09T01:28:40.593Z", "lastAccessedTime" : "2018-05-09T13:27:55.593Z", "maxInactiveInterval" : 1800, "expired" : false }
  • 8.
    8 { "cacheManagers" : { "anotherCacheManager": { "caches" : { "countries" : { "target" : "java.util.concurrent.ConcurrentHashMap" } } }, "cacheManager" : { "caches" : { "cities" : { "target" : "java.util.concurrent.ConcurrentHashMap" }, "countries" : { “target" : "java.util.concurrent.ConcurrentHashMap" } } } } } /actuator/caches Contributed by Johannes Edmeier "cacheManager" : { "caches" : { "cities" : { "target" : "java.util.concurrent.ConcurrentHashMap" }, "countries" : { "target" : "java.util.concurrent.ConcurrentHashMap" } } } { "target" : "java.util.concurrent.ConcurrentHashMap", "name" : "cities", "cacheManager" : "cacheManager" } /{name}
  • 9.
    9 { "contentDescriptor" : { "providerVersion": “5.1.0.RC1”, "providerFormatVersion" : 1.0, "provider" : "spring-integration" }, "nodes" : [ { "nodeId" : 1, "name" : "nullChannel", "componentType" : "channel" }, { "nodeId" : 2, "name" : "errorChannel", "componentType" : "publish-subscribe-channel" }, { "nodeId" : 3, "name" : "_errorLogger", "componentType" : "logging-channel-adapter" } ], "links" : [ { "from" : 2, "to" : 3, "type" : "input" } ] } /actuator/integrationgraph Contributed by Tim Ysewyn "links" : [ { "from" : 2, "to" : 3, "type" : "input" } ] "nodes" : [ { "nodeId" : 1, "name" : "nullChannel", "componentType" : "channel" } ]
  • 10.
    10 { "status" : "UP", "details": { "db" : { "status" : "UP", "details" : { "database" : "HSQL Database Engine", "hello" : 1 } }, "broker" : { "status" : "UP", "details" : { "us1" : { "status" : "UP", "details" : { "version" : "1.0.2" } }, "us2" : { "status" : "UP", "details" : { "version" : "1.0.4" }}}}}} /actuator/health /{component} { "status" : "UP", "details" : { "us1" : { "status" : "UP", "details" : { "version" : "1.0.2" } }, "us2" : { "status" : "UP", "details" : { "version" : "1.0.4" }}}} { "status" : "UP", "details" : { "version" : "1.0.2" } } /{instance}
  • 11.
  • 12.
    CloudFoundry Integration • Endpointsavailable under /cloudfoundryapplication • Requires a valid UAA token • management.cloudfoundry.enabled • Jersey, WebFlux, or Spring MVC 12
  • 13.
  • 14.
  • 15.
    15 /** * {@link Endpoint}to expose a user's {@link Session}s. * * @author Vedran Pavic * @since 2.0.0 */ @Endpoint(id = "sessions") public class SessionsEndpoint { // … } @Endpoint(id = "sessions") http://example.com/actuator/ sessions
  • 16.
    16 @ReadOperation public SessionsReport sessionsForUsername(Stringusername) { Map<String, ? extends Session> sessions = this.sessionRepository.findByIndexNameAndIndexValue( FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username); return new SessionsReport(sessions); } @ReadOperation (String username)SessionsReport GET http://example.com/actuator/sessions username ? =alice
  • 17.
    17 @ReadOperation public SessionDescriptor getSession(@SelectorString sessionId) { Session session = this.sessionRepository.findById(sessionId); if (session == null) { return null; } return new SessionDescriptor(session); } @ReadOperation (@Selector String sessionId)SessionDescriptor GET http://example.com/actuator/sessions sessionId /{ }
  • 18.
    18 @DeleteOperation public void deleteSession(@SelectorString sessionId) { this.sessionRepository.deleteById(sessionId); } @DeleteOperation @Selector String sessionId DELETE http://example.com/actuator/sessions sessionId /{ } void
  • 19.
    19 @Endpoint(id = "loggers") publicclass LoggersEndpoint { @WriteOperation public void configureLogLevel(@Selector String name, @Nullable LogLevel configuredLevel) { Assert.notNull(name, "Name must not be empty"); this.loggingSystem.setLogLevel(name, configuredLevel); } } @Endpoint(id = "loggers") @WriteOperation void @Selector String name @Nullable LogLevel configuredLevel POST http://example.com/actuator/loggers name /{ } { " ": "DEBUG" } configuredLevel
  • 20.
    Writing your ownendpoints Beyond the basics
  • 21.
    21 @EndpointWebExtension(endpoint = HealthEndpoint.class) publicclass HealthEndpointWebExtension { @ReadOperation public WebEndpointResponse<Health> getHealth( SecurityContext securityContext) { return this.responseMapper.map( this.delegate.health(), securityContext); } } @EndpointWebExtension(endpoint = HealthEndpoint.class) WebEndpointResponse<Health> SecurityContext securityContext
  • 22.
  • 23.