rails_event_viewer 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26f6b1798d95bf71759a16da1fd092f8d8296a4118378a8c5ce84e12848efe28
4
- data.tar.gz: b290b0d5803b71aa2f8fda06ee160870672a4073c175c2bb6a0e0856f19b2dbc
3
+ metadata.gz: 5d1fae32fbd34187f3531ab552271d62005f6c8877bd814ca08207053d349178
4
+ data.tar.gz: 05b561609b0ad4d7d7065e61e0a67891a8d9b4f2b970366869097a38a4d17293
5
5
  SHA512:
6
- metadata.gz: ef7fc01c18016ff1f9d48de98fcc1f42b248ac1fea588806c6dc3a409b12f75291e4165ee59ff45220b93a024f6b816d7cb2dba4395c2b5c441dd6f323a8a17a
7
- data.tar.gz: 896dd1e41a71c0c0bd25dbdd0a376ffd7c5c0c68b8d7d6cf445521127560d95ba0d351e1d9f9f4c6c5d73ed00ae6472dbd9096ceddf2d79cd8b1dbf4e3edb37f
6
+ metadata.gz: 38ee32bc44c4fd20adf78b42e8d9110c1328615fe0072bfe00325d9c76fc7eba4ed3f909a98cabf0e6c345b4f8fb64cca6802255c5c7ab3721731bef3bfc91ee
7
+ data.tar.gz: 0040bd1d2ce832bc80e35cac7248bc6461375f703f3ae299913264b43955d07e2b8a947bb0b9b349128f0aa45d352a5e5cc4b52401554943141e3fe5eec06962
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 (2026-10-01)
4
+
5
+ - Forked workers no longer duplicate the parent's buffered events on exit.
6
+ - Payload, tags and context are snapshotted at emit time as JSON values (`Time` becomes a string).
7
+ - Unserializable payloads are stored via `inspect` instead of raising.
8
+ - The flusher writes inside the Rails executor.
9
+ - Shutdown has a deadline and logs buffered and dropped events.
10
+
3
11
  ## 0.2.0 (2026-10-01)
4
12
 
5
13
  - Pluggable buffers: `config.buffer` accepts any object implementing `RailsEventViewer::Buffer` (`push`, `drain`, `commit`, `revert`, `dead`, `size`, `after_fork`). `push` returns the new size. The in-memory buffer remains the default.
data/README.md CHANGED
@@ -63,7 +63,7 @@ RailsEventViewer.configure do |config|
63
63
  end
64
64
  ```
65
65
 
66
- With `async`, events are written by a background thread, never by your web requests. The buffer holds at most 10 times `buffer_size` events. If storage can't keep up, the oldest are dropped and a warning is logged. Events still in the buffer are written on a normal shutdown but lost if the process is killed.
66
+ With `async`, events are written by a background thread, never by your web requests. The buffer holds at most 10 times `buffer_size` events. If storage can't keep up, the oldest are dropped and a warning is logged. On a normal shutdown, buffered events are written for up to 5 seconds. Anything left is logged and lost. If the process is killed, the whole buffer is lost.
67
67
 
68
68
  Internal Rails events such as `active_record.*` and `action_controller.*` are ignored by default. Remove a pattern from `ignored_events` to capture it.
69
69
 
@@ -17,7 +17,7 @@ module RailsEventViewer
17
17
  @strikes = Hash.new(0)
18
18
  end
19
19
 
20
- def write(events)
20
+ def write(events, split: true)
21
21
  RailsEventViewer.adapter.write_events(events)
22
22
  @buffer.commit(events)
23
23
  forget(events)
@@ -26,7 +26,7 @@ module RailsEventViewer
26
26
  rescue => e
27
27
  log_error("[RailsEventViewer] Failed to write #{events.size} events: #{e.message}")
28
28
  @failed_attempts += 1
29
- return write_individually(events) if @failed_attempts >= MAX_ATTEMPTS
29
+ return write_individually(events) if split && @failed_attempts >= MAX_ATTEMPTS
30
30
 
31
31
  @buffer.revert(events)
32
32
  false
@@ -13,6 +13,7 @@ module RailsEventViewer
13
13
  @thread = nil
14
14
  @shutdown_requested = false
15
15
  @dropped_count = 0
16
+ @writing = 0
16
17
  @pid = Process.pid
17
18
  end
18
19
 
@@ -28,23 +29,34 @@ module RailsEventViewer
28
29
  end
29
30
 
30
31
  def flush!
31
- events = @buffer.drain([@buffer.size, max_buffer_size].min)
32
- return true if events.empty?
33
-
34
- written = @writer.write(events)
35
- report_dropped_events if written
36
- written
32
+ handle_fork_if_needed
33
+ return true if @buffer.size.zero?
34
+
35
+ Rails.application.executor.wrap(source: "application.rails_event_viewer") do
36
+ events = @buffer.drain([@buffer.size, max_buffer_size].min)
37
+ next true if events.empty?
38
+
39
+ @writing = events.size
40
+ written = @writer.write(events, split: !shutdown_requested?)
41
+ report_dropped_events if written
42
+ written
43
+ ensure
44
+ @writing = 0
45
+ end
37
46
  rescue => e
38
47
  log_error("[RailsEventViewer] Failed to flush: #{e.message}")
39
48
  false
40
49
  end
41
50
 
42
51
  def stop!
52
+ handle_fork_if_needed
53
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + shutdown_timeout
43
54
  @mutex.synchronize { @shutdown_requested = true }
44
55
 
45
56
  if @thread&.alive?
46
57
  @flush_requests << true
47
- @thread.join(SHUTDOWN_TIMEOUT_SECONDS)
58
+ @thread.join(shutdown_timeout)
59
+ effective_logger&.warn("[RailsEventViewer] Flusher thread still busy after #{shutdown_timeout}s writing #{@writing} events") if @thread.alive?
48
60
  end
49
61
 
50
62
  @mutex.synchronize { @thread = nil }
@@ -52,10 +64,15 @@ module RailsEventViewer
52
64
  loop do
53
65
  remaining = @buffer.size
54
66
  break if remaining.zero?
67
+ break if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
55
68
 
56
69
  flush!
57
- break if @buffer.size >= remaining
70
+ break if @buffer.size >= remaining # no progress, stop retrying
58
71
  end
72
+
73
+ report_dropped_events
74
+ leftover = @buffer.size
75
+ effective_logger&.warn("[RailsEventViewer] #{leftover} events still buffered at shutdown") if leftover.positive?
59
76
  end
60
77
 
61
78
  def running?
@@ -82,6 +99,10 @@ module RailsEventViewer
82
99
  end
83
100
  end
84
101
 
102
+ def shutdown_timeout
103
+ SHUTDOWN_TIMEOUT_SECONDS
104
+ end
105
+
85
106
  def max_buffer_size
86
107
  RailsEventViewer.buffer_size * MAX_BUFFER_MULTIPLIER
87
108
  end
@@ -98,7 +119,11 @@ module RailsEventViewer
98
119
  end
99
120
 
100
121
  def report_dropped_events
101
- dropped = @mutex.synchronize { @dropped_count.tap { @dropped_count = 0 } }
122
+ dropped = @mutex.synchronize do
123
+ count = @dropped_count
124
+ @dropped_count = 0
125
+ count
126
+ end
102
127
  return if dropped.zero?
103
128
 
104
129
  effective_logger&.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
@@ -82,9 +82,9 @@ module RailsEventViewer
82
82
 
83
83
  {
84
84
  name: event[:name],
85
- payload: serialize_payload(event[:payload]),
86
- tags: event[:tags] || {},
87
- context: event[:context] || {},
85
+ payload: snapshot(serialize_payload(event[:payload])),
86
+ tags: snapshot(event[:tags] || {}),
87
+ context: snapshot(event[:context] || {}),
88
88
  source_file: event.dig(:source_location, :filepath),
89
89
  source_line: event.dig(:source_location, :lineno),
90
90
  source_label: event.dig(:source_location, :label),
@@ -92,6 +92,13 @@ module RailsEventViewer
92
92
  }
93
93
  end
94
94
 
95
+ def snapshot(value)
96
+ json = value.as_json
97
+ json.is_a?(Hash) ? json.deep_symbolize_keys : json
98
+ rescue SystemStackError, StandardError
99
+ { value: value.inspect }
100
+ end
101
+
95
102
  def serialize_payload(payload)
96
103
  case payload
97
104
  when Hash
@@ -1,3 +1,3 @@
1
1
  module RailsEventViewer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_event_viewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keshav Biswa