Skip to content

Commit

Permalink
chore(example): sql integration example
Browse files Browse the repository at this point in the history
  • Loading branch information
aldy505 committed Oct 26, 2024
1 parent 18d5f66 commit 3e31bb5
Showing 1 changed file with 197 additions and 0 deletions.
197 changes: 197 additions & 0 deletions _examples/sql/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package main

import (
"context"
"database/sql"
"errors"
"fmt"
"time"

"github.com/getsentry/sentry-go"
"github.com/getsentry/sentry-go/sentrysql"
"github.com/lib/pq"
)

func init() {
// Registering a custom database driver that's wrapped by sentrysql.
// Later, we can call `sql.Open("sentrysql-postgres", databaseDSN)` to use it.
sql.Register("sentrysql-postgres", sentrysql.NewSentrySQL(&pq.Driver{}, sentrysql.WithDatabaseSystem(sentrysql.PostgreSQL), sentrysql.WithDatabaseName("postgres"), sentrysql.WithServerAddress("write.postgres.internal", "5432")))
}

func main() {
err := sentry.Init(sentry.ClientOptions{
// Either set your DSN here or set the SENTRY_DSN environment variable.
Dsn: "",
// Enable printing of SDK debug messages.
// Useful when getting started or trying to figure something out.
Debug: true,
// EnableTracing must be set to true if you want the SQL queries to be traced.
EnableTracing: true,
TracesSampleRate: 1.0,
})
if err != nil {
fmt.Printf("failed to initialize sentry: %s\n", err.Error())
return
}

// We are going to emulate a scenario where an application requires a read database and a write database.
// This is also to show how to use each `sentrysql.NewSentrySQLConnector` and `sentrysql.NewSentrySQL`.

// 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
}

sentryWrappedConnector := sentrysql.NewSentrySQLConnector(
connector,
sentrysql.WithDatabaseSystem(sentrysql.PostgreSQL), // required if you want to see the queries on the Queries Insights page
sentrysql.WithDatabaseName("postgres"),
sentrysql.WithServerAddress("read.postgres.internal", "5432"),
)

readDatabase := sql.OpenDB(sentryWrappedConnector)
defer func() {
err := readDatabase.Close()
if err != nil {
sentry.CaptureException(err)
}
}()

// Create a database connection for write database.
writeDatabase, err := sql.Open("sentrysql-postgres", "postgres://postgres:[email protected]:5432/postgres")
if err != nil {
fmt.Printf("failed to open write postgres database: %s\n", err.Error())
return
}
defer func() {
err := writeDatabase.Close()
if err != nil {
sentry.CaptureException(err)
}
}()

ctx, cancel := context.WithTimeout(
sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()),
time.Minute,
)
defer cancel()

err = ScaffoldDatabase(ctx, writeDatabase)
if err != nil {
fmt.Printf("failed to scaffold database: %s\n", err.Error())
return
}

users, err := GetAllUsers(ctx, readDatabase)
if err != nil {
fmt.Printf("failed to get users: %s\n", err.Error())
return
}

for _, user := range users {
fmt.Printf("User: %+v\n", user)
}
}

// ScaffoldDatabase prepares the database to have the users table.
func ScaffoldDatabase(ctx context.Context, db *sql.DB) error {
// A parent span is required to have the queries to be traced.
// Make sure to override the `context.Context` with the parent span's context.
span := sentry.StartSpan(ctx, "ScaffoldDatabase")
ctx = span.Context()
defer span.Finish()

conn, err := db.Conn(ctx)
if err != nil {
return fmt.Errorf("acquiring connection from pool: %w", err)
}
defer func() {
err := conn.Close()
if err != nil && !errors.Is(err, sql.ErrConnDone) {
if hub := sentry.GetHubFromContext(ctx); hub != nil {
hub.CaptureException(err)
}
}
}()

tx, err := conn.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable, ReadOnly: false})
if err != nil {
return fmt.Errorf("beginning transaction: %w", err)
}
defer func() {
err := tx.Rollback()
if err != nil && !errors.Is(err, sql.ErrTxDone) {
if hub := sentry.GetHubFromContext(ctx); hub != nil {
hub.CaptureException(err)
}
}
}()

_, err = tx.ExecContext(ctx, "CREATE TABLE users (id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR(255), email VARCHAR(255), active BOOLEAN)")
if err != nil {
return fmt.Errorf("creating users table: %w", err)
}

err = tx.Commit()
if err != nil {
return fmt.Errorf("committing transaction: %w", err)
}

return nil
}

// User represents a user in the database.
type User struct {
ID int
Name string
Email string
}

// GetAllUsers returns all the users from the database.
func GetAllUsers(ctx context.Context, db *sql.DB) ([]User, error) {
// A parent span is required to have the queries to be traced.
// Make sure to override the `context.Context` with the parent span's context.
span := sentry.StartSpan(ctx, "GetAllUsers")
ctx = span.Context()
defer span.Finish()

conn, err := db.Conn(ctx)
if err != nil {
return nil, fmt.Errorf("acquiring connection from pool: %w", err)
}
defer func() {
err := conn.Close()
if err != nil && !errors.Is(err, sql.ErrConnDone) {
if hub := sentry.GetHubFromContext(ctx); hub != nil {
hub.CaptureException(err)
}
}
}()

rows, err := conn.QueryContext(ctx, "SELECT id, name, email FROM users WHERE active = $1", true)
if err != nil {
return nil, fmt.Errorf("querying users: %w", err)
}
defer func() {
err := rows.Close()
if err != nil {
if hub := sentry.GetHubFromContext(ctx); hub != nil {
hub.CaptureException(err)
}
}
}()

var users []User
for rows.Next() {
var user User
err := rows.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return nil, fmt.Errorf("scanning user: %w", err)
}
users = append(users, user)
}

return users, nil
}

0 comments on commit 3e31bb5

Please sign in to comment.