New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add custom micrometer registries via NeonBeeConfig #94
Conversation
88c55a9
to
38372d6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is doing far more then only load the NeonBeeConfig earlier during the NeonBee creation process. It introduces a complete new mechanism to register metrics.
You should describe this in the commit message, or even add some docu how I can use this new mechanism. Whats the benefit of creating own registries? Can I add with this my own metrics? Are they automatically visible to the endpoint?
38372d6
to
3ba595e
Compare
src/main/java/io/neonbee/endpoint/metrics/NeonBeePrometheusHandler.java
Outdated
Show resolved
Hide resolved
src/main/java/io/neonbee/endpoint/metrics/NeonBeePrometheusHandler.java
Outdated
Show resolved
Hide resolved
Hey everyone! Happy new year! 🥂🎆 @halber I really like where this change is going and it introduces a lot of simplifications / nicer signatures that I like. Good work so far! I was about to do my initial review on the change, but I unfortunately have to open another round of discussion here for everyone: @pk-work, @s4heid and @halber After reviewing the whole change, what I really do dislike is, that we previously made efforts of removing all blocking code. Now with this change we are re-introducing it across multiple classes like NeonBeeConfig, the ConfigHelper etc, alongside of all the new code that is needed to test the synchronous stuff... With that being said, wouldn't be better of doing it like it was suggested in vertx-config in the first place? See [1]. More specifically (to compare): We could introduce a empty "load()" method in public static Future<NeonBeeConfig> load() {
Vertx vertx = Vertx.vertx();
return load(vertx).eventually(nothing -> vertx.close());
} Advantage is that is introduces zero additional code & just a very little more test code. Then in the static volatile Logger logger;
static void initialize(NeonBeeOptions options) {
// old code ...
if (logger === null) {
synchronized(NeonBee.class) {
if (logger === null) {
// old logging initialization here
}
}
}
}
// ...
static Future<NeonBee> create(Supplier<Future<Vertx>> vertxFutureSupplier, NeonBeeOptions options) {
// static initializer, e.g. for logging
initialize(options);
return NeonBeeConfig.load().compose(config -> create(vertxFutureSupplier, options, config));
}
static Future<NeonBee> create(Supplier<Future<Vertx>> vertxFutureSupplier, NeonBeeOptions options,
NeonBeeConfig config) {
// static initializer, e.g. for logging
initialize(options);
} I am a little unsure about the If you say we keep the current synchronous way, I will finish my review of the existing code, if we say we switch to the temporary Vert.x instance instead, I would like to wait for @halber to do the change first. Sorry for opening another round of discussion on this, even after @halber did invest a lot of work already, but sometimes you only start to see things clearly, if you see what impact is had on the code. [1] https://vertx.io/docs/vertx-config/java/#_configuring_vert_x_itself |
Hi @kristian, thanks for your detailed feedback. There's a few things which came to my mind when reading your comment.
After a discussion with @halber this morning, it seems that adding registries to the
@kristian If you don't mind, please wait with your review until the change has been adapted. I guess it will be quite different thereafter. Best Regards, |
I agree to all your points @s4heid and @halber. In regards to 2., I get your point here. What Vert.x does is to close it's underlying Netty thread pools, which is why it takes some time to complete. As starting multiple Vert.x's side-by-side is actually not a problem at all and (if?) only closing Vert.x introduces the slowdown, we could also close the initial Vert.x instance "fire and forget" (most cleanly, we could just resolve the promise in our shutdown hook). Generally I really do like to have loaded the config upfront, we will run into situations again, where we will require to have it upfront... so scrapping all of @halber 's work is maybe not the best idea (yet?). I really do like the idea, as well as the simplification in the method signatures introduced. I just see the disadvantage of 1.: introducing one-purpose blocking methods, which should / must never to be used again. How to you like the fire & forget Vert.x close suggestion? |
src/main/java/io/neonbee/endpoint/metrics/NeonBeePrometheusHandler.java
Outdated
Show resolved
Hide resolved
src/test/java/io/neonbee/endpoint/metrics/NeonBeeMetricsIt.java
Outdated
Show resolved
Hide resolved
cc8edf6
to
00212c0
Compare
// create a Vert.x instance (clustered or unclustered) | ||
return vertxFutureSupplier.get().compose(vertx -> { | ||
return vertxFutureSupplier.apply(vertxOptions).compose(vertx -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rather provide the vertxFutureSupplier
(renamed to vertxFactory
) with the whole NeonBeeOptions and the factory can do whatever with it? this would leave the create method more clean with lines 221-227 and also resolve the naing conflict with vertxOptions and neonBeeOptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can change this if we Load NeonBee options upfront. Currently I have to set the same CompositeMeterRegistry in the VertxOptions and in the NeonBeeConfig. If we had the NeonBeeConfig before we create the Vertx object, then I could easily change this.
@@ -250,10 +260,7 @@ public static NeonBee get(Vertx vertx) { | |||
} | |||
|
|||
@VisibleForTesting | |||
static Future<Vertx> newVertx(NeonBeeOptions options) { | |||
VertxOptions vertxOptions = new VertxOptions().setEventLoopPoolSize(options.getEventLoopPoolSize()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we keep this in here and rahter move the code down from the create() method into here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #94 (comment)
/** | ||
* Add {@link NeonBeePrometheusMeterRegistry} to the {@link CompositeMeterRegistry}. | ||
* | ||
* Note that this Method is <b>static synchronized</b> to ensure that the registry is only added once. | ||
* | ||
* @param vertx the {@link Vertx} instance | ||
*/ | ||
@SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") | ||
private static synchronized void addRegistry(Vertx vertx) { | ||
CompositeMeterRegistry compositeMeterRegistry = NeonBee.get(vertx).getConfig().getCompositeMeterRegistry(); | ||
boolean isNotRegisterd = compositeMeterRegistry.getRegistries().stream() | ||
.noneMatch(NeonBeePrometheusMeterRegistry.class::isInstance); | ||
if (isNotRegisterd) { | ||
compositeMeterRegistry.add(new NeonBeePrometheusMeterRegistry(PrometheusConfig.DEFAULT)); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove, the NeonBeePrometheusMeterRegistry
should be set as default in the NeonBeeConfig
(instead of initializing the microMeterRegistries
with a private List<String> registries = List.of();
initialize it with a List.of("io.neonbee.endpoint.metric.NeonBeePrometheusMeterRegistry")
. This way it is clear (from a config perspective) which registies get added, it removes the requirement to (with some dubious instance variable) pass the composite metrics registry around via the getConfig()
method and allows to not add the NeonBeePrometheusMeterRegistry
if it is unwanted by the user. If there are no registries set in the config, the metrics endpoint should not get deployed in the NeonBee
class (this way it can be disabled).
The downside of this is: How do you pass configuration from the config into the registry? I think later we should not only have microMeterRegistries
be a List<String>
, but add a new MicroMeterRegistryConfig
object and make it a List<MicroMeterRegistryConfig>
, that also allows to pass a JSON of options towards the registry implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of the exact opposite of what I had discussed with @pk-work directly. The underlying discussion was this one here #94 (comment).
We should filter by ID otr name or something like that, to ensure that we ALWAYS get the default PrometheusMeterRegistry. Otherwise we don't know which PrometheusMeterRegistry we get in case that we also use one or multiple custom regestries of type PrometheusMeterRegistry
I can't filter the PrometheusMeterRegistry at this point and would therefore suggest that if you configure your own PrometheusMeterRegistry the default PrometheusMeterRegistry in the NeonBeeConfig is not added. If someone really has the case that he has two PrometheusMeterRegistry he would have to solve this himself by writing code.
I added the NeonBeePrometheusMeterRegistry class to filter for this registry. I also moved the default initialization of the PrometheusMeterRegistry from the NeonBeeConfig to the MetricsEndpoint. Only when the MetricsEndpoint is loaded, the default NeonBeePrometheusMeterRegistry is added to the CompositeMeterRegistry.
Cool! I'm happy that we had a talk.
src/main/java/io/neonbee/endpoint/metrics/NeonBeePrometheusHandler.java
Outdated
Show resolved
Hide resolved
eca1f72
to
3b3bb4d
Compare
cacb548
to
f28dfe3
Compare
@halber I will now look into your comments. sorry for the delay. let us close this one soon :) |
With this change the config parameter micrometerRegistries is introduced which allows the user to specify a list of (full qualified) class names, which must implement the load() method of the functional interface MicrometerRegistryLoader. The load method must return a MeterRegistry, which will be added to the micrometer registries by NeonBee. The PrometheusMeterRegistry will be the default registry, which is only available when the MetricsEndpoint is loaded. Co-authored-by: Michael Halberstadt <michael.halberstadt@sap.com>
f28dfe3
to
dca4ba5
Compare
lgtm! 👍 |
With this change the config parameter
micrometerRegistries
isintroduced which allows the user to specify a list of (full qualified) class
names, which must implement the
load()
method of the functional interfaceMicrometerRegistryLoader
. The load method must return aMeterRegistry
, which will be added to the micrometer registries byNeonBee. The
PrometheusMeterRegistry
will be the defaultregistry, which is only available when the
MetricsEndpoint
is loaded.