getsentry / sentry-java

A Sentry SDK for Java, Android and other JVM languages.
https://docs.sentry.io/
MIT License
1.11k stars 427 forks source link

JDBC transactions not logged in queries #3483

Open peterdk opened 2 weeks ago

peterdk commented 2 weeks ago

Integration

sentry-jdbc

Java Version

18

Version

7.10.0

Steps to Reproduce

I use Ktor for a API and am looking into a tool that can help monitor performance. I saw that Sentry supports JDBC queries, and that is something I am looking for.

I added to my build.gradle.kts:

id("io.sentry.jvm.gradle") version "4.7.1"

implementation("io.sentry:sentry-jdbc:7.10.0")
implementation("p6spy:p6spy:3.9.1")

sentry {
    includeSourceContext = false
    org = "foo"
    projectName = "bar"
    authToken = System.getenv("SENTRY_AUTH_TOKEN")
    tracingInstrumentation {
        enabled = true
    }
    autoInstallation {
        enabled = true
    }
}

My DB setup logic:

 val db = Database.connect(P6DataSource(MysqlDataSource().apply {
        setURL("jdbc:mysql://$dbHost/$dbName?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedInserts=true&rewriteBatchedStatements=true&batchSize=5000")
        user = dbUser
        password = dbPass
    }))

Sentry init:

 Sentry.init { options ->
        options.dsn = "foo"
        options.tracesSampleRate = 1.0
        options.isDebug = true
        options.enableTracing = true
        options.isEnableMetrics = true
    }

I then instrumented one of the endpoints by wrapping the logic:

 val transaction = Sentry.startTransaction(name, operation)
    try {
        action()
    } catch (e: Exception) {
        transaction.throwable = e
        transaction.status = SpanStatus.INTERNAL_ERROR
        throw e
    } finally {
        transaction.finish()
    }

Expected Result

I expect that Queries section of Sentry is now being filled, but I only see data in Traces. In the logging I don't see anything related to JDBC. In the docs I don't see anything else that I would need to setup.

I am on the developer plan, but from what I see the queries section should be available until half July or so. Or am I already required to have a business plan?

Actual Result

I only see data in Traces, not in Queries and also not in Requests.

I do see the transactions logged in spy.log, so p6spy is working.

Sentry logging after a call happens:


DEBUG: Capturing transaction: ad39f934a059455e8a72d822ae3605c3
DEBUG: Serializing object: {
    "transaction": "foo",
    "start_timestamp": 1718554044.114437,
    "timestamp": 1718554044.445066,
    "type": "transaction",
    "transaction_info": {
        "source": "custom"
    },
    "event_id": "ad39f934a059455e8a72d822ae3605c3",
    "contexts": {
        "runtime": {
            "name": "Amazon.com Inc.",
            "version": "18.0.2"
        },
        "trace": {
            "trace_id": "b3de03059f8648acbc33f36138d887b8",
            "span_id": "dd688cd36b64435e",
            "op": "request",
            "origin": "manual"
        }
    },
    "sdk": {
        "name": "sentry.java",
        "version": "7.10.0",
        "packages": [
            {
                "name": "maven:io.sentry:sentry",
                "version": "7.10.0"
            },
            {
                "name": "maven:io.sentry:sentry-jdbc",
                "version": "7.10.0"
            }
        ],
        "integrations": [
            "UncaughtExceptionHandler",
            "ShutdownHook",
            "JDBC"
        ]
    },
    "environment": "production",
    "platform": "java",
    "user": {
        "ip_address": "{{auto}}"
    },
    "server_name": "localhost"
}```
adinauer commented 2 weeks ago

Hello @peterdk, can you confirm io.sentry.jdbc.SentryJdbcEventListener is actually being invoked in your setup? If so, does it find a valid transaction/span to create a child under?

peterdk commented 2 weeks ago

@adinauer I can check, but how would I do that? I don't know if you can set breakpoints on library code? I'll try.

peterdk commented 2 weeks ago
@Override
  public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) {
    final ISpan parent = hub.getSpan();
    if (parent != null && !parent.isNoOp()) {
      final ISpan span = parent.startChild("db.query", statementInformation.getSql());
      CURRENT_SPAN.set(span);
      span.getSpanContext().setOrigin(TRACE_ORIGIN);
    }
  }

parent seems to be null, the breakpoint on span never triggers after the parent breakpoint.

I do use Kotlin and ktor, so it's inside a coroutine. Already tried wrapping the main trace fun content with

withContext(SentryContext()) {`

and also the DB call parent functions all as suspend, but no changes.

peterdk commented 2 weeks ago

Also only wrapping non suspend functions part (including the DB calls) with a Transaction doesn't do anything.

peterdk commented 2 weeks ago

Am I doing something wrong the the trace wrapping logic? span is empty, right after transaction start

 val transaction = Sentry.startTransaction(name, operation)
        try {
            val hub = Sentry.getCurrentHub() //exists
            val span = hub.span //null
           doSomething()

I would expect that span would not be null?

adinauer commented 2 weeks ago

Can you please take a look at https://docs.sentry.io/platforms/java/tracing/instrumentation/custom-instrumentation/#create-transaction-bound-to-the-current-scope and see if that helps?

Sorry, I guess this isn't too intuitive.

peterdk commented 2 weeks ago

@adinauer I was just looking into that indeed. The span is not empty now! And indeed the JDBC breakpoint triggers and I do get query data now in Sentry! Great.

Maybe improve the docs a bit?

adinauer commented 2 weeks ago

We'll discuss whether it makes sense to have separate methods in the future that make this more clear. We're currently changing lots of things in the SDK for v8 and this is one of the things on the list.