findbug 0.2.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +375 -0
- data/Rakefile +12 -0
- data/app/controllers/findbug/application_controller.rb +105 -0
- data/app/controllers/findbug/dashboard_controller.rb +93 -0
- data/app/controllers/findbug/errors_controller.rb +129 -0
- data/app/controllers/findbug/performance_controller.rb +80 -0
- data/app/jobs/findbug/alert_job.rb +40 -0
- data/app/jobs/findbug/cleanup_job.rb +132 -0
- data/app/jobs/findbug/persist_job.rb +158 -0
- data/app/models/findbug/error_event.rb +197 -0
- data/app/models/findbug/performance_event.rb +237 -0
- data/app/views/findbug/dashboard/index.html.erb +199 -0
- data/app/views/findbug/errors/index.html.erb +137 -0
- data/app/views/findbug/errors/show.html.erb +185 -0
- data/app/views/findbug/performance/index.html.erb +168 -0
- data/app/views/findbug/performance/show.html.erb +203 -0
- data/app/views/layouts/findbug/application.html.erb +601 -0
- data/lib/findbug/alerts/channels/base.rb +75 -0
- data/lib/findbug/alerts/channels/discord.rb +155 -0
- data/lib/findbug/alerts/channels/email.rb +179 -0
- data/lib/findbug/alerts/channels/slack.rb +149 -0
- data/lib/findbug/alerts/channels/webhook.rb +143 -0
- data/lib/findbug/alerts/dispatcher.rb +126 -0
- data/lib/findbug/alerts/throttler.rb +110 -0
- data/lib/findbug/background_persister.rb +142 -0
- data/lib/findbug/capture/context.rb +301 -0
- data/lib/findbug/capture/exception_handler.rb +141 -0
- data/lib/findbug/capture/exception_subscriber.rb +228 -0
- data/lib/findbug/capture/message_handler.rb +104 -0
- data/lib/findbug/capture/middleware.rb +247 -0
- data/lib/findbug/configuration.rb +381 -0
- data/lib/findbug/engine.rb +109 -0
- data/lib/findbug/performance/instrumentation.rb +336 -0
- data/lib/findbug/performance/transaction.rb +193 -0
- data/lib/findbug/processing/data_scrubber.rb +163 -0
- data/lib/findbug/rails/controller_methods.rb +152 -0
- data/lib/findbug/railtie.rb +222 -0
- data/lib/findbug/storage/circuit_breaker.rb +223 -0
- data/lib/findbug/storage/connection_pool.rb +134 -0
- data/lib/findbug/storage/redis_buffer.rb +285 -0
- data/lib/findbug/tasks/findbug.rake +167 -0
- data/lib/findbug/version.rb +5 -0
- data/lib/findbug.rb +216 -0
- data/lib/generators/findbug/install_generator.rb +67 -0
- data/lib/generators/findbug/templates/POST_INSTALL +41 -0
- data/lib/generators/findbug/templates/create_findbug_error_events.rb +44 -0
- data/lib/generators/findbug/templates/create_findbug_performance_events.rb +47 -0
- data/lib/generators/findbug/templates/initializer.rb +157 -0
- data/sig/findbug.rbs +4 -0
- metadata +251 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<div style="margin-bottom: 1.5rem;">
|
|
2
|
+
<h1 style="margin-bottom: 0.5rem;"><%= @transaction_name %></h1>
|
|
3
|
+
<p class="text-muted text-sm">
|
|
4
|
+
<%= link_to "← Back to Performance", findbug.performance_index_path %>
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<% if @stats %>
|
|
9
|
+
<%# Stats overview %>
|
|
10
|
+
<div class="stats-grid" style="grid-template-columns: repeat(6, 1fr);">
|
|
11
|
+
<div class="stat-card">
|
|
12
|
+
<div class="stat-label">Requests</div>
|
|
13
|
+
<div class="stat-value"><%= @stats[:count] %></div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="stat-card">
|
|
17
|
+
<div class="stat-label">Average</div>
|
|
18
|
+
<div class="stat-value"><%= @stats[:avg_duration_ms].round %>ms</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="stat-card">
|
|
22
|
+
<div class="stat-label">P95</div>
|
|
23
|
+
<div class="stat-value"><%= @stats[:p95_duration_ms].round %>ms</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="stat-card">
|
|
27
|
+
<div class="stat-label">P99</div>
|
|
28
|
+
<div class="stat-value"><%= @stats[:p99_duration_ms].round %>ms</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="stat-card">
|
|
32
|
+
<div class="stat-label">Avg Queries</div>
|
|
33
|
+
<div class="stat-value"><%= @stats[:avg_query_count] %></div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="stat-card">
|
|
37
|
+
<div class="stat-label">N+1 Issues</div>
|
|
38
|
+
<div class="stat-value <%= @stats[:n_plus_one_count] > 0 ? 'warning' : 'success' %>">
|
|
39
|
+
<%= @stats[:n_plus_one_count] %>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="grid-2">
|
|
45
|
+
<%# Slowest Requests %>
|
|
46
|
+
<div class="card">
|
|
47
|
+
<div class="card-header">
|
|
48
|
+
<h2 class="card-title">Slowest Requests</h2>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<% if @slowest_requests.any? %>
|
|
52
|
+
<table class="table">
|
|
53
|
+
<thead>
|
|
54
|
+
<tr>
|
|
55
|
+
<th>Duration</th>
|
|
56
|
+
<th>Queries</th>
|
|
57
|
+
<th>When</th>
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
<% @slowest_requests.each do |req| %>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>
|
|
64
|
+
<span class="badge badge-<%= req.duration_ms > 1000 ? 'error' : 'warning' %>">
|
|
65
|
+
<%= req.duration_ms.round %>ms
|
|
66
|
+
</span>
|
|
67
|
+
</td>
|
|
68
|
+
<td>
|
|
69
|
+
<%= req.query_count %> queries
|
|
70
|
+
<% if req.has_n_plus_one %>
|
|
71
|
+
<span class="badge badge-warning" style="margin-left: 0.25rem;">N+1</span>
|
|
72
|
+
<% end %>
|
|
73
|
+
</td>
|
|
74
|
+
<td class="text-muted text-sm">
|
|
75
|
+
<%= time_ago_in_words(req.captured_at) %> ago
|
|
76
|
+
</td>
|
|
77
|
+
</tr>
|
|
78
|
+
<% end %>
|
|
79
|
+
</tbody>
|
|
80
|
+
</table>
|
|
81
|
+
<% else %>
|
|
82
|
+
<div class="empty-state">
|
|
83
|
+
<p>No slow requests found</p>
|
|
84
|
+
</div>
|
|
85
|
+
<% end %>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<%# N+1 Issues %>
|
|
89
|
+
<div class="card">
|
|
90
|
+
<div class="card-header">
|
|
91
|
+
<h2 class="card-title">N+1 Issues</h2>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<% if @n_plus_one_requests.any? %>
|
|
95
|
+
<div class="card-content">
|
|
96
|
+
<% @n_plus_one_requests.each_with_index do |req, idx| %>
|
|
97
|
+
<div>
|
|
98
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
99
|
+
<span class="badge badge-warning"><%= req.query_count %> queries</span>
|
|
100
|
+
<span class="text-muted text-sm"><%= req.duration_ms.round %>ms</span>
|
|
101
|
+
</div>
|
|
102
|
+
<% if req.n_plus_one_queries.present? %>
|
|
103
|
+
<% req.n_plus_one_queries.first(2).each do |nq| %>
|
|
104
|
+
<div style="margin-top: 0.5rem;">
|
|
105
|
+
<div class="text-muted text-xs">
|
|
106
|
+
Repeated <%= nq['count'] || nq[:count] %> times
|
|
107
|
+
</div>
|
|
108
|
+
<div class="code-block" style="margin-top: 0.25rem;">
|
|
109
|
+
<%= truncate((nq['example'] || nq[:example]).to_s, length: 200) %>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<% end %>
|
|
113
|
+
<% end %>
|
|
114
|
+
</div>
|
|
115
|
+
<% unless idx == @n_plus_one_requests.length - 1 %><div class="separator"></div><% end %>
|
|
116
|
+
<% end %>
|
|
117
|
+
</div>
|
|
118
|
+
<% else %>
|
|
119
|
+
<div class="empty-state">
|
|
120
|
+
<div class="empty-state-icon">✓</div>
|
|
121
|
+
<p>No N+1 issues found</p>
|
|
122
|
+
</div>
|
|
123
|
+
<% end %>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<%# Duration breakdown %>
|
|
128
|
+
<div class="card">
|
|
129
|
+
<div class="card-header">
|
|
130
|
+
<h2 class="card-title">Duration Distribution</h2>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="card-content">
|
|
133
|
+
<div style="display: grid; gap: 0.75rem;">
|
|
134
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
135
|
+
<span class="text-muted text-sm">Min</span>
|
|
136
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
137
|
+
<div class="progress">
|
|
138
|
+
<div class="progress-bar info" style="width: <%= (@stats[:min_duration_ms] / [@stats[:max_duration_ms], 1].max * 100).round %>%;"></div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:min_duration_ms].round %>ms</span>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
145
|
+
<span class="text-muted text-sm">P50</span>
|
|
146
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
147
|
+
<div class="progress">
|
|
148
|
+
<div class="progress-bar info" style="width: <%= (@stats[:p50_duration_ms] / [@stats[:max_duration_ms], 1].max * 100).round %>%;"></div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:p50_duration_ms].round %>ms</span>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
155
|
+
<span class="text-muted text-sm">Avg</span>
|
|
156
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
157
|
+
<div class="progress">
|
|
158
|
+
<div class="progress-bar info" style="width: <%= (@stats[:avg_duration_ms] / [@stats[:max_duration_ms], 1].max * 100).round %>%;"></div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:avg_duration_ms].round %>ms</span>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
165
|
+
<span class="text-muted text-sm">P95</span>
|
|
166
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
167
|
+
<div class="progress">
|
|
168
|
+
<div class="progress-bar warning" style="width: <%= (@stats[:p95_duration_ms] / [@stats[:max_duration_ms], 1].max * 100).round %>%;"></div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:p95_duration_ms].round %>ms</span>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
175
|
+
<span class="text-muted text-sm">P99</span>
|
|
176
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
177
|
+
<div class="progress">
|
|
178
|
+
<div class="progress-bar warning" style="width: <%= (@stats[:p99_duration_ms] / [@stats[:max_duration_ms], 1].max * 100).round %>%;"></div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:p99_duration_ms].round %>ms</span>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
185
|
+
<span class="text-muted text-sm">Max</span>
|
|
186
|
+
<div style="flex: 1; margin: 0 1rem;">
|
|
187
|
+
<div class="progress">
|
|
188
|
+
<div class="progress-bar error" style="width: 100%;"></div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
<span class="text-sm" style="min-width: 60px; text-align: right;"><%= @stats[:max_duration_ms].round %>ms</span>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
<% else %>
|
|
197
|
+
<div class="card">
|
|
198
|
+
<div class="empty-state">
|
|
199
|
+
<div class="empty-state-icon">📊</div>
|
|
200
|
+
<p>No data found for this transaction.</p>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
<% end %>
|