Skip to content

Commit

Permalink
Send db.system and db.name in span data (#2894)
Browse files Browse the repository at this point in the history
  • Loading branch information
adinauer authored Aug 14, 2023
1 parent d55d97a commit 3fe387a
Show file tree
Hide file tree
Showing 11 changed files with 562 additions and 0 deletions.
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

0 comments on commit 3fe387a

Please sign in to comment.