dbtrace

package
v1.0.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package dbtrace opens a *sql.DB whose queries are OpenTelemetry spans.

Every query a forge process runs goes through database/sql: the ORM (bun) sits on a *sql.DB, and hand-written SQL calls it directly. Tracing at the driver is therefore the one place that sees all of it; an ORM hook sees only ORM traffic, which in practice is the minority. serverkit.OpenDB opens its pool through this package, so a server gets query spans with no code of its own.

What a span carries, and what it never does:

  • One CLIENT span per statement (Exec/Query on a connection or prepared statement), named "<OPERATION> <table>" — "SELECT chats", "INSERT usage_events" — plus BEGIN/COMMIT/ROLLBACK and a db.connect span when the pool dials. Ping, ResetSession, Prepare and the per-row spans are off: they multiply the span count without saying where the time went.
  • db.query.text is the statement with every literal replaced by ? (see SanitizeSQL). Bind arguments are never recorded: they are user data.
  • A span is only opened under an existing span. A query run with no span in its context — a startup migration, a poller with no root span — would otherwise become a one-span trace of its own, which is noise, not depth. Give the loop a root span (lifecyclekit does) and its queries appear under it.

A native pgx / pgxpool connection (no database/sql) gets the same spans from NewPgxTracer, assigned to cfg.ConnConfig.Tracer.

Metrics are not emitted here: the pool metrics come from RegisterDBStatsMetrics-style registration elsewhere, and the ORM's query histogram (go.sql.query_timing) from pkg/orm.

Index

Constants

View Source
const System = "postgresql"

System is the db.system.name every span carries. Forge is postgres-pinned.

Variables

This section is empty.

Functions

func IsTraced

func IsTraced(db *sql.DB) bool

IsTraced reports whether db's statements are already traced at the driver — opened by this package, or wrapped with otelsql by the application itself. pkg/orm asks, so an ORM on a traced pool does not open a second span per query.

func Open

func Open(driverName, dsn string) (*sql.DB, error)

Open is sql.Open with every statement traced. Like sql.Open it does not connect; the first statement (or a Ping) does.

func OpenDB

func OpenDB(c driver.Connector) *sql.DB

OpenDB is sql.OpenDB with every statement traced. The connector is wrapped, not replaced, so a connector with its own behaviour (serverkit's pooler-drain retry) keeps it.

func SanitizeSQL

func SanitizeSQL(query string) string

SanitizeSQL is Postgres.Sanitize.

func Summarize

func Summarize(query string) (operation, table string)

Summarize is Postgres.Summarize.

Types

type Dialect added in v1.0.2

type Dialect int

Dialect is the set of lexical rules a statement is read by: where its string literals, quoted identifiers and comments start and end. It matters for more than accuracy. Read by another dialect's rules, a literal can end early, and the rest of it is recorded as SQL text: Postgres ends 'a\'secret' at the backslash-quote, ClickHouse does not.

const (
	// Postgres: a backslash escapes only inside E'…'; "…" quotes an
	// identifier; $tag$…$tag$ is a literal and $1 a placeholder.
	Postgres Dialect = iota
	// ClickHouse: a backslash escapes inside every quoted string and
	// identifier; `…` and “…” also quote an identifier and ‘…’ is a literal;
	// //, "# " and #! also start a line comment; $tag$…$tag$ is a heredoc.
	ClickHouse
)

func (Dialect) Sanitize added in v1.0.2

func (d Dialect) Sanitize(query string) string

Sanitize returns query with every literal replaced by ?, so the text can be recorded on a span without carrying the data it names: string literals ('…', E'…', $tag$…$tag$, and the dialect's own forms) and numeric literals become ?. Placeholders ($1, {name:Type}), identifiers (including quoted ones and digits inside names) and keywords are kept. Comments are dropped, runs of whitespace collapse to one space, and the result is capped at maxQueryText bytes.

It is a lexer, not a parser: it does not need to understand the statement, only where literals start and end, which the dialect's lexical rules fix.

func (Dialect) Summarize added in v1.0.2

func (d Dialect) Summarize(query string) (operation, table string)

Summarize names a statement for a span: its operation (SELECT, INSERT, …, upper-cased) and the table it targets, when that is a plain name. A WITH statement is named for the statement its CTEs feed. Either may be empty.

type PgxTracer

type PgxTracer struct{}

PgxTracer traces a native pgx / pgxpool connection with the same spans OpenDB gives a database/sql pool: one client span per statement named "<OPERATION> <table>", db.query.text with every literal replaced by ?, bind arguments never recorded, and no span at all for work that has no caller span to hang under.

Install it before the pool connects:

cfg, err := pgxpool.ParseConfig(dsn)
cfg.ConnConfig.Tracer = dbtrace.NewPgxTracer()
pool, err := pgxpool.NewWithConfig(ctx, cfg)

It implements pgx.QueryTracer, BatchTracer, CopyFromTracer and ConnectTracer; a batch is one BATCH span with an event per statement. Prepare is not traced, for the reason OpenDB leaves it off.

func NewPgxTracer

func NewPgxTracer() *PgxTracer

NewPgxTracer returns the tracer to assign to a pgx ConnConfig.Tracer.

func (*PgxTracer) TraceBatchEnd

func (*PgxTracer) TraceBatchEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceBatchEndData)

TraceBatchEnd implements pgx.BatchTracer.

func (*PgxTracer) TraceBatchQuery

func (*PgxTracer) TraceBatchQuery(ctx context.Context, _ *pgx.Conn, data pgx.TraceBatchQueryData)

TraceBatchQuery implements pgx.BatchTracer. Each statement is an event on the batch span, carrying the same sanitized text a query span would.

func (*PgxTracer) TraceBatchStart

func (t *PgxTracer) TraceBatchStart(ctx context.Context, _ *pgx.Conn, _ pgx.TraceBatchStartData) context.Context

TraceBatchStart implements pgx.BatchTracer.

func (*PgxTracer) TraceConnectEnd

func (*PgxTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData)

TraceConnectEnd implements pgx.ConnectTracer.

func (*PgxTracer) TraceConnectStart

func (t *PgxTracer) TraceConnectStart(ctx context.Context, _ pgx.TraceConnectStartData) context.Context

TraceConnectStart implements pgx.ConnectTracer.

func (*PgxTracer) TraceCopyFromEnd

func (*PgxTracer) TraceCopyFromEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceCopyFromEndData)

TraceCopyFromEnd implements pgx.CopyFromTracer.

func (*PgxTracer) TraceCopyFromStart

func (t *PgxTracer) TraceCopyFromStart(ctx context.Context, _ *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context

TraceCopyFromStart implements pgx.CopyFromTracer.

func (*PgxTracer) TraceQueryEnd

func (*PgxTracer) TraceQueryEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryEndData)

TraceQueryEnd implements pgx.QueryTracer.

func (*PgxTracer) TraceQueryStart

func (t *PgxTracer) TraceQueryStart(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryStartData) context.Context

TraceQueryStart implements pgx.QueryTracer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL