Skip to content

Commit

Permalink
Instrument jdbc datasources with OpenTelemetry (#259)
Browse files Browse the repository at this point in the history
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
  • Loading branch information
cyrille-leclerc and timja authored Sep 20, 2024
1 parent d9f219f commit 1d03c76
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 22 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ public class TestRow {
}
```

## Troubleshooting performance problems with OpenTelemetry and tracing

The Jenkins Database plugin supports OpenTelemetry and tracing to help troubleshoot performance issues.

To enable tracing, you need to:
* Install the [OpenTelemetry plugin](https://plugins.jenkins.io/opentelemetry/) and configure it
* Navigate to the "advanced" section of the Jenkins OpenTelemetry Plugin and set in the "Configuration properties" :
* `otel.instrumentation.jdbc.enabled=true` to enable tracing in the Datasource provided by the Jenkins Database Plugin
* `otel.instrumentation.jenkins.agent.enabled=true` to activate tracing in the Jenkins build agents if database calls are executed from the build agents

Note that changes to the `otel.instrumentation.jdbc.enabled` require to restart the Jenkins Controller and build agents.

Example database call span produced by the Jenkins JUnit SQL Storage plugin:

<img alt="Jenkins pipeline trace with a JDBC span in Grafana" src="resources/images/screenshot-jenkins-pipeline-trace-with-jdbc-span.png" width="250px">

## Developing driver plugin

[MySQL Database
Expand Down
22 changes: 18 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
<properties>
<changelist>999999-SNAPSHOT</changelist>
<gitHubRepo>jenkinsci/database-plugin</gitHubRepo>
<jenkins.version>2.426.3</jenkins.version>
<jenkins.version>2.440.3</jenkins.version>

<jenkins.opentelemetry-api.version>1.40.0-32.v65c59076e638</jenkins.opentelemetry-api.version>
<opentelemetry-instrumentation.version>2.6.0</opentelemetry-instrumentation.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-2.426.x</artifactId>
<version>3208.vb_21177d4b_cd9</version>
<artifactId>bom-2.440.x</artifactId>
<version>3120.v4d898e1e9fc4</version>
<scope>import</scope>
<type>pom</type>
</dependency>
Expand All @@ -35,7 +38,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.11.0</version>
<version>2.12.0</version>
</dependency>

<dependency>
Expand Down Expand Up @@ -102,6 +105,17 @@
<artifactId>workflow-step-api</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${jenkins.opentelemetry-api.version}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-jdbc</artifactId>
<version>${opentelemetry-instrumentation.version}-alpha</version>
</dependency>
</dependencies>

<repositories>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Util;
import hudson.util.Secret;
import java.io.Serializable;
import org.kohsuke.stapler.DataBoundConstructor;
import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Map;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

/**
* Partial default implementation for typical JDBC connector that talks to a remote server
Expand All @@ -36,7 +39,7 @@ public abstract class AbstractRemoteDatabase extends Database implements Seriali

public final String properties;

private transient DataSource source;
private transient DataSource dataSource;

public AbstractRemoteDatabase(String hostname, String database, String username, Secret password, String properties) {
this.hostname = hostname;
Expand All @@ -61,7 +64,7 @@ public String getValidationQuery() {

@Override
public synchronized DataSource getDataSource() throws SQLException {
if (source==null) {
if (dataSource ==null) {
BasicDataSource2 fac = new BasicDataSource2();
fac.setDriverClass(getDriverClass());
fac.setUrl(getJdbcUrl());
Expand All @@ -77,8 +80,12 @@ public synchronized DataSource getDataSource() throws SQLException {
throw new SQLException("Invalid properties",e);
}

source = fac.createDataSource();
if (isOTelJdbcInstrumentationEnabled()) {
dataSource = JdbcTelemetry.create(GlobalOpenTelemetry.get()).wrap(fac.createDataSource());
} else {
dataSource = fac.createDataSource();
}
}
return source;
return dataSource;

Check warning on line 89 in src/main/java/org/jenkinsci/plugins/database/AbstractRemoteDatabase.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 67-89 are not covered by tests
}
}
26 changes: 24 additions & 2 deletions src/main/java/org/jenkinsci/plugins/database/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;

import javax.sql.DataSource;
import java.sql.Connection;
Expand All @@ -17,10 +19,30 @@
* @author Kohsuke Kawaguchi
*/
public abstract class Database extends AbstractDescribableImpl<Database> implements ExtensionPoint {

public abstract DataSource getDataSource() throws SQLException;

/**
* <p>
* Returns true if OpenTelemetry JDBC instrumentation is enabled.
* </p>
* <p>
* Implementations couldn't use {@link io.jenkins.plugins.opentelemetry.api.OpenTelemetryLifecycleListener} to
* retrieve the configuration and get configuration changes because it's the {@link DatabaseDescriptor} that should
* have implemented the {@link io.jenkins.plugins.opentelemetry.api.OpenTelemetryLifecycleListener} interface and
* {@link hudson.model.Descriptor} instances are not available on Jenkins build agent JVMs, only on the Jenkins
* controller.
* </p>
*/
protected boolean isOTelJdbcInstrumentationEnabled() {
ReconfigurableOpenTelemetry reconfigurableOpenTelemetry = ReconfigurableOpenTelemetry.get();
ConfigProperties config = reconfigurableOpenTelemetry.getConfig();
return config.getBoolean("otel.instrumentation.jdbc.enabled", false);

Check warning on line 40 in src/main/java/org/jenkinsci/plugins/database/Database.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 38-40 are not covered by tests
}

@Override
public DatabaseDescriptor getDescriptor() {
return (DatabaseDescriptor)super.getDescriptor();
return (DatabaseDescriptor) super.getDescriptor();
}
}

}
24 changes: 16 additions & 8 deletions src/main/java/org/jenkinsci/plugins/database/GenericDatabase.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
package org.jenkinsci.plugins.database;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.util.FormValidation;
import hudson.util.Secret;
import io.jenkins.plugins.opentelemetry.api.ReconfigurableOpenTelemetry;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import jenkins.model.Jenkins;
import org.apache.tools.ant.AntClassLoader;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.verb.POST;

import edu.umd.cs.findbugs.annotations.NonNull;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import org.kohsuke.stapler.verb.POST;

/**
* {@link Database} implementation that allows the user to specify arbitrary JDBC connection string.
*
* @author Kohsuke Kawaguchi
*/
public class GenericDatabase extends Database {

public final String driver;
public final String username;
public final Secret password;
Expand All @@ -34,7 +37,7 @@ public class GenericDatabase extends Database {
private Integer maxIdle = DescriptorImpl.defaultMaxIdle;
private Integer minIdle = DescriptorImpl.defaultMinIdle;

private transient DataSource source;
private transient DataSource dataSource;

@DataBoundConstructor
public GenericDatabase(String url, String driver, String username, Secret password) {
Expand Down Expand Up @@ -86,7 +89,7 @@ public void setMinIdle(final Integer minIdle) {

@Override
public synchronized DataSource getDataSource() throws SQLException {
if (source==null) {
if (dataSource ==null) {
BasicDataSource2 source = new BasicDataSource2();
source.setDriverClassLoader(getDescriptor().getClassLoader());
source.setDriverClassName(driver);
Expand All @@ -97,9 +100,14 @@ public synchronized DataSource getDataSource() throws SQLException {
source.setMaxTotal(maxTotal);
source.setMaxIdle(maxIdle);
source.setMinIdle(minIdle);
this.source = source.createDataSource();

if (isOTelJdbcInstrumentationEnabled()) {
dataSource = JdbcTelemetry.create(GlobalOpenTelemetry.get()).wrap(source.createDataSource());
} else {
dataSource = source.createDataSource();
}
}
return source;
return dataSource;

Check warning on line 110 in src/main/java/org/jenkinsci/plugins/database/GenericDatabase.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 92-110 are not covered by tests
}

@Override
Expand Down

0 comments on commit 1d03c76

Please sign in to comment.