Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Send `db.system` and `db.name` in span data ([#2894](https://github.com/getsentry/sentry-java/pull/2894))

## 6.28.0

### Features
Expand Down
11 changes: 11 additions & 0 deletions sentry-jdbc/api/sentry-jdbc.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ public final class io/sentry/jdbc/BuildConfig {
public static final field VERSION_NAME Ljava/lang/String;
}

public final class io/sentry/jdbc/DatabaseUtils {
public fun <init> ()V
public static fun parse (Ljava/lang/String;)Lio/sentry/jdbc/DatabaseUtils$DatabaseDetails;
public static fun readFrom (Lcom/p6spy/engine/common/StatementInformation;)Lio/sentry/jdbc/DatabaseUtils$DatabaseDetails;
}

public final class io/sentry/jdbc/DatabaseUtils$DatabaseDetails {
public fun getDbName ()Ljava/lang/String;
public fun getDbSystem ()Ljava/lang/String;
}

public class io/sentry/jdbc/SentryJdbcEventListener : com/p6spy/engine/event/SimpleJdbcEventListener {
public fun <init> ()V
public fun <init> (Lio/sentry/IHub;)V
Expand Down
1 change: 1 addition & 0 deletions sentry-jdbc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
testImplementation(kotlin(Config.kotlinStdLib))
testImplementation(Config.TestLibs.kotlinTestJunit)
testImplementation(Config.TestLibs.mockitoKotlin)
testImplementation(Config.TestLibs.mockitoInline)
testImplementation(Config.TestLibs.awaitility)
testImplementation(Config.TestLibs.hsqldb)
}
Expand Down
156 changes: 156 additions & 0 deletions sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package io.sentry.jdbc;

import com.p6spy.engine.common.ConnectionInformation;
import com.p6spy.engine.common.StatementInformation;
import io.sentry.util.StringUtils;
import java.net.URI;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class DatabaseUtils {

private static final @NotNull DatabaseDetails EMPTY = new DatabaseDetails(null, null);

public static DatabaseDetails readFrom(
final @Nullable StatementInformation statementInformation) {
if (statementInformation == null) {
return EMPTY;
}

final @Nullable ConnectionInformation connectionInformation =
statementInformation.getConnectionInformation();
if (connectionInformation == null) {
return EMPTY;
}

return parse(connectionInformation.getUrl());
}

public static DatabaseDetails parse(final @Nullable String databaseConnectionUrl) {
if (databaseConnectionUrl == null) {
return EMPTY;
}
try {
final @NotNull String rawUrl =
removeP6SpyPrefix(databaseConnectionUrl.toLowerCase(Locale.ROOT));
final @NotNull String[] urlParts = rawUrl.split(":", -1);
if (urlParts.length > 1) {
final @NotNull String dbSystem = urlParts[0];
return parseDbSystemSpecific(dbSystem, urlParts, rawUrl);
}
} catch (Throwable t) {
// ignore
}

return EMPTY;
}

private static @NotNull DatabaseDetails parseDbSystemSpecific(
final @NotNull String dbSystem,
final @NotNull String[] urlParts,
final @NotNull String urlString) {
if ("hsqldb".equalsIgnoreCase(dbSystem)
|| "h2".equalsIgnoreCase(dbSystem)
|| "derby".equalsIgnoreCase(dbSystem)
|| "sqlite".equalsIgnoreCase(dbSystem)) {
if (urlString.contains("//")) {
return parseAsUri(dbSystem, StringUtils.removePrefix(urlString, dbSystem + ":"));
}
if (urlParts.length > 2) {
String dbNameAndMaybeMore = urlParts[2];
return new DatabaseDetails(dbSystem, StringUtils.substringBefore(dbNameAndMaybeMore, ";"));
}
if (urlParts.length > 1) {
String dbNameAndMaybeMore = urlParts[1];
return new DatabaseDetails(dbSystem, StringUtils.substringBefore(dbNameAndMaybeMore, ";"));
}
}
if ("mariadb".equalsIgnoreCase(dbSystem)
|| "mysql".equalsIgnoreCase(dbSystem)
|| "postgresql".equalsIgnoreCase(dbSystem)
|| "mongo".equalsIgnoreCase(dbSystem)) {
return parseAsUri(dbSystem, urlString);
}
if ("sqlserver".equalsIgnoreCase(dbSystem)) {
try {
String dbProperty = ";databasename=";
final int index = urlString.indexOf(dbProperty);
if (index >= 0) {
final @NotNull String dbNameAndMaybeMore =
urlString.substring(index + dbProperty.length());
return new DatabaseDetails(
dbSystem, StringUtils.substringBefore(dbNameAndMaybeMore, ";"));
}
} catch (Throwable t) {
// ignore
}
}
if ("oracle".equalsIgnoreCase(dbSystem)) {
String uriPrefix = "oracle:thin:@//";
final int indexOfUri = urlString.indexOf(uriPrefix);
if (indexOfUri >= 0) {
final @NotNull String uri =
"makethisaprotocol://" + urlString.substring(indexOfUri + uriPrefix.length());
return parseAsUri(dbSystem, uri);
}

final int indexOfTnsNames = urlString.indexOf("oracle:thin:@(");
if (indexOfTnsNames >= 0) {
String serviceNamePrefix = "(service_name=";
final int indexOfServiceName = urlString.indexOf(serviceNamePrefix);
if (indexOfServiceName >= 0) {
final int indexOfClosingBrace = urlString.indexOf(")", indexOfServiceName);
final @NotNull String serviceName =
urlString.substring(
indexOfServiceName + serviceNamePrefix.length(), indexOfClosingBrace);
return new DatabaseDetails(dbSystem, serviceName);
}
}
}
if ("datadirect".equalsIgnoreCase(dbSystem)
|| "tibcosoftware".equalsIgnoreCase(dbSystem)
|| "jtds".equalsIgnoreCase(dbSystem)
|| "microsoft".equalsIgnoreCase(dbSystem)) {
return parse(StringUtils.removePrefix(urlString, dbSystem + ":"));
}

return new DatabaseDetails(dbSystem, null);
}

private static @NotNull DatabaseDetails parseAsUri(
final @NotNull String dbSystem, final @NotNull String urlString) {
try {
final @NotNull URI url = new URI(urlString);
String path = StringUtils.removePrefix(url.getPath(), "/");
String pathWithoutProperties = StringUtils.substringBefore(path, ";");
return new DatabaseDetails(dbSystem, pathWithoutProperties);
} catch (Throwable t) {
System.out.println(t.getMessage());
// ignore
}
return new DatabaseDetails(dbSystem, null);
}

private static @NotNull String removeP6SpyPrefix(final @NotNull String url) {
return StringUtils.removePrefix(StringUtils.removePrefix(url, "jdbc:"), "p6spy:");
}

public static final class DatabaseDetails {
private final @Nullable String dbSystem;
private final @Nullable String dbName;

DatabaseDetails(final @Nullable String dbSystem, final @Nullable String dbName) {
this.dbSystem = dbSystem;
this.dbName = dbName;
}

public @Nullable String getDbSystem() {
return dbSystem;
}

public @Nullable String getDbName() {
return dbName;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.sentry.jdbc;

import static io.sentry.SpanDataConvention.DB_NAME_KEY;
import static io.sentry.SpanDataConvention.DB_SYSTEM_KEY;

import com.jakewharton.nopen.annotation.Open;
import com.p6spy.engine.common.StatementInformation;
import com.p6spy.engine.event.SimpleJdbcEventListener;
Expand All @@ -21,6 +24,9 @@ public class SentryJdbcEventListener extends SimpleJdbcEventListener {
private final @NotNull IHub hub;
private static final @NotNull ThreadLocal<ISpan> CURRENT_SPAN = new ThreadLocal<>();

private volatile @Nullable DatabaseUtils.DatabaseDetails cachedDatabaseDetails = null;
private final @NotNull Object databaseDetailsLock = new Object();

public SentryJdbcEventListener(final @NotNull IHub hub) {
this.hub = Objects.requireNonNull(hub, "hub is required");
addPackageAndIntegrationInfo();
Expand All @@ -46,7 +52,10 @@ public void onAfterAnyExecute(
long timeElapsedNanos,
final @Nullable SQLException e) {
final ISpan span = CURRENT_SPAN.get();

if (span != null) {
applyDatabaseDetailsToSpan(statementInformation, span);

if (e != null) {
span.setThrowable(e);
span.setStatus(SpanStatus.INTERNAL_ERROR);
Expand All @@ -63,4 +72,31 @@ private void addPackageAndIntegrationInfo() {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-jdbc", BuildConfig.VERSION_NAME);
}

private void applyDatabaseDetailsToSpan(
final @NotNull StatementInformation statementInformation, final @NotNull ISpan span) {
final @NotNull DatabaseUtils.DatabaseDetails databaseDetails =
getOrComputeDatabaseDetails(statementInformation);

if (databaseDetails.getDbSystem() != null) {
span.setData(DB_SYSTEM_KEY, databaseDetails.getDbSystem());
}

if (databaseDetails.getDbName() != null) {
span.setData(DB_NAME_KEY, databaseDetails.getDbName());
}
}

private @NotNull DatabaseUtils.DatabaseDetails getOrComputeDatabaseDetails(
final @NotNull StatementInformation statementInformation) {
if (cachedDatabaseDetails == null) {
synchronized (databaseDetailsLock) {
if (cachedDatabaseDetails == null) {
cachedDatabaseDetails = DatabaseUtils.readFrom(statementInformation);
}
}
}

return cachedDatabaseDetails;
}
}
Loading