Skip to content

Commit 2c66e06

Browse files
authored
feat(observability): add Observation SPI (#1682)
BREAKING CHANGE: the experimental driver `Metrics` and `ConnectionPoolMetrics` have been replaced with `ObservationProvider`. ## Observation SPI NOTE: This is a feature preview. The Observation SPI is used by the driver to instrument points of interest in its implementation. For instance, an observation is created around connection acquisition - it starts when acquisition begins and stops when acquisition ends. These observations may be used by SPI implementations to produce some useful output, like: metrics, traces, etc. Importantly, this resposibility is outside the instrumentation itself. Driver configuration example: ```java var config = Config.builder() .withObservationProvider(implementation) // null by default, meaning no-op implementation .build() ``` At present, the SPI is not expected to be implemented by users directly. Instead, 2 new implementation modules are introduced alongside the driver. Their versions will be managed by the driver BOM (`neo4j-java-driver-bom`). Both are described below. ### Neo4j Java Driver (Metrics) NOTE: This is a feature preview. Artifact ID: `neo4j-java-driver-observation-metrics` A basic in-memory implementation with dedicated API - `Metrics` and `ConnectionPoolMetrics`, the effectively relocated interfaces from the driver. Driver configuration example: ```java var provider = MetricsObservationProvider.newInstance(); var config = Config.builder() .withObservationProvider(provider) // enable by registering the provider .build(); var metrics = provider.metrics(); ``` ### Neo4j Java Driver (Micrometer Observation) NOTE: This is a feature preview. Artifact ID: `neo4j-java-driver-observation-micrometer` An implementation based Micrometer Observation API. Driver observations are represented as dedicated Micrometer types: - `Observation.Context` - For example, `SessionRunContext`. - `ObservationConvention` - For example, `SessionRunConvention`. - Default `ObservationConvention` - For example, `DefaultSessionRunConvention`. The dedicated types enable users to benefit from Micrometer features, including high level of customisation of handling, naming and tagging. While the naming used in this module was guided by the OpenTelemetry semantic conventions 1.36.0, it was also adapted to Micrometer naming convention. Driver configuration example: ```java var provider = MicrometerObservationProvider.builder(observationRegistry) // requires Micrometer ObservationRegistry instance .build(); // extra options are omitted for brevity var config = Config.builder() .withObservationProvider(provider) // enable by registering the provider .build(); ``` In addition, it includes `ConnectionPoolMetricsHandler` that implements Micrometer `ObservationHandler`. It manages several additional Micrometer metrics and may be optionally instantiated and registered by users.
1 parent a50c7e1 commit 2c66e06

File tree

210 files changed

+9214
-2648
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

210 files changed

+9214
-2648
lines changed

bom/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
<artifactId>neo4j-java-driver-all</artifactId>
3333
<version>${project.version}</version>
3434
</dependency>
35+
<dependency>
36+
<groupId>org.neo4j.driver</groupId>
37+
<artifactId>neo4j-java-driver-observation-metrics</artifactId>
38+
<version>${project.version}</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.neo4j.driver</groupId>
42+
<artifactId>neo4j-java-driver-observation-micrometer</artifactId>
43+
<version>${project.version}</version>
44+
</dependency>
3545
<dependency>
3646
<groupId>org.neo4j.bolt</groupId>
3747
<artifactId>neo4j-bolt-connection-bom</artifactId>

bundle/pom.xml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<properties>
2121
<moduleName>org.neo4j.driver</moduleName>
2222
<rootDir>${project.basedir}/..</rootDir>
23-
<maven.compiler.xlint.extras>,-try</maven.compiler.xlint.extras>
23+
<maven.compiler.xlint.extras>,-try,-module</maven.compiler.xlint.extras>
2424
<maven.deploy.skip>false</maven.deploy.skip>
2525
</properties>
2626

@@ -34,11 +34,6 @@
3434
</dependency>
3535

3636
<!-- Optional and / or provided dependencies, as they are not transitive to the original driver they must be declared again. -->
37-
<dependency>
38-
<groupId>io.micrometer</groupId>
39-
<artifactId>micrometer-core</artifactId>
40-
<optional>true</optional>
41-
</dependency>
4237
<dependency>
4338
<groupId>org.slf4j</groupId>
4439
<artifactId>slf4j-api</artifactId>
@@ -93,7 +88,6 @@
9388
<plugin>
9489
<groupId>org.apache.maven.plugins</groupId>
9590
<artifactId>maven-resources-plugin</artifactId>
96-
<version>3.2.0</version>
9791
<executions>
9892
<execution>
9993
<id>copy-appCtx</id>

bundle/src/main/jpms/module-info.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@
2929
exports org.neo4j.driver.util;
3030
exports org.neo4j.driver.exceptions;
3131
exports org.neo4j.driver.exceptions.value;
32+
exports org.neo4j.driver.mapping;
33+
exports org.neo4j.driver.observation;
34+
exports org.neo4j.driver.internal.observation to
35+
org.neo4j.driver.observation.metrics,
36+
org.neo4j.driver.observation.micrometer;
3237

3338
requires transitive java.logging;
3439
requires transitive org.reactivestreams;
35-
requires static micrometer.core;
3640
requires static org.graalvm.nativeimage;
3741
requires static org.slf4j;
3842
requires static java.management;

driver/clirr-ignored-differences.xml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,5 +848,62 @@
848848
<method>java.util.Optional rawClassification()</method>
849849
</difference>
850850

851+
<difference>
852+
<className>org/neo4j/driver/Config</className>
853+
<differenceType>7002</differenceType>
854+
<method>boolean isMetricsEnabled()</method>
855+
</difference>
856+
857+
<difference>
858+
<className>org/neo4j/driver/Config</className>
859+
<differenceType>7002</differenceType>
860+
<method>org.neo4j.driver.MetricsAdapter metricsAdapter()</method>
861+
</difference>
862+
863+
<difference>
864+
<className>org/neo4j/driver/Config$ConfigBuilder</className>
865+
<differenceType>7002</differenceType>
866+
<method>org.neo4j.driver.Config$ConfigBuilder withDriverMetrics()</method>
867+
</difference>
868+
869+
<difference>
870+
<className>org/neo4j/driver/Config$ConfigBuilder</className>
871+
<differenceType>7002</differenceType>
872+
<method>org.neo4j.driver.Config$ConfigBuilder withMetricsAdapter(org.neo4j.driver.MetricsAdapter)</method>
873+
</difference>
874+
875+
<difference>
876+
<className>org/neo4j/driver/Config$ConfigBuilder</className>
877+
<differenceType>7002</differenceType>
878+
<method>org.neo4j.driver.Config$ConfigBuilder withoutDriverMetrics()</method>
879+
</difference>
880+
881+
<difference>
882+
<className>org/neo4j/driver/ConnectionPoolMetrics</className>
883+
<differenceType>8001</differenceType>
884+
</difference>
885+
886+
<difference>
887+
<className>org/neo4j/driver/Driver</className>
888+
<differenceType>7002</differenceType>
889+
<method>boolean isMetricsEnabled()</method>
890+
</difference>
891+
892+
<difference>
893+
<className>org/neo4j/driver/Driver</className>
894+
<differenceType>7002</differenceType>
895+
<method>org.neo4j.driver.Metrics metrics()</method>
896+
</difference>
897+
898+
<difference>
899+
<className>org/neo4j/driver/Metrics</className>
900+
<differenceType>8001</differenceType>
901+
</difference>
902+
903+
<difference>
904+
<className>org/neo4j/driver/MetricsAdapter</className>
905+
<differenceType>8001</differenceType>
906+
</difference>
907+
851908

852909
</differences>

driver/pom.xml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<properties>
2020
<rootDir>${project.basedir}/..</rootDir>
2121
<api.classes.directory>${basedir}/target/classes-without-jpms</api.classes.directory>
22-
<maven.compiler.xlint.extras>,-try</maven.compiler.xlint.extras>
22+
<maven.compiler.xlint.extras>,-try,-module</maven.compiler.xlint.extras>
2323
<surefire.jpms.args>--add-reads org.neo4j.driver=io.netty.common --add-reads org.neo4j.driver=org.bouncycastle.provider --add-reads org.neo4j.driver=org.bouncycastle.pkix</surefire.jpms.args>
2424
<failsafe.parallelizable.jpms.args>--add-opens org.neo4j.driver/org.neo4j.driver.internal.util=ALL-UNNAMED --add-opens org.neo4j.driver/org.neo4j.driver.internal.async=ALL-UNNAMED --add-reads org.neo4j.driver=io.netty.common --add-reads org.neo4j.driver=org.bouncycastle.provider --add-reads org.neo4j.driver=org.bouncycastle.pkix</failsafe.parallelizable.jpms.args>
2525
<failsafe.sequential.jpms.args>--add-reads org.neo4j.driver=org.bouncycastle.provider --add-reads org.neo4j.driver=org.bouncycastle.pkix</failsafe.sequential.jpms.args>
@@ -55,11 +55,6 @@
5555
</dependency>
5656

5757
<!-- Optional and / or provided dependencies -->
58-
<dependency>
59-
<groupId>io.micrometer</groupId>
60-
<artifactId>micrometer-core</artifactId>
61-
<optional>true</optional>
62-
</dependency>
6358
<dependency>
6459
<groupId>org.slf4j</groupId>
6560
<artifactId>slf4j-api</artifactId>
@@ -176,7 +171,6 @@
176171
<plugin>
177172
<groupId>org.apache.maven.plugins</groupId>
178173
<artifactId>maven-resources-plugin</artifactId>
179-
<version>3.3.0</version>
180174
<executions>
181175
<execution>
182176
<id>copy-classes-excluding-jpms</id>

driver/src/main/java/module-info.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@
3030
exports org.neo4j.driver.exceptions;
3131
exports org.neo4j.driver.exceptions.value;
3232
exports org.neo4j.driver.mapping;
33+
exports org.neo4j.driver.observation;
34+
exports org.neo4j.driver.internal.observation to
35+
org.neo4j.driver.observation.metrics,
36+
org.neo4j.driver.observation.micrometer;
3337

3438
requires org.neo4j.bolt.connection;
3539
requires org.neo4j.bolt.connection.pooled;
3640
requires org.neo4j.bolt.connection.routed;
3741
requires reactor.core;
3842
requires transitive java.logging;
3943
requires transitive org.reactivestreams;
40-
requires static micrometer.core;
4144
requires static org.graalvm.nativeimage;
4245
requires static org.slf4j;
4346
requires static java.management;

driver/src/main/java/org/neo4j/driver/Config.java

Lines changed: 33 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@
3636
import org.neo4j.driver.internal.InternalNotificationConfig;
3737
import org.neo4j.driver.internal.RoutingSettings;
3838
import org.neo4j.driver.internal.SecuritySettings;
39+
import org.neo4j.driver.internal.observation.DriverObservationProvider;
40+
import org.neo4j.driver.internal.observation.NoopObservationProvider;
3941
import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic;
4042
import org.neo4j.driver.net.ServerAddressResolver;
41-
import org.neo4j.driver.util.Experimental;
43+
import org.neo4j.driver.observation.ObservationProvider;
4244
import org.neo4j.driver.util.Immutable;
45+
import org.neo4j.driver.util.Preview;
4346
import org.neo4j.driver.util.Resource;
4447

4548
/**
@@ -145,17 +148,19 @@ public final class Config implements Serializable {
145148
*/
146149
@SuppressWarnings("deprecation")
147150
private final NotificationConfig notificationConfig;
148-
/**
149-
* The {@link MetricsAdapter}.
150-
*/
151-
private final MetricsAdapter metricsAdapter;
152151

153152
/**
154153
* Specify if telemetry collection is disabled.
155154
* <p>
156155
* By default, the driver will send anonymous usage statistics to the server it connects to if the server requests those.
157156
*/
158157
private final boolean telemetryDisabled;
158+
/**
159+
* The {@link ObservationProvider} if configured.
160+
* @since 6.0.0
161+
*/
162+
@Preview(name = "Observability")
163+
private final transient ObservationProvider observationProvider;
159164

160165
private Config(ConfigBuilder builder) {
161166
this.logging = builder.logging;
@@ -177,8 +182,8 @@ private Config(ConfigBuilder builder) {
177182
this.notificationConfig = builder.notificationConfig;
178183

179184
this.eventLoopThreads = builder.eventLoopThreads;
180-
this.metricsAdapter = builder.metricsAdapter;
181185
this.telemetryDisabled = builder.telemetryDisabled;
186+
this.observationProvider = builder.observationProvider;
182187
}
183188

184189
/**
@@ -345,6 +350,7 @@ public Optional<NotificationSeverity> minimumNotificationSeverity() {
345350

346351
/**
347352
* Returns a set of disabled notification classifications.
353+
*
348354
* @return the {@link Set} of disabled {@link NotificationClassification}
349355
* @since 5.22.0
350356
*/
@@ -367,24 +373,6 @@ public int eventLoopThreads() {
367373
return eventLoopThreads;
368374
}
369375

370-
/**
371-
* Returns whether the metrics is enabled or not on this driver.
372-
*
373-
* @return if the metrics is enabled or not on this driver
374-
*/
375-
public boolean isMetricsEnabled() {
376-
return this.metricsAdapter != MetricsAdapter.DEV_NULL;
377-
}
378-
379-
/**
380-
* Returns the {@link MetricsAdapter}.
381-
*
382-
* @return the metrics adapter
383-
*/
384-
public MetricsAdapter metricsAdapter() {
385-
return this.metricsAdapter;
386-
}
387-
388376
/**
389377
* Returns the user_agent configured for this driver.
390378
*
@@ -406,6 +394,16 @@ public boolean isTelemetryDisabled() {
406394
return telemetryDisabled;
407395
}
408396

397+
/**
398+
* Returns the {@link ObservationProvider} if it is configured.
399+
* @return an {@link Optional} with {@link ObservationProvider} if configured or {@link Optional#empty()} otherwise
400+
* @since 6.0.0
401+
*/
402+
@Preview(name = "Observability")
403+
public Optional<ObservationProvider> observationProvider() {
404+
return Optional.ofNullable(observationProvider);
405+
}
406+
409407
/**
410408
* Used to build new config instances
411409
*/
@@ -425,9 +423,9 @@ public static final class ConfigBuilder {
425423
private int connectionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(30);
426424
private long maxTransactionRetryTimeMillis = ExponentialBackoffRetryLogic.DEFAULT_MAX_RETRY_TIME_MS;
427425
private ServerAddressResolver resolver;
428-
private MetricsAdapter metricsAdapter = MetricsAdapter.DEV_NULL;
429426
private long fetchSize = 1000;
430427
private int eventLoopThreads = 0;
428+
private ObservationProvider observationProvider;
431429

432430
@SuppressWarnings("deprecation")
433431
private NotificationConfig notificationConfig = NotificationConfig.defaultConfig();
@@ -744,47 +742,21 @@ public ConfigBuilder withResolver(ServerAddressResolver resolver) {
744742
}
745743

746744
/**
747-
* Enable driver metrics backed by internal basic implementation. The metrics can be obtained afterwards via {@link Driver#metrics()}.
748-
*
749-
* @return this builder.
750-
*/
751-
public ConfigBuilder withDriverMetrics() {
752-
return withMetricsEnabled(true);
753-
}
754-
755-
/**
756-
* Disable driver metrics. When disabled, driver metrics cannot be accessed via {@link Driver#metrics()}.
757-
*
758-
* @return this builder.
745+
* Sets the {@link ObservationProvider} that the driver should use.
746+
* @param observationProvider the {@link ObservationProvider} or {@code null} to disable
747+
* @return this builder
748+
* @since 6.0.0
759749
*/
760-
public ConfigBuilder withoutDriverMetrics() {
761-
return withMetricsEnabled(false);
762-
}
763-
764-
private ConfigBuilder withMetricsEnabled(boolean enabled) {
765-
if (!enabled) {
766-
withMetricsAdapter(MetricsAdapter.DEV_NULL);
767-
} else if (this.metricsAdapter == null || this.metricsAdapter == MetricsAdapter.DEV_NULL) {
768-
withMetricsAdapter(MetricsAdapter.DEFAULT);
750+
@Preview(name = "Observability")
751+
public ConfigBuilder withObservationProvider(ObservationProvider observationProvider) {
752+
this.observationProvider =
753+
Objects.requireNonNullElseGet(observationProvider, NoopObservationProvider::getInstance);
754+
if (!(observationProvider instanceof DriverObservationProvider)) {
755+
throw new IllegalArgumentException("Unssupported observation provider");
769756
}
770757
return this;
771758
}
772759

773-
/**
774-
* Enable driver metrics with given {@link MetricsAdapter}.
775-
* <p>
776-
* {@link MetricsAdapter#MICROMETER} enables implementation based on <a href="https://micrometer.io">Micrometer</a>. The metrics can be obtained
777-
* afterwards via Micrometer means and {@link Driver#metrics()}. Micrometer must be on classpath when using this option.
778-
*
779-
* @param metricsAdapter the metrics adapter to use. Use {@link MetricsAdapter#DEV_NULL} to disable metrics.
780-
* @return this builder.
781-
*/
782-
@Experimental
783-
public ConfigBuilder withMetricsAdapter(MetricsAdapter metricsAdapter) {
784-
this.metricsAdapter = Objects.requireNonNull(metricsAdapter, "metricsAdapter");
785-
return this;
786-
}
787-
788760
/**
789761
* Configure the event loop thread count. This specifies how many threads the driver can use to handle network I/O events
790762
* and user's events in driver's I/O threads. By default, 2 * NumberOfProcessors amount of threads will be used instead.

driver/src/main/java/org/neo4j/driver/Driver.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.neo4j.driver;
1818

1919
import java.util.concurrent.CompletionStage;
20-
import org.neo4j.driver.exceptions.ClientException;
2120
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
2221

2322
/**
@@ -256,22 +255,6 @@ default <T extends BaseSession> T session(Class<T> sessionClass, SessionConfig s
256255
*/
257256
CompletionStage<Void> closeAsync();
258257

259-
/**
260-
* Returns the driver metrics if metrics reporting is enabled via {@link Config.ConfigBuilder#withDriverMetrics()}.
261-
* Otherwise, a {@link ClientException} will be thrown.
262-
*
263-
* @return the driver metrics if enabled.
264-
* @throws ClientException if the driver metrics reporting is not enabled.
265-
*/
266-
Metrics metrics();
267-
268-
/**
269-
* Returns true if the driver metrics reporting is enabled via {@link Config.ConfigBuilder#withDriverMetrics()}, otherwise false.
270-
*
271-
* @return true if the metrics reporting is enabled.
272-
*/
273-
boolean isMetricsEnabled();
274-
275258
/**
276259
* This verifies if the driver can connect to a remote server or a cluster
277260
* by establishing a network connection with the remote and possibly exchanging a few data before closing the connection.

0 commit comments

Comments
 (0)