Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send db.system and db.name in span data #2894

Merged
merged 4 commits into from
Aug 14, 2023
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 {

Check warning on line 11 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L11

Added line #L11 was not covered by tests

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

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

Check warning on line 18 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L18

Added line #L18 was not covered by tests
}

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

Check warning on line 24 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L24

Added line #L24 was not covered by tests
}

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) {

Check warning on line 42 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L42

Added line #L42 was not covered by tests
// ignore
}

Check warning on line 44 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L44

Added line #L44 was not covered by tests

return EMPTY;

Check warning on line 46 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L46

Added line #L46 was not covered by tests
}

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) {

Check warning on line 85 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L85

Added line #L85 was not covered by tests
// ignore
}

Check warning on line 87 in sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java

View check run for this annotation

Codecov / codecov/patch

sentry-jdbc/src/main/java/io/sentry/jdbc/DatabaseUtils.java#L87

Added line #L87 was not covered by tests
}
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