Instrument SQL
Instrument database/sql queries and execs in Go with Sentry spans.
The sentrysql package wraps database/sql drivers and records SQL operations as tracing spans.
Make sure that there's a transaction running when you create the spans. See Tracing for more information.
go get github.com/getsentry/sentry-go
go get github.com/getsentry/sentry-go/sql
go get github.com/getsentry/sentry-go
go get github.com/getsentry/sentry-go/sql
Enable tracing, then wrap your SQL driver with sentrysql:
import (
"github.com/getsentry/sentry-go"
sentrysql "github.com/getsentry/sentry-go/sql"
)
if err := sentry.Init(sentry.ClientOptions{
Dsn: "___PUBLIC_DSN___",
EnableTracing: true,
TracesSampleRate: 1.0,
}); err != nil {
panic(err)
}
// With well-known driver names, db.system can usually be inferred.
db, err := sentrysql.Open("postgres", dsn,
sentrysql.WithDatabaseName("appdb"),
sentrysql.WithServerAddress("localhost", 5432),
)
if err != nil {
panic(err)
}
import (
"github.com/getsentry/sentry-go"
sentrysql "github.com/getsentry/sentry-go/sql"
)
if err := sentry.Init(sentry.ClientOptions{
Dsn: "___PUBLIC_DSN___",
EnableTracing: true,
TracesSampleRate: 1.0,
}); err != nil {
panic(err)
}
// With well-known driver names, db.system can usually be inferred.
db, err := sentrysql.Open("postgres", dsn,
sentrysql.WithDatabaseName("appdb"),
sentrysql.WithServerAddress("localhost", 5432),
)
if err != nil {
panic(err)
}
Use the integration path that matches how your app opens database connections:
sentrysql.Open(...): best when you already usesql.Open(...)sentrysql.OpenDB(...): use an existingdriver.Connectorsentrysql.WrapDriver(...)orsentrysql.WrapConnector(...): use for custom driver registration or custom connection setup
WithDatabaseSystem(...)is:- optional with
sentrysql.Open(...)for well-known driver names such aspostgres,pgx,mysql,sqlite, andsqlserver - required with
OpenDB,WrapDriver, andWrapConnector - required when
Openuses an unknown driver registration name
- optional with
WithDatabaseName(...)populatesdb.namespaceand is recommended when you know the logical database name.WithDatabaseUser(...)populatesdb.user, but only whenSendDefaultPIIis enabled.WithDriverName(...),WithServerAddress(...), andWithServerSocketAddress(...)are useful for custom setups when you want fuller database metadata on spans.
SQL spans are only created when the query runs with an active Sentry parent span in the context. If you're already using Sentry middleware for Gin, Echo, Fiber, net/http, or another supported framework, use the request context. Otherwise, start a transaction manually.
When using this integration, it's recommended to always provide WithDatabaseSystem(...) to match your database connection. For well-known driver names such as postgres, pgx, mysql, sqlite, and sqlserver, the integration can usually infer the database system automatically, but providing it explicitly is still a good practice.
import (
"context"
"time"
"github.com/getsentry/sentry-go"
sentrysql "github.com/getsentry/sentry-go/sql"
_ "modernc.org/sqlite"
)
func main() {
if err := sentry.Init(sentry.ClientOptions{
Dsn: "___PUBLIC_DSN___",
EnableTracing: true,
TracesSampleRate: 1.0,
}); err != nil {
panic(err)
}
defer sentry.Flush(2 * time.Second)
db, err := sentrysql.Open("sqlite", ":memory:",
sentrysql.WithDatabaseSystem(sentrysql.SystemSQLite),
sentrysql.WithDatabaseName("main"),
)
if err != nil {
panic(err)
}
defer db.Close()
txn := sentry.StartTransaction(context.Background(), "sql-demo")
defer txn.Finish()
ctx := txn.Context()
sqlTx, err := db.BeginTx(ctx, nil)
if err != nil {
panic(err)
}
if _, err := sqlTx.ExecContext(ctx, "CREATE TABLE users (id INTEGER, name TEXT)"); err != nil {
panic(err)
}
if _, err := sqlTx.ExecContext(ctx, "INSERT INTO users (id, name) VALUES (?, ?)", 1, "alice"); err != nil {
panic(err)
}
rows, err := sqlTx.QueryContext(ctx, "SELECT id, name FROM users WHERE id = ?", 1)
if err != nil {
panic(err)
}
_ = rows.Close()
if err := sqlTx.Commit(); err != nil {
panic(err)
}
}
import (
"context"
"time"
"github.com/getsentry/sentry-go"
sentrysql "github.com/getsentry/sentry-go/sql"
_ "modernc.org/sqlite"
)
func main() {
if err := sentry.Init(sentry.ClientOptions{
Dsn: "___PUBLIC_DSN___",
EnableTracing: true,
TracesSampleRate: 1.0,
}); err != nil {
panic(err)
}
defer sentry.Flush(2 * time.Second)
db, err := sentrysql.Open("sqlite", ":memory:",
sentrysql.WithDatabaseSystem(sentrysql.SystemSQLite),
sentrysql.WithDatabaseName("main"),
)
if err != nil {
panic(err)
}
defer db.Close()
txn := sentry.StartTransaction(context.Background(), "sql-demo")
defer txn.Finish()
ctx := txn.Context()
sqlTx, err := db.BeginTx(ctx, nil)
if err != nil {
panic(err)
}
if _, err := sqlTx.ExecContext(ctx, "CREATE TABLE users (id INTEGER, name TEXT)"); err != nil {
panic(err)
}
if _, err := sqlTx.ExecContext(ctx, "INSERT INTO users (id, name) VALUES (?, ?)", 1, "alice"); err != nil {
panic(err)
}
rows, err := sqlTx.QueryContext(ctx, "SELECT id, name FROM users WHERE id = ?", 1)
if err != nil {
panic(err)
}
_ = rows.Close()
if err := sqlTx.Commit(); err != nil {
panic(err)
}
}
This creates db.sql.exec and db.sql.query child spans under the active transaction.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").