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.
- checksums.yaml +4 -4
- data/README.md +30 -3
- data/app/controllers/rails_error_dashboard/errors_controller.rb +81 -4
- data/app/controllers/rails_error_dashboard/webhooks_controller.rb +192 -0
- data/app/jobs/rails_error_dashboard/add_issue_recurrence_comment_job.rb +71 -0
- data/app/jobs/rails_error_dashboard/close_linked_issue_job.rb +43 -0
- data/app/jobs/rails_error_dashboard/create_issue_job.rb +68 -0
- data/app/jobs/rails_error_dashboard/reopen_linked_issue_job.rb +44 -0
- data/app/models/rails_error_dashboard/error_log.rb +2 -1
- data/app/views/layouts/rails_error_dashboard.html.erb +19 -6
- data/app/views/rails_error_dashboard/errors/_breadcrumbs_group.html.erb +1 -1
- data/app/views/rails_error_dashboard/errors/_discussion.html.erb +92 -100
- data/app/views/rails_error_dashboard/errors/_issue_section.html.erb +121 -0
- data/app/views/rails_error_dashboard/errors/_show_scripts.html.erb +1 -0
- data/app/views/rails_error_dashboard/errors/_sidebar_metadata.html.erb +77 -73
- data/app/views/rails_error_dashboard/errors/activestorage_health_summary.html.erb +148 -0
- data/app/views/rails_error_dashboard/errors/show.html.erb +13 -9
- data/config/routes.rb +6 -1
- data/db/migrate/20251223000000_create_rails_error_dashboard_complete_schema.rb +5 -0
- data/db/migrate/20260326000001_add_issue_tracking_to_error_logs.rb +15 -0
- data/lib/generators/rails_error_dashboard/install/install_generator.rb +12 -4
- data/lib/rails_error_dashboard/commands/create_issue.rb +59 -0
- data/lib/rails_error_dashboard/commands/link_existing_issue.rb +65 -0
- data/lib/rails_error_dashboard/configuration.rb +99 -0
- data/lib/rails_error_dashboard/engine.rb +39 -0
- data/lib/rails_error_dashboard/queries/active_storage_summary.rb +101 -0
- data/lib/rails_error_dashboard/services/codeberg_issue_client.rb +122 -0
- data/lib/rails_error_dashboard/services/github_issue_client.rb +117 -0
- data/lib/rails_error_dashboard/services/github_link_generator.rb +19 -1
- data/lib/rails_error_dashboard/services/gitlab_issue_client.rb +121 -0
- data/lib/rails_error_dashboard/services/issue_body_formatter.rb +132 -0
- data/lib/rails_error_dashboard/services/issue_tracker_client.rb +168 -0
- data/lib/rails_error_dashboard/services/markdown_error_formatter.rb +12 -0
- data/lib/rails_error_dashboard/subscribers/active_storage_subscriber.rb +112 -0
- data/lib/rails_error_dashboard/subscribers/issue_tracker_subscriber.rb +71 -0
- data/lib/rails_error_dashboard/version.rb +1 -1
- data/lib/rails_error_dashboard.rb +11 -1
- 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
|
|
@@ -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.
|
|
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://
|
|
486
|
+
funding_uri: https://github.com/sponsors/AnjanJ
|
|
469
487
|
post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
|
|
470
|
-
\ Rails Error Dashboard v0.5.
|
|
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
|