Class: Textus::Infra::AuditLog
- Inherits:
-
Object
- Object
- Textus::Infra::AuditLog
- Defined in:
- lib/textus/infra/audit_log.rb
Constant Summary collapse
- DEFAULT_MAX_SIZE =
10_485_760- DEFAULT_KEEP =
5
Instance Method Summary collapse
- #append(role:, verb:, key:, etag_before:, etag_after:, extras: nil) ⇒ Object
-
#initialize(root, max_size: DEFAULT_MAX_SIZE, keep: DEFAULT_KEEP) ⇒ AuditLog
constructor
A new instance of AuditLog.
- #last_writer_for(key) ⇒ Object
- #latest_seq ⇒ Object
- #min_available_seq ⇒ Object
-
#verify_integrity ⇒ Object
Returns an array of integrity-violation descriptors for the on-disk log.
Constructor Details
#initialize(root, max_size: DEFAULT_MAX_SIZE, keep: DEFAULT_KEEP) ⇒ AuditLog
Returns a new instance of AuditLog.
11 12 13 14 15 16 |
# File 'lib/textus/infra/audit_log.rb', line 11 def initialize(root, max_size: DEFAULT_MAX_SIZE, keep: DEFAULT_KEEP) @root = root @path = File.join(root, "audit.log") @max_size = max_size @keep = keep end |
Instance Method Details
#append(role:, verb:, key:, etag_before:, etag_after:, extras: nil) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/textus/infra/audit_log.rb', line 56 def append(role:, verb:, key:, etag_before:, etag_after:, extras: nil) File.open(@path, File::WRONLY | File::APPEND | File::CREAT, 0o644) do |f| f.flock(File::LOCK_EX) next_seq = current_max_seq_unlocked + 1 row = assemble_row(next_seq, { role: role, verb: verb, key: key, etag_before: etag_before, etag_after: etag_after }, extras) f.write(JSON.generate(row) + "\n") f.flush rotate!(f) if f.size > @max_size end end |
#last_writer_for(key) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/textus/infra/audit_log.rb', line 18 def last_writer_for(key) return nil unless File.exist?(@path) last_role = nil File.foreach(@path) do |line| parsed = parse_row(line.chomp) next unless parsed next unless parsed["key"] == key next unless %w[put delete].include?(parsed["verb"]) last_role = parsed["role"] end last_role end |
#latest_seq ⇒ Object
33 34 35 36 37 38 39 40 41 |
# File 'lib/textus/infra/audit_log.rb', line 33 def latest_seq return scan_max_seq(@path) if File.exist?(@path) && File.size(@path).positive? # Active log is empty/missing — consult the most recent rotated file's sidecar. = (1) return ["max_seq"] if 0 end |
#min_available_seq ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/textus/infra/audit_log.rb', line 43 def min_available_seq = (1..@keep).map { |n| (n) }.compact if .any? .map { |m| m["min_seq"] }.min elsif File.exist?(@path) File.foreach(@path) do |line| parsed = parse_row(line.chomp) return parsed["seq"] if parsed && parsed["seq"] end nil end end |
#verify_integrity ⇒ Object
Returns an array of integrity-violation descriptors for the on-disk log. Each entry is { “lineno” => Integer, “reason” => String, “detail” => String }. Empty array means the log is well-formed (or doesn’t exist yet).
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/textus/infra/audit_log.rb', line 71 def verify_integrity return [] unless File.exist?(@path) out = [] File.foreach(@path).with_index(1) do |line, lineno| violation = check_line(line.chomp, lineno) out << violation if violation end out end |