honeybadger 6.6.2 → 6.9.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: 03a414b7ec784574a3164541f03d9e2b60d3a4af1d09b754d5c630282b3f0c12
4
- data.tar.gz: 26afeb772bb5e998ed9a15a213697b72d3415ffadfc858fc1419913daa5ca500
3
+ metadata.gz: c0df809e34f5f0cbd3e4d63c411f215dc2f9418b8909a7aedb9a5fb6c454afbe
4
+ data.tar.gz: e811145ad02eef49a8078d3cb827ad47cdf35693a986a55e12bf2a6108f1b052
5
5
  SHA512:
6
- metadata.gz: 7ac887b4d796a351c0578cd52b492ca5e75a0232ff9f8fb1ab5f323608bfae9224d1e46a6ae8bd928b88cc5e424f65e5683e204c8c14ab9c7c639d76de651522
7
- data.tar.gz: 0ba659e647134528493c16665e40efa33d7ef4ad13af4472005119d807941d4e3c6be047671d8a0588b3b064b71a8cf6d12d3a7402287e029cf4f66dd5173575
6
+ metadata.gz: e4470a6b43117aad7db8069fe87287fc8486c9b02a79ebf43184228251fb81eeb3549b7458593f0ee0ce0e9784b85790b2ad3b97bc01c0cf46d98f49bafd2699
7
+ data.tar.gz: 2aefdc40100c38c1707642be6ab04d0a72248b09734ef8f681e06753f3945854fbcc07f2e89f71ee3477fca1177e273899bb470983cabeb62b080ebafd0f04be
data/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # Change Log
2
2
 
3
3
 
4
+ ## [6.9.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v6.8.0...v6.9.0) (2026-06-11)
5
+
6
+
7
+ ### Features
8
+
9
+ * make RubyLLM insights subscriber configurable ([#829](https://github.com/honeybadger-io/honeybadger-ruby/issues/829)) ([fc9ff42](https://github.com/honeybadger-io/honeybadger-ruby/commit/fc9ff42689b87f972183286d34c7e4059bf23442))
10
+
11
+ ## [6.8.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v6.7.0...v6.8.0) (2026-06-10)
12
+
13
+
14
+ ### Features
15
+
16
+ * add RubyLLM monitoring plugin ([#827](https://github.com/honeybadger-io/honeybadger-ruby/issues/827)) ([4b2c32d](https://github.com/honeybadger-io/honeybadger-ruby/commit/4b2c32d933b1dd35599b2a292027d50458f2c384))
17
+
18
+ ## [6.7.0](https://github.com/honeybadger-io/honeybadger-ruby/compare/v6.6.2...v6.7.0) (2026-06-05)
19
+
20
+
21
+ ### Features
22
+
23
+ * add after_notify hooks ([#825](https://github.com/honeybadger-io/honeybadger-ruby/issues/825)) ([950fee2](https://github.com/honeybadger-io/honeybadger-ruby/commit/950fee2bb2ab81ebbfa20ea34b36045bd6b57034))
24
+ * make backtrace limit configurable ([#824](https://github.com/honeybadger-io/honeybadger-ruby/issues/824)) ([f62ce3a](https://github.com/honeybadger-io/honeybadger-ruby/commit/f62ce3ab09ffa497fbfb2ec1fa193359b66b1a23))
25
+
4
26
  ## [6.6.2](https://github.com/honeybadger-io/honeybadger-ruby/compare/v6.6.0...v6.6.1) (2026-05-29)
5
27
 
6
28
 
@@ -4,6 +4,8 @@ module Honeybadger
4
4
  # @api private
5
5
  # Front end to parsing the backtrace for each notice.
6
6
  class Backtrace
7
+ DEFAULT_LIMIT = 1000
8
+
7
9
  # Handles backtrace parsing line by line.
8
10
  class Line
9
11
  # Backtrace line regexp (optionally allowing leading X: for windows support).
@@ -122,19 +124,20 @@ module Honeybadger
122
124
  Line.parse(unparsed_line.to_s, opts)
123
125
  end.compact
124
126
 
125
- new(lines)
127
+ new(lines, opts.fetch(:limit, DEFAULT_LIMIT))
126
128
  end
127
129
 
128
- def initialize(lines)
130
+ def initialize(lines, limit = DEFAULT_LIMIT)
129
131
  self.lines = lines
130
132
  self.application_lines = lines.select(&:application?)
133
+ self.limit = limit || DEFAULT_LIMIT
131
134
  end
132
135
 
133
136
  # Convert Backtrace to arry.
134
137
  #
135
138
  # Returns array containing backtrace lines.
136
139
  def to_ary
137
- lines.take(1000).map { |l| {number: l.filtered_number, file: l.filtered_file, method: l.filtered_method, source: l.source} }
140
+ lines.take(limit.clamp(0..)).map { |l| {number: l.filtered_number, file: l.filtered_file, method: l.filtered_method, source: l.source} }
138
141
  end
139
142
  alias_method :to_a, :to_ary
140
143
 
@@ -153,7 +156,7 @@ module Honeybadger
153
156
  end
154
157
 
155
158
  def to_s
156
- lines.map(&:to_s).join("\n")
159
+ lines.join("\n")
157
160
  end
158
161
 
159
162
  def inspect
@@ -172,6 +175,8 @@ module Honeybadger
172
175
 
173
176
  attr_writer :lines, :application_lines
174
177
 
178
+ attr_accessor :limit
179
+
175
180
  class << self
176
181
  private
177
182
 
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
  require "honeybadger/breadcrumbs/active_support"
3
+ require "honeybadger/backtrace"
3
4
 
4
5
  module Honeybadger
5
6
  class Config
@@ -321,6 +322,11 @@ module Honeybadger
321
322
  default: 2,
322
323
  type: Integer
323
324
  },
325
+ "exceptions.backtrace_limit": {
326
+ description: "The maximum number of backtrace lines to include in error reports.",
327
+ default: Backtrace::DEFAULT_LIMIT,
328
+ type: Integer
329
+ },
324
330
  "exceptions.local_variables": {
325
331
  description: "Enable sending local variables. Requires binding_of_caller to be loaded.",
326
332
  default: false,
@@ -568,6 +574,16 @@ module Honeybadger
568
574
  description: "Enable automatic data collection for Flipper.",
569
575
  default: true,
570
576
  type: Boolean
577
+ },
578
+ "ruby_llm.insights.enabled": {
579
+ description: "Enable automatic data collection for RubyLLM.",
580
+ default: true,
581
+ type: Boolean
582
+ },
583
+ "ruby_llm.insights.subscriber": {
584
+ description: "Fully qualified class name of a custom subscriber for RubyLLM instrumentation. A class constant may also be given via Ruby configuration. Defaults to Honeybadger::RubyLLMSubscriber.",
585
+ default: nil,
586
+ type: String
571
587
  }
572
588
  }.freeze
573
589
 
@@ -89,21 +89,61 @@ module Honeybadger
89
89
  def before_notify(action = nil, &block)
90
90
  hooks = Array(get(:before_notify)).dup
91
91
 
92
- if action && validate_before_action(action, "notify")
92
+ if action && validate_hook_action(action, "before notify", 1)
93
93
  hooks << action
94
- elsif block_given? && validate_before_action(block, "notify")
94
+ elsif block_given? && validate_hook_action(block, "before notify", 1)
95
95
  hooks << block
96
96
  end
97
97
 
98
98
  hash[:before_notify] = hooks
99
99
  end
100
100
 
101
+ # Run a hook after each error notice delivery attempt.
102
+ #
103
+ # The hook is called for every backend response, including successful
104
+ # deliveries. Response codes are usually HTTP status integers, but may be
105
+ # symbols such as :stubbed or :error for non-server backends or connection
106
+ # failures. Filter with exact response codes (e.g. `response.code == 413`)
107
+ # rather than broad integer comparisons, or use `response.success?`.
108
+ # (Note: `response.error_message` may raise for non-HTTP responses like `:stubbed`.)
109
+ #
110
+ # Prefer `Honeybadger.event` for reporting failed notice deliveries. Calling
111
+ # `Honeybadger.notify` from this hook can trigger another after_notify call;
112
+ # guard against self-reporting loops if a notice must be sent.
113
+ #
114
+ # @example Report oversized notice payloads
115
+ # config.after_notify do |notice, response|
116
+ # next unless response.code == 413
117
+ #
118
+ # Honeybadger.event("honeybadger.notice_rejected", {
119
+ # reason: "payload_too_large",
120
+ # notice_id: notice.id,
121
+ # payload_bytes: notice.to_json.bytesize
122
+ # })
123
+ # end
124
+ #
125
+ # @yieldparam notice [Honeybadger::Notice] The notice that was sent.
126
+ # @yieldparam response [Honeybadger::Backend::Response] The backend response.
127
+ # @return [Array<Proc>] configured hooks
128
+ # @api public
129
+ def after_notify(action = nil, &block)
130
+ hooks = Array(get(:after_notify)).dup
131
+
132
+ if action && validate_hook_action(action, "after notify", 2)
133
+ hooks << action
134
+ elsif block_given? && validate_hook_action(block, "after notify", 2)
135
+ hooks << block
136
+ end
137
+
138
+ hash[:after_notify] = hooks
139
+ end
140
+
101
141
  def before_event(action = nil, &block)
102
142
  hooks = Array(get(:before_event)).dup
103
143
 
104
- if action && validate_before_action(action, "event")
144
+ if action && validate_hook_action(action, "before event", 1)
105
145
  hooks << action
106
- elsif block_given? && validate_before_action(block, "event")
146
+ elsif block_given? && validate_hook_action(block, "before event", 1)
107
147
  hooks << block
108
148
  end
109
149
 
@@ -139,18 +179,18 @@ module Honeybadger
139
179
 
140
180
  private
141
181
 
142
- def validate_before_action(action, type)
182
+ def validate_hook_action(action, name, arity)
143
183
  if !action.respond_to?(:call)
144
184
  logger.warn(
145
- "You attempted to add a before #{type} hook that does not respond " \
185
+ "You attempted to add a #{name} hook that does not respond " \
146
186
  "to #call. We are discarding this hook so your intended behavior " \
147
187
  "will not occur."
148
188
  )
149
189
  false
150
- elsif action.arity != 1
190
+ elsif action.arity != arity
151
191
  logger.warn(
152
- "You attempted to add a before #{type} hook that has an arity " \
153
- "other than one. We are discarding this hook so your intended " \
192
+ "You attempted to add a #{name} hook that has an arity " \
193
+ "other than #{arity}. We are discarding this hook so your intended " \
154
194
  "behavior will not occur."
155
195
  )
156
196
  false
@@ -92,6 +92,10 @@ module Honeybadger
92
92
  (ruby[:before_notify] || []).clone
93
93
  end
94
94
 
95
+ def after_notify_hooks
96
+ (ruby[:after_notify] || []).clone
97
+ end
98
+
95
99
  def before_event_hooks
96
100
  (ruby[:before_event] || []).clone
97
101
  end
@@ -509,7 +509,8 @@ module Honeybadger
509
509
  backtrace,
510
510
  filters: construct_backtrace_filters(opts),
511
511
  config: config,
512
- source_radius: config[:"exceptions.source_radius"]
512
+ source_radius: config[:"exceptions.source_radius"],
513
+ limit: config[:"exceptions.backtrace_limit"]
513
514
  ).to_a
514
515
  end
515
516
 
@@ -0,0 +1,69 @@
1
+ require "honeybadger/plugin"
2
+ require "honeybadger/notification_subscriber"
3
+
4
+ module Honeybadger
5
+ module Plugins
6
+ module RubyLLM
7
+ Plugin.register :ruby_llm do
8
+ requirement { defined?(::RubyLLM) }
9
+ requirement { defined?(::ActiveSupport::Notifications) }
10
+
11
+ execution do
12
+ if config.load_plugin_insights?(:ruby_llm)
13
+ class_name = config[:"ruby_llm.insights.subscriber"].to_s.strip
14
+ subscriber = if class_name.empty?
15
+ Honeybadger::RubyLLMSubscriber.new
16
+ else
17
+ begin
18
+ candidate = Object.const_get(class_name).new
19
+ unless candidate.respond_to?(:start) && candidate.respond_to?(:finish)
20
+ raise TypeError, "does not respond to #start and #finish"
21
+ end
22
+ candidate
23
+ rescue => e
24
+ logger.error("Unable to load ruby_llm.insights.subscriber=#{class_name} (#{e.class}: #{e.message}); falling back to Honeybadger::RubyLLMSubscriber")
25
+ Honeybadger::RubyLLMSubscriber.new
26
+ end
27
+ end
28
+
29
+ # request.ruby_llm is intentionally excluded: it fires for every
30
+ # provider HTTP request (including retries and streams) and
31
+ # duplicates chat-level metadata.
32
+ ::ActiveSupport::Notifications.subscribe(
33
+ /(chat|tool_call|embedding|image|moderation|transcription|models\.refresh)\.ruby_llm/,
34
+ subscriber
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ module Honeybadger
44
+ class RubyLLMSubscriber < NotificationSubscriber
45
+ # Payloads carry full Ruby objects (chat, messages, responses, tool
46
+ # arguments, inputs), which may contain sensitive content. Allow only
47
+ # scalar metadata through.
48
+ def format_payload(name, payload)
49
+ case name
50
+ when "chat.ruby_llm"
51
+ payload.slice(:provider, :provider_class, :model, :message_count, :temperature, :tool_choice, :tool_call_limit, :streaming, :response_model, :response_role, :tool_call, :input_tokens, :output_tokens, :cached_tokens, :cache_creation_tokens, :thinking_tokens, :exception)
52
+ when "tool_call.ruby_llm"
53
+ payload.slice(:provider, :provider_class, :model, :tool_name, :tool_call_id, :result_class, :exception)
54
+ when "embedding.ruby_llm"
55
+ payload.slice(:provider, :provider_class, :model, :dimensions, :response_model, :input_tokens, :embedding_dimensions, :embedding_count, :exception)
56
+ when "image.ruby_llm"
57
+ payload.slice(:provider, :provider_class, :model, :size, :response_model, :exception)
58
+ when "moderation.ruby_llm"
59
+ payload.slice(:provider, :provider_class, :model, :flagged, :exception)
60
+ when "transcription.ruby_llm"
61
+ payload.slice(:provider, :provider_class, :model, :language, :response_model, :input_tokens, :output_tokens, :exception)
62
+ when "models.refresh.ruby_llm"
63
+ payload.slice(:remote_only, :model_count, :exception)
64
+ else
65
+ payload.slice(:provider, :provider_class, :model, :exception)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,4 +1,4 @@
1
1
  module Honeybadger
2
2
  # The current String Honeybadger version.
3
- VERSION = "6.6.2".freeze
3
+ VERSION = "6.9.0".freeze
4
4
  end
@@ -46,7 +46,9 @@ module Honeybadger
46
46
  end
47
47
 
48
48
  def send_now(msg)
49
- handle_response(msg, notify_backend(msg))
49
+ response = notify_backend(msg)
50
+ run_after_notify_hooks(msg, response)
51
+ handle_response(msg, response)
50
52
  end
51
53
 
52
54
  def shutdown(force = false)
@@ -189,6 +191,21 @@ module Honeybadger
189
191
  backend.notify(:notices, payload)
190
192
  end
191
193
 
194
+ def run_after_notify_hooks(msg, response)
195
+ config.after_notify_hooks.each do |hook|
196
+ with_error_handling { hook.call(msg, response) }
197
+ end
198
+ end
199
+
200
+ def with_error_handling
201
+ yield
202
+ rescue => ex
203
+ error {
204
+ msg = "Rescued an error in an after notify hook class=%s message=%s\n\t%s"
205
+ sprintf(msg, ex.class, ex.message.dump, Array(ex.backtrace).join("\n\t"))
206
+ }
207
+ end
208
+
192
209
  def calc_throttle_interval
193
210
  ((BASE_THROTTLE**throttle) - 1).round(3)
194
211
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeybadger
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.6.2
4
+ version: 6.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Honeybadger Industries LLC
@@ -122,6 +122,7 @@ files:
122
122
  - lib/honeybadger/plugins/passenger.rb
123
123
  - lib/honeybadger/plugins/rails.rb
124
124
  - lib/honeybadger/plugins/resque.rb
125
+ - lib/honeybadger/plugins/ruby_llm.rb
125
126
  - lib/honeybadger/plugins/shoryuken.rb
126
127
  - lib/honeybadger/plugins/sidekiq.rb
127
128
  - lib/honeybadger/plugins/solid_queue.rb