bugwatch-ruby 0.8.0 → 0.8.2

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: c2096369bb2864dc1713e2998bc6438302122dc3237194c4f12349094d4b046e
4
- data.tar.gz: 472e7299734b4f451401a3d828c3465ac1071fb1cd8a1bfba49edebd66db06dc
3
+ metadata.gz: 4359915525161c69c489758beceaae532ccf3cb0b5169d9839a5773b45e570a6
4
+ data.tar.gz: 6d0383a28fd577cebdbc9c9330935a9e454e5605c721df63010a2259d147f278
5
5
  SHA512:
6
- metadata.gz: 013ae4e4faa1dba3cc77ccc7dc9a81646025aaeb30cd9b835e512ee0d47bc3cfd58f44c47f9516e2272ec352e912470163d8dee0d394e490001f37d58c6df1c2
7
- data.tar.gz: 38f4a61e9b51617b4095b3ee6d8320453ec4de615c6a76aa8c13303a2ef9a22cd8c4cde377b261ef447040a9ede948e4e4b462d15ce91019b34ca8ecaef46aaa
6
+ metadata.gz: 619c58df59ff62528a16fd3543e3a630f784af39ad64d824736de56b0e6632fcf3cb577935fe995d6a78f254d727380b0d8b2795ed98a9bcd5c543a9fe0bdec1
7
+ data.tar.gz: 26f434d1ab6ae0e87288e8282f50cce74d7d83e91b10bcbc6e63b0904dabc4384083c15f2c5f754953ca64260cb66010de333fd1a9aa26b27a2bed5cb6d6a7fd
data/README.md CHANGED
@@ -257,8 +257,8 @@ Sensitive params (`password`, `token`, `secret`, `key`, `auth`, `credit`, `card`
257
257
  ## Publishing
258
258
 
259
259
  ```bash
260
- cd /home/max/bugwatch-ruby
260
+ cd gems/bugwatch-ruby
261
261
  gem build bugwatch-ruby.gemspec
262
262
  gem signin
263
- gem push bugwatch-ruby-0.1.0.gem
263
+ gem push bugwatch-ruby-0.8.2.gem
264
264
  ```
@@ -3,6 +3,7 @@ module Bugwatch
3
3
  def call(job, exception)
4
4
  return if Bugwatch.configuration.ignore?(exception)
5
5
  return unless Bugwatch.configuration.notify_for_release_stage?
6
+ return if ReportedExceptions.reported?(exception)
6
7
 
7
8
  payload = ErrorBuilder.new(exception).build
8
9
  payload[:context] = {
@@ -13,6 +14,7 @@ module Bugwatch
13
14
  }
14
15
 
15
16
  Notification.new(payload).deliver
17
+ ReportedExceptions.mark(exception)
16
18
  end
17
19
 
18
20
  private
@@ -0,0 +1,23 @@
1
+ module Bugwatch
2
+ module ControllerRescueHook
3
+ def rescue_with_handler(exception, **kwargs)
4
+ handler = super
5
+
6
+ if handler && exception.is_a?(Exception) && !ReportedExceptions.reported?(exception)
7
+ begin
8
+ context = {
9
+ handled: true,
10
+ controller: self.class.name,
11
+ action: (action_name if respond_to?(:action_name))
12
+ }.compact
13
+
14
+ Bugwatch.notify(exception, context: context)
15
+ rescue StandardError
16
+ # Never let reporting break the host's rescue flow
17
+ end
18
+ end
19
+
20
+ handler
21
+ end
22
+ end
23
+ end
@@ -3,6 +3,7 @@ module Bugwatch
3
3
  def report(error, handled:, severity:, context: {}, source: nil)
4
4
  return if Bugwatch.configuration.ignore?(error)
5
5
  return unless Bugwatch.configuration.notify_for_release_stage?
6
+ return if ReportedExceptions.reported?(error)
6
7
 
7
8
  payload = ErrorBuilder.new(error).build
8
9
  payload[:context] = context.merge(
@@ -12,6 +13,7 @@ module Bugwatch
12
13
  ).compact
13
14
 
14
15
  Notification.new(payload).deliver
16
+ ReportedExceptions.mark(error)
15
17
  end
16
18
  end
17
19
  end
@@ -96,6 +96,6 @@ module Bugwatch
96
96
  nil
97
97
  end
98
98
 
99
- private_class_method :handle_call, :extract_path, :detect_library, :extract_caller
99
+ private_class_method :extract_path, :detect_library, :extract_caller
100
100
  end
101
101
  end
@@ -12,6 +12,7 @@ module Bugwatch
12
12
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
13
13
  BreadcrumbCollector.clear
14
14
  UserContext.clear
15
+ ReportedExceptions.clear
15
16
 
16
17
  config = Bugwatch.configuration
17
18
  collecting = config.enable_db_tracking && (rand < config.db_sample_rate)
@@ -27,10 +28,15 @@ module Bugwatch
27
28
  rescue Exception => e # rubocop:disable Lint/RescueException
28
29
  duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
29
30
 
30
- unless Bugwatch.configuration.ignore?(e)
31
- payload = ErrorBuilder.new(e, env).build
32
- payload[:duration_ms] = duration_ms
33
- Notification.new(payload).deliver
31
+ begin
32
+ if !Bugwatch.configuration.ignore?(e) && !ReportedExceptions.reported?(e)
33
+ payload = ErrorBuilder.new(e, env).build
34
+ payload[:duration_ms] = duration_ms
35
+ Notification.new(payload).deliver
36
+ ReportedExceptions.mark(e)
37
+ end
38
+ rescue StandardError
39
+ # Never let reporting failures drop the original exception
34
40
  end
35
41
 
36
42
  record_transaction(env, 500, start)
@@ -40,6 +40,12 @@ module Bugwatch
40
40
  Rails.error.subscribe(Bugwatch::ErrorSubscriber.new)
41
41
  end
42
42
  end
43
+
44
+ initializer "bugwatch.controller_rescue_hook" do
45
+ ActiveSupport.on_load(:action_controller) do
46
+ prepend Bugwatch::ControllerRescueHook
47
+ end
48
+ end
43
49
  end
44
50
 
45
51
  module ControllerMethods
@@ -0,0 +1,19 @@
1
+ module Bugwatch
2
+ module ReportedExceptions
3
+ THREAD_KEY = :bugwatch_reported_exceptions
4
+
5
+ def self.mark(exception)
6
+ return unless exception
7
+ (Thread.current[THREAD_KEY] ||= []) << exception.object_id
8
+ end
9
+
10
+ def self.reported?(exception)
11
+ return false unless exception
12
+ (Thread.current[THREAD_KEY] || []).include?(exception.object_id)
13
+ end
14
+
15
+ def self.clear
16
+ Thread.current[THREAD_KEY] = nil
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Bugwatch
2
- VERSION = "0.8.0"
2
+ VERSION = "0.8.2"
3
3
  end
data/lib/bugwatch.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "bugwatch/configuration"
6
6
  require_relative "bugwatch/user_context"
7
7
  require_relative "bugwatch/breadcrumb_collector"
8
8
  require_relative "bugwatch/backtrace_cleaner"
9
+ require_relative "bugwatch/reported_exceptions"
9
10
  require_relative "bugwatch/error_builder"
10
11
  require_relative "bugwatch/report_builder"
11
12
  require_relative "bugwatch/notification"
@@ -22,6 +23,7 @@ require_relative "bugwatch/feedback_helper"
22
23
  require_relative "bugwatch/middleware"
23
24
  require_relative "bugwatch/active_job_handler"
24
25
  require_relative "bugwatch/error_subscriber"
26
+ require_relative "bugwatch/controller_rescue_hook"
25
27
  require_relative "bugwatch/railtie" if defined?(Rails::Railtie)
26
28
 
27
29
  module Bugwatch
@@ -37,10 +39,12 @@ module Bugwatch
37
39
  def notify(exception, context: {})
38
40
  return if configuration.ignore?(exception)
39
41
  return unless configuration.notify_for_release_stage?
42
+ return if ReportedExceptions.reported?(exception)
40
43
 
41
44
  payload = ErrorBuilder.new(exception).build
42
45
  payload.merge!(context)
43
46
  Notification.new(payload).deliver
47
+ ReportedExceptions.mark(exception)
44
48
  end
45
49
 
46
50
  def report(title, category: "other", severity: "warning", tags: {})
@@ -92,10 +96,19 @@ module Bugwatch
92
96
  end
93
97
 
94
98
  def track_deploy(version:, environment: nil, description: nil, deployed_by: nil)
95
- return unless configuration.api_key
96
- return unless configuration.endpoint
99
+ logger = configuration.logger
100
+
101
+ unless configuration.api_key
102
+ logger&.warn("[bugwatch] track_deploy skipped: configuration.api_key is not set")
103
+ return
104
+ end
105
+ unless configuration.endpoint
106
+ logger&.warn("[bugwatch] track_deploy skipped: configuration.endpoint is not set")
107
+ return
108
+ end
97
109
 
98
110
  env = environment || configuration.release_stage
111
+ logger&.info("[bugwatch] track_deploy posting version=#{version} environment=#{env} endpoint=#{configuration.endpoint}")
99
112
 
100
113
  Thread.new do
101
114
  uri = URI.parse("#{configuration.endpoint.chomp("/")}/api/v1/deploys")
@@ -117,9 +130,17 @@ module Bugwatch
117
130
 
118
131
  request.body = JSON.generate(payload)
119
132
 
120
- http.request(request)
121
- rescue StandardError
122
- # Fire-and-forget: never affect the host app
133
+ response = http.request(request)
134
+ status = response.code.to_i
135
+ if status.between?(200, 299)
136
+ logger&.info("[bugwatch] track_deploy succeeded status=#{status}")
137
+ else
138
+ logger&.warn("[bugwatch] track_deploy rejected status=#{status} body=#{response.body.to_s[0, 500]}")
139
+ end
140
+ response
141
+ rescue StandardError => e
142
+ logger&.error("[bugwatch] track_deploy error: #{e.class}: #{e.message}")
143
+ e
123
144
  end
124
145
  end
125
146
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugwatch-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - BugWatch
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-04-19 00:00:00.000000000 Z
10
+ date: 2026-05-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: railties
@@ -52,6 +52,7 @@ files:
52
52
  - lib/bugwatch/backtrace_cleaner.rb
53
53
  - lib/bugwatch/breadcrumb_collector.rb
54
54
  - lib/bugwatch/configuration.rb
55
+ - lib/bugwatch/controller_rescue_hook.rb
55
56
  - lib/bugwatch/db_query_buffer.rb
56
57
  - lib/bugwatch/db_query_sender.rb
57
58
  - lib/bugwatch/db_tracker.rb
@@ -66,6 +67,7 @@ files:
66
67
  - lib/bugwatch/notification.rb
67
68
  - lib/bugwatch/railtie.rb
68
69
  - lib/bugwatch/report_builder.rb
70
+ - lib/bugwatch/reported_exceptions.rb
69
71
  - lib/bugwatch/transaction_buffer.rb
70
72
  - lib/bugwatch/transaction_sender.rb
71
73
  - lib/bugwatch/user_context.rb