Documentation
¶
Overview ¶
Package trace provides transparent SQL tracing for modernc.org/sqlite.
It registers a "sqlite-trace" driver that wraps the standard "sqlite" driver, intercepting every Exec and Query at the database/sql/driver level. No application code changes are needed beyond switching the driver name:
import _ "github.com/hazyhaar/pkg/trace" // registers "sqlite-trace"
// Trace store (opened with raw "sqlite" to avoid recursion)
traceDB, _ := sql.Open("sqlite", "traces.db")
store := trace.NewStore(traceDB)
store.Init()
trace.SetStore(store)
// Application DB — all queries are now traced automatically
db, _ := sql.Open("sqlite-trace", "app.db")
Without a Store (SetStore not called or nil), the driver still logs every query via slog with adaptive levels (Debug, Warn >100ms, Error on failure). Trace IDs are read from context via kit.GetTraceID for request correlation.
Index ¶
Constants ¶
const Schema = `` /* 477-byte string literal not displayed */
Schema for the sql_traces table. Call Store.Init() or apply manually.
Variables ¶
This section is empty.
Functions ¶
func IngestHandler ¶
func IngestHandler(store *Store) http.HandlerFunc
IngestHandler returns an HTTP handler that receives trace batches from a RemoteStore (FO side) and writes them to the local Store (BO side).
Expected request: POST with application/json body containing []*Entry. Returns 204 on success, 405 for wrong method, 400 for bad payload.
Mount on the BO:
mux.Handle("/api/internal/traces", trace.IngestHandler(store))
Types ¶
type Entry ¶
type Entry struct {
TraceID string // correlation with HTTP/MCP request
Op string // "Exec" or "Query"
Query string // SQL statement
DurationUs int64 // microseconds
Error string // empty if success
Timestamp int64 // unix microseconds
}
Entry is a single SQL trace record.
type Recorder ¶
Recorder is the interface for trace persistence backends. Store (local SQLite) and RemoteStore (HTTP POST to BO) both implement it.
type RemoteStore ¶
type RemoteStore struct {
// contains filtered or unexported fields
}
RemoteStore sends trace entries to a BO endpoint via HTTP POST. It uses the same async batching pattern as Store: a 1024-capacity channel, batches of up to 64, flushed every second.
Usage (FO side):
rs := trace.NewRemoteStore("https://bo.example.com/api/internal/traces", nil)
trace.SetStore(rs)
defer rs.Close()
func NewRemoteStore ¶
func NewRemoteStore(url string, client *http.Client) *RemoteStore
NewRemoteStore creates a RemoteStore that POSTs trace batches to url. If client is nil, a default client with 5s timeout is used.
func (*RemoteStore) Close ¶
func (rs *RemoteStore) Close() error
Close drains the buffer and stops the flush goroutine.
func (*RemoteStore) RecordAsync ¶
func (rs *RemoteStore) RecordAsync(e *Entry)
RecordAsync queues an entry for async push. Non-blocking; drops if buffer full.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store persists SQL trace entries to a SQLite table asynchronously. It MUST be opened with the raw "sqlite" driver (not "sqlite-trace") to avoid infinite recursion.
func NewStore ¶
NewStore creates a trace store backed by the given database connection. The db should use the raw "sqlite" driver to avoid tracing its own writes.
func (*Store) RecordAsync ¶
RecordAsync queues an entry for async persistence. Non-blocking; drops if buffer full.
type TracingDriver ¶
TracingDriver wraps the modernc.org/sqlite driver, intercepting every Exec and Query at the database/sql/driver level.
Registered as "sqlite-trace" in init(). Open connections with sql.Open("sqlite-trace", path) to get automatic tracing.