Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions lib/history/singleton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package history

import (
"context"
"fmt"
"log/slog"

"github.com/flachnetz/startup/v2/lib/events"
"github.com/flachnetz/startup/v2/lib/ql"
"github.com/flachnetz/startup/v2/startup_base"
"github.com/jackc/pgx/v5"
)

// Options configures the global history Service set up by InitializeGlobal.
type Options struct {
// DB starts the transactions used to create the table and to write records.
DB ql.TxStarter

// ServiceId identifies this service as the sender of the emitted events.
ServiceId string
// HistoryTable is the name of the table that history records are written to.
HistoryTable string
// EventCreator builds the event that is sent out for every tracked record.
EventCreator EventCreator
}

// instance holds the global history Service initialized by InitializeGlobal.
var instance *Service

// InitializeGlobal sets up the global history Service used by Track. It creates
// the history table (if it does not exist yet) and starts the background task
// that sends records tracked outside of a transaction.
func InitializeGlobal(ctx context.Context, opts Options) error {
err := ql.InNewTransaction(ctx, opts.DB, func(ctx ql.TxContext) error {
return CreateTable(ctx, opts.HistoryTable)
})

if err != nil {
return fmt.Errorf("create history table: %w", err)
}

instance = New(opts.DB, pgx.Identifier{opts.HistoryTable}, &EventSending{
EventSender: events.Sender,
EventCreator: opts.EventCreator,
ServiceId: opts.ServiceId,
ServiceVersion: startup_base.BuildVersion,
WriteToOutbox: true,
})

// start the background task that flushes records tracked outside of a
// transaction. Cancel ctx to stop it.
instance.SendAsync(ctx)

return nil
}

// Track uses the global history singleton.
// You need to initialize it using InitializeGlobal first.
func Track[T ~string](ctx context.Context, groupId T, item Item) {
if instance == nil {
// not initialized: just log the event instead of tracking it.
slog.WarnContext(ctx, "Tracking event", slog.String("event", item.HistoryString()))
return
}

instance.Track(ctx, GroupId(groupId), item)
}
23 changes: 23 additions & 0 deletions startup_logging/resty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package sl

import (
"fmt"
"log/slog"
)

// RestyAdapter bridges resty.Logger to *slog.Logger.
type RestyAdapter struct {
Logger *slog.Logger
}

func (a RestyAdapter) Errorf(format string, v ...any) {
a.Logger.Error(fmt.Sprintf(format, v...))
}

func (a RestyAdapter) Warnf(format string, v ...any) {
a.Logger.Warn(fmt.Sprintf(format, v...))
}

func (a RestyAdapter) Debugf(format string, v ...any) {
a.Logger.Debug(fmt.Sprintf(format, v...))
}
Loading