Skip to content

feat: database/sql integration #893

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 33 commits into
base: master
Choose a base branch
from

Conversation

aldy505
Copy link
Contributor

@aldy505 aldy505 commented Oct 18, 2024

Queries insights tracing for Go.

Copy link

codecov bot commented Oct 19, 2024

Codecov Report

Attention: Patch coverage is 82.30563% with 66 lines in your changes missing coverage. Please review.

Project coverage is 83.89%. Comparing base (7996759) to head (9b12d0a).
Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
sentrysql/conn.go 81.65% 25 Missing and 6 partials ⚠️
sentrysql/stmt.go 83.01% 13 Missing and 5 partials ⚠️
sentrysql/driver.go 63.15% 13 Missing and 1 partial ⚠️
sentrysql/sentrysql.go 90.90% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #893      +/-   ##
==========================================
- Coverage   84.00%   83.89%   -0.12%     
==========================================
  Files          50       57       +7     
  Lines        5171     5544     +373     
==========================================
+ Hits         4344     4651     +307     
- Misses        673      726      +53     
- Partials      154      167      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aldy505
Copy link
Contributor Author

aldy505 commented Oct 19, 2024

I really don't like golangci-lint, it's so pedantic :(

@aldy505
Copy link
Contributor Author

aldy505 commented Oct 19, 2024

Should be good by now.

@aldy505 aldy505 marked this pull request as ready for review October 19, 2024 05:20
@aldy505 aldy505 requested a review from ribice October 19, 2024 05:20
@ribice
Copy link
Collaborator

ribice commented Oct 22, 2024

I'll test this locally myself this week and report back. We should also update the docs and main repository with this integration.

@ribice
Copy link
Collaborator

ribice commented Oct 25, 2024

You're missing examples in _examples. These should also include example on how to set the DSN.

@aldy505
Copy link
Contributor Author

aldy505 commented Oct 25, 2024

You're missing examples in _examples. These should also include example on how to set the DSN.

@ribice I've been occupied so much with work this week. Will find the time to work on this later.

@aldy505
Copy link
Contributor Author

aldy505 commented Oct 27, 2024

LGTM Overall. Still misses some tests for the coverage

To be honest, I don't know how to make some of that as covered other than using old Go versions. But I might try creating another database driver implementation that does not implement XXXContext method.

...documentation in main repo and sentry-docs.

I might be able to help with the sentry-docs one, since I also need to update some of the self-hosted docs.

@cleptric
Copy link
Member

I haven't reviewed this PR in full yet, but some things I would like to be changed:

  • I would suggest we start with https://pkg.go.dev/database only, I'm not keen to add support for 3rd party libraries right away.
  • If we end up adding 3rd party dependencies, we should publish this as it's own package. See https://github.com/getsentry/sentry-go/tree/master/otel for prior art.
  • We also support Mongo DB in our Insights query module, so the naming of the package could be more generic, such as sentrydatabase
  • nolint is an immediate code smell.

@aldy505
Copy link
Contributor Author

aldy505 commented Nov 2, 2024

We are not supporting 3rd party libraries here. We only import the database driver since Go does not bring built-in database drivers and rely on third party drivers. The database drivers we're using should not going to be compiled to the resulting binary since it's only used in tests.

Although it seems like we're using it as a library here, it is not, it's still considered a database driver:

// Create a database connection for read database.
connector, err := pq.NewConnector("postgres://postgres:[email protected]:5432/postgres")
if err != nil {
fmt.Printf("failed to create a postgres connector: %s\n", err.Error())
return
}

  • We also support Mongo DB in our Insights query module, so the naming of the package could be more generic, such as sentrydatabase

MongoDB does not have and does not conform to the database/sql driver. So we're not going to add it into the enum here. Instrumentation for MongoDB should be specific for the Go MongoDB library.

  • nolint is an immediate code smell.

Since we're wrapping a database driver and thus making us another database driver, we are required to implement multiple methods that are considered duplicates by the linter, yet again, from the driver documentation, we're still required to implement those.

@jazzido
Copy link

jazzido commented Nov 28, 2024

Don't let this die! :) I'm sure many of us in the community will be grateful for an officially supported DB integration

Copy link
Contributor

@giortzisg giortzisg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good with the most recent changes and should be functionally correct. Just wanna be sure that the context is passed correctly and also add some minor improvements

Copy link
Contributor

@giortzisg giortzisg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some test changes.

@aldy505 aldy505 requested a review from giortzisg May 7, 2025 03:40
Comment on lines +131 to +142
// if WantSpan is nil, yet we got some spans, it should be an error
if tt.WantSpan == nil {
t.Errorf("Expecting no spans, but got %d spans: %v", len(gotSpans), gotSpans)
continue
}

// if WantSpan is not nil, we should have at least one span
if len(gotSpans) == 0 {
t.Errorf("Expecting at least one span, but got %d spans: %v", len(gotSpans), gotSpans)
continue
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not have tt.WantSpans as a []Span so that we can compare immediately with gotSpans? I'd say that it's not very readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did some rounds with this, I can't get []Span comparison working (problems with google/go-cmp package). Is it okay if we backlog this somehow?

Comment on lines +27 to +33
func TestMain(m *testing.M) {
sql.Register("sentrysql-sqlite", sentrysql.NewSentrySQL(&sqlite.Driver{}, sentrysql.WithDatabaseName("memory"), sentrysql.WithDatabaseSystem(sentrysql.DatabaseSystem("sqlite")), sentrysql.WithServerAddress("localhost", "5432")))
// sentrysql-legacy is used by `sentrysql_legacy_test.go`
sql.Register("sentrysql-legacy", sentrysql.NewSentrySQL(ldriver, sentrysql.WithDatabaseSystem(sentrysql.DatabaseSystem("legacydb")), sentrysql.WithDatabaseName("fake")))

os.Exit(m.Run())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is obfuscated from the other tests. Should probably be an init statement on each file that registers whatever we need only for the tests on the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the docs: If Register is called twice with the same name or if driver is nil, it panics.

I believe setting this here is the "right" way to do so, since it'll be run after any library's init invocation.

Comment on lines +154 to +169
var foundMatch = false
gotSpans := got[i]

var diffs []string
for _, gotSpan := range gotSpans {
if diff := cmp.Diff(tt.WantSpan, gotSpan, optstrans); diff != "" {
diffs = append(diffs, diff)
} else {
foundMatch = true
break
}
}

if !foundMatch {
t.Errorf("Span mismatch (-want +got):\n%s", strings.Join(diffs, "\n"))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing applies here. We should probably use a []Span for WantSpans

@cleptric cleptric requested review from Copilot and cleptric June 17, 2025 11:03
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds Sentry tracing integration to Go’s database/sql by wrapping drivers, connectors, connections, statements, and transactions to automatically start spans for SQL operations.

  • Introduce sentrySQLConfig and Option functions to attach metadata (db system, name, host, port).
  • Wrap driver.Driver, driver.Connector, driver.Conn, and driver.Stmt to emit spans on Exec/Query (including context versions).
  • Add parseDatabaseOperation helper to extract SQL operation names and tests for it.

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sentrysql/tx.go Added sentryTx wrapper for driver.Tx (currently no spans).
sentrysql/stmt.go Implemented sentryStmt with Exec/Query and ExecContext/QueryContext tracing.
sentrysql/sentrysql.go Defined DatabaseSystem, config struct, and constructors (NewSentrySQL, NewSentrySQLConnector).
sentrysql/options.go Added Option functions to set database metadata.
sentrysql/operation.go Added parseDatabaseOperation to derive operation names.
sentrysql/operation_test.go Added tests for parseDatabaseOperation.
sentrysql/driver.go Wrapped driver.Driver and driver.Connector for injection.
sentrysql/conn.go Implemented sentryConn with span creation for Exec/Query/Ping/Tx.
sentrysql/go.mod Initialized module and dependencies.
sentrysql/example_test.go Added examples for NewSentrySQL and NewSentrySQLConnector.
_examples/sql/main.go End-to-end example demonstrating tracing of queries and transactions.
Comments suppressed due to low confidence (1)

sentrysql/stmt.go:75

  • There are no unit tests covering ExecContext and QueryContext in sentryStmt. Adding tests would verify span creation and fallback behavior.
func (s *sentryStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error)

return s.Exec(values)
}

parentSpan := sentry.SpanFromContext(s.ctx)
Copy link
Preview

Copilot AI Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ExecContext, you’re calling SpanFromContext on s.ctx instead of the passed-in ctx. This can attach spans to a stale context. Use sentry.SpanFromContext(ctx) instead.

Suggested change
parentSpan := sentry.SpanFromContext(s.ctx)
parentSpan := sentry.SpanFromContext(ctx)

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. ctx provided from the function argument does not have sentry span context. SInce what matters is the context in which the statement was started.

return s.Query(values)
}

parentSpan := sentry.SpanFromContext(s.ctx)
Copy link
Preview

Copilot AI Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In QueryContext, you’re using s.ctx for the parent span lookup instead of the method’s ctx parameter. Update to sentry.SpanFromContext(ctx) to ensure the correct context is used.

Suggested change
parentSpan := sentry.SpanFromContext(s.ctx)
parentSpan := sentry.SpanFromContext(ctx)

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong. ctx provided from the function argument does not have sentry span context. SInce what matters is the context in which the statement was started.

type sentryTx struct {
originalTx driver.Tx
ctx context.Context
config *sentrySQLConfig
Copy link
Preview

Copilot AI Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The config field in sentryTx is never used. Consider removing it or adding tracing for Commit/Rollback to leverage the config metadata.

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracing on Commit / Rollback would be useless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Issue type
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants