rails_error_dashboard 0.5.7 → 0.5.9

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -3
  3. data/app/controllers/rails_error_dashboard/errors_controller.rb +81 -4
  4. data/app/controllers/rails_error_dashboard/webhooks_controller.rb +192 -0
  5. data/app/jobs/rails_error_dashboard/add_issue_recurrence_comment_job.rb +71 -0
  6. data/app/jobs/rails_error_dashboard/close_linked_issue_job.rb +43 -0
  7. data/app/jobs/rails_error_dashboard/create_issue_job.rb +68 -0
  8. data/app/jobs/rails_error_dashboard/reopen_linked_issue_job.rb +44 -0
  9. data/app/models/rails_error_dashboard/error_log.rb +2 -1
  10. data/app/views/layouts/rails_error_dashboard.html.erb +19 -6
  11. data/app/views/rails_error_dashboard/errors/_breadcrumbs_group.html.erb +1 -1
  12. data/app/views/rails_error_dashboard/errors/_discussion.html.erb +92 -100
  13. data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +121 -0
  14. data/app/views/rails_error_dashboard/errors/_show_scripts.html.erb +1 -0
  15. data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +77 -73
  16. data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +148 -0
  17. data/app/views/rails_error_dashboard/errors/show.html.erb +13 -9
  18. data/config/routes.rb +6 -1
  19. data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +5 -0
  20. data/db/migrate/20260326000001_add_issue_tracking_to_error_logs.rb +15 -0
  21. data/lib/generators/rails_error_dashboard/install/install_generator.rb +12 -4
  22. data/lib/rails_error_dashboard/commands/create_issue.rb +59 -0
  23. data/lib/rails_error_dashboard/commands/link_existing_issue.rb +65 -0
  24. data/lib/rails_error_dashboard/configuration.rb +99 -0
  25. data/lib/rails_error_dashboard/engine.rb +39 -0
  26. data/lib/rails_error_dashboard/queries/active_storage_summary.rb +101 -0
  27. data/lib/rails_error_dashboard/services/codeberg_issue_client.rb +122 -0
  28. data/lib/rails_error_dashboard/services/github_issue_client.rb +117 -0
  29. data/lib/rails_error_dashboard/services/github_link_generator.rb +19 -1
  30. data/lib/rails_error_dashboard/services/gitlab_issue_client.rb +121 -0
  31. data/lib/rails_error_dashboard/services/issue_body_formatter.rb +132 -0
  32. data/lib/rails_error_dashboard/services/issue_tracker_client.rb +168 -0
  33. data/lib/rails_error_dashboard/services/markdown_error_formatter.rb +12 -0
  34. data/lib/rails_error_dashboard/subscribers/active_storage_subscriber.rb +112 -0
  35. data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +71 -0
  36. data/lib/rails_error_dashboard/version.rb +1 -1
  37. data/lib/rails_error_dashboard.rb +11 -1
  38. metadata +21 -3
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+
7
+ module RailsErrorDashboard
8
+ module Services
9
+ # Base class and factory for issue tracker API clients.
10
+ #
11
+ # Supports GitHub, GitLab, and Codeberg/Gitea/Forgejo via a unified interface.
12
+ # Each provider implements the same methods with provider-specific API calls.
13
+ #
14
+ # @example
15
+ # client = IssueTrackerClient.for(:github, token: "ghp_xxx", repo: "user/repo")
16
+ # result = client.create_issue(title: "NoMethodError", body: "...", labels: ["bug"])
17
+ # # => { url: "https://github.com/user/repo/issues/42", number: 42 }
18
+ class IssueTrackerClient
19
+ REQUEST_TIMEOUT = 15 # seconds
20
+ MAX_BODY_LENGTH = 65_000 # GitHub has ~65K limit for issue body
21
+
22
+ attr_reader :token, :repo, :api_url
23
+
24
+ # Factory method — returns the correct client for the provider
25
+ #
26
+ # @param provider [Symbol] :github, :gitlab, or :codeberg
27
+ # @param token [String] API authentication token
28
+ # @param repo [String] Repository identifier ("owner/repo")
29
+ # @param api_url [String, nil] Custom API base URL (for self-hosted)
30
+ # @return [IssueTrackerClient] Provider-specific client instance
31
+ def self.for(provider, token:, repo:, api_url: nil)
32
+ case provider&.to_sym
33
+ when :github
34
+ GitHubIssueClient.new(token: token, repo: repo, api_url: api_url)
35
+ when :gitlab
36
+ GitLabIssueClient.new(token: token, repo: repo, api_url: api_url)
37
+ when :codeberg
38
+ CodebergIssueClient.new(token: token, repo: repo, api_url: api_url)
39
+ else
40
+ raise ArgumentError, "Unknown issue tracker provider: #{provider}. Supported: :github, :gitlab, :codeberg"
41
+ end
42
+ end
43
+
44
+ # Build a client from the current gem configuration
45
+ #
46
+ # @return [IssueTrackerClient, nil] Client instance or nil if not configured
47
+ def self.from_config
48
+ config = RailsErrorDashboard.configuration
49
+ return nil unless config.enable_issue_tracking
50
+
51
+ provider = config.effective_issue_tracker_provider
52
+ token = config.effective_issue_tracker_token
53
+ repo = config.effective_issue_tracker_repo
54
+ api_url = config.effective_issue_tracker_api_url
55
+
56
+ return nil unless provider && token && repo
57
+
58
+ self.for(provider, token: token, repo: repo, api_url: api_url)
59
+ rescue => e
60
+ nil
61
+ end
62
+
63
+ def initialize(token:, repo:, api_url: nil)
64
+ @token = token
65
+ @repo = repo
66
+ @api_url = api_url
67
+ end
68
+
69
+ # Create an issue on the platform
70
+ # @return [Hash] { url:, number:, success: true } or { success: false, error: "..." }
71
+ def create_issue(title:, body:, labels: [])
72
+ raise NotImplementedError
73
+ end
74
+
75
+ # Close an issue
76
+ # @return [Hash] { success: true } or { success: false, error: "..." }
77
+ def close_issue(number:)
78
+ raise NotImplementedError
79
+ end
80
+
81
+ # Reopen a closed issue
82
+ # @return [Hash] { success: true } or { success: false, error: "..." }
83
+ def reopen_issue(number:)
84
+ raise NotImplementedError
85
+ end
86
+
87
+ # Add a comment to an issue
88
+ # @return [Hash] { url:, success: true } or { success: false, error: "..." }
89
+ def add_comment(number:, body:)
90
+ raise NotImplementedError
91
+ end
92
+
93
+ # Fetch comments from an issue
94
+ # @return [Hash] { comments: [...], success: true } or { success: false, error: "..." }
95
+ def fetch_comments(number:, per_page: 10)
96
+ raise NotImplementedError
97
+ end
98
+
99
+ # Fetch issue details (status, assignees, labels)
100
+ # @return [Hash] { state:, assignees: [...], labels: [...], title:, success: true } or { success: false }
101
+ def fetch_issue(number:)
102
+ raise NotImplementedError
103
+ end
104
+
105
+ private
106
+
107
+ def http_post(uri, body, headers = {})
108
+ http_request(:post, uri, body, headers)
109
+ end
110
+
111
+ def http_patch(uri, body, headers = {})
112
+ http_request(:patch, uri, body, headers)
113
+ end
114
+
115
+ def http_put(uri, body, headers = {})
116
+ http_request(:put, uri, body, headers)
117
+ end
118
+
119
+ def http_get(uri, headers = {})
120
+ http_request(:get, uri, nil, headers)
121
+ end
122
+
123
+ def http_request(method, uri, body, headers)
124
+ parsed = URI.parse(uri)
125
+ http = Net::HTTP.new(parsed.host, parsed.port)
126
+ http.use_ssl = parsed.scheme == "https"
127
+ http.open_timeout = REQUEST_TIMEOUT
128
+ http.read_timeout = REQUEST_TIMEOUT
129
+
130
+ request = case method
131
+ when :post then Net::HTTP::Post.new(parsed)
132
+ when :patch then Net::HTTP::Patch.new(parsed)
133
+ when :put then Net::HTTP::Put.new(parsed)
134
+ when :get then Net::HTTP::Get.new(parsed)
135
+ end
136
+
137
+ request["Content-Type"] = "application/json"
138
+ headers.each { |k, v| request[k] = v }
139
+ request.body = body.to_json if body
140
+
141
+ response = http.request(request)
142
+ { status: response.code.to_i, body: parse_response(response.body) }
143
+ rescue => e
144
+ { status: 0, body: nil, error: "#{e.class}: #{e.message}" }
145
+ end
146
+
147
+ def parse_response(body)
148
+ return nil if body.nil? || body.empty?
149
+ JSON.parse(body)
150
+ rescue JSON::ParserError
151
+ nil
152
+ end
153
+
154
+ def truncate_body(body)
155
+ return body if body.length <= MAX_BODY_LENGTH
156
+ body[0...MAX_BODY_LENGTH] + "\n\n---\n*Truncated — full details in the error dashboard*"
157
+ end
158
+
159
+ def success_response(data)
160
+ data.merge(success: true)
161
+ end
162
+
163
+ def error_response(message)
164
+ { success: false, error: message }
165
+ end
166
+ end
167
+ end
168
+ end
@@ -130,6 +130,10 @@ module RailsErrorDashboard
130
130
  vars = parse_json(raw)
131
131
  return nil unless vars.is_a?(Hash) && vars.any?
132
132
 
133
+ # Skip [FILTERED] variables — LLM can't use redacted values for debugging
134
+ vars = vars.reject { |_, info| filtered_variable?(info) }
135
+ return nil if vars.empty?
136
+
133
137
  rows = vars.first(MAX_VARIABLES).map { |name, info|
134
138
  if info.is_a?(Hash)
135
139
  "| #{name} | #{info["type"]} | #{truncate_value(info["value"])} |"
@@ -149,6 +153,9 @@ module RailsErrorDashboard
149
153
  return nil unless vars.is_a?(Hash) && vars.any?
150
154
 
151
155
  self_class = vars.delete("_self_class")
156
+
157
+ # Skip [FILTERED] variables — LLM can't use redacted values for debugging
158
+ vars = vars.reject { |_, info| filtered_variable?(info) }
152
159
  return nil if vars.empty? && self_class.nil?
153
160
 
154
161
  lines = []
@@ -358,6 +365,11 @@ module RailsErrorDashboard
358
365
  nil
359
366
  end
360
367
 
368
+ def filtered_variable?(info)
369
+ return false unless info.is_a?(Hash)
370
+ info["filtered"] == true || info["value"] == "[FILTERED]"
371
+ end
372
+
361
373
  def truncate_value(value, max_length = 200)
362
374
  str = value.to_s
363
375
  str.length > max_length ? "#{str[0...max_length]}..." : str
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsErrorDashboard
4
+ module Subscribers
5
+ # Registers ActiveSupport::Notifications subscribers for ActiveStorage events.
6
+ #
7
+ # ActiveStorage emits:
8
+ # - service_upload.active_storage — file uploaded to storage service
9
+ # - service_download.active_storage — file downloaded from storage service
10
+ # - service_streaming_download.active_storage — streaming download
11
+ # - service_delete.active_storage — file deleted from storage service
12
+ # - service_delete_prefixed.active_storage — batch delete by prefix
13
+ # - service_exist.active_storage — existence check
14
+ #
15
+ # Each event is captured as a breadcrumb with category "active_storage",
16
+ # allowing correlation between storage operations and error spikes.
17
+ #
18
+ # SAFETY RULES (HOST_APP_SAFETY.md):
19
+ # - Every subscriber wrapped in rescue => e; nil
20
+ # - Never raise from subscriber callbacks
21
+ # - Skip if buffer is nil (not in a request context)
22
+ class ActiveStorageSubscriber
23
+ EVENTS = %w[
24
+ service_upload.active_storage
25
+ service_download.active_storage
26
+ service_streaming_download.active_storage
27
+ service_delete.active_storage
28
+ service_delete_prefixed.active_storage
29
+ service_exist.active_storage
30
+ ].freeze
31
+
32
+ # Event subscriptions managed by this class
33
+ @subscriptions = []
34
+
35
+ class << self
36
+ attr_reader :subscriptions
37
+
38
+ # Register all ActiveStorage event subscribers
39
+ # @return [Array] Array of subscription objects
40
+ def subscribe!
41
+ @subscriptions = []
42
+
43
+ EVENTS.each do |event_name|
44
+ @subscriptions << subscribe_event(event_name)
45
+ end
46
+
47
+ @subscriptions
48
+ end
49
+
50
+ # Remove all ActiveStorage subscribers
51
+ def unsubscribe!
52
+ @subscriptions.each do |sub|
53
+ ActiveSupport::Notifications.unsubscribe(sub) if sub
54
+ rescue => e
55
+ nil
56
+ end
57
+ @subscriptions = []
58
+ end
59
+
60
+ private
61
+
62
+ def subscribe_event(event_name)
63
+ ActiveSupport::Notifications.subscribe(event_name) do |*args|
64
+ event = ActiveSupport::Notifications::Event.new(*args)
65
+ handle_active_storage(event, event_name)
66
+ rescue => e
67
+ nil
68
+ end
69
+ end
70
+
71
+ def handle_active_storage(event, event_name)
72
+ return unless Services::BreadcrumbCollector.current_buffer
73
+
74
+ payload = event.payload || {}
75
+ service = payload[:service].to_s.presence || "Unknown"
76
+ operation = event_name.split(".").first.sub("service_", "")
77
+ key = (payload[:key] || payload[:prefix]).to_s
78
+
79
+ message = build_message(operation, service, key)
80
+
81
+ metadata = {
82
+ service: service,
83
+ operation: operation
84
+ }
85
+ metadata[:key] = key.truncate(100) if key.present?
86
+
87
+ duration_ms = event.duration if event.respond_to?(:duration)
88
+
89
+ Services::BreadcrumbCollector.add("active_storage", message, duration_ms: duration_ms, metadata: metadata)
90
+ end
91
+
92
+ def build_message(operation, service, key)
93
+ short_key = key.present? ? key.truncate(40) : nil
94
+ case operation
95
+ when "upload"
96
+ short_key ? "upload #{short_key} (#{service})" : "upload (#{service})"
97
+ when "download", "streaming_download"
98
+ short_key ? "download #{short_key} (#{service})" : "download (#{service})"
99
+ when "delete"
100
+ short_key ? "delete #{short_key} (#{service})" : "delete (#{service})"
101
+ when "delete_prefixed"
102
+ short_key ? "delete_prefixed #{short_key} (#{service})" : "delete_prefixed (#{service})"
103
+ when "exist"
104
+ short_key ? "exist? #{short_key} (#{service})" : "exist? (#{service})"
105
+ else
106
+ "#{operation} (#{service})"
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsErrorDashboard
4
+ module Subscribers
5
+ # Hooks into the error lifecycle to trigger issue tracker jobs.
6
+ #
7
+ # Called from the engine initializer via direct integration with
8
+ # the LogError and ResolveError commands' callback mechanisms.
9
+ #
10
+ # All work is done via background jobs — never blocks the capture path.
11
+ class IssueTrackerSubscriber
12
+ class << self
13
+ # Called when a new error is first logged
14
+ def on_error_logged(error_log)
15
+ return unless should_auto_create?(error_log)
16
+ CreateIssueJob.perform_later(error_log.id)
17
+ rescue => e
18
+ nil
19
+ end
20
+
21
+ # Called when a resolved error recurs (auto-reopened)
22
+ def on_error_reopened(error_log)
23
+ return unless error_log.external_issue_url.present?
24
+ return unless RailsErrorDashboard.configuration.enable_issue_tracking
25
+ ReopenLinkedIssueJob.perform_later(error_log.id)
26
+ rescue => e
27
+ nil
28
+ end
29
+
30
+ # Called when an existing error occurs again
31
+ def on_error_recurred(error_log)
32
+ return unless error_log.external_issue_url.present?
33
+ return unless RailsErrorDashboard.configuration.enable_issue_tracking
34
+ AddIssueRecurrenceCommentJob.perform_later(error_log.id)
35
+ rescue => e
36
+ nil
37
+ end
38
+
39
+ # Called when an error is resolved in the dashboard
40
+ def on_error_resolved(error_log)
41
+ return unless error_log.external_issue_url.present?
42
+ return unless RailsErrorDashboard.configuration.enable_issue_tracking
43
+ CloseLinkedIssueJob.perform_later(error_log.id)
44
+ rescue => e
45
+ nil
46
+ end
47
+
48
+ private
49
+
50
+ def should_auto_create?(error_log)
51
+ config = RailsErrorDashboard.configuration
52
+ return false unless config.enable_issue_tracking && config.auto_create_issues
53
+ return false if error_log.external_issue_url.present?
54
+
55
+ # First occurrence check
56
+ if config.auto_create_issues_on_first_occurrence && error_log.occurrence_count == 1
57
+ return true
58
+ end
59
+
60
+ # Severity threshold check
61
+ severity = error_log.severity&.to_sym
62
+ if config.auto_create_issues_for_severities&.include?(severity)
63
+ return true
64
+ end
65
+
66
+ false
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsErrorDashboard
2
- VERSION = "0.5.7"
2
+ VERSION = "0.5.9"
3
3
  end
@@ -56,6 +56,10 @@ require "rails_error_dashboard/services/n_plus_one_detector"
56
56
  require "rails_error_dashboard/services/curl_generator"
57
57
  require "rails_error_dashboard/services/rspec_generator"
58
58
  require "rails_error_dashboard/services/markdown_error_formatter"
59
+ require "rails_error_dashboard/services/issue_tracker_client"
60
+ require "rails_error_dashboard/services/github_issue_client"
61
+ require "rails_error_dashboard/services/gitlab_issue_client"
62
+ require "rails_error_dashboard/services/codeberg_issue_client"
59
63
  require "rails_error_dashboard/services/database_health_inspector"
60
64
  require "rails_error_dashboard/services/cache_analyzer"
61
65
  require "rails_error_dashboard/services/variable_serializer"
@@ -66,14 +70,20 @@ require "rails_error_dashboard/services/diagnostic_dump_generator"
66
70
  require "rails_error_dashboard/subscribers/breadcrumb_subscriber"
67
71
  require "rails_error_dashboard/subscribers/rack_attack_subscriber"
68
72
  require "rails_error_dashboard/subscribers/action_cable_subscriber"
73
+ require "rails_error_dashboard/subscribers/active_storage_subscriber"
74
+ require "rails_error_dashboard/subscribers/issue_tracker_subscriber"
69
75
  require "rails_error_dashboard/queries/co_occurring_errors"
70
76
  require "rails_error_dashboard/queries/action_cable_summary"
77
+ require "rails_error_dashboard/queries/active_storage_summary"
71
78
  require "rails_error_dashboard/queries/error_cascades"
72
79
  require "rails_error_dashboard/queries/baseline_stats"
73
80
  require "rails_error_dashboard/queries/platform_comparison"
74
81
  require "rails_error_dashboard/queries/error_correlation"
75
82
  require "rails_error_dashboard/commands/log_error"
76
83
  require "rails_error_dashboard/commands/resolve_error"
84
+ require "rails_error_dashboard/commands/create_issue"
85
+ require "rails_error_dashboard/commands/link_existing_issue"
86
+ require "rails_error_dashboard/services/issue_body_formatter"
77
87
  require "rails_error_dashboard/commands/batch_resolve_errors"
78
88
  require "rails_error_dashboard/commands/batch_delete_errors"
79
89
  require "rails_error_dashboard/commands/assign_error"
@@ -86,7 +96,7 @@ require "rails_error_dashboard/commands/unmute_error"
86
96
  require "rails_error_dashboard/commands/batch_mute_errors"
87
97
  require "rails_error_dashboard/commands/batch_unmute_errors"
88
98
  require "rails_error_dashboard/commands/update_error_status"
89
- require "rails_error_dashboard/commands/add_error_comment"
99
+ require "rails_error_dashboard/commands/add_error_comment" # Used internally by workflow commands (snooze, mute, status changes)
90
100
  require "rails_error_dashboard/commands/increment_cascade_detection"
91
101
  require "rails_error_dashboard/commands/calculate_cascade_probability"
92
102
  require "rails_error_dashboard/commands/find_or_increment_error"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_error_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.7
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anjan Jagirdar
@@ -244,16 +244,21 @@ files:
244
244
  - Rakefile
245
245
  - app/controllers/rails_error_dashboard/application_controller.rb
246
246
  - app/controllers/rails_error_dashboard/errors_controller.rb
247
+ - app/controllers/rails_error_dashboard/webhooks_controller.rb
247
248
  - app/helpers/rails_error_dashboard/application_helper.rb
248
249
  - app/helpers/rails_error_dashboard/backtrace_helper.rb
249
250
  - app/helpers/rails_error_dashboard/overview_helper.rb
250
251
  - app/helpers/rails_error_dashboard/user_agent_helper.rb
252
+ - app/jobs/rails_error_dashboard/add_issue_recurrence_comment_job.rb
251
253
  - app/jobs/rails_error_dashboard/application_job.rb
252
254
  - app/jobs/rails_error_dashboard/async_error_logging_job.rb
253
255
  - app/jobs/rails_error_dashboard/baseline_alert_job.rb
256
+ - app/jobs/rails_error_dashboard/close_linked_issue_job.rb
257
+ - app/jobs/rails_error_dashboard/create_issue_job.rb
254
258
  - app/jobs/rails_error_dashboard/discord_error_notification_job.rb
255
259
  - app/jobs/rails_error_dashboard/email_error_notification_job.rb
256
260
  - app/jobs/rails_error_dashboard/pagerduty_error_notification_job.rb
261
+ - app/jobs/rails_error_dashboard/reopen_linked_issue_job.rb
257
262
  - app/jobs/rails_error_dashboard/retention_cleanup_job.rb
258
263
  - app/jobs/rails_error_dashboard/slack_error_notification_job.rb
259
264
  - app/jobs/rails_error_dashboard/swallowed_exception_flush_job.rb
@@ -279,6 +284,7 @@ files:
279
284
  - app/views/rails_error_dashboard/errors/_error_info.html.erb
280
285
  - app/views/rails_error_dashboard/errors/_error_row.html.erb
281
286
  - app/views/rails_error_dashboard/errors/_instance_variables.html.erb
287
+ - app/views/rails_error_dashboard/errors/_issue_section.html.erb
282
288
  - app/views/rails_error_dashboard/errors/_local_variables.html.erb
283
289
  - app/views/rails_error_dashboard/errors/_modals.html.erb
284
290
  - app/views/rails_error_dashboard/errors/_pattern_insights.html.erb
@@ -291,6 +297,7 @@ files:
291
297
  - app/views/rails_error_dashboard/errors/_timeline.html.erb
292
298
  - app/views/rails_error_dashboard/errors/_user_errors_table.html.erb
293
299
  - app/views/rails_error_dashboard/errors/actioncable_health_summary.html.erb
300
+ - app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb
294
301
  - app/views/rails_error_dashboard/errors/analytics.html.erb
295
302
  - app/views/rails_error_dashboard/errors/cache_health_summary.html.erb
296
303
  - app/views/rails_error_dashboard/errors/correlation.html.erb
@@ -341,6 +348,7 @@ files:
341
348
  - db/migrate/20260307000001_create_rails_error_dashboard_diagnostic_dumps.rb
342
349
  - db/migrate/20260323000001_add_muted_to_error_logs.rb
343
350
  - db/migrate/20260325000001_fix_swallowed_exceptions_index_for_mysql.rb
351
+ - db/migrate/20260326000001_add_issue_tracking_to_error_logs.rb
344
352
  - lib/generators/rails_error_dashboard/install/install_generator.rb
345
353
  - lib/generators/rails_error_dashboard/install/templates/README
346
354
  - lib/generators/rails_error_dashboard/install/templates/initializer.rb
@@ -355,10 +363,12 @@ files:
355
363
  - lib/rails_error_dashboard/commands/batch_resolve_errors.rb
356
364
  - lib/rails_error_dashboard/commands/batch_unmute_errors.rb
357
365
  - lib/rails_error_dashboard/commands/calculate_cascade_probability.rb
366
+ - lib/rails_error_dashboard/commands/create_issue.rb
358
367
  - lib/rails_error_dashboard/commands/find_or_create_application.rb
359
368
  - lib/rails_error_dashboard/commands/find_or_increment_error.rb
360
369
  - lib/rails_error_dashboard/commands/flush_swallowed_exceptions.rb
361
370
  - lib/rails_error_dashboard/commands/increment_cascade_detection.rb
371
+ - lib/rails_error_dashboard/commands/link_existing_issue.rb
362
372
  - lib/rails_error_dashboard/commands/log_error.rb
363
373
  - lib/rails_error_dashboard/commands/mute_error.rb
364
374
  - lib/rails_error_dashboard/commands/resolve_error.rb
@@ -385,6 +395,7 @@ files:
385
395
  - lib/rails_error_dashboard/plugins/jira_integration_plugin.rb
386
396
  - lib/rails_error_dashboard/plugins/metrics_plugin.rb
387
397
  - lib/rails_error_dashboard/queries/action_cable_summary.rb
398
+ - lib/rails_error_dashboard/queries/active_storage_summary.rb
388
399
  - lib/rails_error_dashboard/queries/analytics_stats.rb
389
400
  - lib/rails_error_dashboard/queries/baseline_stats.rb
390
401
  - lib/rails_error_dashboard/queries/cache_health_summary.rb
@@ -415,6 +426,7 @@ files:
415
426
  - lib/rails_error_dashboard/services/cache_analyzer.rb
416
427
  - lib/rails_error_dashboard/services/cascade_detector.rb
417
428
  - lib/rails_error_dashboard/services/cause_chain_extractor.rb
429
+ - lib/rails_error_dashboard/services/codeberg_issue_client.rb
418
430
  - lib/rails_error_dashboard/services/crash_capture.rb
419
431
  - lib/rails_error_dashboard/services/curl_generator.rb
420
432
  - lib/rails_error_dashboard/services/database_health_inspector.rb
@@ -427,7 +439,11 @@ files:
427
439
  - lib/rails_error_dashboard/services/error_notification_dispatcher.rb
428
440
  - lib/rails_error_dashboard/services/exception_filter.rb
429
441
  - lib/rails_error_dashboard/services/git_blame_reader.rb
442
+ - lib/rails_error_dashboard/services/github_issue_client.rb
430
443
  - lib/rails_error_dashboard/services/github_link_generator.rb
444
+ - lib/rails_error_dashboard/services/gitlab_issue_client.rb
445
+ - lib/rails_error_dashboard/services/issue_body_formatter.rb
446
+ - lib/rails_error_dashboard/services/issue_tracker_client.rb
431
447
  - lib/rails_error_dashboard/services/local_variable_capturer.rb
432
448
  - lib/rails_error_dashboard/services/markdown_error_formatter.rb
433
449
  - lib/rails_error_dashboard/services/n_plus_one_detector.rb
@@ -450,7 +466,9 @@ files:
450
466
  - lib/rails_error_dashboard/services/variable_serializer.rb
451
467
  - lib/rails_error_dashboard/services/webhook_payload_builder.rb
452
468
  - lib/rails_error_dashboard/subscribers/action_cable_subscriber.rb
469
+ - lib/rails_error_dashboard/subscribers/active_storage_subscriber.rb
453
470
  - lib/rails_error_dashboard/subscribers/breadcrumb_subscriber.rb
471
+ - lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb
454
472
  - lib/rails_error_dashboard/subscribers/rack_attack_subscriber.rb
455
473
  - lib/rails_error_dashboard/value_objects/error_context.rb
456
474
  - lib/rails_error_dashboard/version.rb
@@ -465,9 +483,9 @@ metadata:
465
483
  changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
466
484
  documentation_uri: https://AnjanJ.github.io/rails_error_dashboard
467
485
  bug_tracker_uri: https://github.com/AnjanJ/rails_error_dashboard/issues
468
- funding_uri: https://buymeacoffee.com/anjanj
486
+ funding_uri: https://github.com/sponsors/AnjanJ
469
487
  post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
470
- \ Rails Error Dashboard v0.5.7\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
488
+ \ Rails Error Dashboard v0.5.9\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
471
489
  First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
472
490
  db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
473
491
  => '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n