sqlite

package module
v0.0.0-...-0fc0b83 Latest Latest
Warning

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

Go to latest
Published: Apr 9, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package sqlite provides a SQLite-backed implementation of promolog.Storer.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is a SQLite-backed store of traces.

func NewStore

func NewStore(db *sql.DB) *Store

NewStore creates a Store backed by the given database connection.

Example
package main

import (
	"database/sql"
	"fmt"

	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	db.SetMaxOpenConns(1)

	store := sqlite.NewStore(db)
	if err := store.InitSchema(); err != nil {
		panic(err)
	}

	fmt.Println("store ready")
}
Output:
store ready

func (*Store) Aggregate

Aggregate groups traces by the field specified in the filter and returns counts along with the top error chains for each group.

func (*Store) AvailableFilters

func (s *Store) AvailableFilters(ctx context.Context, f promolog.TraceFilter) (promolog.FilterOptions, error)

AvailableFilters returns distinct status codes and methods for filter dropdowns.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	ctx := context.Background()
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-1", StatusCode: 400, Route: "/a", Method: "GET",
		ErrorChain: "bad request",
	})
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-2", StatusCode: 500, Route: "/b", Method: "POST",
		ErrorChain: "internal error",
	})

	// Get distinct values for building filter UI dropdowns.
	opts, err := store.AvailableFilters(ctx, promolog.TraceFilter{})
	if err != nil {
		panic(err)
	}
	fmt.Println("codes:", opts.StatusCodes)
	fmt.Println("methods:", opts.Methods)
}
Output:
codes: [400 500]
methods: [GET POST]

func (*Store) CreateRetentionRule

func (s *Store) CreateRetentionRule(ctx context.Context, rule promolog.RetentionRule) (promolog.RetentionRule, error)

CreateRetentionRule inserts a new retention rule and returns it with its assigned ID and timestamp.

func (*Store) CreateRule

func (s *Store) CreateRule(ctx context.Context, rule promolog.FilterRule) (promolog.FilterRule, error)

CreateRule inserts a new filter rule and returns it with its assigned ID and timestamp.

func (*Store) DeleteRetentionRule

func (s *Store) DeleteRetentionRule(ctx context.Context, id int) error

DeleteRetentionRule removes a retention rule by ID.

func (*Store) DeleteRule

func (s *Store) DeleteRule(ctx context.Context, id int) error

DeleteRule removes a filter rule by ID.

func (*Store) DeleteTrace

func (s *Store) DeleteTrace(ctx context.Context, requestID string) error

DeleteTrace removes a single trace by request ID.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	ctx := context.Background()
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-delete-me", ErrorChain: "gone", StatusCode: 500,
		Route: "/api/test", Method: "DELETE",
	})

	err := store.DeleteTrace(ctx, "req-delete-me")
	fmt.Println("delete err:", err)

	trace, _ := store.Get(ctx, "req-delete-me")
	fmt.Println("after delete:", trace)
}
Output:
delete err: <nil>
after delete: <nil>

func (*Store) Get

func (s *Store) Get(ctx context.Context, requestID string) (*promolog.Trace, error)

Get returns the full trace for a request ID, or nil if not found.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	ctx := context.Background()
	_ = store.Promote(ctx, promolog.Trace{
		RequestID:  "req-lookup",
		ErrorChain: "timeout",
		StatusCode: 504,
		Route:      "/api/orders",
		Method:     "POST",
		Entries: []promolog.Entry{
			{Time: time.Now(), Level: "ERROR", Message: "upstream timeout"},
		},
	})

	// Retrieve the full trace by request ID.
	trace, err := store.Get(ctx, "req-lookup")
	if err != nil {
		panic(err)
	}
	fmt.Println(trace.Route, trace.StatusCode)
	fmt.Println(len(trace.Entries))

	// Non-existent request IDs return nil without error.
	missing, err := store.Get(ctx, "does-not-exist")
	fmt.Println(missing, err)
}
Output:
/api/orders 504
1
<nil> <nil>

func (*Store) InitSchema

func (s *Store) InitSchema() error

InitSchema creates the error_traces table if it doesn't exist. It also applies any necessary migrations for existing databases.

func (*Store) ListRetentionRules

func (s *Store) ListRetentionRules(ctx context.Context) ([]promolog.RetentionRule, error)

ListRetentionRules returns all retention rules ordered by creation time.

func (*Store) ListRules

func (s *Store) ListRules(ctx context.Context) ([]promolog.FilterRule, error)

ListRules returns all filter rules ordered by creation time.

func (*Store) ListTraces

func (s *Store) ListTraces(ctx context.Context, f promolog.TraceFilter) ([]promolog.TraceSummary, int, error)

ListTraces returns a page of trace summaries matching the given filters.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	ctx := context.Background()
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-1", ErrorChain: "not found", StatusCode: 404,
		Route: "/api/users/99", Method: "GET",
	})
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-2", ErrorChain: "db down", StatusCode: 500,
		Route: "/api/orders", Method: "POST",
	})
	_ = store.Promote(ctx, promolog.Trace{
		RequestID: "req-3", ErrorChain: "bad gateway", StatusCode: 502,
		Route: "/api/payments", Method: "POST",
	})

	// Filter by status class and method.
	rows, total, err := store.ListTraces(ctx, promolog.TraceFilter{
		Status:  "5xx",
		Method:  "POST",
		Sort:    "StatusCode",
		Dir:     "asc",
		Page:    1,
		PerPage: 10,
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("total:", total)
	for _, r := range rows {
		fmt.Printf("%s %d %s\n", r.Route, r.StatusCode, r.Method)
	}
}
Output:
total: 2
/api/orders 500 POST
/api/payments 502 POST

func (*Store) LoadRetentionEngine

func (s *Store) LoadRetentionEngine(ctx context.Context) (*promolog.RetentionEngine, error)

LoadRetentionEngine reads all enabled retention rules from the database and returns a ready-to-use RetentionEngine.

func (*Store) LoadRuleEngine

func (s *Store) LoadRuleEngine(ctx context.Context) (*promolog.RuleEngine, error)

LoadRuleEngine reads all enabled filter rules from the database and returns a ready-to-use RuleEngine.

func (*Store) Promote

func (s *Store) Promote(ctx context.Context, trace promolog.Trace) error

Promote persists a trace to the database.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	ctx := context.Background()

	// Promote persists the buffered log entries when a request fails.
	err := store.Promote(ctx, promolog.Trace{
		RequestID:  "req-abc-123",
		ErrorChain: "connection refused",
		StatusCode: 502,
		Route:      "/api/users",
		Method:     "GET",
		UserAgent:  "Mozilla/5.0",
		RemoteIP:   "10.0.0.1",
		UserID:     "user-42",
		Entries: []promolog.Entry{
			{Time: time.Now(), Level: "INFO", Message: "starting request"},
			{Time: time.Now(), Level: "ERROR", Message: "connection refused", Attrs: map[string]string{"host": "db.local"}},
		},
	})
	fmt.Println(err)

	// Promoting the same request ID again returns ErrDuplicateTrace.
	err = store.Promote(ctx, promolog.Trace{
		RequestID:  "req-abc-123",
		ErrorChain: "duplicate",
		StatusCode: 500,
		Route:      "/api/users",
		Method:     "GET",
	})
	fmt.Println(errors.Is(err, promolog.ErrDuplicateTrace))
}
Output:
<nil>
true

func (*Store) PromoteAt

func (s *Store) PromoteAt(ctx context.Context, trace promolog.Trace, createdAt time.Time) error

PromoteAt persists a trace with a specific timestamp.

func (*Store) SetOnPromote

func (s *Store) SetOnPromote(fn func(promolog.TraceSummary))

SetOnPromote registers a callback invoked after each successful promote.

Example
package main

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

	"github.com/catgoose/promolog"
	"github.com/catgoose/promolog/sqlite"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, _ := sql.Open("sqlite3", ":memory:")
	defer db.Close()
	db.SetMaxOpenConns(1)
	store := sqlite.NewStore(db)
	_ = store.InitSchema()

	// Register a callback for real-time notifications (SSE, webhooks, etc.).
	store.SetOnPromote(func(ts promolog.TraceSummary) {
		fmt.Printf("alert: %s %d %s\n", ts.RequestID, ts.StatusCode, ts.Route)
	})

	ctx := context.Background()
	_ = store.Promote(ctx, promolog.Trace{
		RequestID:  "req-notify",
		ErrorChain: "something broke",
		StatusCode: 500,
		Route:      "/api/webhook",
		Method:     "POST",
	})
}
Output:
alert: req-notify 500 /api/webhook

func (*Store) StartCleanup

func (s *Store) StartCleanup(ctx context.Context, ttl, interval time.Duration)

StartCleanup runs a background goroutine that deletes entries older than ttl. If retention rules are configured, traces matching a rule use that rule's TTL instead of the default. The shortest matching TTL wins.

func (*Store) UpdateRetentionRule

func (s *Store) UpdateRetentionRule(ctx context.Context, rule promolog.RetentionRule) error

UpdateRetentionRule updates an existing retention rule identified by its ID.

func (*Store) UpdateRule

func (s *Store) UpdateRule(ctx context.Context, rule promolog.FilterRule) error

UpdateRule updates an existing filter rule identified by its ID.

Jump to

Keyboard shortcuts

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