rails_error_dashboard 0.1.24 → 0.1.25
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 +3 -0
- data/app/controllers/rails_error_dashboard/errors_controller.rb +25 -20
- data/app/models/rails_error_dashboard/error_log.rb +5 -5
- data/app/views/layouts/rails_error_dashboard.html.erb +13 -0
- data/lib/rails_error_dashboard/queries/error_correlation.rb +7 -3
- data/lib/rails_error_dashboard/queries/filter_options.rb +16 -4
- data/lib/rails_error_dashboard/queries/mttr_stats.rb +15 -7
- data/lib/rails_error_dashboard/queries/platform_comparison.rb +35 -21
- data/lib/rails_error_dashboard/queries/recurring_issues.rb +7 -4
- data/lib/rails_error_dashboard/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb29048c8c0c29401f6295d9e2b1d797cd82908b81197f906a9df5a6f221a182
|
|
4
|
+
data.tar.gz: 63f26da41e171cc6956edb1bb5a0027a8c67a37bc94ff323665d4c34a2673d1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a12dfcad4a1f99763ed15bb861d3937ed577eaf2eaa89b66d99dda8fc50e63cf093d76c01dbfb226f18a4181a57352183f2ddfd8678009b02e247f0ae0d651d0
|
|
7
|
+
data.tar.gz: b213451cd2c971ab5c583f2301917a87f2c79d4b5c26526abaa72dfa59bbcc8db949cc4c7452a854966229648a7ae43f861484306e55701e780070ef72054b9b
|
data/README.md
CHANGED
|
@@ -16,6 +16,8 @@ gem 'rails_error_dashboard'
|
|
|
16
16
|
|
|
17
17
|
**5-minute setup** · **Works out-of-the-box** · **100% Rails + Postgres** · **No vendor lock-in**
|
|
18
18
|
|
|
19
|
+
📖 **[Full Documentation](https://anjanj.github.io/rails_error_dashboard/)** · 🎮 **[Live Demo](https://rails-error-dashboard.anjan.dev)** · 💎 **[RubyGems](https://rubygems.org/gems/rails_error_dashboard)**
|
|
20
|
+
|
|
19
21
|
### 🎮 Try the Live Demo
|
|
20
22
|
|
|
21
23
|
**See it in action:** [https://rails-error-dashboard.anjan.dev](https://rails-error-dashboard.anjan.dev)
|
|
@@ -586,6 +588,7 @@ config.webhook_urls = ['https://yourapp.com/hooks/errors']
|
|
|
586
588
|
- **[Error Trend Visualizations](docs/guides/ERROR_TREND_VISUALIZATIONS.md)** - Charts & analytics
|
|
587
589
|
|
|
588
590
|
### Advanced
|
|
591
|
+
- **[Multi-App Support](docs/MULTI_APP_PERFORMANCE.md)** - Track multiple applications
|
|
589
592
|
- **[Plugin System](docs/PLUGIN_SYSTEM.md)** - Build custom integrations
|
|
590
593
|
- **[API Reference](docs/API_REFERENCE.md)** - Complete API documentation
|
|
591
594
|
- **[Customization Guide](docs/CUSTOMIZATION.md)** - Customize everything
|
|
@@ -5,6 +5,7 @@ module RailsErrorDashboard
|
|
|
5
5
|
include Pagy::Backend
|
|
6
6
|
|
|
7
7
|
before_action :authenticate_dashboard_user!
|
|
8
|
+
before_action :set_application_context
|
|
8
9
|
|
|
9
10
|
FILTERABLE_PARAMS = %i[
|
|
10
11
|
error_type
|
|
@@ -24,12 +25,12 @@ module RailsErrorDashboard
|
|
|
24
25
|
].freeze
|
|
25
26
|
|
|
26
27
|
def overview
|
|
27
|
-
# Get dashboard stats using Query
|
|
28
|
-
@stats = Queries::DashboardStats.call
|
|
28
|
+
# Get dashboard stats using Query (pass application filter)
|
|
29
|
+
@stats = Queries::DashboardStats.call(application_id: @current_application_id)
|
|
29
30
|
|
|
30
|
-
# Get platform health summary (if enabled)
|
|
31
|
+
# Get platform health summary (if enabled, pass application filter)
|
|
31
32
|
if RailsErrorDashboard.configuration.enable_platform_comparison
|
|
32
|
-
comparison = Queries::PlatformComparison.new(days: 7)
|
|
33
|
+
comparison = Queries::PlatformComparison.new(days: 7, application_id: @current_application_id)
|
|
33
34
|
@platform_health = comparison.platform_health_summary
|
|
34
35
|
@platform_scores = comparison.platform_stability_scores
|
|
35
36
|
else
|
|
@@ -43,8 +44,8 @@ module RailsErrorDashboard
|
|
|
43
44
|
.where("occurred_at >= ?", 1.hour.ago)
|
|
44
45
|
.where(resolved_at: nil)
|
|
45
46
|
.where(priority_level: [ 3, 4 ]) # 3 = high, 4 = critical (based on severity enum)
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
@critical_alerts = @critical_alerts.where(application_id: @current_application_id) if @current_application_id.present?
|
|
48
|
+
@critical_alerts = @critical_alerts.order(occurred_at: :desc).limit(10)
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def index
|
|
@@ -55,10 +56,10 @@ module RailsErrorDashboard
|
|
|
55
56
|
@pagy, @errors = pagy(errors_query, items: params[:per_page] || 25)
|
|
56
57
|
|
|
57
58
|
# Get dashboard stats using Query (pass application filter)
|
|
58
|
-
@stats = Queries::DashboardStats.call(application_id:
|
|
59
|
+
@stats = Queries::DashboardStats.call(application_id: @current_application_id)
|
|
59
60
|
|
|
60
|
-
# Get filter options using Query
|
|
61
|
-
filter_options = Queries::FilterOptions.call
|
|
61
|
+
# Get filter options using Query (pass application filter)
|
|
62
|
+
filter_options = Queries::FilterOptions.call(application_id: @current_application_id)
|
|
62
63
|
@error_types = filter_options[:error_types]
|
|
63
64
|
@platforms = filter_options[:platforms]
|
|
64
65
|
@applications = filter_options[:applications]
|
|
@@ -69,7 +70,7 @@ module RailsErrorDashboard
|
|
|
69
70
|
# - comments: Used in the comments section (@error.comments.count, @error.comments.recent_first)
|
|
70
71
|
# - parent_cascade_patterns/child_cascade_patterns: Used if cascade detection is enabled
|
|
71
72
|
@error = ErrorLog.includes(:comments, :parent_cascade_patterns, :child_cascade_patterns).find(params[:id])
|
|
72
|
-
@related_errors = @error.related_errors(limit: 5)
|
|
73
|
+
@related_errors = @error.related_errors(limit: 5, application_id: @current_application_id)
|
|
73
74
|
|
|
74
75
|
# Dispatch plugin event for error viewed
|
|
75
76
|
RailsErrorDashboard::PluginRegistry.dispatch(:on_error_viewed, @error)
|
|
@@ -156,7 +157,7 @@ module RailsErrorDashboard
|
|
|
156
157
|
@days = days
|
|
157
158
|
|
|
158
159
|
# Use Query to get analytics data (pass application filter)
|
|
159
|
-
analytics = Queries::AnalyticsStats.call(days, application_id:
|
|
160
|
+
analytics = Queries::AnalyticsStats.call(days, application_id: @current_application_id)
|
|
160
161
|
|
|
161
162
|
@error_stats = analytics[:error_stats]
|
|
162
163
|
@errors_over_time = analytics[:errors_over_time]
|
|
@@ -168,18 +169,18 @@ module RailsErrorDashboard
|
|
|
168
169
|
@mobile_errors = analytics[:mobile_errors]
|
|
169
170
|
@api_errors = analytics[:api_errors]
|
|
170
171
|
|
|
171
|
-
# Get recurring issues data
|
|
172
|
-
recurring = Queries::RecurringIssues.call(days)
|
|
172
|
+
# Get recurring issues data (pass application filter)
|
|
173
|
+
recurring = Queries::RecurringIssues.call(days, application_id: @current_application_id)
|
|
173
174
|
@recurring_data = recurring
|
|
174
175
|
|
|
175
|
-
# Get release correlation data
|
|
176
|
-
correlation = Queries::ErrorCorrelation.new(days: days)
|
|
176
|
+
# Get release correlation data (pass application filter)
|
|
177
|
+
correlation = Queries::ErrorCorrelation.new(days: days, application_id: @current_application_id)
|
|
177
178
|
@errors_by_version = correlation.errors_by_version
|
|
178
179
|
@problematic_releases = correlation.problematic_releases
|
|
179
180
|
@release_comparison = calculate_release_comparison
|
|
180
181
|
|
|
181
|
-
# Get MTTR data
|
|
182
|
-
mttr_data = Queries::MttrStats.call(days)
|
|
182
|
+
# Get MTTR data (pass application filter)
|
|
183
|
+
mttr_data = Queries::MttrStats.call(days, application_id: @current_application_id)
|
|
183
184
|
@mttr_stats = mttr_data
|
|
184
185
|
@overall_mttr = mttr_data[:overall_mttr]
|
|
185
186
|
@mttr_by_platform = mttr_data[:mttr_by_platform]
|
|
@@ -196,8 +197,8 @@ module RailsErrorDashboard
|
|
|
196
197
|
days = (params[:days] || 7).to_i
|
|
197
198
|
@days = days
|
|
198
199
|
|
|
199
|
-
# Use Query to get platform comparison data
|
|
200
|
-
comparison = Queries::PlatformComparison.new(days: days)
|
|
200
|
+
# Use Query to get platform comparison data (pass application filter)
|
|
201
|
+
comparison = Queries::PlatformComparison.new(days: days, application_id: @current_application_id)
|
|
201
202
|
|
|
202
203
|
@error_rate_by_platform = comparison.error_rate_by_platform
|
|
203
204
|
@severity_distribution = comparison.severity_distribution_by_platform
|
|
@@ -245,7 +246,7 @@ module RailsErrorDashboard
|
|
|
245
246
|
|
|
246
247
|
days = (params[:days] || 30).to_i
|
|
247
248
|
@days = days
|
|
248
|
-
correlation = Queries::ErrorCorrelation.new(days: days)
|
|
249
|
+
correlation = Queries::ErrorCorrelation.new(days: days, application_id: @current_application_id)
|
|
249
250
|
|
|
250
251
|
@errors_by_version = correlation.errors_by_version
|
|
251
252
|
@errors_by_git_sha = correlation.errors_by_git_sha
|
|
@@ -286,6 +287,10 @@ module RailsErrorDashboard
|
|
|
286
287
|
params.permit(*FILTERABLE_PARAMS).to_h.symbolize_keys
|
|
287
288
|
end
|
|
288
289
|
|
|
290
|
+
def set_application_context
|
|
291
|
+
@current_application_id = params[:application_id].presence
|
|
292
|
+
end
|
|
293
|
+
|
|
289
294
|
def authenticate_dashboard_user!
|
|
290
295
|
# Authentication is ALWAYS required - no bypass allowed in any environment
|
|
291
296
|
authenticate_or_request_with_http_basic do |username, password|
|
|
@@ -390,11 +390,11 @@ module RailsErrorDashboard
|
|
|
390
390
|
end
|
|
391
391
|
|
|
392
392
|
# Find related errors of the same type
|
|
393
|
-
def related_errors(limit: 5)
|
|
394
|
-
self.class.where(error_type: error_type)
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
def related_errors(limit: 5, application_id: nil)
|
|
394
|
+
scope = self.class.where(error_type: error_type)
|
|
395
|
+
.where.not(id: id)
|
|
396
|
+
scope = scope.where(application_id: application_id) if application_id.present?
|
|
397
|
+
scope.order(occurred_at: :desc).limit(limit)
|
|
398
398
|
end
|
|
399
399
|
|
|
400
400
|
# Extract backtrace frames for similarity comparison
|
|
@@ -1000,6 +1000,19 @@
|
|
|
1000
1000
|
</div>
|
|
1001
1001
|
</div>
|
|
1002
1002
|
|
|
1003
|
+
<!-- Footer -->
|
|
1004
|
+
<footer class="mt-5 py-4 text-center border-top">
|
|
1005
|
+
<div class="container">
|
|
1006
|
+
<p class="text-muted mb-1">
|
|
1007
|
+
<i class="bi bi-bug-fill text-primary"></i>
|
|
1008
|
+
Built with <a href="https://github.com/AnjanJ/rails_error_dashboard" target="_blank" class="text-decoration-none">Rails Error Dashboard</a>
|
|
1009
|
+
</p>
|
|
1010
|
+
<p class="text-muted small mb-0">
|
|
1011
|
+
Created by <a href="https://www.anjan.dev/" target="_blank" class="text-decoration-none">Anjan Jagirdar</a>
|
|
1012
|
+
</p>
|
|
1013
|
+
</div>
|
|
1014
|
+
</footer>
|
|
1015
|
+
|
|
1003
1016
|
<!-- Bootstrap JS -->
|
|
1004
1017
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
1005
1018
|
|
|
@@ -14,11 +14,13 @@ module RailsErrorDashboard
|
|
|
14
14
|
# correlation.errors_by_version
|
|
15
15
|
# # => { "1.0.0" => { count: 100, error_types: 15, critical_count: 5 } }
|
|
16
16
|
class ErrorCorrelation
|
|
17
|
-
attr_reader :days
|
|
17
|
+
attr_reader :days, :application_id
|
|
18
18
|
|
|
19
19
|
# @param days [Integer] Number of days to analyze (default: 30)
|
|
20
|
-
|
|
20
|
+
# @param application_id [Integer, nil] Optional application ID to filter by
|
|
21
|
+
def initialize(days: 30, application_id: nil)
|
|
21
22
|
@days = days
|
|
23
|
+
@application_id = application_id
|
|
22
24
|
@start_date = days.days.ago
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -285,7 +287,9 @@ module RailsErrorDashboard
|
|
|
285
287
|
private
|
|
286
288
|
|
|
287
289
|
def base_query
|
|
288
|
-
ErrorLog.where("occurred_at >= ?", @start_date)
|
|
290
|
+
scope = ErrorLog.where("occurred_at >= ?", @start_date)
|
|
291
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
292
|
+
scope
|
|
289
293
|
end
|
|
290
294
|
|
|
291
295
|
# Check if app_version column exists
|
|
@@ -5,17 +5,29 @@ module RailsErrorDashboard
|
|
|
5
5
|
# Query: Fetch available filter options
|
|
6
6
|
# This is a read operation that returns distinct values for filters
|
|
7
7
|
class FilterOptions
|
|
8
|
-
def self.call
|
|
9
|
-
new.call
|
|
8
|
+
def self.call(application_id: nil)
|
|
9
|
+
new(application_id: application_id).call
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(application_id: nil)
|
|
13
|
+
@application_id = application_id
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
def call
|
|
13
17
|
{
|
|
14
|
-
error_types:
|
|
15
|
-
platforms:
|
|
18
|
+
error_types: base_scope.distinct.pluck(:error_type).compact.sort,
|
|
19
|
+
platforms: base_scope.distinct.pluck(:platform).compact,
|
|
16
20
|
applications: Application.ordered_by_name.pluck(:name, :id)
|
|
17
21
|
}
|
|
18
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def base_scope
|
|
27
|
+
scope = ErrorLog.all
|
|
28
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
29
|
+
scope
|
|
30
|
+
end
|
|
19
31
|
end
|
|
20
32
|
end
|
|
21
33
|
end
|
|
@@ -5,12 +5,13 @@ module RailsErrorDashboard
|
|
|
5
5
|
# Query: Calculate Mean Time to Resolution (MTTR) statistics
|
|
6
6
|
# Provides metrics on how quickly errors are resolved
|
|
7
7
|
class MttrStats
|
|
8
|
-
def self.call(days = 30)
|
|
9
|
-
new(days).call
|
|
8
|
+
def self.call(days = 30, application_id: nil)
|
|
9
|
+
new(days, application_id: application_id).call
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def initialize(days = 30)
|
|
12
|
+
def initialize(days = 30, application_id: nil)
|
|
13
13
|
@days = days
|
|
14
|
+
@application_id = application_id
|
|
14
15
|
@start_date = days.days.ago
|
|
15
16
|
end
|
|
16
17
|
|
|
@@ -29,9 +30,13 @@ module RailsErrorDashboard
|
|
|
29
30
|
private
|
|
30
31
|
|
|
31
32
|
def resolved_errors
|
|
32
|
-
@resolved_errors ||=
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
@resolved_errors ||= begin
|
|
34
|
+
scope = ErrorLog
|
|
35
|
+
.where.not(resolved_at: nil)
|
|
36
|
+
.where("occurred_at >= ?", @start_date)
|
|
37
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
38
|
+
scope
|
|
39
|
+
end
|
|
35
40
|
end
|
|
36
41
|
|
|
37
42
|
def calculate_overall_mttr
|
|
@@ -44,7 +49,9 @@ module RailsErrorDashboard
|
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def mttr_by_platform
|
|
47
|
-
|
|
52
|
+
platforms_scope = ErrorLog.all
|
|
53
|
+
platforms_scope = platforms_scope.where(application_id: @application_id) if @application_id.present?
|
|
54
|
+
platforms = platforms_scope.distinct.pluck(:platform).compact
|
|
48
55
|
|
|
49
56
|
platforms.each_with_object({}) do |platform, result|
|
|
50
57
|
platform_resolved = resolved_errors.where(platform: platform)
|
|
@@ -81,6 +88,7 @@ module RailsErrorDashboard
|
|
|
81
88
|
week_resolved = ErrorLog
|
|
82
89
|
.where.not(resolved_at: nil)
|
|
83
90
|
.where("occurred_at >= ? AND occurred_at < ?", current_date, week_end)
|
|
91
|
+
week_resolved = week_resolved.where(application_id: @application_id) if @application_id.present?
|
|
84
92
|
|
|
85
93
|
if week_resolved.any?
|
|
86
94
|
total_hours = week_resolved.sum { |e| ((e.resolved_at - e.occurred_at) / 3600.0) }
|
|
@@ -17,18 +17,32 @@ module RailsErrorDashboard
|
|
|
17
17
|
# comparison.error_rate_by_platform
|
|
18
18
|
# # => { "ios" => 150, "android" => 200, "api" => 50, "web" => 100 }
|
|
19
19
|
class PlatformComparison
|
|
20
|
-
attr_reader :days
|
|
20
|
+
attr_reader :days, :application_id
|
|
21
21
|
|
|
22
22
|
# @param days [Integer] Number of days to analyze (default: 7)
|
|
23
|
-
|
|
23
|
+
# @param application_id [Integer, nil] Optional application ID to filter by
|
|
24
|
+
def initialize(days: 7, application_id: nil)
|
|
24
25
|
@days = days
|
|
26
|
+
@application_id = application_id
|
|
25
27
|
@start_date = days.days.ago
|
|
26
28
|
end
|
|
27
29
|
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# Base scope that filters by application when present
|
|
33
|
+
# @return [ActiveRecord::Relation]
|
|
34
|
+
def base_scope
|
|
35
|
+
scope = ErrorLog.all
|
|
36
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
37
|
+
scope
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
public
|
|
41
|
+
|
|
28
42
|
# Get error count by platform for the time period
|
|
29
43
|
# @return [Hash] Platform name => error count
|
|
30
44
|
def error_rate_by_platform
|
|
31
|
-
|
|
45
|
+
base_scope
|
|
32
46
|
.where("occurred_at >= ?", @start_date)
|
|
33
47
|
.group(:platform)
|
|
34
48
|
.count
|
|
@@ -37,10 +51,10 @@ module RailsErrorDashboard
|
|
|
37
51
|
# Get severity distribution by platform
|
|
38
52
|
# @return [Hash] Platform => { severity => count }
|
|
39
53
|
def severity_distribution_by_platform
|
|
40
|
-
platforms =
|
|
54
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
41
55
|
|
|
42
56
|
platforms.each_with_object({}) do |platform, result|
|
|
43
|
-
errors =
|
|
57
|
+
errors = base_scope
|
|
44
58
|
.where(platform: platform)
|
|
45
59
|
.where("occurred_at >= ?", @start_date)
|
|
46
60
|
|
|
@@ -57,10 +71,10 @@ module RailsErrorDashboard
|
|
|
57
71
|
# Get average resolution time by platform
|
|
58
72
|
# @return [Hash] Platform => average hours to resolve
|
|
59
73
|
def resolution_time_by_platform
|
|
60
|
-
platforms =
|
|
74
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
61
75
|
|
|
62
76
|
platforms.each_with_object({}) do |platform, result|
|
|
63
|
-
resolved_errors =
|
|
77
|
+
resolved_errors = base_scope
|
|
64
78
|
.where(platform: platform)
|
|
65
79
|
.where.not(resolved_at: nil)
|
|
66
80
|
.where("occurred_at >= ?", @start_date)
|
|
@@ -79,10 +93,10 @@ module RailsErrorDashboard
|
|
|
79
93
|
# Get top 10 errors for each platform
|
|
80
94
|
# @return [Hash] Platform => Array of error hashes
|
|
81
95
|
def top_errors_by_platform
|
|
82
|
-
platforms =
|
|
96
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
83
97
|
|
|
84
98
|
platforms.each_with_object({}) do |platform, result|
|
|
85
|
-
result[platform] =
|
|
99
|
+
result[platform] = base_scope
|
|
86
100
|
.where(platform: platform)
|
|
87
101
|
.where("occurred_at >= ?", @start_date)
|
|
88
102
|
.select(:id, :error_type, :message, :occurrence_count, :occurred_at)
|
|
@@ -105,7 +119,7 @@ module RailsErrorDashboard
|
|
|
105
119
|
# Higher score = more stable (fewer errors, faster resolution)
|
|
106
120
|
# @return [Hash] Platform => stability score
|
|
107
121
|
def platform_stability_scores
|
|
108
|
-
platforms =
|
|
122
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
109
123
|
error_rates = error_rate_by_platform
|
|
110
124
|
resolution_times = resolution_time_by_platform
|
|
111
125
|
|
|
@@ -131,7 +145,7 @@ module RailsErrorDashboard
|
|
|
131
145
|
# @return [Array<Hash>] Errors with their platforms
|
|
132
146
|
def cross_platform_errors
|
|
133
147
|
# Get error types that appear on 2+ platforms
|
|
134
|
-
error_types_with_platforms =
|
|
148
|
+
error_types_with_platforms = base_scope
|
|
135
149
|
.where("occurred_at >= ?", @start_date)
|
|
136
150
|
.group(:error_type, :platform)
|
|
137
151
|
.select(:error_type, :platform)
|
|
@@ -145,7 +159,7 @@ module RailsErrorDashboard
|
|
|
145
159
|
.select { |_, platforms| platforms.map(&:last).uniq.count > 1 }
|
|
146
160
|
.map do |error_type, platform_pairs|
|
|
147
161
|
platforms = platform_pairs.map(&:last).uniq
|
|
148
|
-
total_count =
|
|
162
|
+
total_count = base_scope
|
|
149
163
|
.where(error_type: error_type, platform: platforms)
|
|
150
164
|
.where("occurred_at >= ?", @start_date)
|
|
151
165
|
.sum(:occurrence_count)
|
|
@@ -155,7 +169,7 @@ module RailsErrorDashboard
|
|
|
155
169
|
platforms: platforms.sort,
|
|
156
170
|
total_occurrences: total_count,
|
|
157
171
|
platform_breakdown: platforms.each_with_object({}) do |platform, breakdown|
|
|
158
|
-
breakdown[platform] =
|
|
172
|
+
breakdown[platform] = base_scope
|
|
159
173
|
.where(error_type: error_type, platform: platform)
|
|
160
174
|
.where("occurred_at >= ?", @start_date)
|
|
161
175
|
.sum(:occurrence_count)
|
|
@@ -168,10 +182,10 @@ module RailsErrorDashboard
|
|
|
168
182
|
# Get daily error trend by platform
|
|
169
183
|
# @return [Hash] Platform => { date => count }
|
|
170
184
|
def daily_trend_by_platform
|
|
171
|
-
platforms =
|
|
185
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
172
186
|
|
|
173
187
|
platforms.each_with_object({}) do |platform, result|
|
|
174
|
-
result[platform] =
|
|
188
|
+
result[platform] = base_scope
|
|
175
189
|
.where(platform: platform)
|
|
176
190
|
.where("occurred_at >= ?", @start_date)
|
|
177
191
|
.group_by_day(:occurred_at, range: @start_date..Time.current)
|
|
@@ -182,7 +196,7 @@ module RailsErrorDashboard
|
|
|
182
196
|
# Get platform health summary
|
|
183
197
|
# @return [Hash] Platform => health metrics
|
|
184
198
|
def platform_health_summary
|
|
185
|
-
platforms =
|
|
199
|
+
platforms = base_scope.distinct.pluck(:platform).compact
|
|
186
200
|
error_rates = error_rate_by_platform
|
|
187
201
|
stability_scores = platform_stability_scores
|
|
188
202
|
|
|
@@ -190,18 +204,18 @@ module RailsErrorDashboard
|
|
|
190
204
|
total_errors = error_rates[platform] || 0
|
|
191
205
|
|
|
192
206
|
# Count critical errors by checking severity method
|
|
193
|
-
critical_errors =
|
|
207
|
+
critical_errors = base_scope
|
|
194
208
|
.where(platform: platform)
|
|
195
209
|
.where("occurred_at >= ?", @start_date)
|
|
196
210
|
.select { |error| error.severity == :critical }
|
|
197
211
|
.count
|
|
198
212
|
|
|
199
|
-
unresolved_errors =
|
|
213
|
+
unresolved_errors = base_scope
|
|
200
214
|
.where(platform: platform, resolved_at: nil)
|
|
201
215
|
.where("occurred_at >= ?", @start_date)
|
|
202
216
|
.count
|
|
203
217
|
|
|
204
|
-
resolved_errors =
|
|
218
|
+
resolved_errors = base_scope
|
|
205
219
|
.where(platform: platform)
|
|
206
220
|
.where.not(resolved_at: nil)
|
|
207
221
|
.where("occurred_at >= ?", @start_date)
|
|
@@ -210,12 +224,12 @@ module RailsErrorDashboard
|
|
|
210
224
|
resolution_rate = total_errors.positive? ? ((resolved_errors.to_f / total_errors) * 100).round(1) : 0.0
|
|
211
225
|
|
|
212
226
|
# Calculate error velocity (increasing or decreasing)
|
|
213
|
-
first_half =
|
|
227
|
+
first_half = base_scope
|
|
214
228
|
.where(platform: platform)
|
|
215
229
|
.where("occurred_at >= ? AND occurred_at < ?", @start_date, @start_date + (@days / 2.0).days)
|
|
216
230
|
.count
|
|
217
231
|
|
|
218
|
-
second_half =
|
|
232
|
+
second_half = base_scope
|
|
219
233
|
.where(platform: platform)
|
|
220
234
|
.where("occurred_at >= ?", @start_date + (@days / 2.0).days)
|
|
221
235
|
.count
|
|
@@ -5,12 +5,13 @@ module RailsErrorDashboard
|
|
|
5
5
|
# Query: Analyze recurring and persistent errors
|
|
6
6
|
# Returns data about high-frequency errors, persistent issues, and cyclical patterns
|
|
7
7
|
class RecurringIssues
|
|
8
|
-
def self.call(days = 30)
|
|
9
|
-
new(days).call
|
|
8
|
+
def self.call(days = 30, application_id: nil)
|
|
9
|
+
new(days, application_id: application_id).call
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def initialize(days = 30)
|
|
12
|
+
def initialize(days = 30, application_id: nil)
|
|
13
13
|
@days = days
|
|
14
|
+
@application_id = application_id
|
|
14
15
|
@start_date = days.days.ago
|
|
15
16
|
end
|
|
16
17
|
|
|
@@ -25,7 +26,9 @@ module RailsErrorDashboard
|
|
|
25
26
|
private
|
|
26
27
|
|
|
27
28
|
def base_query
|
|
28
|
-
ErrorLog.where("occurred_at >= ?", @start_date)
|
|
29
|
+
scope = ErrorLog.where("occurred_at >= ?", @start_date)
|
|
30
|
+
scope = scope.where(application_id: @application_id) if @application_id.present?
|
|
31
|
+
scope
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def high_frequency_errors
|
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.1.
|
|
4
|
+
version: 0.1.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anjan Jagirdar
|
|
@@ -386,7 +386,7 @@ metadata:
|
|
|
386
386
|
source_code_uri: https://github.com/AnjanJ/rails_error_dashboard
|
|
387
387
|
changelog_uri: https://github.com/AnjanJ/rails_error_dashboard/blob/main/CHANGELOG.md
|
|
388
388
|
post_install_message: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n
|
|
389
|
-
\ Rails Error Dashboard v0.1.
|
|
389
|
+
\ Rails Error Dashboard v0.1.25\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\U0001F195
|
|
390
390
|
First time? Quick start:\n rails generate rails_error_dashboard:install\n rails
|
|
391
391
|
db:migrate\n # Add to config/routes.rb:\n mount RailsErrorDashboard::Engine
|
|
392
392
|
=> '/error_dashboard'\n\n\U0001F504 Upgrading from v0.1.x?\n rails db:migrate\n
|