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
- func IsTraced(db *sql.DB) bool
- func Open(driverName, dsn string) (*sql.DB, error)
- func OpenDB(c driver.Connector) *sql.DB
- func SanitizeSQL(query string) string
- func Summarize(query string) (operation, table string)
- type Dialect
- type PgxTracer
- func (*PgxTracer) TraceBatchEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceBatchEndData)
- func (*PgxTracer) TraceBatchQuery(ctx context.Context, _ *pgx.Conn, data pgx.TraceBatchQueryData)
- func (t *PgxTracer) TraceBatchStart(ctx context.Context, _ *pgx.Conn, _ pgx.TraceBatchStartData) context.Context
- func (*PgxTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData)
- func (t *PgxTracer) TraceConnectStart(ctx context.Context, _ pgx.TraceConnectStartData) context.Context
- func (*PgxTracer) TraceCopyFromEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceCopyFromEndData)
- func (t *PgxTracer) TraceCopyFromStart(ctx context.Context, _ *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context
- func (*PgxTracer) TraceQueryEnd(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryEndData)
- func (t *PgxTracer) TraceQueryStart(ctx context.Context, _ *pgx.Conn, data pgx.TraceQueryStartData) context.Context
Constants ¶
const System = "postgresql"
System is the db.system.name every span carries. Forge is postgres-pinned.
Variables ¶
This section is empty.
Functions ¶
func IsTraced ¶
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 ¶
Open is sql.Open with every statement traced. Like sql.Open it does not connect; the first statement (or a Ping) does.
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
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.
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 ¶
TraceBatchEnd implements pgx.BatchTracer.
func (*PgxTracer) TraceBatchQuery ¶
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 ¶
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 ¶
TraceQueryEnd implements pgx.QueryTracer.