Documentation
¶
Index ¶
- func MetricsCollector(s *SlowQueryLogger) func(io.Writer)
- func TrimSQL(q string) string
- type Option
- type SlowQueryLogger
- func (s *SlowQueryLogger) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
- func (s *SlowQueryLogger) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (s *SlowQueryLogger) Hits() uint64
- func (s *SlowQueryLogger) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (s *SlowQueryLogger) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MetricsCollector ¶ added in v0.63.0
func MetricsCollector(s *SlowQueryLogger) func(io.Writer)
MetricsCollector returns a Prometheus text-exposition collector emitting slow_queries_total (the running count of queries that crossed the logger's threshold). Register it on the app's *middleware.Metrics via RegisterCollector so the count surfaces on the single /metrics endpoint.
A nil *SlowQueryLogger reports zero, so wiring the collector before the logger exists (or when slow-query logging is disabled) never panics /metrics.
func TrimSQL ¶
TrimSQL is the statement's shape, as the slow-query line prints it when no dialect was named: every string and number literal replaced by ?, comments dropped, whitespace collapsed, control bytes escaped, and the result capped at 240 bytes (cut on a rune boundary, then "…") to keep logs from exploding on big builders. Placeholders ($1, ?, :n) and identifiers stay. Bound arguments were never logged, only their count.
A slow-query line is for the statement's shape, and a value spelled inline (ALTER ROLE … PASSWORD '…', SET app.key = '…') went to every sink verbatim. Where a literal ends depends on the engine, and TrimSQL does not know it, so it masks under every engine's rules at once: "double-quoted" text prints as ? too (SQLite and MySQL can read it as a string), # opens a comment (MySQL), block comments nest (PostgreSQL), and a string whose end depends on whether a backslash escapes masks the rest of the statement. Name the engine with WithDialect to keep quoted identifiers on PostgreSQL. It is the masker core/tracing puts on db.query.text.
Types ¶
type Option ¶ added in v0.96.0
type Option func(*SlowQueryLogger)
Option configures a SlowQueryLogger.
func WithDialect ¶ added in v0.96.0
WithDialect names the database engine, so the sql attr masks its literals by that engine's quoting rules: "postgres" (or "postgresql", "pgx"), "sqlite3" (or "sqlite"), "mysql" (or "mariadb"). The values of framework.DetectDialect are accepted as they are. Without it, or with a name it does not know, the line masks under every engine's rules at once (see TrimSQL), which also hides PostgreSQL's "quoted identifiers".
type SlowQueryLogger ¶
type SlowQueryLogger struct {
// contains filtered or unexported fields
}
SlowQueryLogger emits a slog warning for any DB query whose execution time exceeds Threshold. Wrap the framework's *sql.DB at construction time (or any db.Executor) and pass the result into NewCrudHandler in place of the raw DB.
Threshold of 0 disables logging entirely (default zero-value).
The wrapper is dialect-agnostic, it simply times QueryContext / QueryRowContext / ExecContext on the underlying connection and emits a structured log line when the duration crosses the threshold.
func NewSlowQueryLogger ¶
func NewSlowQueryLogger(inner db.Executor, threshold time.Duration, logger *slog.Logger, opts ...Option) *SlowQueryLogger
NewSlowQueryLogger wraps inner with a slow-query observer. When threshold is zero, the wrapper is a no-op pass-through (the same db.Executor with no instrumentation cost).
logger is a pinned *slog.Logger. Slow-query lines write through it for the lifetime of the wrapper. To follow App.Logger swaps (battery/log replaces it during plugin Init), use NewSlowQueryLoggerFn instead and pass app.Logger as the accessor.
func NewSlowQueryLoggerFn ¶
func NewSlowQueryLoggerFn(inner db.Executor, threshold time.Duration, getLogger func() *slog.Logger, opts ...Option) *SlowQueryLogger
NewSlowQueryLoggerFn is like NewSlowQueryLogger but takes a per-call accessor so log lines route through the current logger at observation time. Use with app.Logger so battery/log's SetLogger swap takes effect without re-wrapping the executor.
If getLogger is nil or returns nil, slog.Default is used as a safety fallback (slow-query lines never disappear silently).
func (*SlowQueryLogger) BeginTx ¶
BeginTx forwards transaction starts to the inner DB if it supports them. Tx-bound queries don't go through this wrapper (the framework hands the raw *sql.Tx to the in-tx handler copy), so multi-statement transactions aren't slow-logged here today. A future enhancement could wrap the tx too.
func (*SlowQueryLogger) ExecContext ¶
func (s *SlowQueryLogger) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
ExecContext mirrors sql.DB.ExecContext, timing the call.
func (*SlowQueryLogger) Hits ¶
func (s *SlowQueryLogger) Hits() uint64
Hits returns the running count of queries that crossed the threshold. Useful for assertions in tests and for /.debug/stats surface metrics.
func (*SlowQueryLogger) QueryContext ¶
func (s *SlowQueryLogger) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryContext mirrors sql.DB.QueryContext, timing the call.
func (*SlowQueryLogger) QueryRowContext ¶
QueryRowContext mirrors sql.DB.QueryRowContext, timing the call.