eventlog

package
v0.3.6 Latest Latest
Warning

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

Go to latest
Published: Apr 2, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package eventlog records and queries notification execution history.

Index

Constants

View Source
const GapThreshold = 5 * time.Minute

GapThreshold is the maximum gap between consecutive entries that is still counted as active time in time-spent calculations.

Variables

View Source
var LogPath = func() string {
	return filepath.Join(paths.DataDir(), paths.LogFileName)
}

LogPath returns the log file location (~/.config/notify/notify.log).

This is a variable so tests can override it.

Functions

func Close added in v0.2.31

func Close()

Close closes the Default store if it implements io.Closer.

func DayCutoff added in v0.2.29

func DayCutoff(days int) time.Time

DayCutoff returns midnight N days ago (inclusive) in the local timezone. For days=1 it returns today at midnight, for days=7 it returns 6 days ago, etc.

func ExtractTimestamp added in v0.2.17

func ExtractTimestamp(line string) (time.Time, bool)

ExtractTimestamp parses the RFC3339 timestamp at the start of a log line (everything before the first " " double-space separator). Returns the parsed time and true on success, or zero time and false on failure.

func FilterBlocksByDays added in v0.2.26

func FilterBlocksByDays(content string, days int) string

FilterBlocksByDays returns only log blocks whose timestamp falls within the last N calendar days. Each block is separated by a blank line.

func FilterBlocksByProfile added in v0.2.28

func FilterBlocksByProfile(content string, profile string) (string, int)

FilterBlocksByProfile removes all log blocks belonging to the named profile. Returns the filtered content and the number of removed blocks.

func KindString added in v0.2.12

func KindString(k EntryKind) string

KindString returns a human-readable string for an EntryKind.

func Log

func Log(action string, steps []config.Step, afk bool, vars tmpl.Vars, desktop *int)

Log appends to the log file a summary line followed by one detail line per step. Errors are printed to stderr but never returned — logging is best-effort.

func LogCooldown added in v0.2.3

func LogCooldown(profile, action string, cooldownSeconds int)

LogCooldown appends a single line noting that an invocation was skipped due to cooldown. Best-effort, same as Log.

func LogCooldownRecord added in v0.2.3

func LogCooldownRecord(profile, action string, cooldownSeconds int)

LogCooldownRecord appends a single line noting that cooldown state was updated for an action. Best-effort, same as Log.

func LogSilent added in v0.2.7

func LogSilent(profile, action string)

LogSilent appends a single line noting that an invocation was skipped due to silent mode. Best-effort, same as Log.

func LogSilentDisable added in v0.2.7

func LogSilentDisable()

LogSilentDisable appends a single line noting that silent mode was disabled. Best-effort, same as Log.

func LogSilentEnable added in v0.2.7

func LogSilentEnable(d time.Duration)

LogSilentEnable appends a single line noting that silent mode was enabled. Best-effort, same as Log.

func OpenDefault added in v0.2.31

func OpenDefault(storage string)

OpenDefault initializes Default with the configured storage backend. Pass "file" for the flat-file backend, or "" / "sqlite" for SQLite. If SQLite fails to open, falls back to FileStore with a warning.

func ReadContent added in v0.2.31

func ReadContent() (string, error)

ReadContent returns the raw log file content.

func SetRetention added in v0.2.34

func SetRetention(days int)

SetRetention configures automatic log cleanup. When days > 0, old entries are pruned after every write operation.

func SplitBlocks added in v0.2.29

func SplitBlocks(content string) []string

SplitBlocks splits log content on blank lines, trims whitespace from each block, and returns only non-empty blocks.

func StepSummary added in v0.2.17

func StepSummary(s config.Step, vars *tmpl.Vars) string

StepSummary returns a human-readable description of what a step does. When vars is non-nil, template variables are expanded (logging mode). When vars is nil, raw values are shown (dry-run mode). When/volume suffixes are always appended if present.

Types

type ActionKey added in v0.2.21

type ActionKey struct{ Profile, Action string }

ActionKey identifies a profile/action pair for aggregation.

type AggregatedData added in v0.2.21

type AggregatedData struct {
	PerAction        map[ActionKey]*Counts
	PerProfile       map[string]*Counts
	ProfileOrder     []string
	ActionsByProfile map[string][]ActionKey
	HasSkipped       bool
}

AggregatedData holds the result of aggregating day groups into per-action and per-profile counts.

func AggregateGroups added in v0.2.21

func AggregateGroups(groups []DayGroup) AggregatedData

AggregateGroups collects per-action and per-profile counts from day groups.

type Counts added in v0.2.21

type Counts struct{ Exec, Skip int }

Counts holds execution and skip totals for a profile or action.

type DayGroup added in v0.2.11

type DayGroup struct {
	Date      time.Time
	Summaries []DaySummary
}

DayGroup holds all summaries for a single calendar day.

func SummarizeByDay added in v0.2.11

func SummarizeByDay(entries []Entry, days int) []DayGroup

SummarizeByDay filters entries to the last N calendar days (local time), groups by date + profile/action, and returns day groups sorted descending with summaries sorted alphabetically. Pass days=0 to include all entries.

type DaySummary added in v0.2.11

type DaySummary struct {
	Profile    string
	Action     string
	Executions int
	Skipped    int
}

DaySummary holds execution and skip counts for one profile/action pair.

type Entry added in v0.2.11

type Entry struct {
	Time          time.Time
	Profile       string
	Action        string
	Kind          EntryKind
	ClaudeHook    string // from claude_hook= field (optional)
	ClaudeMessage string // from claude_message= field (optional)
}

Entry is a single parsed log entry.

func Entries added in v0.2.31

func Entries(days int) ([]Entry, error)

Entries returns parsed log entries filtered to the last N days (0 = all).

func EntriesSince added in v0.2.31

func EntriesSince(cutoff time.Time) ([]Entry, error)

EntriesSince returns parsed log entries after the given cutoff time.

func ParseEntries added in v0.2.11

func ParseEntries(content string) []Entry

ParseEntries splits log content on blank lines and parses summary lines into entries. Each block may contain multiple summary lines (e.g. a cooldown=recorded line followed by an execution line) plus step detail lines (indented with "step["). Step detail lines are skipped. Each summary line produces one Entry. Malformed lines are silently skipped.

type EntryKind added in v0.2.11

type EntryKind int

EntryKind classifies a log entry.

const (
	KindExecution EntryKind = iota // A notification pipeline was fully executed
	KindCooldown                   // Invocation was skipped due to cooldown
	KindSilent                     // Invocation was suppressed by silent mode
	KindOther                      // Informational record (not counted in summaries)
)

Entry classification constants. KindExecution and KindCooldown/KindSilent represent "real" events that appear in history summaries. KindOther covers informational records (cooldown=recorded, silent=enabled/disabled) that are stored for audit but excluded from aggregation counts.

type FileStore added in v0.2.31

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

FileStore implements Store using a flat log file. Note: FileStore has no file locking. Concurrent writes from multiple processes rely on OS-level O_APPEND atomicity. Use SQLiteStore (the default) for guaranteed concurrent safety.

func NewFileStore added in v0.2.31

func NewFileStore(path string) *FileStore

NewFileStore returns a FileStore that reads and writes the given log file.

func (*FileStore) Clean added in v0.2.31

func (f *FileStore) Clean(days int) (int, error)

Clean removes log blocks older than N calendar days. It works by splitting the file on double-newline boundaries (SplitBlocks), filtering blocks by their first-line timestamp, counting the difference between original and kept block counts, and rewriting the file with only recent blocks.

func (*FileStore) Clear added in v0.2.31

func (f *FileStore) Clear() error

Clear deletes the entire log file. Silently succeeds if already absent.

func (*FileStore) Entries added in v0.2.31

func (f *FileStore) Entries(days int) ([]Entry, error)

Entries reads the entire log file, parses it into entries, and optionally filters to the last N calendar days. Pass days=0 to return all entries.

func (*FileStore) EntriesSince added in v0.2.31

func (f *FileStore) EntriesSince(cutoff time.Time) ([]Entry, error)

EntriesSince returns all entries with timestamps at or after cutoff.

func (*FileStore) Log added in v0.2.31

func (f *FileStore) Log(action string, steps []config.Step, afk bool, vars tmpl.Vars, desktop *int) error

Log appends an execution event to the log file: a summary line with profile, action, step types, and metadata, followed by one detail line per step, terminated by a blank line to separate blocks.

func (*FileStore) LogCooldown added in v0.2.31

func (f *FileStore) LogCooldown(profile, action string, seconds int) error

LogCooldown records that an invocation was skipped because the action's cooldown period had not yet elapsed. Ends with "\n\n" (blank line) so this event forms its own block in SplitBlocks.

func (*FileStore) LogCooldownRecord added in v0.2.31

func (f *FileStore) LogCooldownRecord(profile, action string, seconds int) error

LogCooldownRecord records that a cooldown timer was started/updated for an action. Unlike LogCooldown, this writes a single "\n" (not "\n\n"), so the line merges into the same SplitBlocks block as the execution entry that follows it. This is intentional: cooldown=recorded events are grouped with the execution they precede, keeping related log lines together.

func (*FileStore) LogSilent added in v0.2.31

func (f *FileStore) LogSilent(profile, action string) error

LogSilent records that an invocation was suppressed by silent mode.

func (*FileStore) LogSilentDisable added in v0.2.31

func (f *FileStore) LogSilentDisable() error

LogSilentDisable records that silent mode was turned off.

func (*FileStore) LogSilentEnable added in v0.2.31

func (f *FileStore) LogSilentEnable(d time.Duration) error

LogSilentEnable records that silent mode was turned on for duration d.

func (*FileStore) Path added in v0.2.31

func (f *FileStore) Path() string

Path returns the filesystem path of the log file.

func (*FileStore) ReadContent added in v0.2.31

func (f *FileStore) ReadContent() (string, error)

ReadContent returns the raw text content of the log file. Returns "" with no error if the file does not exist.

func (*FileStore) RemoveProfile added in v0.2.31

func (f *FileStore) RemoveProfile(name string) (int, error)

RemoveProfile deletes all log blocks belonging to the named profile. Returns the number of blocks removed.

func (*FileStore) VoiceLines added in v0.2.31

func (f *FileStore) VoiceLines(days int) ([]VoiceLine, error)

VoiceLines scans the log for TTS step detail lines and returns unique texts with their usage counts. Pass days=0 for all history.

type HourProfile added in v0.2.21

type HourProfile struct {
	Hour    int
	Profile string
}

HourProfile is the key for per-cell hourly data.

type HourlyData added in v0.2.21

type HourlyData struct {
	Profiles      []string
	PerCell       map[HourProfile]int
	PerHour       map[int]int
	ProfileTotals []int // parallel to Profiles
	MinHour       int
	MaxHour       int
	GrandTotal    int
}

HourlyData holds computed hourly breakdown data for a single day.

func ComputeHourly added in v0.2.21

func ComputeHourly(entries []Entry, targetDate time.Time, loc *time.Location) HourlyData

ComputeHourly computes per-hour activity breakdown for a target date. Only entries matching targetDate in the given location are included. Entries with KindOther are excluded.

type SQLiteStore added in v0.2.31

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

SQLiteStore implements Store using a SQLite database.

func NewSQLiteStore added in v0.2.31

func NewSQLiteStore(path string) (*SQLiteStore, error)

NewSQLiteStore opens (or creates) a SQLite database at path, creates tables and indexes, and performs one-time migration from notify.log if it exists in the same directory.

func (*SQLiteStore) Clean added in v0.2.31

func (s *SQLiteStore) Clean(days int) (int, error)

Clean deletes events older than N calendar days. Returns the number of deleted rows. Associated step_details rows are automatically removed via ON DELETE CASCADE on the event_id foreign key.

func (*SQLiteStore) Clear added in v0.2.31

func (s *SQLiteStore) Clear() error

Clear deletes all events. Step_details rows are automatically removed via ON DELETE CASCADE when their parent events are deleted.

func (*SQLiteStore) Close added in v0.2.31

func (s *SQLiteStore) Close() error

Close closes the database connection.

func (*SQLiteStore) Entries added in v0.2.31

func (s *SQLiteStore) Entries(days int) ([]Entry, error)

Entries returns parsed execution/cooldown/silent entries, optionally filtered to the last N calendar days. Pass days=0 for all entries.

func (*SQLiteStore) EntriesSince added in v0.2.31

func (s *SQLiteStore) EntriesSince(cutoff time.Time) ([]Entry, error)

EntriesSince returns entries with timestamps at or after cutoff. Same profile/action filter as Entries for consistency.

func (*SQLiteStore) Log added in v0.2.31

func (s *SQLiteStore) Log(action string, steps []config.Step, afk bool, vars tmpl.Vars, desktop *int) error

Log inserts an execution event into the events table, then inserts one step_details row per step in the same transaction. For voice-capable step types (say, discord_voice, telegram_audio, telegram_voice), the expanded text is stored in voice_text for later frequency queries.

func (*SQLiteStore) LogCooldown added in v0.2.31

func (s *SQLiteStore) LogCooldown(profile, action string, seconds int) error

LogCooldown records that an invocation was skipped due to cooldown. Uses KindCooldown so the event appears in Entries/EntriesSince queries (which filter on profile != ” AND action != ”).

func (*SQLiteStore) LogCooldownRecord added in v0.2.31

func (s *SQLiteStore) LogCooldownRecord(profile, action string, seconds int) error

LogCooldownRecord records that a cooldown timer was started/updated. Uses KindOther (not KindCooldown) so these events are excluded from history queries -- Entries/EntriesSince filter on profile != ” AND action != ”, but KindOther entries with profile/action set still pass that filter. The key difference is that KindOther events are skipped by SummarizeByDay and other aggregation functions, preventing cooldown-recorded events from inflating execution or skip counts.

func (*SQLiteStore) LogSilent added in v0.2.31

func (s *SQLiteStore) LogSilent(profile, action string) error

LogSilent records that an invocation was suppressed by silent mode.

func (*SQLiteStore) LogSilentDisable added in v0.2.31

func (s *SQLiteStore) LogSilentDisable() error

LogSilentDisable records that silent mode was turned off.

func (*SQLiteStore) LogSilentEnable added in v0.2.31

func (s *SQLiteStore) LogSilentEnable(d time.Duration) error

LogSilentEnable records that silent mode was turned on for duration d. No profile/action is set, so this event is excluded from Entries queries.

func (*SQLiteStore) Path added in v0.2.31

func (s *SQLiteStore) Path() string

Path returns the filesystem path of the SQLite database file.

func (*SQLiteStore) ReadContent added in v0.2.31

func (s *SQLiteStore) ReadContent() (string, error)

ReadContent reconstructs the flat-file log format from SQL rows. This provides backward compatibility with code that expects text-based log parsing (e.g. historyExport, ParseVoiceLines in FileStore mode). The output matches what FileStore.Log/LogCooldown/etc. would have written.

func (*SQLiteStore) RemoveProfile added in v0.2.31

func (s *SQLiteStore) RemoveProfile(name string) (int, error)

RemoveProfile deletes all events for the named profile. Step details are cascade-deleted. Returns the number of removed event rows.

func (*SQLiteStore) VoiceLines added in v0.2.31

func (s *SQLiteStore) VoiceLines(days int) ([]VoiceLine, error)

VoiceLines queries the step_details table for distinct TTS texts and their usage counts. Uses the partial index on voice_text (WHERE voice_text IS NOT NULL) for efficient lookups. Pass days=0 for all history.

type Store added in v0.2.31

type Store interface {
	// Write — returns error for correctness; FileStore prints to stderr (best-effort).
	Log(action string, steps []config.Step, afk bool, vars tmpl.Vars, desktop *int) error
	LogCooldown(profile, action string, seconds int) error
	LogCooldownRecord(profile, action string, seconds int) error
	LogSilent(profile, action string) error
	LogSilentEnable(d time.Duration) error
	LogSilentDisable() error

	// Read
	Entries(days int) ([]Entry, error)              // parsed entries, 0 = all
	EntriesSince(cutoff time.Time) ([]Entry, error) // entries after cutoff
	VoiceLines(days int) ([]VoiceLine, error)       // TTS text frequency
	ReadContent() (string, error)                   // raw log text

	// Maintenance
	Clean(days int) (int, error)            // remove old entries, return removed count
	RemoveProfile(name string) (int, error) // remove profile entries, return removed count
	Clear() error                           // delete all data

	// Metadata
	Path() string
}

Store abstracts event log storage. Implementations: FileStore (flat log file) and SQLiteStore (indexed SQL queries). Selected via config "storage" option.

var Default Store

Default is the package-level Store used by the convenience functions below. Set by OpenDefault() at program startup, or overridden in tests.

type TimeSpentData added in v0.2.21

type TimeSpentData struct {
	Profiles []TimeSpentProfile
	Total    int
}

TimeSpentData holds approximate time spent across all profiles.

func ComputeTimeSpent added in v0.2.21

func ComputeTimeSpent(entries []Entry, targetDate time.Time, loc *time.Location) TimeSpentData

ComputeTimeSpent estimates approximate time spent per profile on a target date. It walks consecutive entry timestamps per profile; gaps of 5 minutes or less are counted as active time.

type TimeSpentProfile added in v0.2.21

type TimeSpentProfile struct {
	Name    string
	Seconds int
}

TimeSpentProfile holds approximate time spent for one profile.

type VoiceLine added in v0.2.23

type VoiceLine struct {
	Text  string
	Count int
}

VoiceLine holds a unique voice step text and its usage count.

func ParseVoiceLines added in v0.2.23

func ParseVoiceLines(content string) []VoiceLine

ParseVoiceLines scans log content for TTS step detail lines and returns unique texts sorted by frequency (descending), then alphabetically.

func VoiceLines added in v0.2.31

func VoiceLines(days int) ([]VoiceLine, error)

VoiceLines returns TTS text frequency data, optionally filtered to the last N days (0 = all).

Jump to

Keyboard shortcuts

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