error_radar 0.5.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +32 -0
- data/app/controllers/error_radar/dashboard_controller.rb +32 -5
- data/app/views/error_radar/dashboard/index.html.erb +68 -0
- data/config/routes.rb +2 -0
- data/lib/error_radar/capture_job.rb +18 -0
- data/lib/error_radar/cleanup.rb +43 -0
- data/lib/error_radar/configuration.rb +18 -0
- data/lib/error_radar/engine.rb +4 -0
- data/lib/error_radar/tracking.rb +17 -0
- data/lib/error_radar/version.rb +1 -1
- data/lib/error_radar.rb +1 -0
- data/lib/generators/error_radar/install/templates/initializer.rb +19 -0
- data/lib/tasks/error_radar.rake +44 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdfb13eb6566d150855376a07e28a5a76a758b23526d7cf31de5a6adc17e5d27
|
|
4
|
+
data.tar.gz: 713dcf3fa8c7867b971b6e7da6dd7dbf56a6e4057ec284dbadb6a8818b83f6d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b71979e7ef0ca2737d789f3f047a024c9933d8b2149e83e5d88fc734144213226dfb1a160b4080ee008104dd0c19dd35a1da06ce0fbeb2ae61a0a65455a65d0f
|
|
7
|
+
data.tar.gz: 1838775fea5865d76bc746c49bc51efb0a557d6f6caf0924bdcd489b081570ff8667715690e3ef1b8a32ca7af219d7a26a1fe312e17304c27e4a9025d0632bf4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.0] - 2026-07-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Async capture** via ActiveJob: set `config.async_capture = true` to enqueue
|
|
9
|
+
error writes through `CaptureJob` so exceptions don't block the request/job
|
|
10
|
+
thread. Falls back to synchronous capture if ActiveJob is unavailable.
|
|
11
|
+
Configure the queue with `config.capture_job_queue` (default `:default`).
|
|
12
|
+
- **Age-based retention**: set `config.retention_days` to automatically prune
|
|
13
|
+
resolved/ignored records older than N days.
|
|
14
|
+
- **Count-based retention**: set `config.max_records` to cap the table size;
|
|
15
|
+
oldest resolved/ignored records are deleted first when the limit is exceeded.
|
|
16
|
+
- **`ErrorRadar::Cleanup.run`**: underlying cleanup logic, callable from Ruby or
|
|
17
|
+
Rake. Supports `older_than_days:` override and `dry_run: true` for previewing
|
|
18
|
+
without deleting.
|
|
19
|
+
- **Rake tasks**:
|
|
20
|
+
- `rake error_radar:cleanup` — apply `retention_days` + `max_records`
|
|
21
|
+
- `rake error_radar:cleanup:dry_run` — preview without deleting
|
|
22
|
+
- `rake error_radar:cleanup:older_than` — one-off: `DAYS=30 rake …`
|
|
23
|
+
- `rake error_radar:stats` — print table summary (total, by status, oldest)
|
|
24
|
+
- **Dashboard maintenance panel**: data summary (total / purgeable / open),
|
|
25
|
+
oldest record date, and an inline "Purge / Dry run" form with adjustable days.
|
|
26
|
+
- **`POST /maintenance/purge`** endpoint: called by the dashboard's Purge button;
|
|
27
|
+
accepts `days` and `dry_run` params, returns JSON `{ deleted:, dry_run: }`.
|
|
28
|
+
- **Dashboard stat query optimization**: 5 separate COUNT queries replaced by one
|
|
29
|
+
`group(:status).count` query.
|
|
30
|
+
|
|
31
|
+
### Notes
|
|
32
|
+
- `async_capture` is `false` by default to avoid surprising behaviour for apps
|
|
33
|
+
that do not have a queue adapter configured.
|
|
34
|
+
- Cleanup only touches `resolved` and `ignored` records — open/in-progress
|
|
35
|
+
records are never auto-deleted.
|
|
36
|
+
|
|
5
37
|
## [0.5.0] - 2026-07-03
|
|
6
38
|
|
|
7
39
|
### Added
|
|
@@ -10,13 +10,18 @@ module ErrorRadar
|
|
|
10
10
|
SEVERITY_ORDER = { 'critical' => 0, 'error' => 1, 'warning' => 2, 'info' => 3 }.freeze
|
|
11
11
|
|
|
12
12
|
def index
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@
|
|
17
|
-
@
|
|
13
|
+
counts_by_status = ErrorLog.group(:status).count
|
|
14
|
+
# statuses returns {"open"=>0, "in_progress"=>1, "resolved"=>2, "ignored"=>3}
|
|
15
|
+
inv = ErrorLog.statuses.invert
|
|
16
|
+
@open_count = counts_by_status[ErrorLog.statuses['open']] || 0
|
|
17
|
+
@in_progress = counts_by_status[ErrorLog.statuses['in_progress']] || 0
|
|
18
|
+
@resolved_count = counts_by_status[ErrorLog.statuses['resolved']] || 0
|
|
19
|
+
@ignored_count = counts_by_status[ErrorLog.statuses['ignored']] || 0
|
|
20
|
+
@total = counts_by_status.values.sum
|
|
18
21
|
@unresolved = @open_count + @in_progress
|
|
19
22
|
|
|
23
|
+
@oldest_record = ErrorLog.minimum(:first_seen_at)
|
|
24
|
+
|
|
20
25
|
@by_category = ErrorLog.unresolved.group(:category).count
|
|
21
26
|
@by_severity = ErrorLog.unresolved.group(:severity).count
|
|
22
27
|
|
|
@@ -46,6 +51,28 @@ module ErrorRadar
|
|
|
46
51
|
@external_links = build_external_links
|
|
47
52
|
end
|
|
48
53
|
|
|
54
|
+
def purge
|
|
55
|
+
days = params[:days].presence&.to_i
|
|
56
|
+
dry_run = params[:dry_run] == '1'
|
|
57
|
+
|
|
58
|
+
result = ErrorRadar::Cleanup.run(
|
|
59
|
+
older_than_days: (days if days&.positive?),
|
|
60
|
+
dry_run: dry_run
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
respond_to do |format|
|
|
64
|
+
format.json { render json: result }
|
|
65
|
+
format.html do
|
|
66
|
+
msg = if dry_run
|
|
67
|
+
"Dry run: would delete #{result[:deleted]} record(s)."
|
|
68
|
+
else
|
|
69
|
+
"Purged #{result[:deleted]} record(s)."
|
|
70
|
+
end
|
|
71
|
+
redirect_to root_path, notice: msg
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
49
76
|
private
|
|
50
77
|
|
|
51
78
|
def build_external_links
|
|
@@ -101,6 +101,52 @@
|
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
|
+
<div class="panel" style="margin-bottom:20px" id="maintenance-panel">
|
|
105
|
+
<h2>Maintenance & Data Retention</h2>
|
|
106
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:12px;margin-bottom:16px">
|
|
107
|
+
<div style="background:#f9fafb;border-radius:8px;padding:12px;text-align:center">
|
|
108
|
+
<div style="font-size:22px;font-weight:700;color:#111827"><%= @total %></div>
|
|
109
|
+
<div style="font-size:11px;color:#6b7280">Total records</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div style="background:#f9fafb;border-radius:8px;padding:12px;text-align:center">
|
|
112
|
+
<div style="font-size:22px;font-weight:700;color:#28a745"><%= @resolved_count + @ignored_count %></div>
|
|
113
|
+
<div style="font-size:11px;color:#6b7280">Resolved / Ignored (purgeable)</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div style="background:#f9fafb;border-radius:8px;padding:12px;text-align:center">
|
|
116
|
+
<div style="font-size:22px;font-weight:700;color:#fd7e14"><%= @unresolved %></div>
|
|
117
|
+
<div style="font-size:11px;color:#6b7280">Open / In progress</div>
|
|
118
|
+
</div>
|
|
119
|
+
<div style="background:#f9fafb;border-radius:8px;padding:12px;text-align:center">
|
|
120
|
+
<div style="font-size:13px;font-weight:600;color:#111827">
|
|
121
|
+
<%= @oldest_record ? @oldest_record.strftime('%Y-%m-%d') : '—' %>
|
|
122
|
+
</div>
|
|
123
|
+
<div style="font-size:11px;color:#6b7280">Oldest record</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
|
|
127
|
+
<label style="font-size:13px;font-weight:500">Delete resolved/ignored older than</label>
|
|
128
|
+
<input id="purge-days" type="number" min="1" placeholder="days" value="90"
|
|
129
|
+
style="width:80px;padding:5px 8px;border:1px solid #d1d5db;border-radius:6px;font-size:13px">
|
|
130
|
+
<span style="font-size:13px;color:#6b7280">days</span>
|
|
131
|
+
<button onclick="doPurge(false)"
|
|
132
|
+
style="padding:6px 14px;background:#dc3545;color:#fff;border:none;border-radius:6px;font-size:13px;cursor:pointer">
|
|
133
|
+
Purge
|
|
134
|
+
</button>
|
|
135
|
+
<button onclick="doPurge(true)"
|
|
136
|
+
style="padding:6px 14px;background:#6c757d;color:#fff;border:none;border-radius:6px;font-size:13px;cursor:pointer">
|
|
137
|
+
Dry run
|
|
138
|
+
</button>
|
|
139
|
+
<span id="purge-result" style="font-size:13px;color:#16a34a"></span>
|
|
140
|
+
</div>
|
|
141
|
+
<% if ErrorRadar.config.retention_days || ErrorRadar.config.max_records %>
|
|
142
|
+
<div style="margin-top:10px;font-size:12px;color:#6b7280">
|
|
143
|
+
Config: retention_days=<strong><%= ErrorRadar.config.retention_days || '—' %></strong>,
|
|
144
|
+
max_records=<strong><%= ErrorRadar.config.max_records || '—' %></strong>
|
|
145
|
+
(applies automatically when you run <code>rake error_radar:cleanup</code>)
|
|
146
|
+
</div>
|
|
147
|
+
<% end %>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
104
150
|
<div class="panel" style="margin-bottom:20px">
|
|
105
151
|
<h2>Kanban — drag a card to change status</h2>
|
|
106
152
|
<div class="kanban">
|
|
@@ -127,6 +173,28 @@
|
|
|
127
173
|
</div>
|
|
128
174
|
|
|
129
175
|
<script>
|
|
176
|
+
function doPurge(dryRun) {
|
|
177
|
+
var days = document.getElementById('purge-days').value;
|
|
178
|
+
var result = document.getElementById('purge-result');
|
|
179
|
+
var token = document.querySelector('meta[name="csrf-token"]').content;
|
|
180
|
+
result.textContent = 'Running…';
|
|
181
|
+
result.style.color = '#6b7280';
|
|
182
|
+
fetch('<%= maintenance_purge_path %>', {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': token, 'Accept': 'application/json' },
|
|
185
|
+
body: JSON.stringify({ days: days, dry_run: dryRun ? '1' : '0' })
|
|
186
|
+
}).then(function(r) { return r.json(); })
|
|
187
|
+
.then(function(d) {
|
|
188
|
+
var label = dryRun ? 'Would delete' : 'Deleted';
|
|
189
|
+
result.textContent = label + ' ' + d.deleted + ' record(s).';
|
|
190
|
+
result.style.color = dryRun ? '#fd7e14' : '#16a34a';
|
|
191
|
+
})
|
|
192
|
+
.catch(function() {
|
|
193
|
+
result.textContent = 'Request failed.';
|
|
194
|
+
result.style.color = '#dc3545';
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
130
198
|
(function () {
|
|
131
199
|
var token = document.querySelector('meta[name="csrf-token"]').content;
|
|
132
200
|
|
data/config/routes.rb
CHANGED
|
@@ -16,4 +16,6 @@ ErrorRadar::Engine.routes.draw do
|
|
|
16
16
|
get 'errors/:id', to: 'errors#show', as: :error
|
|
17
17
|
patch 'errors/:id/status', to: 'errors#update_status', as: :error_status
|
|
18
18
|
delete 'errors/:id', to: 'errors#destroy'
|
|
19
|
+
|
|
20
|
+
post 'maintenance/purge', to: 'dashboard#purge', as: :maintenance_purge
|
|
19
21
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ErrorRadar
|
|
4
|
+
# Background job for async exception capture. Used when config.async_capture
|
|
5
|
+
# is true. Failures are logged to Rails.logger and swallowed — a broken
|
|
6
|
+
# capture job must not cause retry storms or affect the application.
|
|
7
|
+
class CaptureJob < ActiveJob::Base
|
|
8
|
+
queue_as { ErrorRadar.config.capture_job_queue }
|
|
9
|
+
|
|
10
|
+
def perform(attrs_json)
|
|
11
|
+
attrs = JSON.parse(attrs_json, symbolize_names: true)
|
|
12
|
+
log = ErrorLog.record(**attrs)
|
|
13
|
+
Notifier.dispatch(log) if log
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
ErrorRadar::Tracking.warn_internal("CaptureJob failed: #{e.class}: #{e.message}")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ErrorRadar
|
|
4
|
+
# Prunes stale ErrorLog records. Two independent strategies run in sequence:
|
|
5
|
+
#
|
|
6
|
+
# 1. Age-based: deletes resolved/ignored records whose last_seen_at is
|
|
7
|
+
# older than `older_than_days` (or config.retention_days).
|
|
8
|
+
# 2. Count-based: when the total exceeds config.max_records, deletes the
|
|
9
|
+
# oldest resolved/ignored records until the limit is satisfied.
|
|
10
|
+
#
|
|
11
|
+
# Open and in_progress records are never deleted automatically.
|
|
12
|
+
module Cleanup
|
|
13
|
+
def self.run(older_than_days: nil, dry_run: false)
|
|
14
|
+
deleted = 0
|
|
15
|
+
|
|
16
|
+
# ── Age-based pruning ───────────────────────────────────────────────
|
|
17
|
+
days = (older_than_days || ErrorRadar.config.retention_days)&.to_i
|
|
18
|
+
if days && days > 0
|
|
19
|
+
cutoff = days.days.ago
|
|
20
|
+
scope = stale_scope.where('last_seen_at < ?', cutoff)
|
|
21
|
+
deleted += dry_run ? scope.count : scope.delete_all
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# ── Count-based pruning ────────────────────────────────────────────
|
|
25
|
+
max = ErrorRadar.config.max_records&.to_i
|
|
26
|
+
if max && max > 0
|
|
27
|
+
total = ErrorLog.count
|
|
28
|
+
if total > max
|
|
29
|
+
excess = total - max
|
|
30
|
+
ids = stale_scope.order(last_seen_at: :asc).limit(excess).pluck(:id)
|
|
31
|
+
deleted += dry_run ? ids.size : ErrorLog.where(id: ids).delete_all
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
{ deleted: deleted, dry_run: dry_run }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.stale_scope
|
|
39
|
+
ErrorLog.where(status: [ErrorLog.statuses[:resolved], ErrorLog.statuses[:ignored]])
|
|
40
|
+
end
|
|
41
|
+
private_class_method :stale_scope
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -69,6 +69,19 @@ module ErrorRadar
|
|
|
69
69
|
@error_callbacks << block
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# Performance ─────────────────────────────────────────────────────────────
|
|
73
|
+
# When true, ErrorLog.record is enqueued via ActiveJob (non-blocking).
|
|
74
|
+
# Requires ActiveJob to be configured. Falls back to sync if ActiveJob is absent.
|
|
75
|
+
attr_accessor :async_capture
|
|
76
|
+
# Queue name for CaptureJob when async_capture is enabled.
|
|
77
|
+
attr_accessor :capture_job_queue
|
|
78
|
+
|
|
79
|
+
# Retention ───────────────────────────────────────────────────────────────
|
|
80
|
+
# Auto-delete resolved/ignored records older than this many days (nil = keep forever).
|
|
81
|
+
attr_accessor :retention_days
|
|
82
|
+
# Keep at most this many total records; deletes oldest resolved/ignored first (nil = unlimited).
|
|
83
|
+
attr_accessor :max_records
|
|
84
|
+
|
|
72
85
|
# REST API ────────────────────────────────────────────────────────────────
|
|
73
86
|
# Bearer token for /api/* endpoints. nil = unauthenticated (not for prod).
|
|
74
87
|
attr_accessor :api_token
|
|
@@ -135,6 +148,11 @@ module ErrorRadar
|
|
|
135
148
|
@app_host = nil
|
|
136
149
|
@error_callbacks = []
|
|
137
150
|
|
|
151
|
+
@async_capture = false
|
|
152
|
+
@capture_job_queue = :default
|
|
153
|
+
@retention_days = nil
|
|
154
|
+
@max_records = nil
|
|
155
|
+
|
|
138
156
|
@api_token = nil
|
|
139
157
|
@github_token = nil
|
|
140
158
|
@github_repo = nil
|
data/lib/error_radar/engine.rb
CHANGED
data/lib/error_radar/tracking.rb
CHANGED
|
@@ -27,6 +27,17 @@ module ErrorRadar
|
|
|
27
27
|
attrs.merge!(extra.compact) if extra.is_a?(Hash)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
if ErrorRadar.config.async_capture && defined?(::ActiveJob::Base)
|
|
31
|
+
require 'error_radar/capture_job'
|
|
32
|
+
job_attrs = attrs.merge(
|
|
33
|
+
category: attrs[:category].to_s,
|
|
34
|
+
severity: attrs[:severity].to_s,
|
|
35
|
+
context: safe_json(attrs[:context])
|
|
36
|
+
)
|
|
37
|
+
ErrorRadar::CaptureJob.perform_later(job_attrs.to_json)
|
|
38
|
+
return nil
|
|
39
|
+
end
|
|
40
|
+
|
|
30
41
|
log = ErrorRadar::ErrorLog.record(**attrs)
|
|
31
42
|
ErrorRadar::Notifier.dispatch(log) if log
|
|
32
43
|
log
|
|
@@ -112,6 +123,12 @@ module ErrorRadar
|
|
|
112
123
|
nil
|
|
113
124
|
end
|
|
114
125
|
|
|
126
|
+
def safe_json(value)
|
|
127
|
+
JSON.parse(value.to_json)
|
|
128
|
+
rescue StandardError
|
|
129
|
+
{}
|
|
130
|
+
end
|
|
131
|
+
|
|
115
132
|
def warn_internal(message)
|
|
116
133
|
logger = defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : nil
|
|
117
134
|
logger ? logger.error("[ErrorRadar] #{message}") : warn("[ErrorRadar] #{message}")
|
data/lib/error_radar/version.rb
CHANGED
data/lib/error_radar.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'error_radar/version'
|
|
|
4
4
|
require 'error_radar/configuration'
|
|
5
5
|
require 'error_radar/tracking'
|
|
6
6
|
require 'error_radar/notifier'
|
|
7
|
+
require 'error_radar/cleanup'
|
|
7
8
|
require 'error_radar/middleware'
|
|
8
9
|
require 'error_radar/server_monitor'
|
|
9
10
|
require 'error_radar/engine'
|
|
@@ -83,6 +83,25 @@ ErrorRadar.configure do |config|
|
|
|
83
83
|
# Requires running: bin/rails generate error_radar:upgrade_v050 && bin/rails db:migrate
|
|
84
84
|
# config.github_token = ENV['GITHUB_TOKEN'] # PAT with repo scope
|
|
85
85
|
# config.github_repo = 'myorg/myapp' # "owner/repo" format
|
|
86
|
+
|
|
87
|
+
# --- Performance & Async Capture ---
|
|
88
|
+
# Write ErrorLog records via ActiveJob so exceptions don't block the request.
|
|
89
|
+
# Requires ActiveJob + a queue adapter (Sidekiq, Solid Queue, etc.).
|
|
90
|
+
# Falls back to synchronous capture if ActiveJob is unavailable.
|
|
91
|
+
# config.async_capture = true
|
|
92
|
+
# config.capture_job_queue = :default # queue name for CaptureJob
|
|
93
|
+
|
|
94
|
+
# --- Retention / Cleanup ---
|
|
95
|
+
# Auto-prune old resolved/ignored records to keep the table lean.
|
|
96
|
+
# Run `rake error_radar:cleanup` from a cron job or Heroku Scheduler.
|
|
97
|
+
# config.retention_days = 90 # delete resolved/ignored records older than 90 days
|
|
98
|
+
# config.max_records = 50000 # hard cap; oldest resolved/ignored purged first
|
|
99
|
+
#
|
|
100
|
+
# Rake tasks available:
|
|
101
|
+
# rake error_radar:cleanup # apply retention_days + max_records
|
|
102
|
+
# rake error_radar:cleanup:dry_run # preview without deleting
|
|
103
|
+
# rake error_radar:cleanup:older_than # DAYS=30 rake error_radar:cleanup:older_than
|
|
104
|
+
# rake error_radar:stats # print table summary
|
|
86
105
|
end
|
|
87
106
|
|
|
88
107
|
# ActiveJob is now captured automatically via install_active_job = true above.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :error_radar do
|
|
4
|
+
desc 'Delete old resolved/ignored ErrorLogs per config.retention_days and config.max_records'
|
|
5
|
+
task cleanup: :environment do
|
|
6
|
+
result = ErrorRadar::Cleanup.run
|
|
7
|
+
puts "[ErrorRadar] Cleanup complete — #{result[:deleted]} record(s) deleted."
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
namespace :cleanup do
|
|
11
|
+
desc 'Preview what error_radar:cleanup would delete without actually deleting'
|
|
12
|
+
task dry_run: :environment do
|
|
13
|
+
result = ErrorRadar::Cleanup.run(dry_run: true)
|
|
14
|
+
puts "[ErrorRadar] Dry run — would delete #{result[:deleted]} record(s)."
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'Delete resolved/ignored ErrorLogs older than DAYS (e.g. DAYS=30 rake error_radar:cleanup:older_than)'
|
|
18
|
+
task older_than: :environment do
|
|
19
|
+
days = ENV.fetch('DAYS', nil)&.to_i
|
|
20
|
+
abort '[ErrorRadar] Set DAYS env var (e.g. DAYS=30)' unless days&.positive?
|
|
21
|
+
|
|
22
|
+
result = ErrorRadar::Cleanup.run(older_than_days: days)
|
|
23
|
+
puts "[ErrorRadar] Deleted #{result[:deleted]} record(s) older than #{days} days."
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc 'Print a summary of ErrorLog table stats'
|
|
28
|
+
task stats: :environment do
|
|
29
|
+
counts = ErrorRadar::ErrorLog.group(:status).count
|
|
30
|
+
.transform_keys { |k| ErrorRadar::ErrorLog.statuses.key(k) || k }
|
|
31
|
+
total = counts.values.sum
|
|
32
|
+
|
|
33
|
+
puts "\n[ErrorRadar] Error log summary"
|
|
34
|
+
puts " Total : #{total}"
|
|
35
|
+
puts " Open : #{counts['open'] || 0}"
|
|
36
|
+
puts " In progress: #{counts['in_progress'] || 0}"
|
|
37
|
+
puts " Resolved : #{counts['resolved'] || 0}"
|
|
38
|
+
puts " Ignored : #{counts['ignored'] || 0}"
|
|
39
|
+
|
|
40
|
+
oldest = ErrorRadar::ErrorLog.minimum(:first_seen_at)
|
|
41
|
+
puts " Oldest : #{oldest&.strftime('%Y-%m-%d') || '—'}"
|
|
42
|
+
puts ''
|
|
43
|
+
end
|
|
44
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: error_radar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- chienbn9x
|
|
@@ -75,6 +75,8 @@ files:
|
|
|
75
75
|
- app/views/layouts/error_radar/application.html.erb
|
|
76
76
|
- config/routes.rb
|
|
77
77
|
- lib/error_radar.rb
|
|
78
|
+
- lib/error_radar/capture_job.rb
|
|
79
|
+
- lib/error_radar/cleanup.rb
|
|
78
80
|
- lib/error_radar/configuration.rb
|
|
79
81
|
- lib/error_radar/engine.rb
|
|
80
82
|
- lib/error_radar/integrations/active_job.rb
|
|
@@ -96,6 +98,7 @@ files:
|
|
|
96
98
|
- lib/generators/error_radar/install/templates/initializer.rb
|
|
97
99
|
- lib/generators/error_radar/upgrade_v050/templates/add_github_issue_to_error_radar_error_logs.rb.tt
|
|
98
100
|
- lib/generators/error_radar/upgrade_v050/upgrade_v050_generator.rb
|
|
101
|
+
- lib/tasks/error_radar.rake
|
|
99
102
|
homepage: https://github.com/chienbn9x/error_radar
|
|
100
103
|
licenses:
|
|
101
104
|
- MIT
|