Module: Twin::Journal
- Defined in:
- lib/twin/journal.rb
Overview
Append-only sync journal: one JSON line per synced job in ~/.local/state/twin/log.jsonl. Answers "did yesterday's sync actually run, and what did it do?" — and stays machine-readable (jq/grubber). Journal failures never break a sync; they degrade to a warning.
Class Method Summary collapse
- .log_path ⇒ Object
-
.record(job, success:, transferred:, output: nil) ⇒ Object
Record one job result.
- .state_dir ⇒ Object
-
.tail(n) ⇒ Object
Last n entries, oldest first.
Class Method Details
.log_path ⇒ Object
17 |
# File 'lib/twin/journal.rb', line 17 def log_path = File.join(state_dir, "log.jsonl") |
.record(job, success:, transferred:, output: nil) ⇒ Object
Record one job result. Dry-runs are not journaled.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/twin/journal.rb', line 20 def record(job, success:, transferred:, output: nil) entry = { ts: Time.now.iso8601, program: job.program, path: job.path, target: job.target, ok: success, changed: transferred, } unless success entry[:error] = output.to_s.lines.map(&:strip).reject(&:empty?).last.to_s[0, 200] end FileUtils.mkdir_p(state_dir) File.open(log_path, "a") { |f| f.puts(JSON.generate(entry)) } rescue SystemCallError => e warn "journal: #{e.}" unless @warned @warned = true end |
.state_dir ⇒ Object
13 14 15 |
# File 'lib/twin/journal.rb', line 13 def state_dir ENV["TWIN_STATE_DIR"] || File.join(Dir.home, ".local", "state", "twin") end |
.tail(n) ⇒ Object
Last n entries, oldest first. Unparseable lines are skipped.
40 41 42 43 44 45 46 47 |
# File 'lib/twin/journal.rb', line 40 def tail(n) return [] unless File.exist?(log_path) File.readlines(log_path).last(n).filter_map do |line| JSON.parse(line) rescue JSON::ParserError nil end end |