Class: Alplus::PendingWindow

Inherits:
Object
  • Object
show all
Defined in:
lib/alplus/pending_window.rb

Overview

The post-error log window (issue #47): a built exception item is held here for a short, bounded window before delivery, and log-line breadcrumbs recorded on the SAME thread during the window are appended to it, marked data: { after_error: true }.

Same-thread attribution is the correctness rule: under a concurrent server, another request's log lines must never pollute this error's timeline. Rails logs the unhandled exception (DebugExceptions) on the request thread AFTER RackMiddleware re-raises, which is exactly the window this class keeps open — a request-end seal would run too early to see that line.

One lazily-spawned sealer thread sleeps until the earliest deadline and seals due entries; it exits when the list drains and respawns on the next hold. seal_all! (called by Client#flush/#close) seals everything immediately — the window delays delivery, never loses events.

Defined Under Namespace

Classes: Entry

Constant Summary collapse

MAX_AFTER_ERROR_BREADCRUMBS =
20
MAX_TOTAL_BREADCRUMBS =

The server ceiling (Envelope::SERVER_MAX_BREADCRUMBS).

Envelope::SERVER_MAX_BREADCRUMBS

Instance Method Summary collapse

Constructor Details

#initialize(window_ms, &deliver) ⇒ PendingWindow

Returns a new instance of PendingWindow.



27
28
29
30
31
32
33
# File 'lib/alplus/pending_window.rb', line 27

def initialize(window_ms, &deliver)
  @window_ms = window_ms
  @deliver = deliver
  @entries = []
  @mutex = Mutex.new
  @sealer = nil
end

Instance Method Details

#enabled?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'lib/alplus/pending_window.rb', line 35

def enabled?
  @window_ms.positive?
end

#hold(item) ⇒ Object



39
40
41
42
43
# File 'lib/alplus/pending_window.rb', line 39

def hold(item)
  entry = Entry.new(item: item, thread: Thread.current, deadline: monotonic_now + (@window_ms / 1000.0), appended: 0)
  @mutex.synchronize { @entries << entry }
  ensure_sealer_running
end

#notify_log_breadcrumb(crumb) ⇒ Object

Appends a log-line breadcrumb to every pending entry captured on the calling thread, within the per-entry and total bounds. Never raises.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/alplus/pending_window.rb', line 47

def notify_log_breadcrumb(crumb)
  @mutex.synchronize do
    @entries.each do |entry|
      next unless entry.thread == Thread.current
      next if entry.appended >= MAX_AFTER_ERROR_BREADCRUMBS

      crumbs = (entry.item[:breadcrumbs] ||= [])
      next if crumbs.length >= MAX_TOTAL_BREADCRUMBS

      crumbs << crumb.merge(data: (crumb[:data] || {}).merge(after_error: true))
      entry.appended += 1
    end
  end
  nil
rescue StandardError
  nil
end

#seal_all!Object



65
66
67
68
# File 'lib/alplus/pending_window.rb', line 65

def seal_all!
  entries = @mutex.synchronize { @entries.slice!(0..) }
  entries.each { |entry| @deliver.call(entry.item) }
end