diff --git a/ojdbc-provider-observability/pom.xml b/ojdbc-provider-observability/pom.xml
new file mode 100644
index 00000000..d1a29e0f
--- /dev/null
+++ b/ojdbc-provider-observability/pom.xml
@@ -0,0 +1,95 @@
+
+
+ 4.0.0
+
+
+ com.oracle.database.jdbc
+ ojdbc-extensions
+ 1.0.3
+
+
+ Oracle JDBC Observability Provider
+ com.oracle.database.jdbc
+ ojdbc-provider-observability
+
+
+ 1.44.1
+ 11
+ 11
+
+
+
+
+ com.oracle.database.jdbc
+ ojdbc11
+
+
+ io.opentelemetry
+ opentelemetry-api
+ ${opentelemetry.version}
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ ojdbc-provider-common
+ com.oracle.database.jdbc
+ tests
+ test-jar
+
+
+ com.oracle
+ ucp
+ 11.2.0.4
+ system
+ /Users/abdessamadelaaissaoui/Desktop/jars/ucp21.jar
+
+
+
+ com.oracle
+ oraclepki
+ 1.0
+ system
+ /Users/abdessamadelaaissaoui/Downloads/oraclepki.jar
+
+
+ com.oracle
+ ons
+ 1.0
+ system
+ /Users/abdessamadelaaissaoui/Downloads/ons.jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ none
+ alphabetical
+
+
+
+
+
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfiguration.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfiguration.java
new file mode 100644
index 00000000..0fc81a11
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfiguration.java
@@ -0,0 +1,333 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
+import oracle.jdbc.provider.observability.tracers.jfr.JFRTracer;
+import oracle.jdbc.provider.observability.tracers.otel.OTelTracer;
+
+/**
+ *
+ * Implementation of {@link ObservabilityConfigurationMBean} that allows to
+ * configure the Oracle JDBC Observability Provider. The system properties that
+ * can be used to configure the Observability Provider depend on the provider
+ * used:
+ *
+ * If {@link ObservabilityTraceEventListenerProvider} is being used:
+ *
+ * - {@link ObservabilityConfiguration#ENABLED_TRACERS}: comma separated list
+ * of enabled tracers, default "JFR,OTEL"
+ * - {@link ObservabilityConfiguration#SENSITIVE_DATA_ENABLED}: true if
+ * sensitive data is enabled, default false
+ *
+ * If {@link OpenTelemetryTraceEventListenerProvider} is being used:
+ *
+ * - {@link ObservabilityConfiguration#OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED}:
+ * true if OTEL tracer is enabled, otherwise false. Default true.
+ * - {@link ObservabilityConfiguration#OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED}:
+ * true if sensitive data is enabled, default false
+ *
+ */
+public class ObservabilityConfiguration implements ObservabilityConfigurationMBean {
+
+ /**
+ *
+ * System property used to enabled/disable tracers. The value of this system property should be a comma separated list
+ * of tracers to enable.
+ *
+ *
+ * This extension implements two tracers:
+ *
+ *
+ * - OTEL: which exports traces to Open Telemetry {@link OTelTracer}
+ * - JFR: which exports traces to Java Flight recorder {@link JFRTracer}
+ *
+ *
+ * By default all tracers will be enabled.
+ */
+ public static final String ENABLED_TRACERS = "oracle.jdbc.provider.observability.enabledTracers";
+
+ /**
+ * System property used to enable/disable exporting sensitive data. Set the property to true to enable sensitive data.
+ * By default exporting sensitive data is disabled.
+ */
+ public static final String SENSITIVE_DATA_ENABLED = "oracle.jdbc.provider.observability.sensitiveDataEnabled";
+
+ /**
+ * This property is kept for backward compatibility. It is used to enable/disable the previous verison of the provider:
+ * "open-telemetry-trace-event-listener-provider".
+ */
+ public static final String OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED = "oracle.jdbc.provider.opentelemetry.enabled";
+
+ /**
+ * This property is kept for backward compatibility. It allows to enable/disable sensitive data when using the previous
+ * version of the provider: "open-telemetry-trace-event-listener-provider".
+ */
+ public static final String OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED = "oracle.jdbc.provider.opentelemetry.sensitive-enabled";
+
+ /**
+ * Default values
+ */
+ private static final String DEFAULT_ENABLED_TRACERS = "OTEL,JFR";
+ private static final String DEFAULT_SENSITIVE_DATA_ENABLED = "false";
+ private static final String DEFAULT_OPEN_TELEMETRY_ENABLED = "true";
+
+ /**
+ * Lock used to ensure that only one thread can access the configuration at a time.
+ */
+ private static final ReentrantLock observabilityConfigurationLock = new ReentrantLock();
+
+ /**
+ * Indicates whether traces are enabled
+ */
+ private boolean enabled = true;
+
+ /**
+ * Indicates whether sensitive data is enabled
+ */
+ private boolean sensitiveDataEnabled;
+
+ /**
+ * List of enabled tracers
+ */
+ private List enabledTracers = new ArrayList<>();
+
+ /**
+ * Maps registered tracer's name to its instance.
+ */
+ Map registeredTracers = new HashMap<>(2, 1);
+
+
+ /**
+ * Types of configuration. For backward compatibility allows to use OTEL for
+ * Oracle JDBC Open Telemetry Provider configuration properties.
+ */
+ public enum ObservabilityConfigurationType {
+ /**
+ * Use Oracle JDBC Open Telemetry Provider configuration properties
+ */
+ OTEL,
+ /**
+ * USE Oracle JDBC Observability Provider configuration properties
+ */
+ OBSERVABILITY
+ }
+
+ /**
+ * Constructor
+ */
+ public ObservabilityConfiguration() {
+ this(ObservabilityConfigurationType.OBSERVABILITY);
+ }
+
+ /**
+ * Constructor used by {@link ObservabilityTraceEventListener} to create a configuration.
+ *
+ * @param configurationType indicates which system properties to use. When
+ * {@link ObservabilityConfigurationType#OTEL}, the previous
+ * verison of system properties are used.
+ */
+ ObservabilityConfiguration(ObservabilityConfigurationType configurationType) {
+ String enabledTracers = DEFAULT_ENABLED_TRACERS;
+ String sensitiveDataEnabled = DEFAULT_SENSITIVE_DATA_ENABLED;
+ if (ObservabilityConfigurationType.OBSERVABILITY.equals(configurationType)) {
+ enabledTracers = System.getProperty(ENABLED_TRACERS, DEFAULT_ENABLED_TRACERS);
+ sensitiveDataEnabled = System.getProperty(SENSITIVE_DATA_ENABLED, DEFAULT_SENSITIVE_DATA_ENABLED);
+ } else {
+ String otelEnabled = System.getProperty(OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED, DEFAULT_OPEN_TELEMETRY_ENABLED);
+ if (otelEnabled != null) {
+ enabledTracers = "OTEL";
+ this.enabled = Boolean.parseBoolean(otelEnabled);
+ }
+ String otelSensitiveDataEnabled = System.getProperty(OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED, DEFAULT_SENSITIVE_DATA_ENABLED);
+ if(otelSensitiveDataEnabled != null) {
+ sensitiveDataEnabled = otelSensitiveDataEnabled;
+ }
+ }
+
+ setEnabledTracers(enabledTracers);
+ setSensitiveDataEnabled(Boolean.parseBoolean(sensitiveDataEnabled));
+ }
+
+ /**
+ * Returns true if the provider is enabled, otherwise false.
+ *
+ * @return true if the provider is enabled, otherwise false.
+ */
+ @Override
+ public boolean getEnabled() {
+ observabilityConfigurationLock.lock();
+ try {
+ return enabled;
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Enables/disables the provider.
+ *
+ * @param enabled true to enable the provider, otherwise false.
+ */
+ @Override
+ public void setEnabled(boolean enabled) {
+ observabilityConfigurationLock.lock();
+ try {
+ this.enabled = enabled;
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Returns a comma separated list of enabled tracers. Not {@code null}.
+ */
+ @Override
+ public String getEnabledTracers() {
+ observabilityConfigurationLock.lock();
+ try {
+ return enabledTracers == null ?
+ "" :
+ enabledTracers.stream().collect(Collectors.joining(","));
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Enables the tracers.
+ *
+ * This extension implements two tracers:
+ *
+ *
+ * - OTEL: which exports traces to Open Telemetry {@link OTelTracer}
+ * - JFR: which exports traces to Java Flight recorder {@link JFRTracer}
+ *
+ *
+ * Other tracer can be registered using the {@link ObservabilityConfiguration#registeredTracers}
+ * method.
+ *
+ * @param tracers comma separated list of enabled tracers.
+ */
+ @Override
+ public void setEnabledTracers(String tracers){
+ observabilityConfigurationLock.lock();
+ try {
+ String[] items = tracers.replaceAll("\\s", "").split(",");
+ enabledTracers = Arrays.asList(items);
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+
+ }
+
+ /**
+ * Returns true if sensitive data is enabled, otherwise false.
+ * @return true if sensitive data is enabled, otherwise false.
+ */
+ @Override
+ public boolean getSensitiveDataEnabled() {
+ observabilityConfigurationLock.lock();
+ try {
+ return sensitiveDataEnabled;
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Enables/disables sensitive data.
+ *
+ * @param sensitiveDataEnabled true to enable sensitive data, otherwise false.
+ */
+ @Override
+ public void setSensitiveDataEnabled(boolean sensitiveDataEnabled) {
+ observabilityConfigurationLock.lock();
+ try {
+ this.sensitiveDataEnabled = sensitiveDataEnabled;
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Returns a list of enabled tracers.
+ * @return then list of enabled tracers.
+ */
+ public List getEnabledTracersAsList() {
+ observabilityConfigurationLock.lock();
+ try {
+ return enabledTracers;
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+ /**
+ * Returns the tracer registered with that name.
+ * @param tracerName the name of the tracer.
+ * @return returns the registered tracer that was registered using that name.
+ */
+ public ObservabilityTracer getTracer(String tracerName) {
+ return registeredTracers.get(tracerName);
+ }
+
+ /**
+ * Registeres a tracer.
+ *
+ * @param tracer the tracer to register
+ */
+ public void registerTracer(ObservabilityTracer tracer) {
+ observabilityConfigurationLock.lock();
+ try {
+ registeredTracers.put(tracer.getName(), tracer);
+ } finally {
+ observabilityConfigurationLock.unlock();
+ }
+ }
+
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfigurationMBean.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfigurationMBean.java
new file mode 100644
index 00000000..f516dafa
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityConfigurationMBean.java
@@ -0,0 +1,96 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import oracle.jdbc.provider.observability.tracers.jfr.JFRTracer;
+import oracle.jdbc.provider.observability.tracers.otel.OTelTracer;
+
+/**
+ * MBean that allows to configure the Oracle JDBC Observability Provider.
+ */
+public interface ObservabilityConfigurationMBean {
+
+ /**
+ * Returns true if the provider is enabled, otherwise false.
+ *
+ * @return true if the provider is enabled, otherwise false.
+ */
+ boolean getEnabled();
+
+ /**
+ * Enables/disables the provider.
+ *
+ * @param enabled true to enable the provider, otherwise false.
+ */
+ void setEnabled(boolean enabled);
+
+ /**
+ * Returns a comma separated list of enabled tracers.
+ *
+ * @return a comma separated list of enabled tracers.
+ */
+ String getEnabledTracers();
+
+ /**
+ * Enables the tracers.
+ *
+ * This extension implements two tracers:
+ *
+ *
+ * - OTEL: which exports traces to Open Telemetry {@link OTelTracer}
+ * - JFR: which exports traces to Java Flight recorder {@link JFRTracer}
+ *
+ *
+ * @param tracers comma separated list of enabled tracers.
+ */
+ void setEnabledTracers(String tracers);
+
+ /**
+ * Returns true if sensitive data is enabled, otherwise false.
+ *
+ * @return true if sensitive data is enabled, otherwise false.
+ */
+ boolean getSensitiveDataEnabled();
+
+ /**
+ * Enables/disables sensitive data.
+ *
+ * @param sensitiveDataEnabled true to enable sensitive data, otherwise false.
+ */
+ void setSensitiveDataEnabled(boolean sensitiveDataEnabled);
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListener.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListener.java
new file mode 100644
index 00000000..9fd33725
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListener.java
@@ -0,0 +1,275 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+
+import oracle.jdbc.TraceEventListener;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration.ObservabilityConfigurationType;
+import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
+import oracle.jdbc.provider.observability.tracers.jfr.JFRTracer;
+import oracle.jdbc.provider.observability.tracers.otel.OTelTracer;
+
+/**
+ *
+ * TraceEventListener implementation that receives notifications whenever events
+ * are generated in the driver and publishes these events different tracers
+ * depending on the configuration.
+ *
+ *
+ * These events include:
+ *
+ *
+ * - roundtrips to the database server
+ * - AC begin and success
+ * - VIP down event
+ *
+ *
+ * This extension implements two tracers:
+ *
+ *
+ * - OTEL: which exports traces to Open Telemetry
+ * - JFR: which exports traces to Java Flight recorder
+ *
+ *
+ * The {@link ObservabilityConfiguration} class allows to configure which tracers
+ * are enabled and whether sensitive data should be exported or not.
+ *
+ *
+ * The {@link ObservabilityConfiguration} is a registered MBean the object name
+ * can be retrieved by calling {@link ObservabilityTraceEventListener#getMBeanObjectName()}.
+ * This MBean allows to configure the TraceEventListener by setting attributes.
+ * The following attributes are available:
+ *
+ *
+ * - EnabledTracers: comma separated list of tracers "OTEL,JFR" by
+ * default.
+ * - SensitiveDataEnabled: enables/disables exporting sensiteve data
+ * (false by default)
+ *
+ */
+public class ObservabilityTraceEventListener implements TraceEventListener {
+ /**
+ * MBean object name format
+ */
+ private static final String MBEAN_OBJECT_NAME = "com.oracle.jdbc.provider.observability:type=ObservabilityConfiguration,uniqueIdentifier=%s";
+ private static final String MBEAN_OBJECT_NAME_OTEL = "com.oracle.jdbc.extension.opentelemetry:type=OpenTelemetryTraceEventListener,uniqueIdentifier=%s";
+
+ /**
+ * Default unique identifier, if parameter not set.
+ */
+ static final CharSequence DEFAULT_UNIQUE_IDENTIFIER = "default";
+
+ /**
+ * MBean server
+ */
+ private static final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+
+ /**
+ * Logger
+ */
+ private static final Logger logger = Logger.getLogger(
+ ObservabilityTraceEventListener.class.getPackageName());
+
+ /**
+ * Configuration for this instance of Trace Event Listener
+ */
+ private final ObservabilityConfiguration configuration;
+
+ /**
+ * Static map linking the name of the listener to its instance.
+ */
+ private static final Map INSTANCES
+ = new ConcurrentHashMap<>();
+
+ private ObjectName mBeanObjectName;
+
+ /**
+ * Create a trace event listener identified by the given name.
+ * @param uniqueIdentifier the name of the trace event listener.
+ * @param configurationType configuration type for backward compatibility.
+ */
+ private ObservabilityTraceEventListener(String uniqueIdentifier,
+ ObservabilityConfigurationType configurationType) {
+ // Create the configuration for this instance and register MBean
+ final String mBeanName = ObservabilityConfigurationType.OTEL.equals(configurationType) ?
+ String.format(MBEAN_OBJECT_NAME_OTEL, uniqueIdentifier) :
+ String.format(MBEAN_OBJECT_NAME, uniqueIdentifier);
+ this.configuration = new ObservabilityConfiguration(configurationType);
+ try {
+ mBeanObjectName = new ObjectName(mBeanName);
+ if (!server.isRegistered(mBeanObjectName)) {
+ server.registerMBean(configuration, mBeanObjectName);
+ logger.log(Level.FINEST, "MBean and tracers registered");
+ }
+ } catch (InstanceAlreadyExistsException | MBeanRegistrationException |
+ NotCompliantMBeanException | MalformedObjectNameException e) {
+ logger.log(Level.WARNING, "Could not register MBean", e);
+ }
+ // Register known tracers
+ configuration.registerTracer(new OTelTracer(configuration));
+ configuration.registerTracer(new JFRTracer(configuration));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object roundTrip(Sequence sequence, TraceContext traceContext, Object userContext) {
+ if (!configuration.getEnabled()) { return null;}
+
+ // Cast the userContext to the map this listener uses, or create a new one
+ // if it is being used for the first time. This is the return value of the
+ // method, and will be send back by the driver on the next event.
+ Map currentUserContext = userContext == null ?
+ new HashMap<>() : (Map)userContext;
+
+ // loop through all the enabled tracers
+ for (String tracerName : configuration.getEnabledTracersAsList()) {
+ ObservabilityTracer tracer = configuration.getTracer(tracerName);
+ if (tracer != null) {
+ // call the tracer's round trip event with the tracer's context and store
+ // the new user context returned by the tracer in the user context map
+ Object newUserContext = tracer.traceRoundTrip(sequence, traceContext, currentUserContext.get(tracerName));
+ currentUserContext.put(tracerName, newUserContext);
+ } else {
+ // the listener does not fail if the tracer is unknow, it is possible to
+ // enable a tracer and register it later
+ logger.log(Level.WARNING, "Could not find registered tracer with name: " + tracer);
+ }
+ }
+
+ // return the new user context
+ return currentUserContext;
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object onExecutionEventReceived(JdbcExecutionEvent event, Object userContext, Object... params) {
+ if (!configuration.getEnabled()) { return null;}
+
+ // Cast the userContext to the map this listener uses, or create a new one
+ // if it is being used for the first time. This is the return value of the
+ // method, and will be send back by the driver on the next event.
+ Map currentUserContext = userContext == null ?
+ new HashMap<>() : (Map)userContext;
+
+ // loop through all the enabled tracers
+ for (String tracerName : configuration.getEnabledTracersAsList()) {
+ ObservabilityTracer tracer = configuration.getTracer(tracerName);
+ if (tracer != null) {
+ // call the tracer's execution event with the tracer's context and store
+ // the new user context returned by the tracer in the user context map
+ Object newUserContext = tracer.traceExecutionEvent(event, currentUserContext.get(tracerName), params);
+ currentUserContext.put(tracerName, newUserContext);
+ } else {
+ // the listener does not fail if the tracer is unknow, it is possible to
+ // enable a tracer and register it later
+ logger.log(Level.WARNING, "Could not find registered tracer with name: " + tracer);
+ }
+ }
+
+ // return the new user context
+ return currentUserContext;
+ }
+
+ @Override
+ public boolean isDesiredEvent(JdbcExecutionEvent event) {
+ // Accept all events
+ return true;
+ }
+
+
+ /**
+ * Returns the MBean object name assiciated with the configuration of the
+ * listener.
+ *
+ * @return the MBean object name.
+ */
+ public ObjectName getMBeanObjectName() {
+ return mBeanObjectName;
+ }
+
+ /**
+ * Returns the listener's configuration.
+ *
+ * @return the configuration instance associated to the listener.
+ */
+ public ObservabilityConfiguration getObservabilityConfiguration() {
+ return configuration;
+ }
+
+
+ /**
+ * Returns the trace event listener identified by the given unique idetifier.
+ *
+ * @param uniqueIdentifier the unique identifier, if no unique identifier was
+ * provided as a connection property, the unique identifier is "default".
+ * @return the trace event listener identified by the given unique idetifier,
+ * or {@code null} if no trace event listener with that unique idetifier was
+ * found.
+ */
+ public static ObservabilityTraceEventListener getTraceEventListener(String uniqueIdentifier) {
+ return INSTANCES.get(uniqueIdentifier);
+ }
+
+ /**
+ * Gets or creates an instance of {@link ObservabilityTraceEventListener}
+ * associated to the name.
+ * @param uniqueIdentifier the name of the listener instance.
+ * @param configurationType configuration type for backward compatibility.
+ *
+ * @return an instance of {@link ObservabilityTraceEventListener}.
+ */
+ static ObservabilityTraceEventListener getOrCreateInstance(String uniqueIdentifier,
+ ObservabilityConfigurationType configurationType) {
+ return INSTANCES.computeIfAbsent(uniqueIdentifier, n -> new ObservabilityTraceEventListener(n, configurationType));
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerProvider.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerProvider.java
new file mode 100644
index 00000000..26b726a7
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerProvider.java
@@ -0,0 +1,108 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import oracle.jdbc.TraceEventListener;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration.ObservabilityConfigurationType;
+import oracle.jdbc.spi.TraceEventListenerProvider;
+
+/**
+ * Implementation of Oracle JDBC {@link TraceEventListenerProvider} for
+ * {@link ObservabilityTraceEventListener}.
+ */
+public class ObservabilityTraceEventListenerProvider implements TraceEventListenerProvider {
+
+ /**
+ * Provider name
+ */
+ private static final String PROVIDER_NAME = "observability-trace-event-listener-provider";
+
+ /**
+ * Name Parameter name, identifies the listener
+ */
+ private static final String UNIQUE_IDENTIFIER_PARAMETER_NAME = "UNIQUE_IDENTIFIER";
+
+
+ /**
+ * Unique identifier, identifies a {@link ObservabilityTraceEventListener}.
+ */
+ protected static final Parameter uniqueIdentifierParameter = new Parameter() {
+
+ @Override
+ public boolean isSensitive() {
+ return false;
+ }
+
+ @Override
+ public String name() {
+ return UNIQUE_IDENTIFIER_PARAMETER_NAME;
+ }
+
+ };
+
+ /**
+ * Constructs a new instance of ObservabilityTraceEventListenerProvider. This
+ * constructor will be called by the driver's service provider to create a new
+ * instance.
+ */
+ public ObservabilityTraceEventListenerProvider() { }
+
+ @Override
+ public TraceEventListener getTraceEventListener(Map map) {
+ String uniqueIdentifier =
+ map.getOrDefault(
+ uniqueIdentifierParameter,
+ (CharSequence)ObservabilityTraceEventListener.DEFAULT_UNIQUE_IDENTIFIER).toString();
+ return ObservabilityTraceEventListener.getOrCreateInstance(uniqueIdentifier, ObservabilityConfigurationType.OBSERVABILITY);
+ }
+
+ @Override
+ public String getName() {
+ return PROVIDER_NAME;
+ }
+
+ @Override
+ public Collection extends Parameter> getParameters() {
+ return Collections.singletonList(uniqueIdentifierParameter);
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/OpenTelemetryTraceEventListenerProvider.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/OpenTelemetryTraceEventListenerProvider.java
new file mode 100644
index 00000000..b815bcea
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/OpenTelemetryTraceEventListenerProvider.java
@@ -0,0 +1,78 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import java.util.Map;
+
+import oracle.jdbc.TraceEventListener;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration.ObservabilityConfigurationType;
+
+/**
+ *
+ * This class implements the TraceEventListenerProvider interface exposed by the
+ * Oracle JDBC driver. It provides TraceEventListeners of type {@link
+ * oracle.jdbc.provider.observability.ObservabilityTraceEventListener}.
+ *
+ */
+public class OpenTelemetryTraceEventListenerProvider extends ObservabilityTraceEventListenerProvider {
+
+ /**
+ * Name of the provider
+ */
+ private static final String PROVIDER_NAME = "open-telemetry-trace-event-listener-provider";
+
+ /**
+ * Constructs a new instance of OpenTelemetryTraceEventListenerProvider. This
+ * constructor will be called by the driver's service provider to create a new
+ * instance.
+ */
+ public OpenTelemetryTraceEventListenerProvider() { }
+
+ @Override
+ public String getName() {
+ return PROVIDER_NAME;
+ }
+
+ @Override
+ public TraceEventListener getTraceEventListener(Map map) {
+ String uniqueIdentifier = map.get(uniqueIdentifierParameter).toString();
+ return ObservabilityTraceEventListener.getOrCreateInstance(uniqueIdentifier,
+ ObservabilityConfigurationType.OTEL);
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/ObservabilityTracer.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/ObservabilityTracer.java
new file mode 100644
index 00000000..d0c4c208
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/ObservabilityTracer.java
@@ -0,0 +1,100 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability.tracers;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import oracle.jdbc.TraceEventListener.JdbcExecutionEvent;
+import oracle.jdbc.TraceEventListener.Sequence;
+import oracle.jdbc.TraceEventListener.TraceContext;
+import oracle.jdbc.provider.observability.ObservabilityTraceEventListener;
+
+/**
+ * This interface must be implemented by all Observability tracers.
+ */
+public interface ObservabilityTracer {
+
+ /**
+ * Map containing the number of parameters expected for each execution event
+ */
+ Map EXECUTION_EVENTS_PARAMETERS
+ = new EnumMap(JdbcExecutionEvent.class) {
+ {
+ put(JdbcExecutionEvent.AC_REPLAY_STARTED, 3);
+ put(JdbcExecutionEvent.AC_REPLAY_SUCCESSFUL, 3);
+ put(JdbcExecutionEvent.VIP_RETRY, 8);
+ }
+ };
+
+ /**
+ * Returns the unique name of the tracer.
+ * @return the unique name of the tracer.
+ */
+ String getName();
+
+
+ /**
+ * Called by {@link ObservabilityTraceEventListener} when a round trip event
+ * is received.
+ *
+ * @param sequence BEFORE if before the round trip, AFTER if after the round
+ * trip
+ * @param traceContext Information about the round trip. Valid only during the
+ * call
+ * @param userContext Result of previous call on this Connection or null if no
+ * previous call or if observability was disabled since the previous call.
+ * @return a user context object that is passed to the next call on this
+ * Connection. May be null.
+ */
+ Object traceRoundTrip(Sequence sequence, TraceContext traceContext, Object userContext);
+
+ /**
+ * Called by {@link ObservabilityTraceEventListener} when an execution event
+ * is received.
+ *
+ * @param event the event.
+ * @param userContext the result of the previous call or null if no previous
+ * call has been made.
+ * @param params event specific parameters.
+ * @return a user context object that is passed to the next call for the same
+ * type of event. May be null.
+ */
+ Object traceExecutionEvent(JdbcExecutionEvent event, Object userContext, Object... params);
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFREventFactory.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFREventFactory.java
new file mode 100644
index 00000000..f42c811a
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFREventFactory.java
@@ -0,0 +1,1054 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability.tracers.jfr;
+
+import jdk.jfr.Event;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+
+import java.sql.SQLException;
+import java.util.logging.Logger;
+
+import jdk.jfr.Category;
+import oracle.jdbc.TraceEventListener.JdbcExecutionEvent;
+import oracle.jdbc.TraceEventListener.TraceContext;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration;
+import oracle.jdbc.provider.observability.ObservabilityTraceEventListener;
+import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
+
+/**
+ * Factory class for creating JFR events depending on the database function.
+ */
+public class JFREventFactory {
+
+ /**
+ * Logger
+ */
+ private static final Logger logger = Logger.getLogger(
+ ObservabilityTraceEventListener.class.getPackageName());
+
+ /**
+ * This class only has a static method, no public constructor needed.
+ */
+ private JFREventFactory() { }
+
+ /**
+ * Creates an instance of {@link RoundTripEvent} for the given trace context.
+ * The type of round trip event depends on the database function.
+ *
+ * @param traceContext the trace context received by a TraceEventListener.
+ * @param configuration the configuration
+ * @return the {@link RoundTripEvent} for the database function.
+ */
+ public static RoundTripEvent createJFRRoundTripEvent(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ switch (traceContext.databaseFunction()) {
+ case ADVANCED_QUEUING_12C_EMON_DEQUEUE:
+ return new AdvancedQueuing12cEminDequeueEvent(traceContext, configuration);
+ case ADVANCED_QUEUING_ARRAY_ENQUEUE_DEQUEUE:
+ return new AdvancedQueuingArrayEnqueueDequeue(traceContext, configuration);
+ case ADVANCED_QUEUING_DEQUEUE_V8:
+ return new AdvancedQueuingDequeueV8(traceContext, configuration);
+ case ADVANCED_QUEUING_ENQUEUE:
+ return new AdvancedQueuingEnqueue(traceContext, configuration);
+ case ADVANCED_QUEUING_GET_PROPAGATION_STATUS:
+ return new AdvancedQueuingGetPropagationStatus(traceContext, configuration);
+ case ADVANCED_QUEUING_LISTEN:
+ return new AdvancedQueuingListen(traceContext, configuration);
+ case ADVANCED_QUEUING_SESSION_GET_RPC_1:
+ return new AdvancedQueuingSessionGetRPC1(traceContext, configuration);
+ case ADVANCED_QUEUING_SESSION_GET_RPC_2:
+ return new AdvancedQueuingSessionGetRPC2(traceContext, configuration);
+ case ADVANCED_QUEUING_SHARED_DEQUEUE:
+ return new AdvancedQueuingSharedDequeue(traceContext, configuration);
+ case ADVANCED_QUEUING_SHARED_ENQUEUE:
+ return new AdvancedQueuingSharedEnqueue(traceContext, configuration);
+ case APP_REPLAY:
+ return new AppReplay(traceContext, configuration);
+ case AUTH_CALL:
+ return new AuthCall(traceContext, configuration);
+ case AUTO_COMMIT_OFF:
+ return new AutoCommitOff(traceContext, configuration);
+ case AUTO_COMMIT_ON:
+ return new AutoCommitOn(traceContext, configuration);
+ case CANCEL_ALL:
+ return new CancelAll(traceContext, configuration);
+ case CANCEL_OPERATION:
+ return new CancelOperation(traceContext, configuration);
+ case CHUNCK_INFO:
+ return new ChunkInfo(traceContext, configuration);
+ case CLIENT_FEATURES:
+ return new ClientFeatures(traceContext, configuration);
+ case CLIENT_QUERY_CACHE_IDS:
+ return new ClientQueryCacheIds(traceContext, configuration);
+ case CLIENT_QUERY_CACHE_STATS_UPDATE:
+ return new ClientQueryCacheStatsUpdate(traceContext, configuration);
+ case CLOSE_ALL_CURSOR:
+ return new CloseAllCursor(traceContext, configuration);
+ case CLOSE_CURSOR:
+ return new CloseCursor(traceContext, configuration);
+ case COMMIT:
+ return new Commit(traceContext, configuration);
+ case DB12C_NOTIFICATION_RCV:
+ return new DB12cNotificationRCV(traceContext, configuration);
+ case DBNS_SAGAS:
+ return new DBNSSagas(traceContext, configuration);
+ case DESCRIBE_ANY_V8:
+ return new DescribeAnyV8(traceContext, configuration);
+ case DESCRIBE_ARRAY:
+ return new DescribeArray(traceContext, configuration);
+ case DESCRIBE_QUERY_CALL:
+ return new DescribeQueryCall(traceContext, configuration);
+ case DIRECT_PATH_LOAD_STREAM:
+ return new DirectPathLoadStream(traceContext, configuration);
+ case DIRECT_PATH_MISC_OP:
+ return new DirectPathMISCOp(traceContext, configuration);
+ case DIRECT_PATH_PREPARE:
+ return new DirectPathPrepare(traceContext, configuration);
+ case DISTRIBUTED_TRANS_MGR_RPC:
+ return new DistributedTransMGRRPC(traceContext, configuration);
+ case EXECUTE_QUERY:
+ return new ExecuteQuery(traceContext, configuration);
+ case EXTENSIBLE_SECURITY_SESSION_CREATE:
+ return new ExtensibleSecuritySessionCreate(traceContext, configuration);
+ case EXTENSIBLE_SECURITY_SESSION_PIGGYBACK:
+ return new ExtensibleSecuritySessionPiggyback(traceContext, configuration);
+ case EXTENSIBLE_SECURITY_SESSION_ROUNDTRIP:
+ return new ExtensibleSecuritySessionRoundtrip(traceContext, configuration);
+ case FAST_UPI_CALLS:
+ return new FastUPICalls(traceContext, configuration);
+ case FETCH_ROW:
+ return new FetchRow(traceContext, configuration);
+ case GET_VERSION:
+ return new GetVersion(traceContext, configuration);
+ case KERNEL_PROGRAMMATIC_NOTIFICATION:
+ return new KernelProgrammaticNotification(traceContext, configuration);
+ case KEY_VALUE:
+ return new KeyValue(traceContext, configuration);
+ case LOB_FILE_CALL:
+ return new LOBFileCall(traceContext, configuration);
+ case LOGOFF:
+ return new LogOff(traceContext, configuration);
+ case LOGON_CHALLENGE_RESPONSE_1:
+ return new LogonChallengeResponse1(traceContext, configuration);
+ case LOGON_CHALLENGE_RESPONSE_2:
+ return new LogonChallengeResponse2(traceContext, configuration);
+ case OEXFEN:
+ return new OEXFEN(traceContext, configuration);
+ case OPEN_CURSOR:
+ return new OpenCursor(traceContext, configuration);
+ case OSQL7:
+ return new OSQL7(traceContext, configuration);
+ case OSTART:
+ return new OStart(traceContext, configuration);
+ case OSTOP:
+ return new OStop(traceContext, configuration);
+ case PARAMETER_PUT_SPFILE:
+ return new ParameterPutSPFile(traceContext, configuration);
+ case PING:
+ return new Ping(traceContext, configuration);
+ case PIPELINE_END:
+ return new PipelineEnd(traceContext, configuration);
+ case PIPELINE_PIGGYBACK_BEGIN:
+ return new PipelinePiggybackBegin(traceContext, configuration);
+ case PIPELINE_PIGGYBACK_OP:
+ return new PipelinePiggybackOp(traceContext, configuration);
+ case ROLLBACK:
+ return new Rollback(traceContext, configuration);
+ case SESSION_KEY:
+ return new SessionKey(traceContext, configuration);
+ case SESSION_STATE_OPS:
+ return new SessionStateOps(traceContext, configuration);
+ case SESSION_STATE_TEMPLATE:
+ return new SessionStateTemplate(traceContext, configuration);
+ case SESSION_SWITCH_V8:
+ return new SessionSwitchV8(traceContext, configuration);
+ case TRACING_MESSAGE:
+ return new TracingMessage(traceContext, configuration);
+ case TRANSACTION_COMMIT:
+ return new TransactionCommit(traceContext, configuration);
+ case TRANSACTION_START:
+ return new TransactionStart(traceContext, configuration);
+ case TTC_DTY_ROUNDTRIP:
+ return new TTCDTYRoundtrip(traceContext, configuration);
+ case TTC_PRO_ROUNDTRIP:
+ return new TTCPRORoundtrip(traceContext, configuration);
+ case XS_ATTACH_SESSION:
+ return new XSAttachSession(traceContext, configuration);
+ case XS_CREATE_SESSION:
+ return new XSCreateSession(traceContext, configuration);
+ case XS_DESTROY_SESSION:
+ return new XSDestroySession(traceContext, configuration);
+ case XS_DETACH_SESSION:
+ return new XSDetachSession(traceContext, configuration);
+ case XS_NAMESPACE_OP:
+ return new XSNamespaceOp(traceContext, configuration);
+ case XS_NAMESPACE_OPS:
+ return new XSNamespaceOps(traceContext, configuration);
+ case XS_SET_SESSION_PARAMETER:
+ return new XSSetSessionParameter(traceContext, configuration);
+ case XS_STATE_SYNC_OP:
+ return new XSStateSyncOp(traceContext, configuration);
+ default:
+ logger.warning("Unknown round trip received: " + traceContext.databaseFunction());
+ return new RoundTripEvent(traceContext, configuration);
+ }
+ }
+
+ /**
+ * Creates an instance {@link ExecutionEvent} for the given {@link
+ * JdbcExecutionEvent}.
+ *
+ * @param event the event.
+ * @param params the parameters to populate the event properties.
+ * @return the execution event.
+ */
+ public static Event createExecutionEvent(JdbcExecutionEvent event, Object... params) {
+ switch (event) {
+ case AC_REPLAY_STARTED:
+ return new ACReplayStarted(event, params);
+ case AC_REPLAY_SUCCESSFUL:
+ return new ACReplaySuccessful(event, params);
+ case VIP_RETRY:
+ return new VIPRetry(event, params);
+ default:
+ logger.warning("Unknow event received: " + event);
+ return new ExecutionEvent(event, params);
+ }
+ }
+
+ // Round-trip events
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_12C_EMON_DEQUEUE")
+ @Label("AQ 12c emon dequeue")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuing12cEminDequeueEvent extends RoundTripEvent{
+ public AdvancedQueuing12cEminDequeueEvent(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_ARRAY_ENQUEUE_DEQUEUE")
+ @Label("AQ Array Enqueue/Dequeue")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingArrayEnqueueDequeue extends RoundTripEvent{
+ public AdvancedQueuingArrayEnqueueDequeue(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_DEQUEUE_V8")
+ @Label("AQ Dequeue before 8.1")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingDequeueV8 extends RoundTripEvent{
+ public AdvancedQueuingDequeueV8(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_ENQUEUE")
+ @Label("AQ EnQueue")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingEnqueue extends RoundTripEvent{
+ public AdvancedQueuingEnqueue(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_GET_PROPAGATION_STATUS")
+ @Label("AQ get propagation status entries")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingGetPropagationStatus extends RoundTripEvent{
+ public AdvancedQueuingGetPropagationStatus(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_LISTEN")
+ @Label("AQ Listen")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingListen extends RoundTripEvent{
+ public AdvancedQueuingListen(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_SESSION_GET_RPC_1")
+ @Label("Session get RPC in server pool scenario")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingSessionGetRPC1 extends RoundTripEvent{
+ public AdvancedQueuingSessionGetRPC1(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_SESSION_GET_RPC_2")
+ @Label("Session get RPC in server pool scenario")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingSessionGetRPC2 extends RoundTripEvent{
+ public AdvancedQueuingSessionGetRPC2(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_SHARED_DEQUEUE")
+ @Label("AQ Sharded dequeue")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingSharedDequeue extends RoundTripEvent{
+ public AdvancedQueuingSharedDequeue(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ADVANCED_QUEUING_SHARED_ENQUEUE")
+ @Label("AQ Sharded enqueue")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AdvancedQueuingSharedEnqueue extends RoundTripEvent{
+ public AdvancedQueuingSharedEnqueue(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.APP_REPLAY")
+ @Label("Application continuity REPLAY")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AppReplay extends RoundTripEvent{
+ public AppReplay(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.AUTH_CALL")
+ @Label("Generic authentication call")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AuthCall extends RoundTripEvent{
+ public AuthCall(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.AUTO_COMMIT_OFF")
+ @Label("Auto commit off")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AutoCommitOff extends RoundTripEvent{
+ public AutoCommitOff(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.AUTO_COMMIT_ON")
+ @Label("Auto commit on")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class AutoCommitOn extends RoundTripEvent{
+ public AutoCommitOn(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CANCEL_ALL")
+ @Label("Cancel All")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class CancelAll extends RoundTripEvent{
+ public CancelAll(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CANCEL_OPERATION")
+ @Label("Cancel the current operation")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class CancelOperation extends RoundTripEvent{
+ public CancelOperation(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CHUNCK_INFO")
+ @Label("Chunk info RPC")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ChunkInfo extends RoundTripEvent{
+ public ChunkInfo(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CLIENT_FEATURES")
+ @Label("Client features")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ClientFeatures extends RoundTripEvent{
+ public ClientFeatures(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CLIENT_QUERY_CACHE_IDS")
+ @Label("Client query cache IDs")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ClientQueryCacheIds extends RoundTripEvent{
+ public ClientQueryCacheIds(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CLIENT_QUERY_CACHE_STATS_UPDATE")
+ @Label("Client query cache statistics update")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ClientQueryCacheStatsUpdate extends RoundTripEvent{
+ public ClientQueryCacheStatsUpdate(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CLOSE_ALL_CURSOR")
+ @Label("Cursor close all")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class CloseAllCursor extends RoundTripEvent{
+ public CloseAllCursor(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.CLOSE_CURSOR")
+ @Label("Close a cursor")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class CloseCursor extends RoundTripEvent{
+ public CloseCursor(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.COMMIT")
+ @Label("Commit")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class Commit extends RoundTripEvent{
+ public Commit(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DB12C_NOTIFICATION_RCV")
+ @Label("12c notification receive")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DB12cNotificationRCV extends RoundTripEvent{
+ public DB12cNotificationRCV(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DBNS_SAGAS")
+ @Label("DBMS Sagas")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DBNSSagas extends RoundTripEvent{
+ public DBNSSagas(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DESCRIBE_ANY_V8")
+ @Label("V8 Describe Any")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DescribeAnyV8 extends RoundTripEvent{
+ public DescribeAnyV8(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DESCRIBE_ARRAY")
+ @Label("Array describe")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DescribeArray extends RoundTripEvent{
+ public DescribeArray(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DESCRIBE_QUERY_CALL")
+ @Label("New describe query call")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DescribeQueryCall extends RoundTripEvent{
+ public DescribeQueryCall(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DIRECT_PATH_LOAD_STREAM")
+ @Label("Direct Path Load Stream")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DirectPathLoadStream extends RoundTripEvent{
+ public DirectPathLoadStream(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DIRECT_PATH_MISC_OP")
+ @Label("Direct Path Misc Operations")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DirectPathMISCOp extends RoundTripEvent{
+ public DirectPathMISCOp(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DIRECT_PATH_PREPARE")
+ @Label("Direct Path Prepare")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DirectPathPrepare extends RoundTripEvent{
+ public DirectPathPrepare(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.DISTRIBUTED_TRANS_MGR_RPC")
+ @Label("Distributed transaction manager RPC")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class DistributedTransMGRRPC extends RoundTripEvent{
+ public DistributedTransMGRRPC(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.EXECUTE_QUERY")
+ @Label("Execute query")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ExecuteQuery extends RoundTripEvent{
+ public ExecuteQuery(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.EXTENSIBLE_SECURITY_SESSION_CREATE")
+ @Label("eXtensible Security Sessions Create Session")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ExtensibleSecuritySessionCreate extends RoundTripEvent{
+ public ExtensibleSecuritySessionCreate(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.EXTENSIBLE_SECURITY_SESSION_PIGGYBACK")
+ @Label("eXtensible Security Sessions Piggyback")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ExtensibleSecuritySessionPiggyback extends RoundTripEvent{
+ public ExtensibleSecuritySessionPiggyback(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.EXTENSIBLE_SECURITY_SESSION_ROUNDTRIP")
+ @Label("eXtensible Security Session Roundtrip")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ExtensibleSecuritySessionRoundtrip extends RoundTripEvent{
+ public ExtensibleSecuritySessionRoundtrip(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.FAST_UPI_CALLS")
+ @Label("Fast UPI calls to opial7")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class FastUPICalls extends RoundTripEvent{
+ public FastUPICalls(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.FETCH_ROW")
+ @Label("Fetch a row")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class FetchRow extends RoundTripEvent{
+ public FetchRow(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.GET_VERSION")
+ @Label("Get Oracle version-date string in new format")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class GetVersion extends RoundTripEvent{
+ public GetVersion(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.KERNEL_PROGRAMMATIC_NOTIFICATION")
+ @Label("Kernel Programmatic Notification")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class KernelProgrammaticNotification extends RoundTripEvent{
+ public KernelProgrammaticNotification(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.KEY_VALUE")
+ @Label("Client app context, configurationspace, attribute, values")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class KeyValue extends RoundTripEvent{
+ public KeyValue(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.LOB_FILE_CALL")
+ @Label("LOB and FILE related calls")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class LOBFileCall extends RoundTripEvent{
+ public LOBFileCall(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.LOGOFF")
+ @Label("Logoff of Oracle")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class LogOff extends RoundTripEvent{
+ public LogOff(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.LOGON_CHALLENGE_RESPONSE_1")
+ @Label("First half of challenge-response logon")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class LogonChallengeResponse1 extends RoundTripEvent{
+ public LogonChallengeResponse1(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.LOGON_CHALLENGE_RESPONSE_2")
+ @Label("Second half of challenge-response logon")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class LogonChallengeResponse2 extends RoundTripEvent{
+ public LogonChallengeResponse2(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.OEXFEN")
+ @Label("OEXFEN")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class OEXFEN extends RoundTripEvent{
+ public OEXFEN(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.OPEN_CURSOR")
+ @Label("Open a cursor")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class OpenCursor extends RoundTripEvent{
+ public OpenCursor(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.OSQL7")
+ @Label("OSQL7")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class OSQL7 extends RoundTripEvent{
+ public OSQL7(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.OSTART")
+ @Label("Starts Oracle")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class OStart extends RoundTripEvent{
+ public OStart(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.OSTOP")
+ @Label("Stops Oracle")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class OStop extends RoundTripEvent{
+ public OStop(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.PARAMETER_PUT_SPFILE")
+ @Label("Put parameter using spfile (for startup)")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class ParameterPutSPFile extends RoundTripEvent{
+ public ParameterPutSPFile(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.PING")
+ @Label("Ping")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class Ping extends RoundTripEvent{
+ public Ping(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.PIPELINE_END")
+ @Label("Pipeline End")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class PipelineEnd extends RoundTripEvent{
+ public PipelineEnd(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.PIPELINE_PIGGYBACK_BEGIN")
+ @Label("Pipeline Begin Piggyback")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class PipelinePiggybackBegin extends RoundTripEvent{
+ public PipelinePiggybackBegin(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.PIPELINE_PIGGYBACK_OP")
+ @Label("Pipeline Operation Piggyback")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class PipelinePiggybackOp extends RoundTripEvent{
+ public PipelinePiggybackOp(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.ROLLBACK")
+ @Label("Rollback")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class Rollback extends RoundTripEvent{
+ public Rollback(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.SESSION_KEY")
+ @Label("Get the session key")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class SessionKey extends RoundTripEvent{
+ public SessionKey(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.SESSION_STATE_OPS")
+ @Label("Session state ops")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class SessionStateOps extends RoundTripEvent{
+ public SessionStateOps(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.SESSION_STATE_TEMPLATE")
+ @Label("Session state template")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class SessionStateTemplate extends RoundTripEvent{
+ public SessionStateTemplate(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.SESSION_SWITCH_V8")
+ @Label("V8 session switching piggyback")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class SessionSwitchV8 extends RoundTripEvent{
+ public SessionSwitchV8(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.TRACING_MESSAGE")
+ @Label("End to end tracing message")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class TracingMessage extends RoundTripEvent{
+ public TracingMessage(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.TRANSACTION_COMMIT")
+ @Label("Transaction commit, rollback, recover")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class TransactionCommit extends RoundTripEvent{
+ public TransactionCommit(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.TRANSACTION_START")
+ @Label("Transaction start, attach, detach")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class TransactionStart extends RoundTripEvent{
+ public TransactionStart(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.TTC_DTY_ROUNDTRIP")
+ @Label("Data type message exchange")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class TTCDTYRoundtrip extends RoundTripEvent{
+ public TTCDTYRoundtrip(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.TTC_PRO_ROUNDTRIP")
+ @Label("Protocol negotiation message exchange")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class TTCPRORoundtrip extends RoundTripEvent{
+ public TTCPRORoundtrip(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_ATTACH_SESSION")
+ @Label("XS Attach Session")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSAttachSession extends RoundTripEvent{
+ public XSAttachSession(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_CREATE_SESSION")
+ @Label("XS Create Session")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSCreateSession extends RoundTripEvent{
+ public XSCreateSession(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_DESTROY_SESSION")
+ @Label("XS Destroy Session")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSDestroySession extends RoundTripEvent{
+ public XSDestroySession(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_DETACH_SESSION")
+ @Label("XS Detach Session")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSDetachSession extends RoundTripEvent{
+ public XSDetachSession(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_NAMESPACE_OP")
+ @Label("XS Namespace OP")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSNamespaceOp extends RoundTripEvent{
+ public XSNamespaceOp(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_NAMESPACE_OPS")
+ @Label("XS namespace OPs")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSNamespaceOps extends RoundTripEvent{
+ public XSNamespaceOps(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_SET_SESSION_PARAMETER")
+ @Label("XS Set Session Parameter")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSSetSessionParameter extends RoundTripEvent{
+ public XSSetSessionParameter(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip.XS_STATE_SYNC_OP")
+ @Label("XS State Sync OP")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class XSStateSyncOp extends RoundTripEvent{
+ public XSStateSyncOp(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ super(traceContext, configuration);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.RoundTrip")
+ @Label("Round trip")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class RoundTripEvent extends Event {
+
+ public RoundTripEvent(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ setValues(traceContext, configuration);
+ }
+
+ public void setValues(TraceContext traceContext, ObservabilityConfiguration configuration) {
+ this.connectionID = traceContext.getConnectionId();
+ this.databaseOperation = traceContext.databaseOperation();
+ this.tenant = traceContext.tenant();
+ this.sqlID = traceContext.getSqlId();
+ if (configuration.getSensitiveDataEnabled()) {
+ this.originalSQLText = traceContext.originalSqlText();
+ this.actualSQLText = traceContext.actualSqlText();
+ this.databaseUser = traceContext.user();
+ }
+ }
+
+ @Label("Connection ID")
+ String connectionID;
+
+ @Label("Database operation")
+ String databaseOperation;
+
+ @Label("Database tenant")
+ String tenant;
+
+ @Label("SQL ID")
+ String sqlID;
+
+ @Label("Original SQL text")
+ String originalSQLText;
+
+ @Label("Actual SQL text")
+ String actualSQLText;
+
+ @Label("Database user")
+ String databaseUser;
+
+ }
+
+ // Execution Events
+ @Name("oracle.jdbc.provider.observability.ExecutionEvent.AC_REPLAY_STARTED")
+ @Label("AC replay started")
+ @Category({"Oracle JDBC", "Execution events"})
+ static class ACReplayStarted extends ACReplay {
+ public ACReplayStarted(JdbcExecutionEvent event, Object... params) {
+ super(event, params);
+ }
+ }
+
+ @Name("oracle.jdbc.provider.observability.ExecutionEvent.AC_REPLAY_SUCCESSFUL")
+ @Label("AC replay successful")
+ @Category({"Oracle JDBC", "Execution events"})
+ static class ACReplaySuccessful extends ACReplay {
+ public ACReplaySuccessful(JdbcExecutionEvent event, Object... params) {
+ super(event, params);
+ }
+ }
+
+
+ @Name("oracle.jdbc.provider.observability.ExecutionEvent.AC_REPLAY")
+ @Label("AC replay")
+ @Category({"Oracle JDBC", "Execution events"})
+ static class ACReplay extends ExecutionEvent {
+ public ACReplay(JdbcExecutionEvent event, Object... params) {
+ super(event, params);
+ if (ObservabilityTracer.EXECUTION_EVENTS_PARAMETERS.get(event) == params.length) {
+ this.errorCode = ((SQLException) params[1]).getErrorCode();
+ this.sqlState = ((SQLException) params[1]).getSQLState();
+ this.currentReplayRetryCount = params[2].toString();
+ }
+ }
+ @Label("Error code")
+ public int errorCode;
+
+ @Label("SQL state")
+ public String sqlState;
+
+ @Label("Current replay retry count")
+ public String currentReplayRetryCount;
+ }
+
+ @Name("oracle.jdbc.provider.observability.ExecutionEvent.VIP_RETRY")
+ @Label("VIP retry")
+ @Category({"Oracle JDBC", "Round trips"})
+ static class VIPRetry extends ExecutionEvent {
+ public VIPRetry(JdbcExecutionEvent event, Object... params) {
+ super(event, params);
+ if (ObservabilityTracer.EXECUTION_EVENTS_PARAMETERS.get(event) == params.length) {
+ protocol = params[1].toString();
+ host = params[2].toString();
+ port = params[3].toString();
+ serviceName = params[4].toString();
+ sid = params[5].toString();
+ connectionData = params[6].toString();
+ vipAddress = params[7].toString();
+ }
+ }
+
+ @Label("The protocol")
+ public String protocol;
+
+ @Label("The host")
+ public String host;
+
+ @Label("The port")
+ public String port;
+
+ @Label("The service name")
+ public String serviceName;
+
+ @Label("The SID")
+ public String sid;
+
+ @Label("The connection data")
+ public String connectionData;
+
+ @Label("The VIP address")
+ public String vipAddress;
+ }
+
+ @Name("oracle.jdbc.provider.observability.ExecutionEvent")
+ @Label("Execution event")
+ @Category({"Oracle JDBC", "Execution events"})
+ static class ExecutionEvent extends Event {
+ public ExecutionEvent(JdbcExecutionEvent event, Object... params) {
+ if (ObservabilityTracer.EXECUTION_EVENTS_PARAMETERS.get(event) == params.length) {
+ if (params != null && params.length > 0) {
+ this.errorMessage = params[0].toString();
+ }
+ }
+ }
+
+ @Label("Error message")
+ String errorMessage;
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFRTracer.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFRTracer.java
new file mode 100644
index 00000000..2c195506
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/jfr/JFRTracer.java
@@ -0,0 +1,112 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability.tracers.jfr;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jdk.jfr.Event;
+import oracle.jdbc.TraceEventListener.JdbcExecutionEvent;
+import oracle.jdbc.TraceEventListener.Sequence;
+import oracle.jdbc.TraceEventListener.TraceContext;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration;
+import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
+import oracle.jdbc.provider.observability.tracers.jfr.JFREventFactory.RoundTripEvent;
+
+/**
+ * {@link ObservabilityTracer} for tracing Java Flight Recorder events.
+ */
+public class JFRTracer implements ObservabilityTracer{
+
+ /**
+ * Configuraiton
+ */
+ private final ObservabilityConfiguration configuration;
+
+ /**
+ * Logger.
+ */
+ private static Logger logger = Logger.getLogger(JFRTracer.class.getPackageName());
+
+ /**
+ * Creates a new instance.
+ *
+ * @param configuration the configuraiton.
+ */
+ public JFRTracer(ObservabilityConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public String getName() {
+ return "JFR";
+ }
+
+ @Override
+ public Object traceRoundTrip(Sequence sequence, TraceContext traceContext, Object userContext) {
+ if (sequence.equals(Sequence.BEFORE)) {
+ // Create the event and start measuring event duration
+ RoundTripEvent event = JFREventFactory.createJFRRoundTripEvent(traceContext, configuration);
+ event.begin();
+ return event;
+ } else {
+ if (userContext != null) {
+ RoundTripEvent event = (RoundTripEvent) userContext;
+ // set event attributes
+ event.setValues(traceContext, configuration);
+ // stop the measuring event durating and commit
+ event.commit();
+ } else {
+ logger.log(Level.WARNING, "Unknown or null user context received from the driver on " +
+ "database operation: " + traceContext.databaseOperation());
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public Object traceExecutionEvent(JdbcExecutionEvent event, Object userContext, Object... params) {
+ // Create event and commit
+ Event executionEvent = JFREventFactory.createExecutionEvent(event, params);
+ executionEvent.begin();
+ executionEvent.commit();
+ //Return previous user context
+ return userContext;
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/otel/OTelTracer.java b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/otel/OTelTracer.java
new file mode 100644
index 00000000..f922004f
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/jdbc/provider/observability/tracers/otel/OTelTracer.java
@@ -0,0 +1,272 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability.tracers.otel;
+
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.TraceState;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Scope;
+import oracle.jdbc.TraceEventListener;
+import oracle.jdbc.TraceEventListener.JdbcExecutionEvent;
+import oracle.jdbc.TraceEventListener.Sequence;
+import oracle.jdbc.TraceEventListener.TraceContext;
+import oracle.jdbc.provider.observability.ObservabilityConfiguration;
+import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
+
+/**
+ * Open Telemetry tracer. Exports round trip event and execution events to
+ * Open Telemetry.
+ */
+public class OTelTracer implements ObservabilityTracer {
+
+ /**
+ * Key used to send the current Open Telemetry Trace Context to the server
+ * using {@link TraceContext#setClientInfo(String, String)}.
+ */
+ private static final String TRACE_KEY = "clientcontext.ora$opentelem$tracectx";
+
+ /**
+ * The trace context is sent to the server in two lines, this is the first
+ * line it contains the version and the span context.
+ */
+ private static final String TRACE_FORMAT = "traceparent: %s-%s-%s-%s\r\n";
+
+ /**
+ * Trace context version.
+ */
+ private static final String TRACE_VERSION = "00";
+
+ /**
+ * Format of the second line sent to the server containing the trace context.
+ * It contains the trace state.
+ */
+ private static final String TRACE_STATE_FORMAT = "tracestate: %s\r\n";
+
+ /**
+ * Logger.
+ */
+ private static Logger logger = Logger.getLogger(OTelTracer.class.getPackageName());
+
+ /**
+ * Configuraiton
+ */
+ private final ObservabilityConfiguration configuration;
+
+
+ /**
+ * Constructor. This tracer always uses {@link GlobalOpenTelemetry} to get
+ * the Open Telemetry tracer.
+ *
+ * @param configuration the configuration.
+ */
+ public OTelTracer(ObservabilityConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public String getName() {
+ return "OTEL";
+ }
+
+ @Override
+ public Object traceRoundTrip(Sequence sequence, TraceContext traceContext, Object userContext) {
+ if (sequence == Sequence.BEFORE) {
+ // Create the Span before the round-trip.
+ final Span span = initAndGetSpan(traceContext, traceContext.databaseOperation());
+ makeSpanCurrentAndSendContextToServer(traceContext, span);
+ // Return the Span instance to the driver. The driver holds this instance and
+ // supplies it as user context parameter on the next round-trip call.
+ return span;
+ } else {
+ // End the Span after the round-trip.
+ if (userContext != null) {
+ final Span span = (Span) userContext;
+ span.setStatus(traceContext.isCompletedExceptionally() ? StatusCode.ERROR : StatusCode.OK);
+ span.end();
+ } else {
+ logger.log(Level.WARNING, "Unknown or null user context received from the driver on " +
+ "database operation: " + traceContext.databaseOperation());
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public Object traceExecutionEvent(JdbcExecutionEvent event, Object userContext, Object... params) {
+ if (EXECUTION_EVENTS_PARAMETERS.get(event) == params.length) {
+ Tracer tracer = GlobalOpenTelemetry.get().getTracer(OTelTracer.class.getName());
+ if (event == TraceEventListener.JdbcExecutionEvent.VIP_RETRY) {
+ SpanBuilder spanBuilder = tracer
+ .spanBuilder(event.getDescription())
+ .setAttribute("Error message", params[0].toString())
+ .setAttribute("VIP Address", params[7].toString());
+ // Add sensitive information (URL and SQL) if it is enabled
+ if (configuration.getSensitiveDataEnabled()) {
+ logger.log(Level.FINEST, "Sensitive information on");
+ spanBuilder.setAttribute("Protocol", params[1].toString())
+ .setAttribute("Host", params[2].toString())
+ .setAttribute("Port", params[3].toString())
+ .setAttribute("Service name", params[4].toString())
+ .setAttribute("SID", params[5].toString())
+ .setAttribute("Connection data", params[6].toString());
+ }
+ // start and end span.
+ spanBuilder.startSpan().end();
+ } else if (event == TraceEventListener.JdbcExecutionEvent.AC_REPLAY_STARTED
+ || event == TraceEventListener.JdbcExecutionEvent.AC_REPLAY_SUCCESSFUL) {
+ SpanBuilder spanBuilder = tracer
+ .spanBuilder(event.getDescription())
+ .setAttribute("Error Message", params[0].toString())
+ .setAttribute("Error code", ((SQLException) params[1]).getErrorCode())
+ .setAttribute("SQL state", ((SQLException) params[1]).getSQLState())
+ .setAttribute("Current replay retry count", params[2].toString());
+ spanBuilder.startSpan().end();
+ } else {
+ logger.log(Level.WARNING, "Unknown event received : " + event.toString());
+ }
+ } else {
+ // log wrong number of parameters returned for execution event
+ logger.log(Level.WARNING, "Wrong number of parameters received for event " + event.toString());
+ }
+ // return the previous userContext
+ return userContext;
+ }
+
+ /**
+ * Creates a Open Telemetry Span and sets it's attributes according to the
+ * trace context and the configuration.
+ *
+ * @param traceContext the trace context.
+ * @param spanName then span name.
+ * @return returns the Span.
+ */
+ private Span initAndGetSpan(TraceContext traceContext, String spanName) {
+ /*
+ * If this is in the context of current span, the following becomes a nested or
+ * child span to the current span. I.e. the current span in context becomes
+ * parent to this child span.
+ */
+ Tracer tracer = GlobalOpenTelemetry.get().getTracer(OTelTracer.class.getName());
+ SpanBuilder spanBuilder = tracer
+ .spanBuilder(spanName)
+ .setAttribute("thread.id", Thread.currentThread().getId())
+ .setAttribute("thread.name", Thread.currentThread().getName())
+ .setAttribute("Connection ID", traceContext.getConnectionId())
+ .setAttribute("Database Operation", traceContext.databaseOperation())
+ .setAttribute("Database Tenant", traceContext.tenant())
+ .setAttribute("SQL ID", traceContext.getSqlId());
+
+ // Add sensitive information (URL and SQL) if it is enabled
+ if (configuration.getSensitiveDataEnabled()) {
+ logger.log(Level.FINEST, "Sensitive information on");
+ spanBuilder
+ .setAttribute("Database User", traceContext.user())
+ .setAttribute("Original SQL Text", traceContext.originalSqlText())
+ .setAttribute("Actual SQL Text", traceContext.actualSqlText());
+ }
+
+ // According to the semantic conventions the Span Kind should be CLIENT,
+ // used to be SERVER.
+ return spanBuilder.setSpanKind(SpanKind.CLIENT).startSpan();
+
+ }
+
+ /**
+ * Sets the span as the current Open Telemetry Span and sends context information
+ * to the database server.
+ *
+ * @param traceContext the trace context
+ * @param span the currect spans
+ */
+ private void makeSpanCurrentAndSendContextToServer(TraceContext traceContext, Span span) {
+ final String traceParent = initAndGetTraceParent(span);
+ final String traceState = initAndGetTraceState(span);
+
+ try (Scope ignored = span.makeCurrent()) {
+ // Send the current context to the server
+ traceContext.setClientInfo(TRACE_KEY, traceParent + traceState);
+ } catch (Exception ex) {
+ logger.log(Level.WARNING, "An error occured while sending the current Open Telemetry context to the server. "
+ + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Formats the current Open Telemetry context in a format that the server can
+ * understand.
+ *
+ * @param span the current Span
+ * @return the current Open Telemetry context formatted so that the server
+ * can understand.
+ */
+ private String initAndGetTraceParent(Span span) {
+ final SpanContext spanContext = span.getSpanContext();
+ final String traceId = spanContext.getTraceId();
+ // parent-id is known as the span-id
+ final String parentId = spanContext.getSpanId();
+ final String traceFlags = spanContext.getTraceFlags().toString();
+
+ return String.format(TRACE_FORMAT, TRACE_VERSION, traceId, parentId, traceFlags);
+ }
+
+ /**
+ * Formats the current Open Telemetry Span state in a format that the server
+ * can understand.
+ *
+ * @param span the current Span
+ * @return the current Open Telemetry Span state formatted so that the server
+ * can understand.
+ */
+ private String initAndGetTraceState(Span span) {
+ final TraceState traceState = span.getSpanContext().getTraceState();
+ final StringBuilder stringBuilder = new StringBuilder();
+
+ traceState.forEach((k, v) -> stringBuilder.append(k).append("=").append(v));
+ return String.format(TRACE_STATE_FORMAT, stringBuilder);
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/JFRUCPEventListenerProvider.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/JFRUCPEventListenerProvider.java
new file mode 100644
index 00000000..b45bc0bc
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/JFRUCPEventListenerProvider.java
@@ -0,0 +1,81 @@
+package oracle.ucp.provider.observability.jfr.core;
+
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.events.core.UCPEventListener;
+import oracle.ucp.events.core.UCPEventListenerProvider;
+
+import java.util.Map;
+
+/**
+ * A {@link UCPEventListenerProvider} implementation that records UCP events as
+ * Java Flight Recorder (JFR) events for advanced monitoring and diagnostics.
+ *
+ * Key Features:
+ *
+ * - Integrates with Java Flight Recorder for low-overhead event recording
+ * - Captures complete event context as JFR event attributes
+ * - Thread-safe singleton listener instance
+ * - Registered name: "jfr-ucp-listener"
+ *
+ *
+ * Prerequisites:
+ *
+ * - JFR must be enabled on the JVM (-XX:StartFlightRecording)
+ * - Requires JDK 7u4+ (with JFR support)
+ *
+ *
+ * @see UCPEventFactory
+ * @see jdk.jfr.Event
+ */
+public final class JFRUCPEventListenerProvider implements
+ UCPEventListenerProvider {
+
+ private final UCPEventListener listener;
+
+ /**
+ * The singleton event listener instance that records events via JFR.
+ * Characteristics:
+ *
+ * - Records events with full context as JFR events
+ * - Adds all pool metrics as event attributes
+ * - Extremely low overhead (JFR-optimized)
+ * - Thread-safe for concurrent event recording
+ *
+ */
+ public static final UCPEventListener TRACE_EVENT_LISTENER =
+ new UCPEventListener() {
+ @Override
+ public void onUCPEvent(EventType eventType, UCPEventContext context) {
+ UCPEventFactory.recordEvent(eventType, context);
+ }
+ };
+
+ /**
+ * Creates a new provider instance that will supply
+ * the {@link #TRACE_EVENT_LISTENER}.
+ */
+ public JFRUCPEventListenerProvider() {
+ this.listener = TRACE_EVENT_LISTENER;
+ }
+
+ /**
+ * Returns the provider's unique name "jfr-ucp-listener".
+ *
+ * @return The constant provider name
+ */
+ @Override
+ public String getName() {
+ return "jfr-ucp-listener";
+ }
+
+ /**
+ * Returns the JFR recording listener instance, ignoring any configuration.
+ *
+ * @param config Configuration map (ignored by this implementation)
+ * @return The {@link #TRACE_EVENT_LISTENER} instance
+ */
+ @Override
+ public UCPEventListener getListener(Map config) {
+ return listener;
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPBaseEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPBaseEvent.java
new file mode 100644
index 00000000..e633efc2
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPBaseEvent.java
@@ -0,0 +1,144 @@
+package oracle.ucp.provider.observability.jfr.core;
+
+import jdk.jfr.*;
+import oracle.ucp.events.core.UCPEventContext;
+
+import java.util.Objects;
+
+/**
+ * The abstract base class for all UCP (Universal Connection Pool) JFR (Java
+ * Flight Recorder) events. Provides common fields and initialization for
+ * concrete UCP event types.
+ *
+ * Metadata Annotations:
+ *
+ * - {@code @Category("UCP Events")} - Groups all UCP events together in
+ * JFR recordings
+ * - {@code @Description} - Provides human-readable descriptions for
+ * events
+ * - {@code @Label} - Defines display names for fields in JFR tools
+ * - {@code @Timespan} - Specifies unit formatting for time fields
+ *
+ *
+ * Common Fields:
+ *
+ * Field | Description | Source |
+ * poolName | Name of the connection pool | {@link
+ * UCPEventContext#poolName()} |
+ * timestamp | Event time in milliseconds | {@link
+ * UCPEventContext#timestamp()} |
+ * maxPoolSize | Configured maximum pool size | {@link
+ * UCPEventContext#maxPoolSize()} |
+ * minPoolSize | Configured minimum pool size | {@link
+ * UCPEventContext#minPoolSize()} |
+ * borrowedConnections | Currently borrowed connections |
+ * {@link UCPEventContext#borrowedConnectionsCount()} |
+ * availableConnections | Currently available connections |
+ * {@link UCPEventContext#availableConnectionsCount()} |
+ * totalConnections | Total active connections | {@link
+ * UCPEventContext#totalConnections()} |
+ * closedConnections | Lifetime closed connections |
+ * {@link UCPEventContext#closedConnections()} |
+ * createdConnections | Lifetime created connections |
+ * {@link UCPEventContext#createdConnections()} |
+ * avgWaitTime | Average connection wait time (ms) |
+ * {@link UCPEventContext#getAverageConnectionWaitTime()} |
+ *
+ *
+ * @see Event
+ */
+@Category("UCP Events")
+@Description("Base UCP Event")
+public abstract class UCPBaseEvent extends Event {
+
+ /**
+ * The name of the connection pool that generated this event.
+ * Appears as "Pool Name" in JFR tools.
+ */
+ @Label("Pool Name")
+ protected String poolName;
+
+ /**
+ * The timestamp when the event occurred (milliseconds since epoch).
+ * Appears as "Timestamp" in JFR tools.
+ */
+ @Label("Timestamp")
+ protected long timestamp;
+
+ /**
+ * The configured maximum size of the connection pool.
+ * Appears as "Max Pool Size" in JFR tools.
+ */
+ @Label("Max Pool Size")
+ protected int maxPoolSize;
+
+ /**
+ * The configured minimum size of the connection pool.
+ * Appears as "Min Pool Size" in JFR tools.
+ */
+ @Label("Min Pool Size")
+ protected int minPoolSize;
+
+ /**
+ * Current count of borrowed connections.
+ * Appears as "Borrowed Connections" in JFR tools.
+ */
+ @Label("Borrowed Connections")
+ protected int borrowedConnections;
+
+ /**
+ * Current count of available (idle) connections.
+ * Appears as "Available Connections" in JFR tools.
+ */
+ @Label("Available Connections")
+ protected int availableConnections;
+
+ /**
+ * Total count of active connections (borrowed + available).
+ * Appears as "Total Connections" in JFR tools.
+ */
+ @Label("Total Connections")
+ protected int totalConnections;
+
+ /**
+ * Lifetime count of closed connections.
+ * Appears as "Closed Connections" in JFR tools.
+ */
+ @Label("Closed Connections")
+ protected int closedConnections;
+
+ /**
+ * Lifetime count of created connections.
+ * Appears as "Created Connections" in JFR tools.
+ */
+ @Label("Created Connections")
+ protected int createdConnections;
+
+ /**
+ * Average wait time for connection requests in milliseconds.
+ * Formatted as a timespan in JFR tools.
+ */
+ @Label("Average Wait Time (ms)")
+ @Timespan(Timespan.MILLISECONDS)
+ protected long avgWaitTime;
+
+ /**
+ * Initializes common fields from a UCP event context.
+ *
+ * @param ctx The event context containing pool metrics (must not be null)
+ * @throws NullPointerException if the context is null
+ */
+ protected void initCommonFields(UCPEventContext ctx) {
+ Objects.requireNonNull(ctx, "UCPEventContext cannot be null");
+ this.poolName = ctx.poolName();
+ this.timestamp = ctx.timestamp();
+ this.maxPoolSize = ctx.maxPoolSize();
+ this.minPoolSize = ctx.minPoolSize();
+ this.borrowedConnections = ctx.borrowedConnectionsCount();
+ this.availableConnections = ctx.availableConnectionsCount();
+ this.totalConnections = ctx.totalConnections();
+ this.closedConnections = ctx.closedConnections();
+ this.createdConnections = ctx.createdConnections();
+ this.avgWaitTime = ctx.getAverageConnectionWaitTime();
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPEventFactory.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPEventFactory.java
new file mode 100644
index 00000000..89fb29fd
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/core/UCPEventFactory.java
@@ -0,0 +1,114 @@
+package oracle.ucp.provider.observability.jfr.core;
+
+import jdk.jfr.Event;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.events.core.UCPEventListener;
+import oracle.ucp.provider.observability.jfr.events.connection.ConnectionBorrowedEvent;
+import oracle.ucp.provider.observability.jfr.events.connection.ConnectionClosedEvent;
+import oracle.ucp.provider.observability.jfr.events.connection.ConnectionCreatedEvent;
+import oracle.ucp.provider.observability.jfr.events.connection.ConnectionReturnedEvent;
+import oracle.ucp.provider.observability.jfr.events.lifecycle.*;
+import oracle.ucp.provider.observability.jfr.events.maintenance.*;
+
+/**
+ * A factory class for creating and recording JFR (Java Flight Recorder) events
+ * corresponding to UCP (Universal Connection Pool) operations and state
+ * changes.
+ *
+ * Key Responsibilities:
+ *
+ * - Maps UCP event types to specific JFR event classes
+ * - Creates properly configured JFR event instances
+ * - Handles event recording lifecycle
+ *
+ * Event Categories:
+ *
+ * - Pool Lifecycle Events - Creation, start, stop, restart, and
+ * destruction
+ * - Connection Lifecycle Events - Borrow, return, and close
+ * operations
+ * - Maintenance Operations - Refresh, recycle, and purge operations
+ *
+ *
+ * @see Event
+ */
+public class UCPEventFactory {
+
+ /**
+ * Creates a JFR event instance corresponding to the specified UCP event type.
+ *
+ * Event Mapping:
+ * Each UCP event type is mapped to a specific JFR event class that captures
+ * relevant context information in its fields.
+ *
+ * @param type The UCP event type (must not be null)
+ * @param ctx The event context containing pool metrics (must not be null)
+ * @return Configured JFR event instance ready for recording
+ * @throws IllegalStateException if the event type is not recognized
+ * @throws NullPointerException if either parameter is null
+ */
+ public static Event createEvent(UCPEventListener.EventType type, UCPEventContext ctx) {
+ switch (type) {
+ // Pool Lifecycle Events
+ case POOL_CREATED:
+ return new PoolCreatedEvent(ctx);
+
+ case POOL_STARTING:
+ return new PoolStartingEvent(ctx);
+ case POOL_STARTED:
+ return new PoolStartedEvent(ctx);
+
+ case POOL_STOPPED:
+ return new PoolStoppedEvent(ctx);
+
+ case POOL_RESTARTING:
+ return new PoolRestartingEvent(ctx);
+ case POOL_RESTARTED:
+ return new PoolRestartedEvent(ctx);
+
+ case POOL_DESTROYED:
+ return new PoolDestroyedEvent(ctx);
+
+ // Connection Lifecycle Events
+ case CONNECTION_CREATED:
+ return new ConnectionCreatedEvent(ctx);
+ case CONNECTION_BORROWED:
+ return new ConnectionBorrowedEvent(ctx);
+ case CONNECTION_RETURNED:
+ return new ConnectionReturnedEvent(ctx);
+ case CONNECTION_CLOSED:
+ return new ConnectionClosedEvent(ctx);
+
+ // Maintenance Operations
+ case POOL_REFRESHED:
+ return new PoolRefreshedEvent(ctx);
+
+ case POOL_RECYCLED:
+ return new PoolRecycledEvent(ctx);
+
+ case POOL_PURGED:
+ return new PoolPurgedEvent(ctx);
+
+ default:
+ throw new IllegalStateException("Unexpected event type: " + type);
+ }
+ }
+
+ /**
+ * Creates and immediately records a JFR event for the specified UCP operation.
+ * Lifecycle:
+ *
+ * - Creates the appropriate event type via {@link #createEvent}
+ * - Populates all event fields from the context
+ * li>Commits the event to JFR
+ *
+ *
+ * @param type The UCP event type to record (must not be null)
+ * @param ctx The event context containing pool metrics (must not be null)
+ * @throws NullPointerException if either parameter is null
+ */
+ public static void recordEvent(UCPEventListener.EventType type, UCPEventContext ctx) {
+ Event event = createEvent(type, ctx);
+ event.commit();
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionBorrowedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionBorrowedEvent.java
new file mode 100644
index 00000000..b33be973
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionBorrowedEvent.java
@@ -0,0 +1,16 @@
+package oracle.ucp.provider.observability.jfr.events.connection;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.ConnectionBorrowed")
+@Label("Connection Borrowed")
+@Category({"UCP Events","Connection Lifecycle Events"})
+public class ConnectionBorrowedEvent extends UCPBaseEvent {
+ public ConnectionBorrowedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionClosedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionClosedEvent.java
new file mode 100644
index 00000000..f1062968
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionClosedEvent.java
@@ -0,0 +1,16 @@
+package oracle.ucp.provider.observability.jfr.events.connection;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.ConnectionClosed") @Label("Connection Closed")
+@Category({"UCP Events","Connection Lifecycle Events"})
+
+public class ConnectionClosedEvent extends UCPBaseEvent {
+ public ConnectionClosedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionCreatedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionCreatedEvent.java
new file mode 100644
index 00000000..0d951112
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionCreatedEvent.java
@@ -0,0 +1,17 @@
+package oracle.ucp.provider.observability.jfr.events.connection;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.ConnectionCreated")
+@Label("Connection Created")
+@Category({"UCP Events","Connection Lifecycle Events"})
+
+public class ConnectionCreatedEvent extends UCPBaseEvent {
+ public ConnectionCreatedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionReturnedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionReturnedEvent.java
new file mode 100644
index 00000000..badb10f4
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/connection/ConnectionReturnedEvent.java
@@ -0,0 +1,17 @@
+package oracle.ucp.provider.observability.jfr.events.connection;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.ConnectionReturned")
+@Label("Connection Returned")
+@Category({"UCP Events","Connection Lifecycle Events"})
+
+public class ConnectionReturnedEvent extends UCPBaseEvent {
+ public ConnectionReturnedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolCreatedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolCreatedEvent.java
new file mode 100644
index 00000000..709da6b4
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolCreatedEvent.java
@@ -0,0 +1,19 @@
+package oracle.ucp.provider.observability.jfr.events.lifecycle;
+
+import jdk.jfr.Category;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolCreated")
+@Label("Pool Created")
+@Description("Triggered when pool creation completes")
+@Category({"UCP Events","Pool Lifecycle Events"})
+
+public class PoolCreatedEvent extends UCPBaseEvent {
+ public PoolCreatedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolDestroyedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolDestroyedEvent.java
new file mode 100644
index 00000000..ea46d83b
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolDestroyedEvent.java
@@ -0,0 +1,14 @@
+package oracle.ucp.provider.observability.jfr.events.lifecycle;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolDestroyed") @Label("Pool Destroyed")
+@Category({"UCP Events","Pool Lifecycle Events"})
+
+public class PoolDestroyedEvent extends UCPBaseEvent {
+ public PoolDestroyedEvent(UCPEventContext ctx) { initCommonFields(ctx); }
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartedEvent.java
new file mode 100644
index 00000000..732861af
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartedEvent.java
@@ -0,0 +1,17 @@
+package oracle.ucp.provider.observability.jfr.events.lifecycle;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolStarted")
+@Label("Pool Started")
+@Category({"UCP Events","Pool Lifecycle Events"})
+
+public class PoolStartedEvent extends UCPBaseEvent {
+ public PoolStartedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartingEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartingEvent.java
new file mode 100644
index 00000000..58b40d2a
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStartingEvent.java
@@ -0,0 +1,18 @@
+package oracle.ucp.provider.observability.jfr.events.lifecycle;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+// Pool Start/Stop Events
+@Name("ucp.PoolStarting")
+@Label("Pool Starting")
+@Category({"UCP Events","Pool Lifecycle Events"})
+
+public class PoolStartingEvent extends UCPBaseEvent {
+ public PoolStartingEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStoppedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStoppedEvent.java
new file mode 100644
index 00000000..67fa0a4f
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/lifecycle/PoolStoppedEvent.java
@@ -0,0 +1,13 @@
+package oracle.ucp.provider.observability.jfr.events.lifecycle;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolStopped") @Label("Pool Stopped")
+@Category({"UCP Events","Pool Lifecycle Events"})
+public class PoolStoppedEvent extends UCPBaseEvent {
+ public PoolStoppedEvent(UCPEventContext ctx) { initCommonFields(ctx); }
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolPurgedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolPurgedEvent.java
new file mode 100644
index 00000000..8f8b3f7d
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolPurgedEvent.java
@@ -0,0 +1,17 @@
+package oracle.ucp.provider.observability.jfr.events.maintenance;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolPurged")
+@Label("Pool Purged")
+@Category({"UCP Events","Maintenance Operations Events"})
+
+public class PoolPurgedEvent extends UCPBaseEvent {
+ public PoolPurgedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRecycledEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRecycledEvent.java
new file mode 100644
index 00000000..e514049f
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRecycledEvent.java
@@ -0,0 +1,17 @@
+package oracle.ucp.provider.observability.jfr.events.maintenance;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+// Pool Recycle
+@Name("ucp.PoolRecycled") @Label("Pool Recycled")
+@Category({"UCP Events","Maintenance Operations Events"})
+
+public class PoolRecycledEvent extends UCPBaseEvent {
+ public PoolRecycledEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRefreshedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRefreshedEvent.java
new file mode 100644
index 00000000..4f977820
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRefreshedEvent.java
@@ -0,0 +1,18 @@
+package oracle.ucp.provider.observability.jfr.events.maintenance;
+
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolRefreshed")
+@Label("Pool Refreshed")
+@Category({"UCP Events","Maintenance Operations Events"})
+
+public class PoolRefreshedEvent extends UCPBaseEvent {
+ public PoolRefreshedEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartedEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartedEvent.java
new file mode 100644
index 00000000..847ef9d7
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartedEvent.java
@@ -0,0 +1,13 @@
+package oracle.ucp.provider.observability.jfr.events.maintenance;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+@Name("ucp.PoolRestarted") @Label("Pool Restarted")
+@Category({"UCP Events","Pool Lifecycle Events"})
+public class PoolRestartedEvent extends UCPBaseEvent {
+ public PoolRestartedEvent(UCPEventContext ctx) { initCommonFields(ctx); }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartingEvent.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartingEvent.java
new file mode 100644
index 00000000..23c29190
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/jfr/events/maintenance/PoolRestartingEvent.java
@@ -0,0 +1,16 @@
+package oracle.ucp.provider.observability.jfr.events.maintenance;
+
+import jdk.jfr.Category;
+import jdk.jfr.Label;
+import jdk.jfr.Name;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.provider.observability.jfr.core.UCPBaseEvent;
+
+// Pool Restart
+@Name("ucp.PoolRestarting") @Label("Pool Restarting")
+@Category({"UCP Events","Pool Lifecycle Events"})
+public class PoolRestartingEvent extends UCPBaseEvent {
+ public PoolRestartingEvent(UCPEventContext ctx) {
+ initCommonFields(ctx);
+ }
+}
diff --git a/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/otel/OpenTelemetryUCPEventListenerProvider.java b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/otel/OpenTelemetryUCPEventListenerProvider.java
new file mode 100644
index 00000000..e4982111
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/java/oracle/ucp/provider/observability/otel/OpenTelemetryUCPEventListenerProvider.java
@@ -0,0 +1,378 @@
+package oracle.ucp.provider.observability.otel;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.logs.Logger;
+import io.opentelemetry.api.logs.Severity;
+import io.opentelemetry.api.metrics.DoubleHistogram;
+import io.opentelemetry.api.metrics.LongCounter;
+import io.opentelemetry.api.metrics.LongUpDownCounter;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.Tracer;
+import oracle.ucp.events.core.UCPEventContext;
+import oracle.ucp.events.core.UCPEventListener;
+import oracle.ucp.events.core.UCPEventListenerProvider;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * OpenTelemetry provider for UCP events that exports observability data
+ * including metrics, traces, and logs to OpenTelemetry-compatible backends.
+ *
+ * Registration Name: "otel-ucp-listener"
+ *
+ * @see UCPEventListenerProvider
+ */
+public final class OpenTelemetryUCPEventListenerProvider implements UCPEventListenerProvider {
+
+ private final UCPEventListener listener;
+
+ /**
+ * OpenTelemetry event listener that exports UCP events as telemetry data.
+ */
+ public static final class OpenTelemetryUCPEventListener implements UCPEventListener {
+
+ private final OpenTelemetry openTelemetry;
+ private final Meter meter;
+ private final Tracer tracer;
+ private final Logger logger;
+
+ // Metrics instruments
+ private final LongUpDownCounter borrowedConnectionsGauge;
+ private final LongUpDownCounter availableConnectionsGauge;
+ private final LongUpDownCounter totalConnectionsGauge;
+ private final LongCounter connectionsCreatedCounter;
+ private final LongCounter connectionsClosedCounter;
+ private final DoubleHistogram connectionWaitTimeHistogram;
+ private final LongCounter poolEventsCounter;
+
+ // Active spans tracking
+ private final Map activePoolSpans = new ConcurrentHashMap<>();
+
+ // Common attribute keys
+ private static final AttributeKey POOL_NAME = AttributeKey.stringKey("ucp.pool.name");
+ private static final AttributeKey EVENT_TYPE = AttributeKey.stringKey("ucp.event.type");
+ private static final AttributeKey MAX_POOL_SIZE = AttributeKey.longKey("ucp.pool.max_size");
+ private static final AttributeKey MIN_POOL_SIZE = AttributeKey.longKey("ucp.pool.min_size");
+ private static final AttributeKey BORROWED_CONNECTIONS = AttributeKey.longKey("ucp.connections.borrowed");
+ private static final AttributeKey AVAILABLE_CONNECTIONS = AttributeKey.longKey("ucp.connections.available");
+ private static final AttributeKey TOTAL_CONNECTIONS = AttributeKey.longKey("ucp.connections.total");
+
+ public OpenTelemetryUCPEventListener() {
+ this(GlobalOpenTelemetry.get());
+ }
+
+ public OpenTelemetryUCPEventListener(OpenTelemetry openTelemetry) {
+ this.openTelemetry = openTelemetry;
+ this.meter = openTelemetry.getMeter("oracle.ucp.events");
+ this.tracer = openTelemetry.getTracer("oracle.ucp.events");
+ this.logger = openTelemetry.getLogsBridge().get("oracle.ucp.events");
+
+ // Initialize metrics instruments
+ this.borrowedConnectionsGauge = meter
+ .upDownCounterBuilder("ucp.connections.borrowed")
+ .setDescription("Number of currently borrowed connections")
+ .setUnit("connections")
+ .build();
+
+ this.availableConnectionsGauge = meter
+ .upDownCounterBuilder("ucp.connections.available")
+ .setDescription("Number of currently available connections")
+ .setUnit("connections")
+ .build();
+
+ this.totalConnectionsGauge = meter
+ .upDownCounterBuilder("ucp.connections.total")
+ .setDescription("Total number of active connections")
+ .setUnit("connections")
+ .build();
+
+ this.connectionsCreatedCounter = meter
+ .counterBuilder("ucp.connections.created")
+ .setDescription("Total number of connections created since pool start")
+ .setUnit("connections")
+ .build();
+
+ this.connectionsClosedCounter = meter
+ .counterBuilder("ucp.connections.closed")
+ .setDescription("Total number of connections closed since pool start")
+ .setUnit("connections")
+ .build();
+
+ this.connectionWaitTimeHistogram = meter
+ .histogramBuilder("ucp.connection.wait_time")
+ .setDescription("Time spent waiting for connections")
+ .setUnit("ms")
+ .build();
+
+ this.poolEventsCounter = meter
+ .counterBuilder("ucp.events.total")
+ .setDescription("Total number of UCP events")
+ .setUnit("events")
+ .build();
+ }
+
+ @Override
+ public void onUCPEvent(EventType eventType, UCPEventContext context) {
+ try {
+ // Common attributes for all telemetry data
+ Attributes commonAttributes = buildCommonAttributes(eventType, context);
+
+ // Record metrics
+ recordMetrics(context, commonAttributes);
+
+ // Record traces for relevant events
+ recordTraces(eventType, context, commonAttributes);
+
+ // Record structured logs
+ recordLogs(eventType, context, commonAttributes);
+
+ // Increment event counter
+ poolEventsCounter.add(1, commonAttributes);
+
+ } catch (Exception e) {
+ // Log error but don't throw to avoid disrupting pool operations
+ logger.logRecordBuilder()
+ .setSeverity(Severity.ERROR)
+ .setBody("Failed to process UCP event: " + e.getMessage())
+ .setAttribute(EVENT_TYPE, eventType.name())
+ .setAttribute(POOL_NAME, context.poolName())
+ .setAttribute(AttributeKey.stringKey("error.message"), e.getMessage())
+ .emit();
+ }
+ }
+
+ /**
+ * Records metrics for pool state and performance.
+ */
+ private void recordMetrics(UCPEventContext context, Attributes attributes) {
+ // Record current connection counts as gauge values
+ borrowedConnectionsGauge.add(context.borrowedConnectionsCount(), attributes);
+ availableConnectionsGauge.add(context.availableConnectionsCount(), attributes);
+ totalConnectionsGauge.add(context.totalConnections(), attributes);
+
+ // Record cumulative counters
+ connectionsCreatedCounter.add(context.createdConnections(), attributes);
+ connectionsClosedCounter.add(context.closedConnections(), attributes);
+
+ // Record wait time histogram
+ if (context.getAverageConnectionWaitTime() > 0) {
+ connectionWaitTimeHistogram.record(context.getAverageConnectionWaitTime(), attributes);
+ }
+ }
+
+ /**
+ * Records distributed traces for pool and connection lifecycle events.
+ */
+ private void recordTraces(EventType eventType, UCPEventContext context, Attributes attributes) {
+ String poolName = context.poolName();
+
+ switch (eventType) {
+ case POOL_CREATED:
+ Span span = tracer.spanBuilder("pool.created")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan();
+ activePoolSpans.put(poolName + ":lifecycle", span);
+ break;
+
+ case POOL_STARTING:
+ tracer.spanBuilder("pool.starting")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_STARTED:
+ tracer.spanBuilder("pool.started")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_STOPPED:
+ tracer.spanBuilder("pool.stopped")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_RESTARTING:
+ tracer.spanBuilder("pool.restarting")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_RESTARTED:
+ tracer.spanBuilder("pool.restarted")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_DESTROYED:
+ tracer.spanBuilder("pool.destroyed")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ // End the lifecycle span if it exists
+ Span lifecycleSpan = activePoolSpans.remove(poolName + ":lifecycle");
+ if (lifecycleSpan != null) {
+ lifecycleSpan.setAllAttributes(attributes).end();
+ }
+ break;
+
+ case CONNECTION_CREATED:
+ tracer.spanBuilder("connection.created")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case CONNECTION_BORROWED:
+ tracer.spanBuilder("connection.borrowed")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case CONNECTION_RETURNED:
+ tracer.spanBuilder("connection.returned")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case CONNECTION_CLOSED:
+ tracer.spanBuilder("connection.closed")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_REFRESHED:
+ tracer.spanBuilder("pool.refreshed")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_RECYCLED:
+ tracer.spanBuilder("pool.recycled")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+
+ case POOL_PURGED:
+ tracer.spanBuilder("pool.purged")
+ .setSpanKind(SpanKind.INTERNAL)
+ .setAllAttributes(attributes)
+ .startSpan()
+ .end();
+ break;
+ }
+ }
+
+ /**
+ * Records structured logs for all events.
+ */
+ private void recordLogs(EventType eventType, UCPEventContext context, Attributes attributes) {
+ Severity severity;
+
+ if (eventType == EventType.POOL_CREATED || eventType == EventType.POOL_STARTED ||
+ eventType == EventType.POOL_DESTROYED || eventType == EventType.POOL_REFRESHED ||
+ eventType == EventType.POOL_RECYCLED || eventType == EventType.POOL_PURGED) {
+ severity = Severity.INFO;
+ } else if (eventType == EventType.CONNECTION_CREATED || eventType == EventType.CONNECTION_BORROWED ||
+ eventType == EventType.CONNECTION_RETURNED || eventType == EventType.CONNECTION_CLOSED) {
+ severity = Severity.DEBUG;
+ } else if (eventType == EventType.POOL_STOPPED || eventType == EventType.POOL_RESTARTING ||
+ eventType == EventType.POOL_RESTARTED || eventType == EventType.POOL_STARTING) {
+ severity = Severity.WARN;
+ } else {
+ severity = Severity.DEBUG;
+ }
+
+ String message = String.format("UCP Event: %s - Pool: %s, MaxSize: %d, MinSize: %d, Borrowed: %d, Available: %d, Total: %d, Created: %d, Closed: %d, AvgWaitTime: %d ms",
+ eventType.name(),
+ context.poolName(),
+ context.maxPoolSize(),
+ context.minPoolSize(),
+ context.borrowedConnectionsCount(),
+ context.availableConnectionsCount(),
+ context.totalConnections(),
+ context.createdConnections(),
+ context.closedConnections(),
+ context.getAverageConnectionWaitTime());
+
+ logger.logRecordBuilder()
+ .setTimestamp(Instant.ofEpochMilli(context.timestamp()))
+ .setSeverity(severity)
+ .setBody(message)
+ .setAllAttributes(attributes)
+ .emit();
+ }
+
+ /**
+ * Builds common attributes used across all telemetry data.
+ */
+ private Attributes buildCommonAttributes(EventType eventType, UCPEventContext context) {
+ return Attributes.builder()
+ .put(POOL_NAME, context.poolName())
+ .put(EVENT_TYPE, eventType.name())
+ .put(AttributeKey.longKey("ucp.timestamp"), context.timestamp())
+ .put(MAX_POOL_SIZE, (long) context.maxPoolSize())
+ .put(MIN_POOL_SIZE, (long) context.minPoolSize())
+ .put(BORROWED_CONNECTIONS, (long) context.borrowedConnectionsCount())
+ .put(AVAILABLE_CONNECTIONS, (long) context.availableConnectionsCount())
+ .put(TOTAL_CONNECTIONS, (long) context.totalConnections())
+ .put(AttributeKey.longKey("ucp.connections.created"), (long) context.createdConnections())
+ .put(AttributeKey.longKey("ucp.connections.closed"), (long) context.closedConnections())
+ .put(AttributeKey.longKey("ucp.connection.avg_wait_time_ms"), context.getAverageConnectionWaitTime())
+ .build();
+ }
+
+ @Override
+ public boolean isDesiredEvent(EventType eventType) {
+ // Listen to all events for comprehensive observability
+ return true;
+ }
+ }
+
+ /**
+ * Creates a new OpenTelemetry UCP event listener provider.
+ */
+ public OpenTelemetryUCPEventListenerProvider() {
+ this.listener = new OpenTelemetryUCPEventListener();
+ }
+
+ @Override
+ public String getName() {
+ return "otel-ucp-listener";
+ }
+
+ @Override
+ public UCPEventListener getListener(Map config) {
+ return listener;
+ }
+}
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.jdbc.spi.TraceEventListenerProvider b/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.jdbc.spi.TraceEventListenerProvider
new file mode 100644
index 00000000..b1c86eef
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.jdbc.spi.TraceEventListenerProvider
@@ -0,0 +1,2 @@
+oracle.jdbc.provider.observability.ObservabilityTraceEventListenerProvider
+oracle.jdbc.provider.observability.OpenTelemetryTraceEventListenerProvider
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.ucp.events.core.UCPEventListenerProvider b/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.ucp.events.core.UCPEventListenerProvider
new file mode 100644
index 00000000..6442f6d0
--- /dev/null
+++ b/ojdbc-provider-observability/src/main/resources/META-INF/services/oracle.ucp.events.core.UCPEventListenerProvider
@@ -0,0 +1,2 @@
+oracle.ucp.provider.observability.jfr.core.JFRUCPEventListenerProvider
+oracle.ucp.provider.observability.otel.OpenTelemetryUCPEventListenerProvider
\ No newline at end of file
diff --git a/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/BackwardCompatibilityTest.java b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/BackwardCompatibilityTest.java
new file mode 100644
index 00000000..81e4e7f9
--- /dev/null
+++ b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/BackwardCompatibilityTest.java
@@ -0,0 +1,119 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.jupiter.api.Test;
+
+import oracle.jdbc.spi.OracleResourceProvider.Parameter;
+import oracle.jdbc.spi.TraceEventListenerProvider;
+
+public class BackwardCompatibilityTest {
+ private static final String INSTANCE_NAME = "test-instance";
+
+ @Test
+ public void testConfiguration() throws Exception {
+
+ // System properties
+ System.setProperty("oracle.jdbc.provider.opentelemetry.enabled", "true");
+ System.setProperty("oracle.jdbc.provider.opentelemetry.sensitive-enabled", "true");
+
+ TraceEventListenerProvider provider = new OpenTelemetryTraceEventListenerProvider();
+ Map parameters = new HashMap<>();
+ provider.getParameters().forEach(parameter -> {
+ parameters.put(parameter, (CharSequence)INSTANCE_NAME);
+ });
+ ObservabilityTraceEventListener listener = (ObservabilityTraceEventListener)provider.getTraceEventListener(parameters);
+ ObservabilityConfiguration configuration = listener.getObservabilityConfiguration();
+
+ assertEquals(true, configuration.getEnabled());
+ assertEquals("OTEL", configuration.getEnabledTracers());
+ assertEquals(true, configuration.getSensitiveDataEnabled());
+
+ assertEquals(1, configuration.getEnabledTracersAsList().size());
+ assertEquals("OTEL", configuration.getEnabledTracersAsList().get(0));
+
+ // MBean
+ ObjectName objectName = listener.getMBeanObjectName();
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ String enabled = server.getAttribute(objectName, "Enabled").toString();
+ String enabledTracers = server.getAttribute(objectName, "EnabledTracers").toString();
+ String sensitiveDataEnabled = server.getAttribute(objectName, "SensitiveDataEnabled").toString();
+
+ assertEquals(enabled, "true");
+ assertEquals(enabledTracers, "OTEL");
+ assertEquals(sensitiveDataEnabled, "true");
+
+ server.setAttribute(objectName, new Attribute("Enabled", false));
+ server.setAttribute(objectName, new Attribute("SensitiveDataEnabled", false));
+
+ assertEquals(false, configuration.getEnabled());
+ assertEquals(false, configuration.getSensitiveDataEnabled());
+
+ assertEquals("OTEL", configuration.getEnabledTracers());
+ assertEquals(false, configuration.getSensitiveDataEnabled());
+
+ assertEquals(1, configuration.getEnabledTracersAsList().size());
+ assertEquals("OTEL", configuration.getEnabledTracersAsList().get(0));
+
+ // Singleton
+ configuration.setEnabled(true);
+ configuration.setSensitiveDataEnabled(true);
+
+ enabled = server.getAttribute(objectName, "Enabled").toString();
+ enabledTracers = server.getAttribute(objectName, "EnabledTracers").toString();
+ sensitiveDataEnabled = server.getAttribute(objectName, "SensitiveDataEnabled").toString();
+
+ assertEquals("true", enabled);
+ assertEquals("OTEL", enabledTracers);
+ assertEquals("true", sensitiveDataEnabled);
+
+ assertEquals(1, configuration.getEnabledTracersAsList().size());
+ assertEquals("OTEL", configuration.getEnabledTracersAsList().get(0));
+
+ }
+}
diff --git a/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityConfigurationTest.java b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityConfigurationTest.java
new file mode 100644
index 00000000..df8e2ab6
--- /dev/null
+++ b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityConfigurationTest.java
@@ -0,0 +1,203 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.jupiter.api.Test;
+
+import oracle.jdbc.spi.TraceEventListenerProvider;
+import oracle.jdbc.spi.OracleResourceProvider.Parameter;
+
+public class ObservabilityConfigurationTest {
+
+ private static final String INSTANCE_NAME = "configuration-single";
+ private static final String INSTANCE_NAME_1 = "configuration-test-one";
+ private static final String INSTANCE_NAME_2 = "configuration-test-two";
+
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+
+ // Set system properties before starting
+ static {
+ System.setProperty("oracle.jdbc.provider.observability.enabledTracers", "JFR");
+ System.setProperty("oracle.jdbc.provider.observability.sensitiveDataEnabled", "true");
+ }
+
+ @Test
+ public void testConfiguration() throws Exception {
+
+ // Create a TraceEventListner named test-instance
+ TraceEventListenerProvider provider = new ObservabilityTraceEventListenerProvider();
+ ObservabilityTraceEventListener listener = createTraceEventListener(provider, INSTANCE_NAME);
+
+ // Get the configuration object
+ ObservabilityConfiguration configuration = listener.getObservabilityConfiguration();
+
+ // Verify that listener one is configured with values from system properties
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "JFR", 1, true);
+
+ // Update configuration using MBean
+ server.setAttribute(listener.getMBeanObjectName(), new Attribute("EnabledTracers", "OTEL, JFR"));
+ server.setAttribute(listener.getMBeanObjectName(), new Attribute("SensitiveDataEnabled", false));
+
+ // check that the values have been updated using the instance of the configuration
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "OTEL,JFR", 2, false);
+
+ // Update the configuration using the instance of the configuration
+ configuration.setEnabledTracers("OTEL");
+ configuration.setSensitiveDataEnabled(true);
+
+ // Check that the values returned by the MBean correspond to the values set using the instance
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "OTEL", 1, true);
+
+ }
+
+ @Test
+ public void testConfigurationWith2Instances() throws Exception {
+
+ // Create a TraceEventListner named test-instance
+ TraceEventListenerProvider provider = new ObservabilityTraceEventListenerProvider();
+ ObservabilityTraceEventListener listener1 = createTraceEventListener(provider, INSTANCE_NAME_1);
+ ObservabilityTraceEventListener listener2 = createTraceEventListener(provider, INSTANCE_NAME_2);
+
+ // Verify that listener one is configured with values from system properties
+ verifyConfiguration(listener1.getObservabilityConfiguration(),
+ listener1.getMBeanObjectName(), "JFR", 1, true);
+
+ // Verify that listener two is configured with values from system properties
+ verifyConfiguration(listener2.getObservabilityConfiguration(),
+ listener2.getMBeanObjectName(), "JFR", 1, true);
+
+ // Update configuration one using MBean
+ server.setAttribute(listener1.getMBeanObjectName(), new Attribute("EnabledTracers", "OTEL,JFR"));
+ server.setAttribute(listener1.getMBeanObjectName(), new Attribute("SensitiveDataEnabled", false));
+
+ // Verify that listener one's configuration with the values set using the MBean
+ verifyConfiguration(listener1.getObservabilityConfiguration(),
+ listener1.getMBeanObjectName(), "OTEL,JFR", 2, false);
+
+ // Verify that listener two's configuration did not change
+ verifyConfiguration(listener2.getObservabilityConfiguration(),
+ listener2.getMBeanObjectName(), "JFR", 1, true);
+
+ // Update the configuration using the instance of the configuration
+ ObservabilityConfiguration configuration2 = listener2.getObservabilityConfiguration();
+ configuration2.setEnabledTracers("OTEL");
+ configuration2.setSensitiveDataEnabled(false);
+
+ // Verify that listener one's configuration did not change
+ verifyConfiguration(listener1.getObservabilityConfiguration(),
+ listener1.getMBeanObjectName(), "OTEL,JFR", 2, false);
+
+ // Verify that listener two's configuration has been updated
+ verifyConfiguration(listener2.getObservabilityConfiguration(),
+ listener2.getMBeanObjectName(), "OTEL", 1, false);
+
+ }
+
+ @Test
+ public void testDefaultUniqueIdentifier() throws Exception {
+
+ // Create a TraceEventListner named test-instance
+ TraceEventListenerProvider provider = new ObservabilityTraceEventListenerProvider();
+ ObservabilityTraceEventListener listener = (ObservabilityTraceEventListener)provider.getTraceEventListener(new HashMap<>());
+
+ // Get the configuration object
+ ObservabilityConfiguration configuration = listener.getObservabilityConfiguration();
+
+ // Verify that listener one is configured with values from system properties
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "JFR", 1, true);
+
+ // Update configuration using MBean
+ server.setAttribute(listener.getMBeanObjectName(), new Attribute("EnabledTracers", "OTEL, JFR"));
+ server.setAttribute(listener.getMBeanObjectName(), new Attribute("SensitiveDataEnabled", false));
+
+ // check that the values have been updated using the instance of the configuration
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "OTEL,JFR", 2, false);
+
+ // Update the configuration using the instance of the configuration
+ configuration.setEnabledTracers("OTEL");
+ configuration.setSensitiveDataEnabled(true);
+
+ // Check that the values returned by the MBean correspond to the values set using the instance
+ verifyConfiguration(configuration, listener.getMBeanObjectName(), "OTEL", 1, true);
+
+ }
+
+ private ObservabilityTraceEventListener createTraceEventListener(TraceEventListenerProvider provider,
+ String instanceName) {
+ Map parameters = new HashMap<>();
+ provider.getParameters().forEach(parameter -> {
+ parameters.put(parameter, (CharSequence)instanceName);
+ });
+ return (ObservabilityTraceEventListener)provider.getTraceEventListener(parameters);
+ }
+
+ private void verifyConfiguration(ObservabilityConfiguration configuration,
+ ObjectName mBeanObjectName,
+ String expectedTracers,
+ int expectedTracerCount,
+ boolean expectedSensitiveDataEnabled) throws Exception{
+
+ String[] expectedTracersArray = expectedTracers.split(",");
+ // Verify that the configuration matches the configuration set using system properties
+ assertEquals(expectedTracers, configuration.getEnabledTracers());
+ assertEquals(expectedSensitiveDataEnabled, configuration.getSensitiveDataEnabled());
+ assertEquals(expectedTracerCount, configuration.getEnabledTracersAsList().size());
+ assertEquals(expectedTracerCount, expectedTracersArray.length, "Wrong number of tracers.");
+ for (int i = 0; i < expectedTracerCount; i++) {
+ assertEquals(expectedTracersArray[i], configuration.getEnabledTracersAsList().get(i));
+ }
+
+ // Get configuration using MBean and check that it matches the configuration set using system properties
+ String enabledTracers = server.getAttribute(mBeanObjectName, "EnabledTracers").toString();
+ String sensitiveDataEnabled = server.getAttribute(mBeanObjectName, "SensitiveDataEnabled").toString();
+ assertEquals(enabledTracers, expectedTracers);
+ assertEquals(Boolean.parseBoolean(sensitiveDataEnabled), expectedSensitiveDataEnabled);
+
+ }
+
+}
diff --git a/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTestProperties.java b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTestProperties.java
new file mode 100644
index 00000000..7a765d5a
--- /dev/null
+++ b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTestProperties.java
@@ -0,0 +1,44 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+public enum ObservabilityTestProperties {
+ OBSERVABILITY_URL,
+ OBSERVABILITY_USERNAME,
+ OBSERVABILITY_PASSWORD
+}
diff --git a/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerTest.java b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerTest.java
new file mode 100644
index 00000000..52432893
--- /dev/null
+++ b/ojdbc-provider-observability/src/test/java/oracle/jdbc/provider/observability/ObservabilityTraceEventListenerTest.java
@@ -0,0 +1,235 @@
+/*
+ ** Copyright (c) 2025 Oracle and/or its affiliates.
+ **
+ ** The Universal Permissive License (UPL), Version 1.0
+ **
+ ** Subject to the condition set forth below, permission is hereby granted to any
+ ** person obtaining a copy of this software, associated documentation and/or data
+ ** (collectively the "Software"), free of charge and under any and all copyright
+ ** rights in the Software, and any and all patent rights owned or freely
+ ** licensable by each licensor hereunder covering either (i) the unmodified
+ ** Software as contributed to or provided by such licensor, or (ii) the Larger
+ ** Works (as defined below), to deal in both
+ **
+ ** (a) the Software, and
+ ** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ ** one is included with the Software (each a "Larger Work" to which the Software
+ ** is contributed by such licensors),
+ **
+ ** without restriction, including without limitation the rights to copy, create
+ ** derivative works of, display, perform, and distribute the Software and make,
+ ** use, sell, offer for sale, import, export, have made, and have sold the
+ ** Software and the Larger Work(s), and to sublicense the foregoing rights on
+ ** either these or other terms.
+ **
+ ** This license is subject to the following condition:
+ ** The above copyright notice and either this complete permission notice or at
+ ** a minimum a reference to the UPL must be included in all copies or
+ ** substantial portions of the Software.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ ** SOFTWARE.
+ */
+package oracle.jdbc.provider.observability;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mockito;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.metrics.MeterBuilder;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.TraceFlags;
+import io.opentelemetry.api.trace.TraceState;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.TracerProvider;
+import jdk.jfr.Configuration;
+import jdk.jfr.Recording;
+import jdk.jfr.consumer.RecordedEvent;
+import jdk.jfr.consumer.RecordingFile;
+import oracle.jdbc.DatabaseFunction;
+import oracle.jdbc.driver.OracleConnection;
+import oracle.jdbc.provider.TestProperties;
+
+public class ObservabilityTraceEventListenerTest {
+ String url = TestProperties.getOrAbort(ObservabilityTestProperties.OBSERVABILITY_URL);
+ String userName = TestProperties.getOrAbort(ObservabilityTestProperties.OBSERVABILITY_USERNAME);
+ String password = TestProperties.getOrAbort(ObservabilityTestProperties.OBSERVABILITY_PASSWORD);
+
+ // JFR
+ private static final String SESSION_KEY = "oracle.jdbc.provider.observability.RoundTrip.SESSION_KEY";
+ private static final String AUTH_CALL = "oracle.jdbc.provider.observability.RoundTrip.AUTH_CALL";
+ private static final String EXECUTE_QUERY = "oracle.jdbc.provider.observability.RoundTrip.EXECUTE_QUERY";
+ private static final String LOGOFF = "oracle.jdbc.provider.observability.RoundTrip.LOGOFF";
+
+ // OTEL
+ private static final OpenTelemetry openTelemetry = Mockito.mock(OpenTelemetry.class);
+ private static final Span span = Mockito.mock(Span.class);
+ private static final SpanContext spanContext = Mockito.mock(SpanContext.class);
+ private static final TraceFlags traceFlags = Mockito.mock(TraceFlags.class);
+ private static final TraceState traceState = Mockito.mock(TraceState.class);
+ private static final SpanBuilder spanBuilder = Mockito.mock(SpanBuilder.class);
+ private static final TracerProvider tracerProvider = Mockito.mock(TracerProvider.class);
+ private static final Tracer tracer = Mockito.mock(Tracer.class);
+ private static final MeterBuilder meterBuilder = Mockito.mock(MeterBuilder.class);
+
+ static {
+ GlobalOpenTelemetry.set(openTelemetry);
+ configureOTEL();
+ }
+
+ @ParameterizedTest(name = "JFRTraceTest - {arguments}")
+ @ValueSource(booleans = {true, false})
+ public void JFRTraceTest(boolean sensitiveDataEnabled) throws Exception {
+
+ System.setProperty("oracle.jdbc.provider.observability.enabledTracers", "JFR");
+ System.setProperty("oracle.jdbc.provider.observability.sensitiveDataEnabled", String.valueOf(sensitiveDataEnabled));
+ Configuration configuration = Configuration.getConfiguration("default");
+ String connectionId = null;
+ try (Recording recording = new Recording(configuration)) {
+ recording.start();
+ String jfrUrl = url + "?oracle.jdbc.provider.traceEventListener=observability-trace-event-listener-provider&oracle.jdbc.provider.traceEventListener.unique_identifier=test-jfr" + sensitiveDataEnabled;
+ try (Connection connection = DriverManager.getConnection(jfrUrl, userName, password);
+ Statement statement = connection.createStatement();
+ ResultSet resultSet = statement.executeQuery("SELECT 'OK' FROM DUAL")) {
+ connectionId = ((OracleConnection)connection).getNetConnectionId();
+ while (resultSet.next()) {
+ assertEquals("OK", resultSet.getString(1));
+ }
+ }
+ recording.stop();
+ recording.dump(Path.of("dump" + sensitiveDataEnabled + ".jfr"));
+
+ try (RecordingFile recordingFile = new RecordingFile(Path.of("dump" + sensitiveDataEnabled + ".jfr"))) {
+ int countRoundTrips = 0;
+ while (recordingFile.hasMoreEvents()) {
+ RecordedEvent event = recordingFile.readEvent();
+ if (event.getEventType().getCategoryNames().contains("Round trips")) {
+ countRoundTrips++;
+ switch (event.getEventType().getName()) {
+ case SESSION_KEY:
+ assertEquals(connectionId, event.getString("connectionID"));
+ assertNotNull(event.getString("databaseOperation"));
+ assertNull(event.getString("sqlID"));
+ assertNull(event.getString("originalSQLText"));
+ assertNull(event.getString("actualSQLText"));
+ assertEquals(sensitiveDataEnabled, event.getString("databaseUser") != null);
+ break;
+ case AUTH_CALL:
+ assertEquals(connectionId, event.getString("connectionID"));
+ assertNotNull(event.getString("databaseOperation"));
+ assertNull(event.getString("sqlID"));
+ assertNull(event.getString("originalSQLText"));
+ assertNull(event.getString("actualSQLText"));
+ assertEquals(sensitiveDataEnabled, event.getString("databaseUser") != null);
+
+ break;
+ case EXECUTE_QUERY:
+ assertEquals(connectionId, event.getString("connectionID"));
+ assertNotNull(event.getString("databaseOperation"));
+ assertNotNull(event.getString("sqlID"));
+ assertEquals(sensitiveDataEnabled, event.getString("originalSQLText") != null);
+ assertEquals(sensitiveDataEnabled, event.getString("actualSQLText") != null);
+ assertEquals(sensitiveDataEnabled, event.getString("databaseUser") != null);
+ break;
+ case LOGOFF:
+ assertEquals(connectionId, event.getString("connectionID"));
+ assertNotNull(event.getString("databaseOperation"));
+ assertNull(event.getString("sqlID"));
+ assertNull(event.getString("originalSQLText"));
+ assertNull(event.getString("actualSQLText"));
+ assertEquals(sensitiveDataEnabled, event.getString("databaseUser") != null);
+ break;
+ default:
+ fail("Unexpected event");
+ }
+ }
+ }
+ assertTrue(countRoundTrips > 0, "Application should have performed at least one round trip");
+ }
+ }
+
+ }
+
+ @ParameterizedTest(name = "OTELTraceTest - {arguments}")
+ @ValueSource(booleans = {true, false})
+ public void OTELTraceTest(boolean sensitiveDataEnabled) throws Exception {
+ Mockito.clearInvocations(tracer, spanBuilder);
+ System.setProperty("oracle.jdbc.provider.observability.enabledTracers", "OTEL");
+ System.setProperty("oracle.jdbc.provider.observability.sensitiveDataEnabled", String.valueOf(sensitiveDataEnabled));
+ String otelUrl = url + "?oracle.jdbc.provider.traceEventListener=observability-trace-event-listener-provider&oracle.jdbc.provider.traceEventListener.unique_identifier=test-otel-" + sensitiveDataEnabled ;
+ String connectionId = null;
+ try (Connection connection = DriverManager.getConnection(otelUrl, userName, password);
+ Statement statement = connection.createStatement();
+ ResultSet resultSet = statement.executeQuery("SELECT 'OK' FROM DUAL")) {
+ connectionId = ((OracleConnection)connection).getNetConnectionId();
+ while (resultSet.next()) {
+ assertEquals("OK", resultSet.getString(1));
+ }
+ }
+
+ Mockito.verify(tracer, atLeastOnce()).spanBuilder(DatabaseFunction.SESSION_KEY.getDescription());
+ Mockito.verify(tracer, atLeastOnce()).spanBuilder(DatabaseFunction.AUTH_CALL.getDescription());
+ Mockito.verify(tracer, atLeastOnce()).spanBuilder(DatabaseFunction.EXECUTE_QUERY.getDescription());
+ Mockito.verify(tracer, atLeastOnce()).spanBuilder(DatabaseFunction.LOGOFF.getDescription());
+ Mockito.verify(spanBuilder, atLeastOnce()).startSpan();
+ Mockito.verify(spanBuilder, Mockito.atLeast(4)).setAttribute("thread.id", Thread.currentThread().getId());
+ Mockito.verify(spanBuilder, Mockito.atLeast(4)).setAttribute("thread.name", Thread.currentThread().getName());
+ Mockito.verify(spanBuilder, Mockito.atLeast(1)).setAttribute("Connection ID", connectionId);
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Database Operation", DatabaseFunction.SESSION_KEY.getDescription());
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Database Operation", DatabaseFunction.AUTH_CALL.getDescription());
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Database Operation", DatabaseFunction.EXECUTE_QUERY.getDescription());
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Database Operation", DatabaseFunction.LOGOFF.getDescription());
+ if (sensitiveDataEnabled) {
+ Mockito.verify(spanBuilder, Mockito.times(4)).setAttribute("Database User", userName);
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Original SQL Text", "SELECT 'OK' FROM DUAL");
+ Mockito.verify(spanBuilder, Mockito.times(1)).setAttribute("Actual SQL Text", "SELECT 'OK' FROM DUAL");
+ } else {
+ Mockito.verify(spanBuilder, Mockito.times(0)).setAttribute("Database User", userName);
+ Mockito.verify(spanBuilder, Mockito.times(0)).setAttribute("Original SQL Text", "SELECT 'OK' FROM DUAL");
+ Mockito.verify(spanBuilder, Mockito.times(0)).setAttribute("Actual SQL Text", "SELECT 'OK' FROM DUAL");
+ }
+ Mockito.verify(span, atLeast(4)).end();
+
+ }
+
+ private static void configureOTEL() {
+ Mockito.when(openTelemetry.getTracerProvider()).thenReturn(tracerProvider);
+ Mockito.when(tracerProvider.get(Mockito.anyString())).thenReturn(tracer);
+ Mockito.when(openTelemetry.meterBuilder(Mockito.anyString())).thenReturn(meterBuilder);
+ Mockito.when(spanContext.getTraceFlags()).thenReturn(traceFlags);
+ Mockito.when(spanContext.getTraceState()).thenReturn(traceState);
+ Mockito.when(span.getSpanContext()).thenReturn(spanContext);
+ Mockito.when(spanBuilder.setAttribute(Mockito.anyString(), Mockito.anyString())).thenReturn(spanBuilder);
+ Mockito.when(spanBuilder.setAttribute(Mockito.anyString(), Mockito.anyLong())).thenReturn(spanBuilder);
+ Mockito.when(spanBuilder.setAttribute(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(spanBuilder);
+ Mockito.when(spanBuilder.setAttribute(Mockito.anyString(), Mockito.any())).thenReturn(spanBuilder);
+ Mockito.when(spanBuilder.setSpanKind(Mockito.any(SpanKind.class))).thenReturn(spanBuilder);
+ Mockito.when(spanBuilder.startSpan()).thenReturn(span);
+ Mockito.when(tracer.spanBuilder(Mockito.anyString())).thenReturn(spanBuilder);
+ }
+
+}
diff --git a/ojdbc-provider-opentelemetry/src/main/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListener.java b/ojdbc-provider-opentelemetry/src/main/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListener.java
index 00ac32c7..4c63944d 100644
--- a/ojdbc-provider-opentelemetry/src/main/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListener.java
+++ b/ojdbc-provider-opentelemetry/src/main/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListener.java
@@ -73,9 +73,9 @@
*
*
* The system properties
- * {@value #OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_ENABLED}
+ * {@value #OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED}
* and
- * {@value #OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED}
+ * {@value #OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED}
* can be used
* to enable/disable this listener and the use of sensitive data by this
* listener. A MBean registered by the {@link
@@ -88,12 +88,12 @@ public class OpenTelemetryTraceEventListener
/**
* Name of the property used to enable or disable this listener.
*/
- public static final String OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_ENABLED = "oracle.jdbc.provider.opentelemetry.enabled";
+ public static final String OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED = "oracle.jdbc.provider.opentelemetry.enabled";
/**
* Name of the property used to enable or disable sensitive data for this
* listener.
*/
- public static final String OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED = "oracle.jdbc.provider.opentelemetry.sensitive-enabled";
+ public static final String OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED = "oracle.jdbc.provider.opentelemetry.sensitive-enabled";
private static final String TRACE_KEY = "clientcontext.ora$opentelem$tracectx";
@@ -107,7 +107,7 @@ public class OpenTelemetryTraceEventListener
}
};
- private static Logger logger = Logger.getLogger(OpenTelemetryTraceEventListener.class.getName());
+ private static Logger logger = Logger.getLogger(OpenTelemetryTraceEventListener.class.getPackageName());
private Tracer tracer;
@@ -132,8 +132,8 @@ private enum Configuration {
private AtomicBoolean sensitiveDataEnabled;
private Configuration(boolean enabled, boolean sensitiveDataEnabled) {
- String enabledStr = System.getProperty(OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_ENABLED);
- String sensitiveStr = System.getProperty(OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED);
+ String enabledStr = System.getProperty(OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED);
+ String sensitiveStr = System.getProperty(OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED);
this.enabled = new AtomicBoolean(enabledStr == null ? enabled : Boolean.parseBoolean(enabledStr));
this.sensitiveDataEnabled = new AtomicBoolean(
sensitiveStr == null ? sensitiveDataEnabled : Boolean.parseBoolean(sensitiveStr));
diff --git a/ojdbc-provider-opentelemetry/src/test/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListenerTest.java b/ojdbc-provider-opentelemetry/src/test/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListenerTest.java
index 8bac137e..3fd05dba 100644
--- a/ojdbc-provider-opentelemetry/src/test/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListenerTest.java
+++ b/ojdbc-provider-opentelemetry/src/test/java/oracle/jdbc/provider/opentelemetry/OpenTelemetryTraceEventListenerTest.java
@@ -97,8 +97,8 @@ public void setupMocks() throws Exception {
@Test
void testPropertiesDisabled() throws Exception {
- System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_ENABLED, "false");
- System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED, "false");
+ System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED, "false");
+ System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED, "false");
OpenTelemetryTraceEventListener traceEventListener = new OpenTelemetryTraceEventListener(tracer);
Assertions.assertFalse(traceEventListener.isEnabled(), "Set to false using system property");
Assertions.assertFalse(traceEventListener.isSensitiveDataEnabled(), "Set to false using system property");
@@ -106,8 +106,8 @@ void testPropertiesDisabled() throws Exception {
@Test
void testPropertiesEnabled() throws Exception {
- System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_ENABLED, "true");
- System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMENTRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED, "true");
+ System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMETRY_TRACE_EVENT_LISTENER_ENABLED, "true");
+ System.setProperty(OpenTelemetryTraceEventListener.OPEN_TELEMETRY_TRACE_EVENT_LISTENER_SENSITIVE_ENABLED, "true");
OpenTelemetryTraceEventListener traceEventListener = new OpenTelemetryTraceEventListener(tracer);
Assertions.assertTrue(traceEventListener.isEnabled(), "Set to false using system property");
Assertions.assertTrue(traceEventListener.isSensitiveDataEnabled(), "Set to false using system property");
diff --git a/ojdbc-provider-samples/pom.xml b/ojdbc-provider-samples/pom.xml
index fe4ec2bc..a815c2ee 100644
--- a/ojdbc-provider-samples/pom.xml
+++ b/ojdbc-provider-samples/pom.xml
@@ -5,7 +5,7 @@
Oracle JDBC Provider Code Samples
ojdbc-provider-samples
- ${project.parent.version}
+ 1.0.3
jar
@@ -23,17 +23,17 @@
com.oracle.database.jdbc
ojdbc-provider-azure
- ${project.parent.version}
+ 1.0.3
com.oracle.database.jdbc
ojdbc-provider-oci
- ${project.parent.version}
+ 1.0.3
com.oracle.database.jdbc
ojdbc-provider-jackson-oson
- ${project.parent.version}
+ 1.0.3
com.oracle.database.jdbc
diff --git a/pom.xml b/pom.xml
index 28583708..03ac4cba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,15 @@
8
+
+ javac-release-jfr
+
+ [9,)
+
+
+ 11
+
+
@@ -63,6 +72,7 @@
ojdbc-provider-opentelemetry
ojdbc-provider-aws
ojdbc-provider-jackson-oson
+ ojdbc-provider-observability
@@ -77,6 +87,11 @@
ojdbc8
${jdbc.version}
+
+ com.oracle.database.jdbc
+ ojdbc11
+ ${jdbc.version}
+
com.oracle.database.security
oraclepki