Skip to content

feat(state): add incremental state persistence via --state flag#1

Open
PseudoSky wants to merge 1 commit into
probelabs:mainfrom
PseudoSky:feat/state-persistence
Open

feat(state): add incremental state persistence via --state flag#1
PseudoSky wants to merge 1 commit into
probelabs:mainfrom
PseudoSky:feat/state-persistence

Conversation

@PseudoSky

@PseudoSky PseudoSky commented Apr 25, 2026

Copy link
Copy Markdown

Motivation

Added to enable incremental log analysis across repeated invocations. Each run processes only new content and updates a persistent state file with cluster templates and temporal baselines, so anomaly detection improves over time without reprocessing old data.

Example: passive shell command monitoring via zsh preexec

Add to ~/.zshrc:

STATE_FILE="$HOME/.logoscope/shell.state.json"
preexec() {
  echo "$3" | npx @probelabs/logoscope - \
    --state "$STATE_FILE" \
    --max-patterns 500 \
    --state-max-age 7d
}

preexec fires before every command with the command string as $3. Each invocation sends one line to logoscope; the state file accumulates pattern history so the first time an unusual command class appears it surfaces as a new_pattern anomaly.

Summary

  • Adds --state <file> flag for incremental log processing — on each run, logoscope seeks to the saved byte offset (file mode) or loads historical baselines (stdin mode) and processes only new content
  • Persists Drain cluster templates, param baselines, and temporal baselines across runs; DrainTree is reconstructed from saved templates via insert_masked replay (drain_rs v0.3 is not serde-compatible)
  • Rate-spike detection: when --state is loaded, current events/hr per template is compared against historical baseline; ≥3× ratio emits a rate_spike temporal anomaly
  • Inode-based log rotation detection: saves file inode in state, resets offset on inode change so rotated logs are read from the start

New flags

Flag Requires Description
--state <file> Path to JSON state checkpoint
--no-update-state --state Read-only mode; skip saving updated state
--state-max-age <dur> --state Evict clusters older than duration (e.g. 7d, 24h)

Files changed

  • logoscope/src/state.rs — new module: StateFile, PersistedCluster, DrainConfig, TemporalBaseline, parse_duration, atomic save, eviction/cap logic
  • logoscope/src/ai.rsSummarizeOpts gains historical_rates; new summarize_lines_with_preseeded_drain; rate-spike detection in temporal analysis
  • logoscope/src/bin/logoscope.rs--state CLI wiring, byte-offset seek with inode rotation detection, seed DrainAdapter construction, state merge/save
  • logoscope/src/drain_adapter.rs — expose cluster_count()
  • logoscope/src/lib.rs — add pub mod state
  • logoscope/tests/ai_anomalies_output_tests.rs — rate-spike detection tests
  • logoscope/tests/drain_adapter_tests.rs — template continuity test for pre-seeded drain
  • logoscope/tests/state_rotation_tests.rs — integration tests for inode-based rotation detection

Test plan

  • cargo test passes (all new tests green)
  • First run with --state /tmp/s.json creates the file; second run on same file seeks to offset and skips old lines
  • --state-max-age 1s evicts all clusters on a file not touched in >1s
  • Rate-spike anomaly appears when current rate is ≥3× historical baseline
  • --no-update-state leaves state file unchanged after a run
  • Rename+create rotation detected via inode; new file read from byte 0

Adds checkpoint-based incremental processing so repeated invocations
on a growing log file only parse new lines (O(n_new) instead of O(n_total)).

New CLI flags:
  --state <FILE>          Load/save analysis checkpoint (byte offset + Drain model)
  --no-update-state       Load checkpoint read-only without advancing it
  --state-max-age <DUR>   Evict clusters not seen within duration at save time (e.g. 7d, 24h)

--max-patterns (existing) now also caps the state file cluster count when --state is active.

State file (JSON) persists: byte offset for seek-based incremental reads, Drain cluster
templates with first/last_seen timestamps, param_baselines, temporal_baselines, and
drain_config including max_patterns so subsequent runs honour the saved ceiling.

On load, saved templates are replayed into a fresh DrainTree to preserve cluster
continuity across runs. Anomaly detection uses loaded baselines: known templates flag
new patterns; historical_rates (events/hr per template) trigger rate-spike anomalies
when current rate exceeds 3x the baseline.

Handles: log rotation (file shorter than offset → fresh run), version mismatch
(ignores stale checkpoint), stdin mode (offset tracking disabled with info message),
atomic saves (tmp file + rename to prevent corruption on mid-write kill).

New module: src/state.rs — StateFile, PersistedCluster, DrainConfig,
TemporalBaseline, parse_duration(), evict_stale(), cap_clusters(), save_atomic().

Tests added for rate-spike detection and DrainTree template continuity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant