0
# Application Management
1
2
Micronaut provides built-in management endpoints for monitoring, health checks, metrics, and application information, following Spring Boot Actuator patterns.
3
4
## Capabilities
5
6
### Health Checks
7
8
Built-in and custom health indicators for monitoring application health.
9
10
```java { .api }
11
/**
12
* Custom health indicator
13
*/
14
@Singleton
15
public class DatabaseHealthIndicator implements HealthIndicator {
16
17
private final DataSource dataSource;
18
19
public DatabaseHealthIndicator(DataSource dataSource) {
20
this.dataSource = dataSource;
21
}
22
23
@Override
24
public Publisher<HealthResult> getResult() {
25
return Single.fromCallable(() -> {
26
try (Connection connection = dataSource.getConnection()) {
27
return HealthResult.builder("database")
28
.status(HealthStatus.UP)
29
.details("connection", "available")
30
.build();
31
} catch (SQLException e) {
32
return HealthResult.builder("database")
33
.status(HealthStatus.DOWN)
34
.exception(e)
35
.build();
36
}
37
});
38
}
39
}
40
```
41
42
### Custom Management Endpoints
43
44
Create custom endpoints for application management and monitoring.
45
46
```java { .api }
47
/**
48
* Custom management endpoint
49
*/
50
@Endpoint(id = "custom", defaultEnabled = true)
51
public class CustomEndpoint {
52
53
@Read
54
public Map<String, Object> status() {
55
return Map.of(
56
"status", "healthy",
57
"timestamp", Instant.now(),
58
"version", "1.0.0"
59
);
60
}
61
62
@Write
63
public String refresh(@Selector String component) {
64
// Perform refresh operation
65
return "Refreshed component: " + component;
66
}
67
68
@Delete
69
public String reset(@Selector String component) {
70
// Perform reset operation
71
return "Reset component: " + component;
72
}
73
}
74
```
75
76
### Metrics Collection
77
78
Collect and expose application metrics using Micrometer.
79
80
```java { .api }
81
/**
82
* Custom metrics
83
*/
84
@Singleton
85
public class OrderMetrics {
86
87
private final Counter orderCounter;
88
private final Timer orderProcessingTimer;
89
private final Gauge activeOrdersGauge;
90
91
public OrderMetrics(MeterRegistry meterRegistry) {
92
this.orderCounter = Counter.builder("orders.created")
93
.description("Total orders created")
94
.register(meterRegistry);
95
96
this.orderProcessingTimer = Timer.builder("orders.processing.time")
97
.description("Order processing time")
98
.register(meterRegistry);
99
100
this.activeOrdersGauge = Gauge.builder("orders.active")
101
.description("Active orders count")
102
.register(meterRegistry, this, OrderMetrics::getActiveOrdersCount);
103
}
104
105
public void recordOrderCreated() {
106
orderCounter.increment();
107
}
108
109
public void recordProcessingTime(Duration duration) {
110
orderProcessingTimer.record(duration);
111
}
112
113
private double getActiveOrdersCount() {
114
// Return current active orders count
115
return activeOrders.size();
116
}
117
}
118
```
119
120
### Application Info
121
122
Provide application information through the info endpoint.
123
124
```java { .api }
125
/**
126
* Custom application info contributor
127
*/
128
@Singleton
129
public class CustomInfoContributor implements InfoContributor {
130
131
@Override
132
public Publisher<Map<String, Object>> getInfo() {
133
return Single.fromCallable(() -> Map.of(
134
"app", Map.of(
135
"name", "My Application",
136
"description", "Sample Micronaut application",
137
"version", "1.0.0"
138
),
139
"build", Map.of(
140
"timestamp", "2023-01-01T00:00:00Z",
141
"version", "1.0.0-SNAPSHOT"
142
)
143
));
144
}
145
}
146
```
147
148
### Environment Information
149
150
Access environment and configuration information.
151
152
```java { .api }
153
/**
154
* Environment endpoint for configuration details
155
*/
156
@Endpoint(id = "env", defaultEnabled = false)
157
public class EnvironmentEndpoint {
158
159
private final Environment environment;
160
161
public EnvironmentEndpoint(Environment environment) {
162
this.environment = environment;
163
}
164
165
@Read
166
public Map<String, Object> getEnvironment() {
167
return Map.of(
168
"activeNames", environment.getActiveNames(),
169
"packages", environment.getPackages(),
170
"properties", getProperties()
171
);
172
}
173
174
@Read
175
public Map<String, Object> getProperty(@Selector String name) {
176
return environment.getProperty(name, Object.class)
177
.map(value -> Map.of("property", name, "value", value))
178
.orElse(Map.of("property", name, "value", null));
179
}
180
181
private Map<String, Object> getProperties() {
182
// Return filtered properties (exclude sensitive data)
183
return environment.getPropertySources().stream()
184
.flatMap(ps -> ps.asMap().entrySet().stream())
185
.filter(entry -> !isSensitive(entry.getKey()))
186
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
187
}
188
}
189
```
190
191
### Thread Dump
192
193
Monitor application threads and their states.
194
195
```java { .api }
196
/**
197
* Thread dump endpoint
198
*/
199
@Endpoint(id = "threaddump", defaultEnabled = true)
200
public class ThreadDumpEndpoint {
201
202
@Read
203
public ThreadDumpInfo getThreadDump() {
204
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
205
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
206
207
return ThreadDumpInfo.builder()
208
.threads(Arrays.stream(threadInfos)
209
.map(this::mapThreadInfo)
210
.collect(Collectors.toList()))
211
.build();
212
}
213
214
private ThreadDetail mapThreadInfo(ThreadInfo threadInfo) {
215
return ThreadDetail.builder()
216
.threadName(threadInfo.getThreadName())
217
.threadId(threadInfo.getThreadId())
218
.threadState(threadInfo.getThreadState().name())
219
.blockedTime(threadInfo.getBlockedTime())
220
.waitedTime(threadInfo.getWaitedTime())
221
.stackTrace(Arrays.stream(threadInfo.getStackTrace())
222
.map(StackTraceElement::toString)
223
.collect(Collectors.toList()))
224
.build();
225
}
226
}
227
```
228
229
## Types
230
231
```java { .api }
232
// Management annotations
233
@Target({ElementType.TYPE})
234
@Retention(RetentionPolicy.RUNTIME)
235
public @interface Endpoint {
236
String id();
237
boolean defaultEnabled() default true;
238
boolean defaultSensitive() default true;
239
}
240
241
@Target({ElementType.METHOD})
242
@Retention(RetentionPolicy.RUNTIME)
243
public @interface Read {
244
}
245
246
@Target({ElementType.METHOD})
247
@Retention(RetentionPolicy.RUNTIME)
248
public @interface Write {
249
}
250
251
@Target({ElementType.METHOD})
252
@Retention(RetentionPolicy.RUNTIME)
253
public @interface Delete {
254
}
255
256
@Target({ElementType.PARAMETER})
257
@Retention(RetentionPolicy.RUNTIME)
258
public @interface Selector {
259
}
260
261
// Health check interfaces
262
public interface HealthIndicator {
263
Publisher<HealthResult> getResult();
264
}
265
266
public final class HealthResult {
267
public static Builder builder(String name);
268
public String getName();
269
public HealthStatus getStatus();
270
public Map<String, Object> getDetails();
271
272
public static class Builder {
273
public Builder status(HealthStatus status);
274
public Builder details(String key, Object value);
275
public Builder details(Map<String, Object> details);
276
public Builder exception(Throwable exception);
277
public HealthResult build();
278
}
279
}
280
281
public enum HealthStatus {
282
UP, DOWN, OUT_OF_SERVICE, UNKNOWN
283
}
284
285
// Info contributor interface
286
public interface InfoContributor {
287
Publisher<Map<String, Object>> getInfo();
288
}
289
290
// Metrics interfaces
291
public interface MeterRegistry {
292
Counter counter(String name);
293
Timer timer(String name);
294
Gauge gauge(String name, Object obj, ToDoubleFunction<Object> valueFunction);
295
}
296
297
public interface Counter extends Meter {
298
void increment();
299
void increment(double amount);
300
double count();
301
}
302
303
public interface Timer extends Meter {
304
void record(long amount, TimeUnit unit);
305
void record(Duration duration);
306
long count();
307
double totalTime(TimeUnit unit);
308
}
309
```