opentrace 0.15.1 → 0.16.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: e5c67d0aecd2be7ec0624bd339bda99a46d8c2de8d0a04009d6778f94cefee3d
4
- data.tar.gz: 0b54c97978becc24a2339e1f615de4f715388fc9247dc88099880dc7e1532a43
3
+ metadata.gz: e9eff1e2688a1239f8ebafd7cd59487b4ffab4619107c7fda9db580f9de04bed
4
+ data.tar.gz: 6a4151670c6f6b803e7fff47fe2e7621ab17c52a7576d2d43c0fd416e337ccc6
5
5
  SHA512:
6
- metadata.gz: 60bc99ef6e4e950851358a9dbf3b2b8342209f81ff24d58eb248c07362657d4c040c1f60e6e8fdb3cb56d625cac4f8b9a4641e953241a1af52195f27232790f6
7
- data.tar.gz: d655b2237f513c8b3fe8a12e936086937fc80b1f50d5bb3766b6e5ef3ed1a137d90bf9798546aa99413633dba9e9588c87f2b99d4e54e226c0842fea720f309e
6
+ metadata.gz: 1931c39a5b24f9aea0eeb81aacb77945a4c0f2386f13c3862172119e59c90eb13fab43e7af42981c6c8626f660a6a548bd040d6f1d087262716045e971d81ae0
7
+ data.tar.gz: ef27898fabf3fe17bc1aaf98cd4003b4008740685124398c5eeb80ddb47c737284ee36e99c516103d9306d00f2c18ea016d2346b93380452dfa56b15f6c6b01c
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTrace
4
+ # Rails 7.0+ Error Reporter subscriber.
5
+ #
6
+ # Captures ALL exceptions reported via Rails.error (including those
7
+ # rescued by rescue_from in controllers). This fills the gap where
8
+ # process_action.action_controller only includes exception data for
9
+ # unhandled exceptions — rescued exceptions show status 500 but no
10
+ # exception_class, backtrace, or error_fingerprint.
11
+ #
12
+ # Registered automatically in the Railtie when Rails.error is available.
13
+ class ErrorSubscriber
14
+ SEVERITY_MAP = {
15
+ error: "ERROR",
16
+ warning: "WARN",
17
+ info: "INFO"
18
+ }.freeze
19
+
20
+ def report(error, handled:, severity:, context: {}, source: nil)
21
+ return unless OpenTrace.enabled?
22
+
23
+ # Skip if already inside an OpenTrace logging call (re-entrance guard)
24
+ return if Fiber[:opentrace_logging]
25
+
26
+ level = SEVERITY_MAP[severity] || "ERROR"
27
+
28
+ meta = {}
29
+ meta[:exception_class] = error.class.name
30
+ meta[:exception_message] = error.message&.slice(0, 500)
31
+ meta[:handled] = handled
32
+ meta[:error_source] = source if source
33
+
34
+ if error.backtrace
35
+ cleaned = clean_backtrace(error.backtrace)
36
+ meta[:backtrace] = cleaned.first(15)
37
+ meta[:error_fingerprint] = OpenTrace.send(:compute_error_fingerprint, error.class.name, cleaned)
38
+ end
39
+
40
+ # Capture exception cause chain
41
+ if error.cause
42
+ meta[:exception_causes] = OpenTrace.send(:build_cause_chain, error.cause, depth: 0)
43
+ end
44
+
45
+ # Include request params from context if available
46
+ if context[:params].is_a?(Hash)
47
+ params = context[:params].except("controller", "action")
48
+ meta[:params] = truncate_hash(params, 2048) unless params.empty?
49
+ end
50
+
51
+ # Include controller/action context
52
+ meta[:controller] = context[:controller] if context[:controller]
53
+ meta[:action] = context[:action] if context[:action]
54
+
55
+ # Merge any additional context from the reporter
56
+ context.each do |k, v|
57
+ next if %i[params controller action].include?(k)
58
+ meta[k] = v
59
+ end
60
+
61
+ OpenTrace.log(level, "#{error.class}: #{error.message&.slice(0, 200)}", meta)
62
+ rescue StandardError
63
+ # Never raise to the host app
64
+ end
65
+
66
+ private
67
+
68
+ def clean_backtrace(backtrace)
69
+ if defined?(::Rails) && ::Rails.respond_to?(:backtrace_cleaner)
70
+ ::Rails.backtrace_cleaner.clean(backtrace)
71
+ else
72
+ backtrace.reject { |line| line.include?("/gems/") }
73
+ end
74
+ end
75
+
76
+ def truncate_hash(hash, max_bytes)
77
+ json = JSON.generate(hash)
78
+ return hash if json.bytesize <= max_bytes
79
+ { _truncated: true, _size: json.bytesize }
80
+ rescue StandardError
81
+ { _truncated: true }
82
+ end
83
+ end
84
+ end
@@ -15,6 +15,13 @@ if defined?(::Rails::Railtie)
15
15
  config.after_initialize do |app|
16
16
  next unless OpenTrace.enabled?
17
17
 
18
+ # Rails Error Reporter subscriber (Rails 7.0+) — captures ALL exceptions
19
+ # including those rescued by rescue_from in controllers.
20
+ if defined?(Rails.error) && Rails.error.respond_to?(:subscribe)
21
+ require_relative "error_subscriber"
22
+ Rails.error.subscribe(OpenTrace::ErrorSubscriber.new)
23
+ end
24
+
18
25
  # Automatic log trace injection (opt-in) — wraps Rails logger formatter
19
26
  if OpenTrace.config.log_trace_injection
20
27
  require_relative "trace_formatter"
@@ -207,6 +214,13 @@ if defined?(::Rails::Railtie)
207
214
  extract_request_headers(payload, extra)
208
215
  end
209
216
 
217
+ # Always include params on error responses (status >= 500)
218
+ # so the AI agent can diagnose what input caused the failure.
219
+ if !extra&.key?(:params) && payload[:status].to_i >= 500
220
+ extra ||= {}
221
+ extract_params(payload, extra)
222
+ end
223
+
210
224
  # SQL counters for non-collector path (only present for sampled requests)
211
225
  sql_count = Fiber[:opentrace_sql_count]
212
226
  if sql_count && !Fiber[:opentrace_collector]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenTrace
4
- VERSION = "0.15.1"
4
+ VERSION = "0.16.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenTrace
@@ -38,6 +38,7 @@ files:
38
38
  - lib/opentrace/circuit_breaker.rb
39
39
  - lib/opentrace/client.rb
40
40
  - lib/opentrace/config.rb
41
+ - lib/opentrace/error_subscriber.rb
41
42
  - lib/opentrace/http_tracker.rb
42
43
  - lib/opentrace/local_vars.rb
43
44
  - lib/opentrace/log_forwarder.rb