Skip to content

Commit 6db2af2

Browse files
committed
First commit
1 parent e2982af commit 6db2af2

9 files changed

+545
-0
lines changed

ojdbc-provider-observability/pom.xml

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.oracle.database.jdbc</groupId>
8+
<artifactId>ojdbc-extensions</artifactId>
9+
<version>1.0.2</version>
10+
</parent>
11+
12+
<groupId>com.oracle.database.jdbc</groupId>
13+
<artifactId>ojdbc-provider-observability</artifactId>
14+
<version>1.0.2</version>
15+
16+
<properties>
17+
<opentelemetry.version>1.44.1</opentelemetry.version>
18+
<maven.compiler.source>11</maven.compiler.source>
19+
<maven.compiler.target>11</maven.compiler.target>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>com.oracle.database.jdbc</groupId>
25+
<artifactId>ojdbc11</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>io.opentelemetry</groupId>
29+
<artifactId>opentelemetry-api</artifactId>
30+
<version>${opentelemetry.version}</version>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.mockito</groupId>
34+
<artifactId>mockito-core</artifactId>
35+
<scope>test</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.junit.jupiter</groupId>
39+
<artifactId>junit-jupiter-engine</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
44+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package oracle.jdbc.provider.observability;
2+
3+
import java.util.EnumMap;
4+
5+
import oracle.jdbc.TraceEventListener;
6+
import oracle.jdbc.provider.observability.configuration.ObservabilityConfiguration;
7+
import oracle.jdbc.provider.observability.tracers.JFRTracer;
8+
import oracle.jdbc.provider.observability.tracers.OTelTracer;
9+
import oracle.jdbc.provider.observability.tracers.ObservabilityTracer;
10+
11+
public class ObservabilityTraceEventListener implements TraceEventListener {
12+
13+
14+
public enum Tracers {
15+
OTEL(new OTelTracer()),
16+
JFR(new JFRTracer());
17+
18+
private ObservabilityTracer tracer;
19+
20+
Tracers(ObservabilityTracer tracer) {
21+
this.tracer = tracer;
22+
}
23+
24+
public ObservabilityTracer getTracer() {
25+
return tracer;
26+
}
27+
}
28+
29+
@Override
30+
public Object roundTrip(Sequence sequence, TraceContext traceContext, Object userContext) {
31+
EnumMap<Tracers, Object> currentUserContext = getCurrentUserContext(userContext);
32+
for (Tracers tracer : ObservabilityConfiguration.getInstance().getEnabledTracersSet()) {
33+
Object newUserContext = tracer.getTracer().traceRoudtrip(sequence, traceContext, currentUserContext.get(tracer));
34+
currentUserContext.put(tracer, newUserContext);
35+
}
36+
return currentUserContext;
37+
}
38+
39+
40+
@Override
41+
public Object onExecutionEventReceived(JdbcExecutionEvent event, Object userContext, Object... params) {
42+
EnumMap<Tracers, Object> currentUserContext = getCurrentUserContext(userContext);
43+
for (Tracers tracer : ObservabilityConfiguration.getInstance().getEnabledTracersSet()) {
44+
Object newUserContext = tracer.getTracer().traceExecutionEvent(event, currentUserContext.get(tracer), params);
45+
currentUserContext.put(tracer, newUserContext);
46+
}
47+
return currentUserContext;
48+
}
49+
50+
@Override
51+
public boolean isDesiredEvent(JdbcExecutionEvent event) {
52+
// Accept all events
53+
return true;
54+
}
55+
56+
@SuppressWarnings("unchecked")
57+
private EnumMap<Tracers, Object> getCurrentUserContext(Object userContext) {
58+
EnumMap<Tracers, Object> currentUserContext;
59+
if (userContext != null && (userContext instanceof EnumMap<?,?>)) {
60+
currentUserContext = (EnumMap<Tracers, Object>) userContext;
61+
} else {
62+
currentUserContext = new EnumMap<Tracers, Object>(Tracers.class);
63+
}
64+
return currentUserContext;
65+
}
66+
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package oracle.jdbc.provider.observability;
2+
3+
import java.lang.management.ManagementFactory;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.Map;
7+
import java.util.logging.Level;
8+
import java.util.logging.Logger;
9+
10+
import javax.management.InstanceAlreadyExistsException;
11+
import javax.management.MBeanException;
12+
import javax.management.MBeanRegistrationException;
13+
import javax.management.MBeanServer;
14+
import javax.management.MalformedObjectNameException;
15+
import javax.management.NotCompliantMBeanException;
16+
import javax.management.ObjectName;
17+
import javax.management.ReflectionException;
18+
19+
import oracle.jdbc.TraceEventListener;
20+
import oracle.jdbc.spi.TraceEventListenerProvider;
21+
22+
public class ObservabilityTraceEventListenerProvider implements TraceEventListenerProvider {
23+
24+
private static final String PROVIDER_NAME = "observability-trace-event-listener-provider";
25+
private static final String MBEAN_OBJECT_NAME = "com.oracle.jdbc.extension.opentelemetry:type=ObservabilityTraceEventListener";
26+
27+
private static final MBeanServer server = ManagementFactory.getPlatformMBeanServer();;
28+
private static ObjectName objectName;
29+
30+
Logger logger = Logger.getLogger(ObservabilityTraceEventListenerProvider.class.getName());
31+
32+
static {
33+
try {
34+
objectName = new ObjectName(MBEAN_OBJECT_NAME);
35+
} catch (MalformedObjectNameException e) {
36+
objectName = null;
37+
}
38+
}
39+
40+
@Override
41+
public TraceEventListener getTraceEventListener(Map<Parameter, CharSequence> map) {
42+
ObservabilityTraceEventListener observabilityBean;
43+
try {
44+
if (objectName != null && server.isRegistered(objectName)) {
45+
observabilityBean = (ObservabilityTraceEventListener) server
46+
.instantiate(ObservabilityTraceEventListener.class.getName());
47+
return observabilityBean;
48+
}
49+
} catch (ReflectionException | MBeanException e) {
50+
logger.log(Level.WARNING, "Could not retrieve MBean from server", e);
51+
}
52+
observabilityBean = new ObservabilityTraceEventListener();
53+
try {
54+
server.registerMBean(observabilityBean, objectName);
55+
} catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) {
56+
logger.log(Level.WARNING, "Could not register MBean", e);
57+
}
58+
return observabilityBean;
59+
}
60+
61+
@Override
62+
public String getName() {
63+
return PROVIDER_NAME;
64+
}
65+
66+
@Override
67+
public Collection<? extends Parameter> getParameters() {
68+
return Collections.emptyList();
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package oracle.jdbc.provider.observability.configuration;
2+
3+
import java.util.EnumSet;
4+
import java.util.concurrent.locks.ReentrantLock;
5+
import java.util.stream.Collectors;
6+
7+
import oracle.jdbc.provider.observability.ObservabilityTraceEventListener;
8+
9+
public class ObservabilityConfiguration implements ObservabilityConfigurationMBean {
10+
11+
private final ReentrantLock observabilityConfiguraitonLock = new ReentrantLock();
12+
13+
private static final ObservabilityConfiguration INSTANCE = new ObservabilityConfiguration();
14+
15+
private ObservabilityConfiguration() { }
16+
17+
private boolean sensitiveDataEnabled;
18+
private String tracers;
19+
20+
private EnumSet<ObservabilityTraceEventListener.Tracers> enabledTracers;
21+
22+
@Override
23+
public String getEnabledTracers() {
24+
try {
25+
observabilityConfiguraitonLock.lock();
26+
return tracers;
27+
} finally {
28+
observabilityConfiguraitonLock.unlock();
29+
}
30+
}
31+
32+
@Override
33+
public void setEnabledTracers(String tracers) {
34+
try {
35+
enabledTracers.clear();
36+
String[] items = tracers.split(",");
37+
for (String item : items) {
38+
if (item != null) {
39+
enabledTracers.add(ObservabilityTraceEventListener.Tracers.valueOf(item.toUpperCase()));
40+
}
41+
}
42+
this.tracers = enabledTracers.stream().map((item) -> item.toString()).collect(Collectors.joining(","));
43+
} finally {
44+
observabilityConfiguraitonLock.unlock();
45+
}
46+
47+
}
48+
49+
@Override
50+
public boolean getSensitiveDataEnabled() {
51+
return sensitiveDataEnabled;
52+
}
53+
54+
@Override
55+
public void setSensitiveDataEnabled(boolean sensitiveDataEnabled) {
56+
this.sensitiveDataEnabled = sensitiveDataEnabled;
57+
}
58+
59+
public static ObservabilityConfiguration getInstance() {
60+
return INSTANCE;
61+
}
62+
63+
public EnumSet<ObservabilityTraceEventListener.Tracers> getEnabledTracersSet() {
64+
return enabledTracers.clone();
65+
}
66+
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package oracle.jdbc.provider.observability.configuration;
2+
3+
public interface ObservabilityConfigurationMBean {
4+
5+
public String getEnabledTracers();
6+
7+
public void setEnabledTracers(String tracers);
8+
9+
public boolean getSensitiveDataEnabled();
10+
11+
public void setSensitiveDataEnabled(boolean sensitiveDataEnabled);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package oracle.jdbc.provider.observability.tracers;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import oracle.jdbc.DatabaseFunction;
7+
import oracle.jdbc.TraceEventListener.JdbcExecutionEvent;
8+
import oracle.jdbc.TraceEventListener.Sequence;
9+
import oracle.jdbc.TraceEventListener.TraceContext;
10+
import oracle.jdbc.provider.observability.configuration.ObservabilityConfiguration;
11+
import jdk.jfr.AnnotationElement;
12+
import jdk.jfr.Event;
13+
import jdk.jfr.EventFactory;
14+
import jdk.jfr.Label;
15+
import jdk.jfr.Name;
16+
import jdk.jfr.Category;
17+
import jdk.jfr.ValueDescriptor;
18+
19+
public class JFRTracer implements ObservabilityTracer{
20+
21+
public JFRTracer() {}
22+
23+
@Override
24+
public Object traceRoudtrip(Sequence sequence, TraceContext traceContext, Object userContext) {
25+
if (sequence.equals(Sequence.BEFORE)) {
26+
Event event = createEvent(
27+
traceContext.databaseFunction());
28+
event.begin();
29+
return event;
30+
} else {
31+
if (userContext != null) {
32+
Event event = (Event) userContext;
33+
event.set(0, traceContext.getConnectionId());
34+
event.set(1, traceContext.databaseOperation());
35+
event.set(2, traceContext.tenant());
36+
event.set(3, traceContext.getSqlId());
37+
if (ObservabilityConfiguration.getInstance().getSensitiveDataEnabled()) {
38+
event.set(4, traceContext.originalSqlText());
39+
event.set(5, traceContext.actualSqlText());
40+
event.set(6, traceContext.user());
41+
}
42+
event.end();
43+
event.commit();
44+
}
45+
}
46+
return null;
47+
}
48+
49+
@Override
50+
public Object traceExecutionEvent(JdbcExecutionEvent event, Object userContext, Object... params) {
51+
return null;
52+
}
53+
54+
/**
55+
* Creates an event for a given database operation.
56+
* @param databaseFunction The database function originating the round trip.
57+
* @return returns a Java Flight Recorder Event containing the following
58+
* fields:
59+
* <ul>
60+
* <li>ConnectionID</li>
61+
* <li>DatabaseOperation</li>
62+
* <li>OriginalSqlText</li>
63+
* <li>ActualSqlText</li>
64+
* <li>User</li>
65+
* </ul>
66+
*/
67+
private static Event createEvent(DatabaseFunction databaseFunction) {
68+
String eventName = "oracle.jdbc.provider.jfr.roundtrip." + databaseFunction.toString();
69+
List<AnnotationElement> eventAnnotations = new ArrayList<AnnotationElement>();
70+
eventAnnotations
71+
.add(new AnnotationElement(Name.class, eventName));
72+
eventAnnotations.add(new AnnotationElement(Label.class, databaseFunction.getDescription()));
73+
eventAnnotations.add(new AnnotationElement(Category.class, new String[] { "Oracle JDBC", "Round trips" }));
74+
75+
List<ValueDescriptor> fields = new ArrayList<ValueDescriptor>();
76+
fields.add(new ValueDescriptor(String.class, "Connection_ID"));
77+
fields.add(new ValueDescriptor(String.class, "Database_operation"));
78+
fields.add(new ValueDescriptor(String.class, "Database_tenant"));
79+
fields.add(new ValueDescriptor(String.class, "SQL_ID"));
80+
fields.add(new ValueDescriptor(String.class, "Original_SQL_text"));
81+
fields.add(new ValueDescriptor(String.class, "Actual_SQL_text"));
82+
fields.add(new ValueDescriptor(String.class, "Database_user"));
83+
84+
EventFactory f = EventFactory.create(eventAnnotations, fields);
85+
return f.newEvent();
86+
}
87+
88+
89+
}

0 commit comments

Comments
 (0)