activerabbit-ai 0.4.7 → 0.5.0

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: 06e58258544f159566a756467253a1a2a6a0a696479b37cd1f265902928a3f8c
4
- data.tar.gz: e12b3dcd16a43ca524027e2b0ecd608d8bbe0441f2f89586d7bb5d1f57dbda1a
3
+ metadata.gz: 915dd514027b844f4959d32085c1c26dfb69924b911fe31472c9f89eeaba0fd5
4
+ data.tar.gz: 128a2fe0aaaf197b72ee457c1f240df1057d63c029fd65dba5a92f890ff18de5
5
5
  SHA512:
6
- metadata.gz: eb2e853ad6f3791c567a9498af2a6684756042d0b7fa626e94ae057910422a594f0d705f5fbc923334af52c110062701db37c86766d14be17ef41d495e758fae
7
- data.tar.gz: d498e0a7c16cb8abeb8c96724caf88ad32ac598e0d84994c90712de3e8f182a763aaeda73f39c8a3f94b00c31ff1b5b3629535946313a8adec952ccb0bf0466b
6
+ metadata.gz: 382c8d0304b2048f8fa26c481530f1bfe19abac6ec8ec21c9fed57aebac84c2e40750fca4329b9864c301d6e4de7d9b0b411a97bd66d5ee9fe00cfbc58406d6e
7
+ data.tar.gz: d6ce4cf362160793d49d1d6d00444352c24b03eda9cb22be079d37ce65e37ee42e918be33ade5b76df761670d8bde4837dcd6c7a3893b09048774aee2a2e5fd2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.5.0] - 2025-12-12
6
+
7
+ ### Changed
8
+ - **Minor release**: Bumped version to 0.5.0 to reflect latest stable changes and internal improvements
9
+
10
+ ## [0.4.9] - 2025-11-30
11
+
12
+ ### Fixed
13
+ - **Rails Integration**: Fixed `LocalJumpError: unexpected return` when `ActiveRabbit` is installed but not configured
14
+ - This error occurred when `subscribed` callbacks tried to return early if configuration was missing
15
+ - Changed `return` to `next` in Railtie subscription blocks
16
+
17
+ ## [0.4.8] - 2025-11-27
18
+
19
+ ### Added
20
+ - **Deploy notification API**: Added `ActiveRabbit::Client.notify_deploy` for reporting deploys to ActiveRabbit
21
+ - Sends deploy metadata (revision, environment, project slug, version, status, user, timestamps) to `/api/v1/deploys`
22
+
23
+ ### Fixed
24
+ - **Test suite stability**: RSpec tests and CI config updated to keep 0.4.x series green
25
+
5
26
  ## [0.4.7] - 2025-11-06
6
27
 
7
28
  ### Fixed
@@ -15,6 +15,8 @@ module ActiveRabbit
15
15
  attr_accessor :release, :server_name, :logger
16
16
  attr_accessor :before_send_event, :before_send_exception
17
17
  attr_accessor :dedupe_window # Time window in seconds for error deduplication (0 = disabled)
18
+ attr_accessor :revision
19
+ attr_accessor :disable_console_logs
18
20
 
19
21
  def initialize
20
22
  @api_url = ENV.fetch("active_rabbit_API_URL", "https://api.activerabbit.ai")
@@ -37,6 +39,7 @@ module ActiveRabbit
37
39
  @enable_performance_monitoring = true
38
40
  @enable_n_plus_one_detection = true
39
41
  @enable_pii_scrubbing = true
42
+ @disable_console_logs = true
40
43
 
41
44
  # PII scrubbing
42
45
  @pii_fields = %w[
@@ -32,25 +32,25 @@ module ActiveRabbit
32
32
  end
33
33
 
34
34
  # Send exception to API and return response
35
- configuration.logger&.info("[ActiveRabbit] Preparing to send exception: #{exception.class.name}")
36
- configuration.logger&.debug("[ActiveRabbit] Exception data: #{exception_data.inspect}")
35
+ log(:info, "[ActiveRabbit] Preparing to send exception: #{exception.class.name}")
36
+ log(:debug, "[ActiveRabbit] Exception data: #{exception_data.inspect}")
37
37
 
38
38
  # Ensure we have required fields
39
39
  unless exception_data[:exception_class] && exception_data[:message] && exception_data[:backtrace]
40
- configuration.logger&.error("[ActiveRabbit] Missing required fields in exception data")
41
- configuration.logger&.debug("[ActiveRabbit] Available fields: #{exception_data.keys.inspect}")
40
+ log(:error, "[ActiveRabbit] Missing required fields in exception data")
41
+ log(:debug, "[ActiveRabbit] Available fields: #{exception_data.keys.inspect}")
42
42
  return nil
43
43
  end
44
44
 
45
45
  response = http_client.post_exception(exception_data)
46
46
 
47
47
  if response.nil?
48
- configuration.logger&.error("[ActiveRabbit] Failed to send exception - both primary and fallback endpoints failed")
48
+ log(:error, "[ActiveRabbit] Failed to send exception - both primary and fallback endpoints failed")
49
49
  return nil
50
50
  end
51
51
 
52
- configuration.logger&.info("[ActiveRabbit] Exception successfully sent to API")
53
- configuration.logger&.debug("[ActiveRabbit] API Response: #{response.inspect}")
52
+ log(:info, "[ActiveRabbit] Exception successfully sent to API")
53
+ log(:debug, "[ActiveRabbit] API Response: #{response.inspect}")
54
54
  response
55
55
  end
56
56
 
@@ -60,6 +60,22 @@ module ActiveRabbit
60
60
 
61
61
  private
62
62
 
63
+ def log(level, message)
64
+ cfg = configuration
65
+ return if cfg&.disable_console_logs
66
+
67
+ logger = cfg&.logger
68
+ return unless logger
69
+
70
+ case level
71
+ when :info then logger.info(message)
72
+ when :debug then logger.debug(message)
73
+ when :error then logger.error(message)
74
+ end
75
+
76
+ rescue
77
+ end
78
+
63
79
  def build_exception_data(exception:, context:, user_id:, tags:, handled: nil)
64
80
  parsed_bt = parse_backtrace(exception.backtrace || [])
65
81
  backtrace_lines = parsed_bt.map { |frame| frame[:line] }
@@ -132,10 +148,10 @@ module ActiveRabbit
132
148
  end
133
149
 
134
150
  # Log what we're sending
135
- configuration.logger&.debug("[ActiveRabbit] Built exception data:")
136
- configuration.logger&.debug("[ActiveRabbit] - Required fields: class=#{data[:exception_class]}, message=#{data[:message]}, backtrace=#{data[:backtrace]&.first}")
137
- configuration.logger&.debug("[ActiveRabbit] - Error details: type=#{data[:error_type]}, source=#{data[:error_source]}, component=#{data[:error_component]}")
138
- configuration.logger&.debug("[ActiveRabbit] - Request info: path=#{data[:request_path]}, method=#{data[:request_method]}, action=#{data[:controller_action]}")
151
+ log(:debug, "[ActiveRabbit] Built exception data:")
152
+ log(:debug, "[ActiveRabbit] - Required fields: class=#{data[:exception_class]}, message=#{data[:message]}, backtrace=#{data[:backtrace]&.first}")
153
+ log(:debug, "[ActiveRabbit] - Error details: type=#{data[:error_type]}, source=#{data[:error_source]}, component=#{data[:error_component]}")
154
+ log(:debug, "[ActiveRabbit] - Request info: path=#{data[:request_path]}, method=#{data[:request_method]}, action=#{data[:controller_action]}")
139
155
 
140
156
  data
141
157
  end
@@ -37,33 +37,33 @@ module ActiveRabbit
37
37
  # Primary endpoint path
38
38
  path = "/api/v1/events/errors"
39
39
 
40
- configuration.logger&.info("[ActiveRabbit] Sending exception to API...")
41
- configuration.logger&.debug("[ActiveRabbit] Exception payload (pre-JSON): #{safe_preview(exception_data_with_type)}")
42
- configuration.logger&.debug("[ActiveRabbit] Target path: #{path}")
40
+ log(:info, "[ActiveRabbit] Sending exception to API...")
41
+ log(:debug, "[ActiveRabbit] Exception payload (pre-JSON): #{safe_preview(exception_data_with_type)}")
42
+ log(:debug, "[ActiveRabbit] Target path: #{path}")
43
43
 
44
44
  begin
45
45
  # Primary endpoint attempt
46
- configuration.logger&.info("[ActiveRabbit] Making request to primary endpoint: POST #{path}")
47
- response = make_request(:post, path, exception_data_with_type)
48
- configuration.logger&.info("[ActiveRabbit] Exception sent successfully (errors endpoint)")
46
+ log(:info, "[ActiveRabbit] Making request to primary endpoint: POST #{path}")
47
+ response = enqueue_request(:post, path, exception_data_with_type)
48
+ log(:info, "[ActiveRabbit] Exception sent successfully (errors endpoint)")
49
49
  return response
50
50
  rescue => e
51
- configuration.logger&.error("[ActiveRabbit] Primary send failed: #{e.class}: #{e.message}")
52
- configuration.logger&.error("[ActiveRabbit] Primary error backtrace: #{e.backtrace&.first(3)}")
53
- configuration.logger&.debug("[ActiveRabbit] Falling back to /api/v1/events with type=error")
51
+ log(:error, "[ActiveRabbit] Primary send failed: #{e.class}: #{e.message}")
52
+ log(:error, "[ActiveRabbit] Primary error backtrace: #{e.backtrace&.first(3)}")
53
+ log(:debug, "[ActiveRabbit] Falling back to /api/v1/events with type=error")
54
54
 
55
55
  begin
56
56
  # Fallback to generic events endpoint
57
57
  fallback_path = "/api/v1/events"
58
58
  fallback_body = { type: "error", data: exception_data_with_type }
59
- configuration.logger&.info("[ActiveRabbit] Making request to fallback endpoint: POST #{fallback_path}")
60
- response = make_request(:post, fallback_path, fallback_body)
61
- configuration.logger&.info("[ActiveRabbit] Exception sent via fallback endpoint")
59
+ log(:info, "[ActiveRabbit] Making request to fallback endpoint: POST #{fallback_path}")
60
+ response = enqueue_request(:post, fallback_path, fallback_body)
61
+ log(:info, "[ActiveRabbit] Exception sent via fallback endpoint")
62
62
  return response
63
63
  rescue => e2
64
- configuration.logger&.error("[ActiveRabbit] Fallback send failed: #{e2.class}: #{e2.message}")
65
- configuration.logger&.error("[ActiveRabbit] Fallback error backtrace: #{e2.backtrace&.first(3)}")
66
- configuration.logger&.error("[ActiveRabbit] All exception sending attempts failed")
64
+ log(:error, "[ActiveRabbit] Fallback send failed: #{e2.class}: #{e2.message}")
65
+ log(:error, "[ActiveRabbit] Fallback error backtrace: #{e2.backtrace&.first(3)}")
66
+ log(:error, "[ActiveRabbit] All exception sending attempts failed")
67
67
  nil
68
68
  end
69
69
  end
@@ -85,13 +85,34 @@ module ActiveRabbit
85
85
  end
86
86
 
87
87
  # Send batch to API
88
- configuration.logger&.info("[ActiveRabbit] Sending batch of #{events.length} events...")
88
+ log(:info, "[ActiveRabbit] Sending batch of #{events.length} events...")
89
89
  response = make_request(:post, "/api/v1/events/batch", { events: events })
90
- configuration.logger&.info("[ActiveRabbit] Batch sent successfully")
91
- configuration.logger&.debug("[ActiveRabbit] Batch response: #{response.inspect}")
90
+ log(:info, "[ActiveRabbit] Batch sent successfully")
91
+ log(:debug, "[ActiveRabbit] Batch response: #{response.inspect}")
92
92
  response
93
93
  end
94
94
 
95
+ def post(path, payload)
96
+ uri = URI.join(@base_uri.to_s, path)
97
+ req = Net::HTTP::Post.new(uri)
98
+ req['Content-Type'] = 'application/json'
99
+ req["X-Project-Token"] = configuration.api_key
100
+ req.body = payload.to_json
101
+
102
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
103
+ http.request(req)
104
+ end
105
+
106
+ unless res.is_a?(Net::HTTPSuccess)
107
+ raise APIError, "ActiveRabbit API request failed: #{res.code} #{res.body}"
108
+ end
109
+
110
+ JSON.parse(res.body)
111
+ rescue => e
112
+ log(:error, "[ActiveRabbit] HTTP POST failed: #{e.class}: #{e.message}")
113
+ nil
114
+ end
115
+
95
116
  def test_connection
96
117
  response = make_request(:post, "/api/v1/test/connection", {
97
118
  gem_version: ActiveRabbit::Client::VERSION,
@@ -105,18 +126,18 @@ module ActiveRabbit
105
126
  def flush
106
127
  return if @request_queue.empty?
107
128
 
108
- configuration.logger&.info("[ActiveRabbit] Starting flush with #{@request_queue.length} items")
129
+ log(:info, "[ActiveRabbit] Starting flush with #{@request_queue.length} items")
109
130
  batch = @request_queue.shift(@request_queue.length)
110
131
  return if batch.empty?
111
132
 
112
133
  begin
113
- configuration.logger&.info("[ActiveRabbit] Sending batch of #{batch.length} items")
134
+ log(:info, "[ActiveRabbit] Sending batch of #{batch.length} items")
114
135
  response = post_batch(batch)
115
- configuration.logger&.info("[ActiveRabbit] Batch sent successfully: #{response.inspect}")
136
+ log(:info, "[ActiveRabbit] Batch sent successfully: #{response.inspect}")
116
137
  response
117
138
  rescue => e
118
- configuration.logger&.error("[ActiveRabbit] Failed to send batch: #{e.message}")
119
- configuration.logger&.error("[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
139
+ log(:error, "[ActiveRabbit] Failed to send batch: #{e.message}")
140
+ log(:error, "[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
120
141
  raise APIError, "Failed to send batch: #{e.message}"
121
142
  end
122
143
  end
@@ -140,8 +161,8 @@ module ActiveRabbit
140
161
  timestamp: Time.now.to_f
141
162
  }
142
163
 
143
- configuration.logger&.info("[ActiveRabbit] Enqueueing request: #{method} #{path}")
144
- configuration.logger&.debug("[ActiveRabbit] Request data: #{data.inspect}")
164
+ log(:info, "[ActiveRabbit] Enqueueing request: #{method} #{path}")
165
+ log(:debug, "[ActiveRabbit] Request data: #{data.inspect}")
145
166
 
146
167
  @request_queue << formatted_data
147
168
 
@@ -169,9 +190,9 @@ module ActiveRabbit
169
190
  # Ensure path starts with a single leading slash
170
191
  normalized_path = path.start_with?("/") ? path : "/#{path}"
171
192
  uri = URI.join(current_base, normalized_path)
172
- configuration.logger&.info("[ActiveRabbit] Making request: #{method.upcase} #{uri}")
173
- configuration.logger&.debug("[ActiveRabbit] Request headers: X-Project-Token=#{configuration.api_key}, X-Project-ID=#{configuration.project_id}")
174
- configuration.logger&.debug("[ActiveRabbit] Request body: #{safe_preview(data)}")
193
+ log(:info, "[ActiveRabbit] Making request: #{method.upcase} #{uri}")
194
+ log(:debug, "[ActiveRabbit] Request headers: X-Project-Token=#{configuration.api_key}, X-Project-ID=#{configuration.project_id}")
195
+ log(:debug, "[ActiveRabbit] Request body: #{safe_preview(data)}")
175
196
 
176
197
  # Retry logic with exponential backoff
177
198
  retries = 0
@@ -179,17 +200,17 @@ module ActiveRabbit
179
200
 
180
201
  begin
181
202
  response = perform_request(uri, method, data)
182
- configuration.logger&.info("[ActiveRabbit] Response status: #{response.code}")
183
- configuration.logger&.debug("[ActiveRabbit] Response headers: #{response.to_hash.inspect}")
184
- configuration.logger&.debug("[ActiveRabbit] Response body: #{response.body}")
203
+ log(:info, "[ActiveRabbit] Response status: #{response.code}")
204
+ log(:debug, "[ActiveRabbit] Response headers: #{response.to_hash.inspect}")
205
+ log(:debug, "[ActiveRabbit] Response body: #{response.body}")
185
206
 
186
207
  result = handle_response(response)
187
- configuration.logger&.debug("[ActiveRabbit] Parsed response: #{result.inspect}")
208
+ log(:debug, "[ActiveRabbit] Parsed response: #{result.inspect}")
188
209
  result
189
210
  rescue RetryableError => e
190
211
  if retries < max_retries
191
212
  retries += 1
192
- configuration.logger&.info("[ActiveRabbit] Retrying request (#{retries}/#{max_retries})")
213
+ log(:info, "[ActiveRabbit] Retrying request (#{retries}/#{max_retries})")
193
214
  sleep(configuration.retry_delay * (2 ** (retries - 1)))
194
215
  retry
195
216
  end
@@ -197,7 +218,7 @@ module ActiveRabbit
197
218
  rescue Net::OpenTimeout, Net::ReadTimeout => e
198
219
  if retries < max_retries && should_retry_error?(e)
199
220
  retries += 1
200
- configuration.logger&.info("[ActiveRabbit] Retrying request after timeout (#{retries}/#{max_retries})")
221
+ log(:info, "[ActiveRabbit] Retrying request after timeout (#{retries}/#{max_retries})")
201
222
  sleep(configuration.retry_delay * (2 ** (retries - 1))) # Exponential backoff
202
223
  retry
203
224
  end
@@ -205,24 +226,24 @@ module ActiveRabbit
205
226
  rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError => e
206
227
  if retries < max_retries
207
228
  retries += 1
208
- configuration.logger&.info("[ActiveRabbit] Retrying request after connection error (#{retries}/#{max_retries})")
229
+ log(:info, "[ActiveRabbit] Retrying request after connection error (#{retries}/#{max_retries})")
209
230
  sleep(configuration.retry_delay * (2 ** (retries - 1)))
210
231
  retry
211
232
  end
212
233
  raise APIError, "Connection failed after #{retries} retries: #{e.message}"
213
234
  rescue APIError, RateLimitError => e
214
235
  # Re-raise API errors as-is
215
- configuration.logger&.error("[ActiveRabbit] API error: #{e.class}: #{e.message}")
236
+ log(:error, "[ActiveRabbit] API error: #{e.class}: #{e.message}")
216
237
  raise e
217
238
  rescue => e
218
- configuration.logger&.error("[ActiveRabbit] Request failed: #{e.class}: #{e.message}")
219
- configuration.logger&.error("[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
239
+ log(:error, "[ActiveRabbit] Request failed: #{e.class}: #{e.message}")
240
+ log(:error, "[ActiveRabbit] Backtrace: #{e.backtrace&.first(3)}")
220
241
  raise APIError, "Request failed: #{e.message}"
221
242
  end
222
243
  end
223
244
 
224
245
  def perform_request(uri, method, data)
225
- configuration.logger&.debug("[ActiveRabbit] Making HTTP request: #{method.upcase} #{uri}")
246
+ log(:debug, "[ActiveRabbit] Making HTTP request: #{method.upcase} #{uri}")
226
247
  http = Net::HTTP.new(uri.host, uri.port)
227
248
 
228
249
  # Configure SSL if HTTPS
@@ -249,7 +270,7 @@ module ActiveRabbit
249
270
  raise ArgumentError, "Unsupported HTTP method: #{method}"
250
271
  end
251
272
 
252
- configuration.logger&.debug("[ActiveRabbit] Request path: #{uri.request_uri}")
273
+ log(:debug, "[ActiveRabbit] Request path: #{uri.request_uri}")
253
274
 
254
275
  # Set headers
255
276
  request['Content-Type'] = 'application/json'
@@ -270,8 +291,8 @@ module ActiveRabbit
270
291
  end
271
292
 
272
293
  def handle_response(response)
273
- configuration.logger&.debug("[ActiveRabbit] Response code: #{response.code}")
274
- configuration.logger&.debug("[ActiveRabbit] Response body: #{response.body}")
294
+ log(:debug, "[ActiveRabbit] Response code: #{response.code}")
295
+ log(:debug, "[ActiveRabbit] Response body: #{response.body}")
275
296
 
276
297
  case response.code.to_i
277
298
  when 200..299
@@ -279,23 +300,23 @@ module ActiveRabbit
279
300
  if response.body && !response.body.empty?
280
301
  begin
281
302
  parsed = JSON.parse(response.body)
282
- configuration.logger&.debug("[ActiveRabbit] Parsed response: #{parsed.inspect}")
303
+ log(:debug, "[ActiveRabbit] Parsed response: #{parsed.inspect}")
283
304
  parsed
284
305
  rescue JSON::ParserError => e
285
- configuration.logger&.error("[ActiveRabbit] Failed to parse response: #{e.message}")
286
- configuration.logger&.error("[ActiveRabbit] Raw response: #{response.body}")
306
+ log(:error, "[ActiveRabbit] Failed to parse response: #{e.message}")
307
+ log(:error, "[ActiveRabbit] Raw response: #{response.body}")
287
308
  response.body
288
309
  end
289
310
  else
290
- configuration.logger&.debug("[ActiveRabbit] Empty response body")
311
+ log(:debug, "[ActiveRabbit] Empty response body")
291
312
  {}
292
313
  end
293
314
  when 429
294
- configuration.logger&.error("[ActiveRabbit] Rate limit exceeded")
315
+ log(:error, "[ActiveRabbit] Rate limit exceeded")
295
316
  raise RateLimitError, "Rate limit exceeded"
296
317
  when 400..499
297
318
  error_message = extract_error_message(response)
298
- configuration.logger&.error("[ActiveRabbit] Client error (#{response.code}): #{error_message}")
319
+ log(:error, "[ActiveRabbit] Client error (#{response.code}): #{error_message}")
299
320
  raise APIError, "Client error (#{response.code}): #{error_message}"
300
321
  when 500..599
301
322
  error_message = extract_error_message(response)
@@ -303,11 +324,11 @@ module ActiveRabbit
303
324
  configuration.logger&.warn("[ActiveRabbit] Retryable server error (#{response.code}): #{error_message}")
304
325
  raise RetryableError, "Server error (#{response.code}): #{error_message}"
305
326
  else
306
- configuration.logger&.error("[ActiveRabbit] Server error (#{response.code}): #{error_message}")
327
+ log(:error, "[ActiveRabbit] Server error (#{response.code}): #{error_message}")
307
328
  raise APIError, "Server error (#{response.code}): #{error_message}"
308
329
  end
309
330
  else
310
- configuration.logger&.error("[ActiveRabbit] Unexpected response code: #{response.code}")
331
+ log(:error, "[ActiveRabbit] Unexpected response code: #{response.code}")
311
332
  raise APIError, "Unexpected response code: #{response.code}"
312
333
  end
313
334
  end
@@ -362,7 +383,22 @@ module ActiveRabbit
362
383
  s = obj.inspect
363
384
  s.length > 2000 ? s[0,2000] + "...(truncated)" : s
364
385
  end
365
- end
366
386
 
387
+ def log(level, message)
388
+ return if configuration.disable_console_logs
389
+
390
+ logger = configuration.logger
391
+ return unless logger
392
+
393
+ case level
394
+ when :info
395
+ logger.info(message)
396
+ when :debug
397
+ logger.debug(message)
398
+ when :error
399
+ logger.error(message)
400
+ end
401
+ end
402
+ end
367
403
  end
368
404
  end
@@ -21,40 +21,46 @@ module ActiveRabbit
21
21
  config.active_rabbit = ActiveSupport::OrderedOptions.new
22
22
 
23
23
  initializer "active_rabbit.configure", after: :initialize_logger do |app|
24
- if Rails.env.development?
25
- puts "\n=== ActiveRabbit Configure ==="
26
- puts "Environment: #{Rails.env}"
27
- puts "Already configured? #{ActiveRabbit::Client.configured?}"
28
- puts "================================\n"
24
+ apply_rails_configuration(app.config.active_rabbit)
25
+
26
+ if ActiveRabbit::Client.configuration && !ActiveRabbit::Client.configuration.disable_console_logs
27
+ if Rails.env.development?
28
+ ar_puts "\n=== ActiveRabbit Configure ==="
29
+ ar_puts "Environment: #{Rails.env}"
30
+ ar_puts "Already configured? #{ActiveRabbit::Client.configured?}"
31
+ ar_puts "================================\n"
32
+ end
29
33
  end
30
34
 
31
35
  # Configure ActiveRabbit from Rails configuration
32
36
  ActiveRabbit::Client.configure do |config|
33
- config.environment = Rails.env
34
- config.logger = Rails.logger rescue Logger.new(STDOUT)
35
- config.release = detect_release(app)
37
+ config.environment ||= Rails.env
38
+ config.logger ||= Rails.logger rescue Logger.new(STDOUT)
39
+ config.release ||= detect_release(app)
36
40
  end
37
41
 
38
- if Rails.env.development?
39
- puts "\n=== ActiveRabbit Post-Configure ==="
40
- puts "Now configured? #{ActiveRabbit::Client.configured?}"
41
- puts "Configuration: #{ActiveRabbit::Client.configuration.inspect}"
42
- puts "================================\n"
43
- end
42
+ # if ActiveRabbit::Client.configuration && !ActiveRabbit::Client.configuration.disable_console_logs
43
+ # if Rails.env.development?
44
+ # ar_puts "\n=== ActiveRabbit Post-Configure ==="
45
+ # ar_puts "Now configured? #{ActiveRabbit::Client.configured?}"
46
+ # ar_puts "Configuration: #{ActiveRabbit::Client.configuration.inspect}"
47
+ # ar_puts "================================\n"
48
+ # end
49
+ # end
44
50
 
45
51
  # Set up exception tracking
46
52
  setup_exception_tracking(app) if ActiveRabbit::Client.configured?
47
53
  end
48
54
 
49
55
  initializer "active_rabbit.subscribe_to_notifications", after: :load_config_initializers do |app|
50
- Rails.logger.info "[ActiveRabbit] Setting up performance notifications subscriptions"
56
+ ar_log(:info, "[ActiveRabbit] Setting up performance notifications subscriptions")
51
57
  # Subscribe regardless; each handler guards on configured?
52
58
  subscribe_to_controller_events
53
59
  subscribe_to_active_record_events
54
60
  subscribe_to_action_view_events
55
61
  subscribe_to_action_mailer_events if defined?(ActionMailer)
56
62
  subscribe_to_exception_notifications
57
- Rails.logger.info "[ActiveRabbit] Subscriptions setup complete"
63
+ ar_log(:info, "[ActiveRabbit] Subscriptions setup complete")
58
64
 
59
65
  # Defer complex subscriptions until after initialization
60
66
  app.config.after_initialize do
@@ -85,83 +91,83 @@ module ActiveRabbit
85
91
  # Configure middleware after logger is initialized to avoid init cycles
86
92
  initializer "active_rabbit.add_middleware", after: :initialize_logger do |app|
87
93
  if Rails.env.development?
88
- puts "\n=== ActiveRabbit Railtie Loading ==="
89
- puts "Rails Environment: #{Rails.env}"
90
- puts "Rails Middleware Stack Phase: #{app.middleware.respond_to?(:middlewares) ? 'Ready' : 'Not Ready'}"
91
- puts "================================\n"
92
- puts "\n=== Initial Middleware Stack ==="
93
- puts "(not available at this boot phase)"
94
- puts "=======================\n"
94
+ ar_puts "\n=== ActiveRabbit Railtie Loading ==="
95
+ ar_puts "Rails Environment: #{Rails.env}"
96
+ ar_puts "Rails Middleware Stack Phase: #{app.middleware.respond_to?(:middlewares) ? 'Ready' : 'Not Ready'}"
97
+ ar_puts "================================\n"
98
+ ar_puts "\n=== Initial Middleware Stack ==="
99
+ ar_puts "(not available at this boot phase)"
100
+ ar_puts "=======================\n"
95
101
  end
96
102
 
97
- puts "\n=== Adding ActiveRabbit Middleware ===" if Rails.env.development?
103
+ ar_puts "\n=== Adding ActiveRabbit Middleware ===" if Rails.env.development?
98
104
  # Handle both development (DebugExceptions) and production (ShowExceptions)
99
105
  if defined?(ActionDispatch::DebugExceptions)
100
- puts "[ActiveRabbit] Found DebugExceptions, configuring middleware..." if Rails.env.development?
106
+ ar_puts "[ActiveRabbit] Found DebugExceptions, configuring middleware..." if Rails.env.development?
101
107
 
102
108
  # First remove any existing middleware to avoid duplicates
103
109
  begin
104
110
  app.config.middleware.delete(ActiveRabbit::Client::ExceptionMiddleware)
105
111
  app.config.middleware.delete(ActiveRabbit::Client::RequestContextMiddleware)
106
112
  app.config.middleware.delete(ActiveRabbit::Client::RoutingErrorCatcher)
107
- puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
113
+ ar_puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
108
114
  rescue => e
109
- puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
115
+ ar_puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
110
116
  end
111
117
 
112
118
  # Insert middleware in the correct order
113
- puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
119
+ ar_puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
114
120
 
115
121
  # Insert ErrorCaptureMiddleware after DebugExceptions to rely on rescue path
116
122
  app.config.middleware.insert_after(ActionDispatch::DebugExceptions, ActiveRabbit::Middleware::ErrorCaptureMiddleware)
117
123
 
118
124
  # Insert RequestContextMiddleware early in the stack
119
- puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
125
+ ar_puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
120
126
  app.config.middleware.insert_before(ActionDispatch::RequestId, ActiveRabbit::Client::RequestContextMiddleware)
121
127
 
122
128
  # Insert ExceptionMiddleware before Rails' exception handlers (kept for env-based reporting)
123
- puts "[ActiveRabbit] Inserting ExceptionMiddleware before DebugExceptions" if Rails.env.development?
129
+ ar_puts "[ActiveRabbit] Inserting ExceptionMiddleware before DebugExceptions" if Rails.env.development?
124
130
  app.config.middleware.insert_before(ActionDispatch::DebugExceptions, ActiveRabbit::Client::ExceptionMiddleware)
125
131
 
126
132
  # Insert RoutingErrorCatcher after Rails' exception handlers
127
- puts "[ActiveRabbit] Inserting RoutingErrorCatcher after DebugExceptions" if Rails.env.development?
133
+ ar_puts "[ActiveRabbit] Inserting RoutingErrorCatcher after DebugExceptions" if Rails.env.development?
128
134
  app.config.middleware.insert_after(ActionDispatch::DebugExceptions, ActiveRabbit::Client::RoutingErrorCatcher)
129
135
 
130
- puts "[ActiveRabbit] Middleware insertion complete" if Rails.env.development?
136
+ ar_puts "[ActiveRabbit] Middleware insertion complete" if Rails.env.development?
131
137
 
132
138
  elsif defined?(ActionDispatch::ShowExceptions)
133
- puts "[ActiveRabbit] Found ShowExceptions, configuring middleware..." if Rails.env.development?
139
+ ar_puts "[ActiveRabbit] Found ShowExceptions, configuring middleware..." if Rails.env.development?
134
140
 
135
141
  # First remove any existing middleware to avoid duplicates
136
142
  begin
137
143
  app.config.middleware.delete(ActiveRabbit::Client::ExceptionMiddleware)
138
144
  app.config.middleware.delete(ActiveRabbit::Client::RequestContextMiddleware)
139
145
  app.config.middleware.delete(ActiveRabbit::Client::RoutingErrorCatcher)
140
- puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
146
+ ar_puts "[ActiveRabbit] Cleaned up existing middleware" if Rails.env.development?
141
147
  rescue => e
142
- puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
148
+ ar_puts "[ActiveRabbit] Error cleaning middleware: #{e.message}"
143
149
  end
144
150
 
145
151
  # Insert middleware in the correct order
146
- puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
152
+ ar_puts "[ActiveRabbit] Inserting middleware..." if Rails.env.development?
147
153
 
148
154
  # Insert ErrorCaptureMiddleware after ShowExceptions
149
155
  app.config.middleware.insert_after(ActionDispatch::ShowExceptions, ActiveRabbit::Middleware::ErrorCaptureMiddleware)
150
156
 
151
157
  # Insert RequestContextMiddleware early in the stack
152
- puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
158
+ ar_puts "[ActiveRabbit] Inserting RequestContextMiddleware before RequestId" if Rails.env.development?
153
159
  app.config.middleware.insert_before(ActionDispatch::RequestId, ActiveRabbit::Client::RequestContextMiddleware)
154
160
 
155
161
  # Insert ExceptionMiddleware before Rails' exception handlers
156
- puts "[ActiveRabbit] Inserting ExceptionMiddleware before ShowExceptions" if Rails.env.development?
162
+ ar_puts "[ActiveRabbit] Inserting ExceptionMiddleware before ShowExceptions" if Rails.env.development?
157
163
  app.config.middleware.insert_before(ActionDispatch::ShowExceptions, ActiveRabbit::Client::ExceptionMiddleware)
158
164
 
159
165
  # Insert RoutingErrorCatcher after Rails' exception handlers
160
- puts "[ActiveRabbit] Inserting RoutingErrorCatcher after ShowExceptions" if Rails.env.development?
166
+ ar_puts "[ActiveRabbit] Inserting RoutingErrorCatcher after ShowExceptions" if Rails.env.development?
161
167
  app.config.middleware.insert_after(ActionDispatch::ShowExceptions, ActiveRabbit::Client::RoutingErrorCatcher)
162
168
 
163
169
  else
164
- puts "[ActiveRabbit] No exception handlers found, using fallback configuration" if Rails.env.development?
170
+ ar_puts "[ActiveRabbit] No exception handlers found, using fallback configuration" if Rails.env.development?
165
171
  app.config.middleware.use(ActiveRabbit::Middleware::ErrorCaptureMiddleware)
166
172
  app.config.middleware.use(ActiveRabbit::Client::RequestContextMiddleware)
167
173
  app.config.middleware.use(ActiveRabbit::Client::ExceptionMiddleware)
@@ -169,9 +175,9 @@ module ActiveRabbit
169
175
  end
170
176
 
171
177
  if Rails.env.development?
172
- puts "\n=== Final Middleware Stack ==="
173
- puts "(will be printed after initialize)"
174
- puts "=======================\n"
178
+ ar_puts "\n=== Final Middleware Stack ==="
179
+ ar_puts "(will be printed after initialize)"
180
+ ar_puts "=======================\n"
175
181
  end
176
182
 
177
183
  # Add debug wrappers in development
@@ -180,41 +186,42 @@ module ActiveRabbit
180
186
  ActiveRabbit::Client::ExceptionMiddleware.class_eval do
181
187
  alias_method :__ar_original_call, :call unless method_defined?(:__ar_original_call)
182
188
  def call(env)
183
- puts "\n=== ExceptionMiddleware Enter ==="
184
- puts "Path: #{env['PATH_INFO']}"
185
- puts "Method: #{env['REQUEST_METHOD']}"
186
- puts "Current Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
187
- puts "Current Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
188
- puts "Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
189
- puts "Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
190
- puts "Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
191
- puts "Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
192
- puts "============================\n"
189
+ cfg = ActiveRabbit::Client.configuration
190
+ ar_puts "\n=== ExceptionMiddleware Enter ==="
191
+ ar_puts "Path: #{env['PATH_INFO']}"
192
+ ar_puts "Method: #{env['REQUEST_METHOD']}"
193
+ ar_puts "Current Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
194
+ ar_puts "Current Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
195
+ ar_puts "Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
196
+ ar_puts "Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
197
+ ar_puts "Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
198
+ ar_puts "Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
199
+ ar_puts "============================\n"
193
200
 
194
201
  begin
195
202
  status, headers, body = __ar_original_call(env)
196
- puts "\n=== ExceptionMiddleware Exit (Success) ==="
197
- puts "Status: #{status}"
198
- puts "Headers: #{headers.inspect}"
199
- puts "Final Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
200
- puts "Final Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
201
- puts "Final Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
202
- puts "Final Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
203
- puts "Final Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
204
- puts "Final Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
205
- puts "===========================\n"
203
+ ar_puts "\n=== ExceptionMiddleware Exit (Success) ==="
204
+ ar_puts "Status: #{status}"
205
+ ar_puts "Headers: #{headers.inspect}"
206
+ ar_puts "Final Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
207
+ ar_puts "Final Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
208
+ ar_puts "Final Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
209
+ ar_puts "Final Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
210
+ ar_puts "Final Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
211
+ ar_puts "Final Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
212
+ ar_puts "===========================\n"
206
213
  [status, headers, body]
207
214
  rescue => e
208
- puts "\n=== ExceptionMiddleware Exit (Error) ==="
209
- puts "Error: #{e.class} - #{e.message}"
210
- puts "Error Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
211
- puts "Original Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
212
- puts "Original Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
213
- puts "Original Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
214
- puts "Original Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
215
- puts "Original Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
216
- puts "Original Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
217
- puts "===========================\n"
215
+ ar_puts "\n=== ExceptionMiddleware Exit (Error) ==="
216
+ ar_puts "Error: #{e.class} - #{e.message}"
217
+ ar_puts "Error Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
218
+ ar_puts "Original Exception: #{env['action_dispatch.exception']&.class} - #{env['action_dispatch.exception']&.message}"
219
+ ar_puts "Original Error: #{env['action_dispatch.error']&.class} - #{env['action_dispatch.error']&.message}"
220
+ ar_puts "Original Rack Exception: #{env['rack.exception']&.class} - #{env['rack.exception']&.message}"
221
+ ar_puts "Original Exception Backtrace: #{env['action_dispatch.exception']&.backtrace&.first(3)&.join("\n ")}"
222
+ ar_puts "Original Error Backtrace: #{env['action_dispatch.error']&.backtrace&.first(3)&.join("\n ")}"
223
+ ar_puts "Original Rack Backtrace: #{env['rack.exception']&.backtrace&.first(3)&.join("\n ")}"
224
+ ar_puts "===========================\n"
218
225
  raise
219
226
  end
220
227
  end
@@ -224,23 +231,23 @@ module ActiveRabbit
224
231
  ActiveRabbit::Client::RoutingErrorCatcher.class_eval do
225
232
  alias_method :__ar_routing_original_call, :call unless method_defined?(:__ar_routing_original_call)
226
233
  def call(env)
227
- puts "\n=== RoutingErrorCatcher Enter ==="
228
- puts "Path: #{env['PATH_INFO']}"
229
- puts "Method: #{env['REQUEST_METHOD']}"
230
- puts "Status: #{env['action_dispatch.exception']&.class}"
231
- puts "============================\n"
234
+ ar_puts "\n=== RoutingErrorCatcher Enter ==="
235
+ ar_puts "Path: #{env['PATH_INFO']}"
236
+ ar_puts "Method: #{env['REQUEST_METHOD']}"
237
+ ar_puts "Status: #{env['action_dispatch.exception']&.class}"
238
+ ar_puts "============================\n"
232
239
 
233
240
  begin
234
241
  status, headers, body = __ar_routing_original_call(env)
235
- puts "\n=== RoutingErrorCatcher Exit (Success) ==="
236
- puts "Status: #{status}"
237
- puts "===========================\n"
242
+ ar_puts "\n=== RoutingErrorCatcher Exit (Success) ==="
243
+ ar_puts "Status: #{status}"
244
+ ar_puts "===========================\n"
238
245
  [status, headers, body]
239
246
  rescue => e
240
- puts "\n=== RoutingErrorCatcher Exit (Error) ==="
241
- puts "Error: #{e.class} - #{e.message}"
242
- puts "Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
243
- puts "===========================\n"
247
+ ar_puts "\n=== RoutingErrorCatcher Exit (Error) ==="
248
+ ar_puts "Error: #{e.class} - #{e.message}"
249
+ ar_puts "Backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
250
+ ar_puts "===========================\n"
244
251
  raise
245
252
  end
246
253
  end
@@ -250,18 +257,18 @@ module ActiveRabbit
250
257
  # In development, add a hook to verify middleware after initialization
251
258
  if Rails.env.development?
252
259
  app.config.after_initialize do
253
- Rails.logger.info "\n=== ActiveRabbit Configuration ==="
254
- Rails.logger.info "Version: #{ActiveRabbit::Client::VERSION}"
255
- Rails.logger.info "Environment: #{Rails.env}"
256
- Rails.logger.info "API URL: #{ActiveRabbit::Client.configuration.api_url}"
257
- Rails.logger.info "================================"
260
+ ar_log(:info, "\n=== ActiveRabbit Configuration ===")
261
+ ar_log(:info, "Version: #{ActiveRabbit::Client::VERSION}")
262
+ ar_log(:info, "Environment: #{Rails.env}")
263
+ ar_log(:info, "API URL: #{ActiveRabbit::Client.configuration.api_url}")
264
+ ar_log(:info, "================================")
258
265
 
259
- Rails.logger.info "\n=== Middleware Stack ==="
266
+ ar_log(:info, "\n=== Middleware Stack ===")
260
267
  (Rails.application.middleware.middlewares rescue []).each do |mw|
261
268
  klass = (mw.respond_to?(:klass) ? mw.klass.name : mw.to_s) rescue mw.inspect
262
- Rails.logger.info " #{klass}"
269
+ ar_log(:info, " #{klass}")
263
270
  end
264
- Rails.logger.info "======================="
271
+ ar_log(:info, "=======================")
265
272
 
266
273
  # Skip missing-middleware warnings in development since we may inject via alternate paths
267
274
  unless Rails.env.development?
@@ -284,7 +291,7 @@ module ActiveRabbit
284
291
  end
285
292
  end
286
293
 
287
- Rails.logger.info "[ActiveRabbit] Middleware configured successfully"
294
+ ar_log(:info, "[ActiveRabbit] Middleware configured successfully")
288
295
  end
289
296
 
290
297
  initializer "active_rabbit.error_reporter" do |app|
@@ -374,8 +381,8 @@ module ActiveRabbit
374
381
  begin
375
382
  reporting_file, reporting_line = ActiveRabbit::Reporting.method(:report_exception).source_location
376
383
  http_file, http_line = ActiveRabbit::Client::HttpClient.instance_method(:post_exception).source_location
377
- Rails.logger.info "[ActiveRabbit] Reporting loaded from #{reporting_file}:#{reporting_line}" if defined?(Rails)
378
- Rails.logger.info "[ActiveRabbit] HttpClient#post_exception from #{http_file}:#{http_line}" if defined?(Rails)
384
+ ar_log(:info, "[ActiveRabbit] Reporting loaded from #{reporting_file}:#{reporting_line}") if defined?(Rails)
385
+ ar_log(:info, "[ActiveRabbit] HttpClient#post_exception from #{http_file}:#{http_line}") if defined?(Rails)
379
386
  rescue => e
380
387
  Rails.logger.debug "[ActiveRabbit] boot diagnostics failed: #{e.message}" if defined?(Rails)
381
388
  end
@@ -384,6 +391,34 @@ module ActiveRabbit
384
391
 
385
392
  private
386
393
 
394
+ def ar_puts(msg)
395
+ cfg = ActiveRabbit::Client.configuration
396
+ return if cfg && cfg.disable_console_logs
397
+ puts msg
398
+ end
399
+
400
+ def ar_log(level, msg)
401
+ cfg = ActiveRabbit::Client.configuration
402
+ return if cfg && cfg.disable_console_logs
403
+ Rails.logger.public_send(level, msg) if Rails.logger
404
+ end
405
+
406
+ def apply_rails_configuration(rails_config)
407
+ return unless rails_config
408
+
409
+ options = rails_config.respond_to?(:to_h) ? rails_config.to_h : rails_config
410
+ return if options.nil? || options.empty?
411
+
412
+ ActiveRabbit::Client.configure do |config|
413
+ options.each do |key, value|
414
+ next if value.nil?
415
+
416
+ writer = "#{key}="
417
+ config.public_send(writer, value) if config.respond_to?(writer)
418
+ end
419
+ end
420
+ end
421
+
387
422
  def setup_exception_tracking(app)
388
423
  # Handle uncaught exceptions in development
389
424
  if Rails.env.development? || Rails.env.test?
@@ -392,19 +427,19 @@ module ActiveRabbit
392
427
  end
393
428
 
394
429
  def subscribe_to_controller_events
395
- Rails.logger.info "[ActiveRabbit] Subscribing to controller events (configured=#{ActiveRabbit::Client.configured?})"
430
+ ar_log(:info, "[ActiveRabbit] Subscribing to controller events (configured=#{ActiveRabbit::Client.configured?})")
396
431
 
397
432
  ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
398
433
  begin
399
434
  unless ActiveRabbit::Client.configured?
400
435
  Rails.logger.debug "[ActiveRabbit] Skipping performance tracking - not configured"
401
- return
436
+ next
402
437
  end
403
438
 
404
439
  duration_ms = ((finished - started) * 1000).round(2)
405
440
 
406
- Rails.logger.info "[ActiveRabbit] 📊 Controller action: #{payload[:controller]}##{payload[:action]} - #{duration_ms}ms"
407
- Rails.logger.info "[ActiveRabbit] 📊 DB runtime: #{payload[:db_runtime]}, View runtime: #{payload[:view_runtime]}"
441
+ ar_log(:info, "[ActiveRabbit] 📊 Controller action: #{payload[:controller]}##{payload[:action]} - #{duration_ms}ms")
442
+ ar_log(:info, "[ActiveRabbit] 📊 DB runtime: #{payload[:db_runtime]}, View runtime: #{payload[:view_runtime]}")
408
443
 
409
444
  ActiveRabbit::Client.track_performance(
410
445
  "controller.action",
@@ -672,7 +707,7 @@ module ActiveRabbit
672
707
 
673
708
  def call(env)
674
709
  # debug start - using Rails.logger to ensure it appears in development.log
675
- Rails.logger.info "[AR] ExceptionMiddleware ENTER path=#{env['PATH_INFO']}" if defined?(Rails)
710
+ ar_log(:info, "[AR] ExceptionMiddleware ENTER path=#{env['PATH_INFO']}") if defined?(Rails)
676
711
  warn "[AR] ExceptionMiddleware ENTER path=#{env['PATH_INFO']}"
677
712
  warn "[AR] Current exceptions in env:"
678
713
  warn " - action_dispatch.exception: #{env['action_dispatch.exception']&.class}"
@@ -686,12 +721,12 @@ module ActiveRabbit
686
721
 
687
722
  # Check for exceptions in env after app call
688
723
  if (ex = env["action_dispatch.exception"] || env["rack.exception"] || env["action_dispatch.error"])
689
- Rails.logger.info "[AR] env exception present: #{ex.class}: #{ex.message}" if defined?(Rails)
724
+ ar_log(:info, "[AR] env exception present: #{ex.class}: #{ex.message}") if defined?(Rails)
690
725
  warn "[AR] env exception present: #{ex.class}: #{ex.message}"
691
726
  warn "[AR] Exception backtrace: #{ex.backtrace&.first(3)&.join("\n ")}"
692
727
  safe_report(ex, env, 'Rails rescued exception')
693
728
  else
694
- Rails.logger.info "[AR] env exception NOT present" if defined?(Rails)
729
+ ar_log(:info, "[AR] env exception NOT present") if defined?(Rails)
695
730
  warn "[AR] env exception NOT present"
696
731
  warn "[AR] Final env check:"
697
732
  warn " - action_dispatch.exception: #{env['action_dispatch.exception']&.class}"
@@ -703,7 +738,7 @@ module ActiveRabbit
703
738
  [status, headers, body]
704
739
  rescue => e
705
740
  # Primary path: catch raw exceptions before Rails rescuers
706
- Rails.logger.info "[AR] RESCUE caught: #{e.class}: #{e.message}" if defined?(Rails)
741
+ ar_log(:info, "[AR] RESCUE caught: #{e.class}: #{e.message}") if defined?(Rails)
707
742
  warn "[AR] RESCUE caught: #{e.class}: #{e.message}"
708
743
  warn "[AR] Rescue backtrace: #{e.backtrace&.first(3)&.join("\n ")}"
709
744
 
@@ -748,7 +783,7 @@ module ActiveRabbit
748
783
  result = ActiveRabbit::Client.track_exception(exception, context: context)
749
784
  warn "[AR] Track result: #{result.inspect}"
750
785
 
751
- Rails.logger.info "[ActiveRabbit] Tracked #{source}: #{exception.class.name} - #{exception.message}" if defined?(Rails)
786
+ ar_log(:info, "[ActiveRabbit] Tracked #{source}: #{exception.class.name} - #{exception.message}") if defined?(Rails)
752
787
  rescue => tracking_error
753
788
  # Log tracking errors but don't let them interfere with exception handling
754
789
  warn "[AR] Error in safe_report: #{tracking_error.class} - #{tracking_error.message}"
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module ActiveRabbit
4
2
  module Client
5
- VERSION = "0.4.7"
3
+ VERSION = "0.5.0"
6
4
  end
7
5
  end
@@ -77,8 +77,8 @@ module ActiveRabbit
77
77
  result = exception_tracker.track_exception(**args)
78
78
 
79
79
  # Log the result
80
- configuration.logger&.info("[ActiveRabbit] Exception tracked: #{exception.class.name}")
81
- configuration.logger&.debug("[ActiveRabbit] Exception tracking result: #{result.inspect}")
80
+ ActiveRabbit::Client.log(:info, "[ActiveRabbit] Exception tracked: #{exception.class.name}")
81
+ ActiveRabbit::Client.log(:debug, "[ActiveRabbit] Exception tracking result: #{result.inspect}")
82
82
 
83
83
  result
84
84
  end
@@ -127,6 +127,32 @@ module ActiveRabbit
127
127
  track_exception(exception, context: context, user_id: user_id, tags: tags)
128
128
  end
129
129
 
130
+ def notify_deploy(project_slug:, status:, user:, version:, started_at: nil, finished_at: nil)
131
+ payload = {
132
+ revision: Client.configuration.revision,
133
+ environment: Client.configuration.environment,
134
+ project_slug: project_slug,
135
+ version: version,
136
+ status: status,
137
+ user: user,
138
+ started_at: started_at,
139
+ finished_at: finished_at
140
+ }
141
+
142
+ http_client.post("/api/v1/deploys", payload)
143
+ end
144
+
145
+ def log(level, message)
146
+ cfg = configuration
147
+ return if cfg.nil? || cfg.disable_console_logs
148
+
149
+ case level
150
+ when :info then cfg.logger&.info(message)
151
+ when :debug then cfg.logger&.debug(message)
152
+ when :error then cfg.logger&.error(message)
153
+ end
154
+ end
155
+
130
156
  private
131
157
 
132
158
  def event_processor
@@ -13,7 +13,7 @@ module ActiveRabbit
13
13
  begin
14
14
  ActiveRabbit::Reporting.report_exception(e, env: env, handled: false, source: "middleware", force: true)
15
15
  rescue => inner
16
- Rails.logger&.error("[ActiveRabbit] ErrorCaptureMiddleware failed: #{inner.class}: #{inner.message}") if defined?(Rails)
16
+ ActiveRabbit::Client.log(:error, "[ActiveRabbit] ErrorCaptureMiddleware failed: #{inner.class}: #{inner.message}")
17
17
  end
18
18
  raise
19
19
  end
@@ -40,7 +40,7 @@ module ActiveRabbit
40
40
  force: force)
41
41
  rescue => e
42
42
  if defined?(Rails)
43
- Rails.logger&.error("[ActiveRabbit] report_exception failed: #{e.class}: #{e.message}")
43
+ ActiveRabbit::Client.log(:error, "[ActiveRabbit] report_exception failed: #{e.class}: #{e.message}")
44
44
  end
45
45
  nil
46
46
  end
@@ -8,7 +8,7 @@ module ActiveRabbit
8
8
  begin
9
9
  ActiveRabbit::Reporting.report_exception(error, env: env, handled: true, source: "router", force: true)
10
10
  rescue => e
11
- Rails.logger&.error("[ActiveRabbit] NotFoundApp failed to report: #{e.class}: #{e.message}") if defined?(Rails)
11
+ ActiveRabbit::Client.log(:error, "[ActiveRabbit] NotFoundApp failed to report: #{e.class}: #{e.message}")
12
12
  end
13
13
  [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerabbit-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Shapalov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-12 00:00:00.000000000 Z
11
+ date: 2025-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby