dead_bro 0.2.12 → 0.2.14

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: 56f6a72ccfa1a5e362c74eddace39a1ffeba97d06bab551f38d9091311609770
4
- data.tar.gz: 20ee34708c5eed8ac00af8d462953422ba7a24e3b9701e56e31eea8ba35752f0
3
+ metadata.gz: 3f7618eda689dd41bbb5c71cdff40619e5857a71b31a182f234064d89197d79c
4
+ data.tar.gz: fbcac3f8c189834690a5a75e013f07cec20044f4e74285139aa22f66ae0cf65d
5
5
  SHA512:
6
- metadata.gz: 932ce6601969ac9f95ba0ce327b93a5412c167a4c7fe367d0c14bd05c5c751b69bb3da48eaf829e73bcf6ca10c66fd7678ff0caca4eba7e0ec42a3b4bae72e54
7
- data.tar.gz: 032ee4230c5b043a7ef8cf176963500eb60157927ad18f18c8e657f2439b31ab8b3526e7dd90036f79ec5fc7e978b55de540fcad14925f1fb33ffb6308379964
6
+ metadata.gz: 68864135e9fd51e65293ee369f1002e6f37341627fd674d570781fa163dd5b2345a4e7421356f49bc2bc00c1b8f37946b7fe9d31ff0370a6781f892392c7ec5d
7
+ data.tar.gz: 5e72422ba6297af9ef9a521e675d832dd94a0f81783ae964be53ecdd16eaa96dd4bc55448baceb697ea1b0b4bfd024090009d43dd87a3893f197df607cc08fd4
@@ -15,11 +15,12 @@ module DeadBro
15
15
  def post_metric(event_name:, payload:, force: false)
16
16
  return if @configuration.api_key.nil?
17
17
  return unless @configuration.enabled
18
+ return if @configuration.skip_tracking?
18
19
  return if !force && !@configuration.should_sample?
19
20
  return if circuit_open?
20
21
 
21
22
  payload = truncate_payload_for_request(payload)
22
- body = {event: event_name, payload: payload, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id}
23
+ body = {event: event_name, payload: payload, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id, gem_version: DeadBro::VERSION}
23
24
 
24
25
  dispatch_request(
25
26
  url: metrics_endpoint_url,
@@ -35,7 +36,7 @@ module DeadBro
35
36
  return if @configuration.api_key.nil?
36
37
 
37
38
  @configuration.last_heartbeat_attempt_at = Time.now.utc
38
- body = {event: "heartbeat", payload: {}, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id}
39
+ body = {event: "heartbeat", payload: {}, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id, gem_version: DeadBro::VERSION}
39
40
 
40
41
  dispatch_request(
41
42
  url: metrics_endpoint_url,
@@ -50,10 +51,11 @@ module DeadBro
50
51
  def post_monitor_stats(payload)
51
52
  return if @configuration.api_key.nil?
52
53
  return unless @configuration.enabled
54
+ return if @configuration.skip_tracking?
53
55
  return unless @configuration.job_queue_monitoring_enabled
54
56
  return if circuit_open?
55
57
 
56
- body = {payload: payload, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id}
58
+ body = {payload: payload, sent_at: Time.now.utc.iso8601, revision: @configuration.resolve_deploy_id, gem_version: DeadBro::VERSION}
57
59
 
58
60
  dispatch_request(
59
61
  url: monitor_endpoint_url,
@@ -140,6 +142,12 @@ module DeadBro
140
142
  @configuration.last_heartbeat_at = Time.now.utc
141
143
  end
142
144
  end
145
+
146
+ if response.is_a?(Net::HTTPSuccess)
147
+ @configuration.skip_until = nil
148
+ elsif response.is_a?(Net::HTTPInsufficientStorage)
149
+ @configuration.skip_until = Time.now.utc + DeadBro::Configuration::METRICS_BACKEND_SKIP_AFTER_507_SECONDS
150
+ end
143
151
  elsif @circuit_breaker && @configuration.circuit_breaker_enabled
144
152
  @circuit_breaker.record_failure
145
153
  end
@@ -22,6 +22,10 @@ module DeadBro
22
22
  # Tracks when we last received settings from the backend (in-memory only)
23
23
  attr_accessor :settings_received_at
24
24
 
25
+ # After HTTP 507 Insufficient Storage from the API, skip all tracking until this
26
+ # UTC time (in-memory only). Cleared on the next successful API response.
27
+ attr_accessor :skip_until
28
+
25
29
  # Last successful heartbeat HTTP response time while disabled (in-memory only)
26
30
  attr_accessor :last_heartbeat_at
27
31
 
@@ -30,6 +34,8 @@ module DeadBro
30
34
 
31
35
  HEARTBEAT_INTERVAL = 60 # seconds
32
36
 
37
+ METRICS_BACKEND_SKIP_AFTER_507_SECONDS = 600 # 10 minutes
38
+
33
39
  # First non-empty ENV value wins for release/revision payloads and deploy grouping on the server.
34
40
  # Order is roughly: DeadBro-native → common CI/hosting → observability tooling.
35
41
  DEPLOY_REVISION_ENV_KEYS = %w[
@@ -87,6 +93,7 @@ module DeadBro
87
93
  @enable_system_stats = false
88
94
 
89
95
  @settings_received_at = nil
96
+ @skip_until = nil
90
97
  @last_heartbeat_at = nil
91
98
  @last_heartbeat_attempt_at = nil
92
99
  @settings_mutex = Mutex.new
@@ -152,6 +159,13 @@ module DeadBro
152
159
  last_heartbeat_attempt_at.nil? || (Time.now.utc - last_heartbeat_attempt_at) >= HEARTBEAT_INTERVAL
153
160
  end
154
161
 
162
+ def skip_tracking?
163
+ t = skip_until
164
+ return false unless t
165
+
166
+ Time.now.utc < t
167
+ end
168
+
155
169
  def resolve_deploy_id
156
170
  explicit = @explicit_deploy_revision&.to_s&.strip
157
171
  return explicit unless explicit.nil? || explicit.empty?
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeadBro
4
+ module DbConnectionSubscriber
5
+ WAIT_KEY = :dead_bro_db_connection_wait_ms
6
+ COUNT_KEY = :dead_bro_db_connection_checkouts
7
+
8
+ # Prepended onto ConnectionPool so every checkout is timed.
9
+ # Only accumulates when a request is being tracked (thread-local is a Numeric).
10
+ module CheckoutInstrumentation
11
+ def checkout(*args)
12
+ return super unless Thread.current[DbConnectionSubscriber::WAIT_KEY].is_a?(Numeric)
13
+
14
+ # Initialize conn before calling super so the rescue block can tell whether
15
+ # checkout succeeded before timing code raised (avoids double-checkout).
16
+ conn = nil
17
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
+ conn = super
19
+ Thread.current[DbConnectionSubscriber::WAIT_KEY] += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000.0
20
+ Thread.current[DbConnectionSubscriber::COUNT_KEY] += 1
21
+ conn
22
+ rescue
23
+ conn || super
24
+ end
25
+ end
26
+
27
+ def self.install!
28
+ return unless defined?(ActiveRecord::ConnectionAdapters::ConnectionPool)
29
+ return if ActiveRecord::ConnectionAdapters::ConnectionPool.ancestors.include?(CheckoutInstrumentation)
30
+
31
+ ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(CheckoutInstrumentation)
32
+ rescue StandardError => e
33
+ warn "[DeadBro] DbConnectionSubscriber install failed: #{e.class}: #{e.message}"
34
+ end
35
+
36
+ def self.start_request_tracking
37
+ Thread.current[WAIT_KEY] = 0.0
38
+ Thread.current[COUNT_KEY] = 0
39
+ end
40
+
41
+ def self.stop_request_tracking
42
+ wait_ms = Thread.current[WAIT_KEY]
43
+ checkouts = Thread.current[COUNT_KEY]
44
+ Thread.current[WAIT_KEY] = nil
45
+ Thread.current[COUNT_KEY] = nil
46
+ { wait_ms: wait_ms&.round(2), checkouts: checkouts }
47
+ end
48
+ end
49
+ end
@@ -5,6 +5,8 @@ module DeadBro
5
5
  def self.subscribe!
6
6
  # Start SQL tracking when a job begins - use the start event, not the complete event
7
7
  ActiveSupport::Notifications.subscribe("perform_start.active_job") do |name, started, finished, _unique_id, data|
8
+ next if DeadBro.configuration.skip_tracking?
9
+
8
10
  # Clear logs for this job
9
11
  DeadBro.logger.clear
10
12
 
@@ -22,6 +24,11 @@ module DeadBro
22
24
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
23
25
  DeadBro::MemoryTrackingSubscriber.start_request_tracking
24
26
  end
27
+
28
+ # Start DB connection pool wait tracking
29
+ if defined?(DeadBro::DbConnectionSubscriber)
30
+ DeadBro::DbConnectionSubscriber.start_request_tracking
31
+ end
25
32
  end
26
33
  rescue
27
34
  # Never raise from instrumentation install
@@ -15,6 +15,11 @@ module DeadBro
15
15
  # Track job execution
16
16
  ActiveSupport::Notifications.subscribe(JOB_EVENT_NAME) do |name, started, finished, _unique_id, data|
17
17
  begin
18
+ if DeadBro.configuration.skip_tracking?
19
+ drain_job_tracking
20
+ next
21
+ end
22
+
18
23
  job_class_name = data[:job].class.name
19
24
  if DeadBro.configuration.excluded_job?(job_class_name)
20
25
  drain_job_tracking
@@ -38,6 +43,7 @@ module DeadBro
38
43
  end
39
44
 
40
45
  duration_ms = ((finished - started) * 1000.0).round(2)
46
+ queue_duration_ms = job_queue_duration_ms(data[:job], started)
41
47
 
42
48
  # Ensure tracking was started (fallback if perform_start.active_job didn't fire)
43
49
  # This handles job backends that don't emit perform_start events
@@ -45,6 +51,7 @@ module DeadBro
45
51
  DeadBro.logger.clear
46
52
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
47
53
  DeadBro::SqlSubscriber.start_request_tracking
54
+ DeadBro::DbConnectionSubscriber.start_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
48
55
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
49
56
  DeadBro::MemoryTrackingSubscriber.start_request_tracking
50
57
  else
@@ -54,6 +61,7 @@ module DeadBro
54
61
 
55
62
  # Get SQL queries executed during this job
56
63
  sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
64
+ db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
57
65
 
58
66
  # Stop memory tracking and get collected memory data
59
67
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
@@ -89,6 +97,9 @@ module DeadBro
89
97
  queue_name: data[:job].queue_name,
90
98
  arguments: safe_arguments(data[:job].arguments),
91
99
  duration_ms: duration_ms,
100
+ queue_duration_ms: queue_duration_ms,
101
+ db_connection_wait_ms: db_connection_stats[:wait_ms],
102
+ db_connection_checkouts: db_connection_stats[:checkouts],
92
103
  status: "completed",
93
104
  sql_queries: sql_queries,
94
105
  rails_env: DeadBro.env,
@@ -106,6 +117,11 @@ module DeadBro
106
117
  # Track job exceptions
107
118
  ActiveSupport::Notifications.subscribe(JOB_EXCEPTION_EVENT_NAME) do |name, started, finished, _unique_id, data|
108
119
  begin
120
+ if DeadBro.configuration.skip_tracking?
121
+ drain_job_tracking
122
+ next
123
+ end
124
+
109
125
  job_class_name = data[:job].class.name
110
126
  if DeadBro.configuration.excluded_job?(job_class_name)
111
127
  next
@@ -119,13 +135,14 @@ module DeadBro
119
135
 
120
136
  duration_ms = ((finished - started) * 1000.0).round(2)
121
137
  exception = data[:exception_object]
122
- data[:job].class.name
138
+ queue_duration_ms = job_queue_duration_ms(data[:job], started)
123
139
 
124
140
  # Ensure tracking was started (fallback if perform_start.active_job didn't fire)
125
141
  unless DeadBro::SqlSubscriber.tracking_active?
126
142
  DeadBro.logger.clear
127
143
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
128
144
  DeadBro::SqlSubscriber.start_request_tracking
145
+ DeadBro::DbConnectionSubscriber.start_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
129
146
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
130
147
  DeadBro::MemoryTrackingSubscriber.start_request_tracking
131
148
  else
@@ -135,6 +152,7 @@ module DeadBro
135
152
 
136
153
  # Get SQL queries executed during this job
137
154
  sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
155
+ db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
138
156
 
139
157
  # Stop memory tracking and get collected memory data
140
158
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
@@ -170,6 +188,9 @@ module DeadBro
170
188
  queue_name: data[:job].queue_name,
171
189
  arguments: safe_arguments(data[:job].arguments),
172
190
  duration_ms: duration_ms,
191
+ queue_duration_ms: queue_duration_ms,
192
+ db_connection_wait_ms: db_connection_stats[:wait_ms],
193
+ db_connection_checkouts: db_connection_stats[:checkouts],
173
194
  status: "failed",
174
195
  sql_queries: sql_queries,
175
196
  exception_class: exception&.class&.name,
@@ -195,6 +216,7 @@ module DeadBro
195
216
  # build a payload (excluded job / sampled out). Matches Subscriber.drain_request_tracking.
196
217
  def self.drain_job_tracking
197
218
  DeadBro::SqlSubscriber.stop_request_tracking if defined?(DeadBro::SqlSubscriber)
219
+ DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
198
220
  DeadBro::LightweightMemoryTracker.stop_request_tracking if defined?(DeadBro::LightweightMemoryTracker)
199
221
  if DeadBro.configuration.allocation_tracking_enabled && defined?(DeadBro::MemoryTrackingSubscriber)
200
222
  DeadBro::MemoryTrackingSubscriber.stop_request_tracking
@@ -205,6 +227,17 @@ module DeadBro
205
227
 
206
228
  private
207
229
 
230
+ def self.job_queue_duration_ms(job, perform_started)
231
+ enqueued_at = job.enqueued_at
232
+ return nil if enqueued_at.nil?
233
+
234
+ enqueued_time = enqueued_at.is_a?(Time) ? enqueued_at : Time.parse(enqueued_at.to_s)
235
+ diff_ms = ((perform_started - enqueued_time) * 1000.0).round(2)
236
+ diff_ms >= 0 ? diff_ms : nil
237
+ rescue
238
+ nil
239
+ end
240
+
208
241
  def self.safe_arguments(arguments)
209
242
  return [] unless arguments.is_a?(Array)
210
243
 
@@ -36,6 +36,10 @@ if defined?(Rails) && defined?(Rails::Railtie)
36
36
  require "dead_bro/elasticsearch_subscriber"
37
37
  DeadBro::ElasticsearchSubscriber.subscribe!
38
38
 
39
+ # Install DB connection pool wait tracking
40
+ require "dead_bro/db_connection_subscriber"
41
+ DeadBro::DbConnectionSubscriber.install!
42
+
39
43
  # Install view rendering tracking
40
44
  require "dead_bro/view_rendering_subscriber"
41
45
  DeadBro::ViewRenderingSubscriber.subscribe!(client: shared_client)
@@ -7,6 +7,17 @@ module DeadBro
7
7
  end
8
8
 
9
9
  def call(env)
10
+ return @app.call(env) if DeadBro.configuration.skip_tracking?
11
+
12
+ # Capture rack entry time before any setup so middleware overhead is accurately measured.
13
+ rack_entry = Time.now
14
+ Thread.current[DeadBro::TRACKING_START_TIME_KEY] = rack_entry
15
+
16
+ # Queue time: gap between when the upstream proxy accepted the connection and when a Rack
17
+ # worker picked it up. Heroku sets X-Request-Start as "t=<microseconds>"; nginx typically
18
+ # uses "t=<seconds.ms>". Both are parsed below.
19
+ Thread.current[:dead_bro_queue_duration_ms] = parse_queue_start(env, rack_entry)
20
+
10
21
  # Clear logs for this request
11
22
  DeadBro.logger.clear
12
23
 
@@ -45,12 +56,14 @@ module DeadBro
45
56
  DeadBro::ElasticsearchSubscriber.start_request_tracking
46
57
  end
47
58
 
59
+ # Start DB connection pool wait tracking
60
+ if defined?(DeadBro::DbConnectionSubscriber)
61
+ DeadBro::DbConnectionSubscriber.start_request_tracking
62
+ end
63
+
48
64
  # Start outgoing HTTP accumulation for this request
49
65
  Thread.current[:dead_bro_http_events] = []
50
66
 
51
- # Set tracking start time once for all subscribers (before starting any tracking)
52
- Thread.current[DeadBro::TRACKING_START_TIME_KEY] = Time.now
53
-
54
67
  @app.call(env)
55
68
  ensure
56
69
  # Clean up thread-local storage
@@ -79,10 +92,40 @@ module DeadBro
79
92
  Thread.current[:dead_bro_lightweight_memory] = nil
80
93
  end
81
94
 
82
- # Clean up HTTP events, ES events, and tracking start time
95
+ # Clean up HTTP events, ES events, DB connection tracking, and tracking start time
83
96
  Thread.current[:dead_bro_elasticsearch_events] = nil
84
97
  Thread.current[:dead_bro_http_events] = nil
98
+ Thread.current[:dead_bro_queue_duration_ms] = nil
99
+ DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
85
100
  Thread.current[DeadBro::TRACKING_START_TIME_KEY] = nil
86
101
  end
102
+
103
+ private
104
+
105
+ def parse_queue_start(env, rack_entry)
106
+ raw = env["HTTP_X_REQUEST_START"] || env["HTTP_X_QUEUE_START"]
107
+ return nil if raw.nil? || raw.empty?
108
+
109
+ # Strip "t=" prefix used by Heroku and nginx
110
+ raw = raw.sub(/\At=/, "")
111
+ num = raw.to_f
112
+ return nil if num <= 0
113
+
114
+ request_start =
115
+ if num > 1_000_000_000_000_000 # microseconds (Heroku)
116
+ Time.at(num / 1_000_000.0)
117
+ elsif num > 1_000_000_000_000 # milliseconds
118
+ Time.at(num / 1_000.0)
119
+ else # seconds (nginx)
120
+ Time.at(num)
121
+ end
122
+
123
+ # Guard against clocks being out of sync or wildly misconfigured proxy timestamps.
124
+ # Cap at 60 s — anything larger almost certainly means the header value is wrong.
125
+ diff_ms = ((rack_entry - request_start) * 1000.0).round(2)
126
+ diff_ms >= 0 && diff_ms <= 60_000 ? diff_ms : nil
127
+ rescue
128
+ nil
129
+ end
87
130
  end
88
131
  end
@@ -16,6 +16,12 @@ module DeadBro
16
16
  next
17
17
  end
18
18
 
19
+ if DeadBro.configuration.skip_tracking?
20
+ client.post_heartbeat if DeadBro.configuration.heartbeat_due?
21
+ drain_request_tracking
22
+ next
23
+ end
24
+
19
25
  # Skip excluded controllers or controller#action pairs
20
26
  # Also check exclusive_controller_actions - if defined, only track those
21
27
  notification = data.is_a?(Hash) ? data : {}
@@ -43,6 +49,11 @@ module DeadBro
43
49
  end
44
50
 
45
51
  duration_ms = ((finished - started) * 1000.0).round(2)
52
+
53
+ # Time spent in Rack middleware before ActionController took over (routing, session, auth, etc.)
54
+ rack_start = Thread.current[DeadBro::TRACKING_START_TIME_KEY]
55
+ rack_duration_ms = rack_start ? ([((started - rack_start) * 1000.0), 0].max).round(2) : nil
56
+
46
57
  # Stop SQL tracking and get collected queries (this was started by the request)
47
58
  sql_queries = DeadBro::SqlSubscriber.stop_request_tracking
48
59
 
@@ -51,6 +62,9 @@ module DeadBro
51
62
  redis_events = defined?(DeadBro::RedisSubscriber) ? DeadBro::RedisSubscriber.stop_request_tracking : []
52
63
  elasticsearch_events = defined?(DeadBro::ElasticsearchSubscriber) ? DeadBro::ElasticsearchSubscriber.stop_request_tracking : []
53
64
 
65
+ # Stop DB connection pool wait tracking
66
+ db_connection_stats = defined?(DeadBro::DbConnectionSubscriber) ? DeadBro::DbConnectionSubscriber.stop_request_tracking : {}
67
+
54
68
  # Stop view rendering tracking and get collected view events
55
69
  view_events = DeadBro::ViewRenderingSubscriber.stop_request_tracking
56
70
  view_performance = DeadBro::ViewRenderingSubscriber.analyze_view_performance(view_events)
@@ -160,6 +174,10 @@ module DeadBro
160
174
  view_performance: view_performance,
161
175
  memory_events: memory_events,
162
176
  memory_performance: memory_performance,
177
+ rack_duration_ms: rack_duration_ms,
178
+ queue_duration_ms: Thread.current[:dead_bro_queue_duration_ms],
179
+ db_connection_wait_ms: db_connection_stats[:wait_ms],
180
+ db_connection_checkouts: db_connection_stats[:checkouts],
163
181
  logs: DeadBro.logger.logs
164
182
  }
165
183
  client.post_metric(event_name: name, payload: payload)
@@ -180,6 +198,7 @@ module DeadBro
180
198
  DeadBro::MemoryTrackingSubscriber.stop_request_tracking
181
199
  end
182
200
  Thread.current[:dead_bro_http_events] = nil
201
+ DeadBro::DbConnectionSubscriber.stop_request_tracking if defined?(DeadBro::DbConnectionSubscriber)
183
202
  rescue
184
203
  # Best effort — draining must never raise from the notifications callback.
185
204
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeadBro
4
- VERSION = "0.2.12"
4
+ VERSION = "0.2.14"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dead_bro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.12
4
+ version: 0.2.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuel Comsa
@@ -33,6 +33,7 @@ files:
33
33
  - lib/dead_bro/collectors/sample_store.rb
34
34
  - lib/dead_bro/collectors/system.rb
35
35
  - lib/dead_bro/configuration.rb
36
+ - lib/dead_bro/db_connection_subscriber.rb
36
37
  - lib/dead_bro/dispatcher.rb
37
38
  - lib/dead_bro/elasticsearch_subscriber.rb
38
39
  - lib/dead_bro/error_middleware.rb