Skip to content

Commit 221faa7

Browse files
committed
improve: Make MicrometerMetrics extendable
Signed-off-by: David Sondermann <[email protected]>
1 parent ecb7513 commit 221faa7

File tree

9 files changed

+220
-21
lines changed

9 files changed

+220
-21
lines changed

micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,13 @@ public static MicrometerMetricsBuilder newMicrometerMetricsBuilder(MeterRegistry
9696

9797
/**
9898
* Creates a micrometer-based Metrics implementation that cleans up {@link Meter}s associated with
99-
* deleted resources as specified by the (possibly {@code null}) provided {@link Cleaner}
100-
* instance.
99+
* deleted resources as specified by the provided {@link Cleaner} instance.
101100
*
102101
* @param registry the {@link MeterRegistry} instance to use for metrics recording
103102
* @param cleaner the {@link Cleaner} to use
104103
* @param collectingPerResourceMetrics whether to collect per resource metrics
105104
*/
106-
private MicrometerMetrics(
105+
protected MicrometerMetrics(
107106
MeterRegistry registry, Cleaner cleaner, boolean collectingPerResourceMetrics) {
108107
this.registry = registry;
109108
this.cleaner = cleaner;
@@ -315,11 +314,10 @@ protected Set<Meter.Id> recordedMeterIdsFor(ResourceID resourceID) {
315314
public static class PerResourceCollectingMicrometerMetricsBuilder
316315
extends MicrometerMetricsBuilder {
317316

318-
private int cleaningThreadsNumber;
319-
private int cleanUpDelayInSeconds;
320-
321317
private PerResourceCollectingMicrometerMetricsBuilder(MeterRegistry registry) {
322318
super(registry);
319+
cleanerBuilder.withCleanerType(CleanerType.DELAYED);
320+
collectingPerResourceMetrics = true;
323321
}
324322

325323
/**
@@ -329,7 +327,7 @@ private PerResourceCollectingMicrometerMetricsBuilder(MeterRegistry registry) {
329327
*/
330328
public PerResourceCollectingMicrometerMetricsBuilder withCleaningThreadNumber(
331329
int cleaningThreadsNumber) {
332-
this.cleaningThreadsNumber = cleaningThreadsNumber <= 0 ? 1 : cleaningThreadsNumber;
330+
cleanerBuilder.withCleaningThreadNumber(cleaningThreadsNumber);
333331
return this;
334332
}
335333

@@ -343,30 +341,24 @@ public PerResourceCollectingMicrometerMetricsBuilder withCleaningThreadNumber(
343341
*/
344342
public PerResourceCollectingMicrometerMetricsBuilder withCleanUpDelayInSeconds(
345343
int cleanUpDelayInSeconds) {
346-
this.cleanUpDelayInSeconds = Math.max(cleanUpDelayInSeconds, 1);
344+
cleanerBuilder.withCleanUpDelayInSeconds(cleanUpDelayInSeconds);
347345
return this;
348346
}
349-
350-
@Override
351-
public MicrometerMetrics build() {
352-
final var cleaner =
353-
new DelayedCleaner(registry, cleanUpDelayInSeconds, cleaningThreadsNumber);
354-
return new MicrometerMetrics(registry, cleaner, true);
355-
}
356347
}
357348

358349
public static class MicrometerMetricsBuilder {
359350
protected final MeterRegistry registry;
360-
private boolean collectingPerResourceMetrics = true;
351+
protected CleanerBuilder cleanerBuilder;
352+
protected boolean collectingPerResourceMetrics = true;
361353

362354
private MicrometerMetricsBuilder(MeterRegistry registry) {
363355
this.registry = registry;
356+
this.cleanerBuilder = new CleanerBuilder(registry);
364357
}
365358

366359
/** Configures the instance to collect metrics on a per-resource basis. */
367360
@SuppressWarnings("unused")
368361
public PerResourceCollectingMicrometerMetricsBuilder collectingMetricsPerResource() {
369-
collectingPerResourceMetrics = true;
370362
return new PerResourceCollectingMicrometerMetricsBuilder(registry);
371363
}
372364

@@ -381,11 +373,79 @@ public MicrometerMetricsBuilder notCollectingMetricsPerResource() {
381373
}
382374

383375
public MicrometerMetrics build() {
384-
return new MicrometerMetrics(registry, Cleaner.NOOP, collectingPerResourceMetrics);
376+
return new MicrometerMetrics(registry, cleanerBuilder.build(), collectingPerResourceMetrics);
377+
}
378+
}
379+
380+
public static class CleanerBuilder {
381+
382+
private final MeterRegistry registry;
383+
private CleanerType cleanerType = CleanerType.NOOP;
384+
private int cleaningThreadsNumber = 0;
385+
private int cleanUpDelayInSeconds = 0;
386+
387+
public CleanerBuilder(MeterRegistry registry) {
388+
this.registry = registry;
389+
}
390+
391+
/**
392+
* @param cleanerType the type of cleaner to use, defaults to {@link CleanerType#NOOP} if not
393+
* specified
394+
*/
395+
public CleanerBuilder withCleanerType(CleanerType cleanerType) {
396+
this.cleanerType = cleanerType;
397+
return this;
398+
}
399+
400+
/**
401+
* @param cleaningThreadsNumber the maximal number of threads that can be assigned to the
402+
* removal of {@link Meter}s associated with deleted resources, defaults to 1 if not
403+
* specified or if the provided number is lesser or equal to 0
404+
* @throws IllegalStateException if the cleaner type is not {@link CleanerType#DELAYED}
405+
*/
406+
public CleanerBuilder withCleaningThreadNumber(int cleaningThreadsNumber) {
407+
if (cleanerType != CleanerType.DELAYED) {
408+
throw new IllegalStateException(
409+
"Cleaning threads can only be configured when using the DELAYED cleaner type");
410+
}
411+
this.cleaningThreadsNumber = cleaningThreadsNumber <= 0 ? 1 : cleaningThreadsNumber;
412+
return this;
385413
}
414+
415+
/**
416+
* @param cleanUpDelayInSeconds the number of seconds to wait before {@link Meter}s are removed
417+
* for deleted resources, defaults to 1 (meaning meters will be removed one second after the
418+
* associated resource is deleted) if not specified or if the provided number is lesser than
419+
* 0. Threading and the general interaction model of interacting with the API server means
420+
* that it's not possible to ensure that meters are immediately deleted in all cases so a
421+
* minimal delay of one second is always enforced
422+
* @throws IllegalStateException if the cleaner type is not {@link CleanerType#DELAYED}
423+
*/
424+
public CleanerBuilder withCleanUpDelayInSeconds(int cleanUpDelayInSeconds) {
425+
if (cleanerType != CleanerType.DELAYED) {
426+
throw new IllegalStateException(
427+
"Clean up delay can only be configured when using the DELAYED cleaner type");
428+
}
429+
this.cleanUpDelayInSeconds = Math.max(cleanUpDelayInSeconds, 1);
430+
return this;
431+
}
432+
433+
public Cleaner build() {
434+
return switch (cleanerType) {
435+
case NOOP -> Cleaner.NOOP;
436+
case DEFAULT -> new DefaultCleaner(registry);
437+
case DELAYED -> new DelayedCleaner(registry, cleanUpDelayInSeconds, cleaningThreadsNumber);
438+
};
439+
}
440+
}
441+
442+
public enum CleanerType {
443+
NOOP,
444+
DEFAULT,
445+
DELAYED
386446
}
387447

388-
interface Cleaner {
448+
public interface Cleaner {
389449
Cleaner NOOP = new Cleaner() {};
390450

391451
default void removeMetersFor(ResourceID resourceID) {}
@@ -444,5 +504,10 @@ public void removeMetersFor(ResourceID resourceID) {
444504
metersCleaner.schedule(
445505
() -> super.removeMetersFor(resourceID), cleanUpDelayInSeconds, TimeUnit.SECONDS);
446506
}
507+
508+
// for testing purposes only
509+
int getCleanUpDelayInSeconds() {
510+
return cleanUpDelayInSeconds;
511+
}
447512
}
448513
}

micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/AbstractMicrometerMetricsTestFixture.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public DeleteControl cleanup(ConfigMap resource, Context<ConfigMap> context) {
8787
}
8888
}
8989

90-
static class TestSimpleMeterRegistry extends SimpleMeterRegistry {
90+
protected static class TestSimpleMeterRegistry extends SimpleMeterRegistry {
9191
private final Set<Meter.Id> removed = new HashSet<>();
9292

9393
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package io.javaoperatorsdk.operator.monitoring.micrometer;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import io.javaoperatorsdk.operator.monitoring.micrometer.MicrometerMetrics.CleanerBuilder;
6+
import io.javaoperatorsdk.operator.monitoring.micrometer.MicrometerMetrics.CleanerType;
7+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
8+
9+
import static org.assertj.core.api.Assertions.*;
10+
11+
class CleanerBuilderTest {
12+
13+
private final SimpleMeterRegistry registry = new SimpleMeterRegistry();
14+
private final CleanerBuilder cleanerBuilder = new CleanerBuilder(registry);
15+
16+
@Test
17+
void test_withCleanerTypeNoop() {
18+
final var cleaner = cleanerBuilder.withCleanerType(CleanerType.NOOP).build();
19+
assertThat(cleaner).isSameAs(MicrometerMetrics.Cleaner.NOOP);
20+
}
21+
22+
@Test
23+
void test_withCleanerTypeDefault() {
24+
final var cleaner = cleanerBuilder.withCleanerType(CleanerType.DEFAULT).build();
25+
assertThat(cleaner).isInstanceOf(MicrometerMetrics.DefaultCleaner.class);
26+
}
27+
28+
@Test
29+
void test_withCleanerTypeDelayed() {
30+
final var cleaner = cleanerBuilder.withCleanerType(CleanerType.DELAYED).build();
31+
assertThat(cleaner).isInstanceOf(MicrometerMetrics.DelayedCleaner.class);
32+
if (cleaner instanceof MicrometerMetrics.DelayedCleaner delayedCleaner) {
33+
assertThat(delayedCleaner.getCleanUpDelayInSeconds()).isEqualTo(0);
34+
}
35+
}
36+
37+
@Test
38+
void test_withCleaningThreadNumber() {
39+
final var cleaner =
40+
cleanerBuilder.withCleanerType(CleanerType.DELAYED).withCleaningThreadNumber(42).build();
41+
assertThat(cleaner).isInstanceOf(MicrometerMetrics.DelayedCleaner.class);
42+
if (cleaner instanceof MicrometerMetrics.DelayedCleaner delayedCleaner) {
43+
assertThat(delayedCleaner.getCleanUpDelayInSeconds()).isEqualTo(0);
44+
}
45+
}
46+
47+
@Test
48+
void test_withCleaningThreadNumber_whenTypeIsNoDelayed_thenThrowException() {
49+
assertThatThrownBy(() -> cleanerBuilder.withCleaningThreadNumber(23))
50+
.isInstanceOf(IllegalStateException.class)
51+
.hasMessage("Cleaning threads can only be configured when using the DELAYED cleaner type");
52+
}
53+
54+
@Test
55+
void test_withCleanUpDelayInSeconds() {
56+
final var cleaner =
57+
cleanerBuilder.withCleanerType(CleanerType.DELAYED).withCleanUpDelayInSeconds(23).build();
58+
assertThat(cleaner).isInstanceOf(MicrometerMetrics.DelayedCleaner.class);
59+
if (cleaner instanceof MicrometerMetrics.DelayedCleaner delayedCleaner) {
60+
assertThat(delayedCleaner.getCleanUpDelayInSeconds()).isEqualTo(23);
61+
}
62+
}
63+
64+
@Test
65+
void test_withCleanUpDelayInSeconds_whenTypeIsNoDelayed_thenThrowException() {
66+
assertThatThrownBy(() -> cleanerBuilder.withCleanUpDelayInSeconds(1))
67+
.isInstanceOf(IllegalStateException.class)
68+
.hasMessage("Clean up delay can only be configured when using the DELAYED cleaner type");
69+
}
70+
}

micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DefaultBehaviorIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.assertj.core.api.Assertions.assertThat;
1010

1111
public class DefaultBehaviorIT extends AbstractMicrometerMetricsTestFixture {
12+
1213
@Override
1314
protected MicrometerMetrics getMetrics() {
1415
return MicrometerMetrics.newMicrometerMetricsBuilder(registry).build();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.javaoperatorsdk.operator.monitoring.micrometer;
2+
3+
import io.micrometer.core.instrument.MeterRegistry;
4+
5+
public class DefaultBehaviorWithCustomImplementationIT extends DefaultBehaviorIT {
6+
7+
@Override
8+
protected MicrometerMetrics getMetrics() {
9+
return new TestMetrics(registry);
10+
}
11+
12+
private static class TestMetrics extends MicrometerMetrics {
13+
14+
private TestMetrics(MeterRegistry registry) {
15+
super(registry, new CleanerBuilder(registry).build(), true);
16+
}
17+
}
18+
}

micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DelayedMetricsCleaningOnDeleteIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
public class DelayedMetricsCleaningOnDeleteIT extends AbstractMicrometerMetricsTestFixture {
1212

13-
private static final int testDelay = 1;
13+
protected static final int testDelay = 1;
1414

1515
@Override
1616
protected MicrometerMetrics getMetrics() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.javaoperatorsdk.operator.monitoring.micrometer;
2+
3+
import io.micrometer.core.instrument.MeterRegistry;
4+
5+
public class DelayedMetricsCleaningOnDeleteWithCustomImplementationIT
6+
extends DelayedMetricsCleaningOnDeleteIT {
7+
8+
@Override
9+
protected MicrometerMetrics getMetrics() {
10+
return new TestMetrics(registry);
11+
}
12+
13+
private static class TestMetrics extends MicrometerMetrics {
14+
15+
private TestMetrics(MeterRegistry registry) {
16+
super(
17+
registry,
18+
new CleanerBuilder(registry)
19+
.withCleanerType(CleanerType.DELAYED)
20+
.withCleanUpDelayInSeconds(testDelay)
21+
.withCleaningThreadNumber(2)
22+
.build(),
23+
true);
24+
}
25+
}
26+
}

micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/NoPerResourceCollectionIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.assertj.core.api.Assertions.assertThat;
1010

1111
public class NoPerResourceCollectionIT extends AbstractMicrometerMetricsTestFixture {
12+
1213
@Override
1314
protected MicrometerMetrics getMetrics() {
1415
return MicrometerMetrics.withoutPerResourceMetrics(registry);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.javaoperatorsdk.operator.monitoring.micrometer;
2+
3+
import io.micrometer.core.instrument.MeterRegistry;
4+
5+
public class NoPerResourceCollectionWithCustomImplementationIT extends NoPerResourceCollectionIT {
6+
7+
@Override
8+
protected MicrometerMetrics getMetrics() {
9+
return new TestMetrics(registry);
10+
}
11+
12+
private static class TestMetrics extends MicrometerMetrics {
13+
14+
private TestMetrics(MeterRegistry registry) {
15+
super(registry, new CleanerBuilder(registry).build(), false);
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)