sidekiq_insight 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8c2db5cf7bb0e99fe1af7f752c3ad0999cc865c7e030d9b1c78b59c183e226c
4
- data.tar.gz: 0c5594584fb8682227d72669cd1942ff0175051582efaddcc49af15511ff877e
3
+ metadata.gz: 930b6bcd68d1c5dc1fde7fcae30c3cb6a3cd2474a97452b5010938ff035a1bd4
4
+ data.tar.gz: e05a5f5fc51daab73546c665440ceee09b501365dabcdcac012a7484436269c7
5
5
  SHA512:
6
- metadata.gz: 5c1be3e9a2f1101c4ecd2fe32080d4c7f42dc47f7652fd27735e93e63a181ce5b12f75bb720f9b6e061e97f148e90cf3a6dd817c7b099e42721edc6dc61fb087
7
- data.tar.gz: ed781d6af149092780b7051ebf7d4456b8444d84424245846407294a622daa4bd57a30230633b072b47de7d2ffed9024e10b3bfd43693e8a3f283674548a2016
6
+ metadata.gz: f109133f4a1783e2540881b0f3645303a1dbea628a424a64e65484fa907710e1a5678c3ae336a08336754dcfa9f8e40a9d3614ec3af35b66a9289f29435a9d28
7
+ data.tar.gz: 53c8919b50ae46727fb6873a25c3277190e367c4527982cecb1080ba16706e09c03e6973c710ae82b8d9c163ed16993027d4024e0e1f67f66ede05796229118b
@@ -26,13 +26,19 @@ module SidekiqInsight
26
26
  private
27
27
 
28
28
  def build_series(metric_sym)
29
- data = {}
30
- SidekiqInsight.storage.top_jobs(20).each do |job|
29
+ points = []
30
+
31
+ SidekiqInsight.storage.top_jobs(50).each do |job|
31
32
  samples = SidekiqInsight.storage.recent(job[:key], 200)
32
- series = samples.reverse.map { |s| [s[:started_at], s[metric_sym].to_f] }
33
- data[job[:key]] = series
33
+
34
+ samples.each do |s|
35
+ points << [s[:started_at], s[metric_sym].to_f]
36
+ end
34
37
  end
35
- data
38
+
39
+ points.sort_by! { |t, _| t }
40
+
41
+ points
36
42
  end
37
43
  end
38
44
  end
@@ -32,6 +32,8 @@
32
32
  <li class="nav-item"><a class="nav-link" href="<%= sidekiq_insight.cpu_path %>">CPU</a></li>
33
33
  <li class="nav-item"><a class="nav-link" href="<%= sidekiq_insight.rss_path %>">Memory</a></li>
34
34
  <li class="nav-item"><a class="nav-link" href="<%= sidekiq_insight.leaks_path %>">Leak Detector</a></li>
35
+ <li class="nav-item"><a class="nav-link" href="<%= sidekiq_insight.wall_path %>">Wall Time (ms)</a></li>
36
+
35
37
  </ul>
36
38
  </div>
37
39
  </div>
@@ -6,43 +6,55 @@
6
6
  <div class="col-md-8">
7
7
  <div class="card shadow-sm h-100">
8
8
  <div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
9
- <h4 class="mb-0">Top Jobs by Avg CPU</h4>
10
- <span class="badge bg-light text-primary">
9
+ <h4 class="mb-0"><i class="bi bi-speedometer2"></i> Top Jobs by Avg CPU</h4>
10
+ <span class="badge bg-light text-primary fs-6">
11
11
  <%= @jobs.count %> jobs
12
12
  </span>
13
13
  </div>
14
14
 
15
15
  <div class="card-body p-0">
16
- <table class="table table-hover table-striped mb-0">
16
+
17
+ <table class="table table-hover table-striped table-bordered mb-0 align-middle">
17
18
  <thead class="table-light">
18
19
  <tr>
19
- <th>Job</th>
20
- <th>Count</th>
21
- <th>Avg CPU (ms)</th>
22
- <th>Avg Mem (KB)</th>
23
- <th></th>
20
+ <th style="width: 40%;">Job</th>
21
+ <th class="text-center" style="width: 10%">Runs</th>
22
+ <th class="text-center" style="width: 15%">Avg CPU</th>
23
+ <th class="text-center" style="width: 15%">Avg Mem</th>
24
+ <th class="text-center" style="width: 10%;">Action</th>
24
25
  </tr>
25
26
  </thead>
26
27
 
27
28
  <tbody>
28
29
  <% @jobs.each do |j| %>
29
30
  <tr>
30
- <td class="fw-semibold"><%= j[:key] %></td>
31
- <td><%= j[:count] %></td>
32
- <td>
33
- <span class="badge bg-info text-dark">
34
- <%= number_with_precision(j[:avg_cpu_ms], precision: 2) %>
31
+ <!-- JOB NAME -->
32
+ <td class="fw-semibold text-break"><%= j[:key] %></td>
33
+
34
+ <!-- RUNS -->
35
+ <td class="text-center">
36
+ <span class="badge bg-secondary"><%= j[:count] %></span>
37
+ </td>
38
+
39
+ <!-- AVG CPU -->
40
+ <td class="text-center">
41
+ <span class="badge bg-info text-dark px-3 py-2">
42
+ <%= number_with_precision(j[:avg_cpu_ms], precision: 2) %> ms
35
43
  </span>
36
44
  </td>
37
- <td>
38
- <span class="badge bg-success">
39
- <%= number_with_precision(j[:avg_mem_kb], precision: 2) %>
45
+
46
+ <!-- AVG MEMORY -->
47
+ <td class="text-center">
48
+ <span class="badge bg-success px-3 py-2">
49
+ <%= number_with_precision(j[:avg_mem_kb], precision: 2) %> KB
40
50
  </span>
41
51
  </td>
42
- <td>
43
- <a class="btn btn-sm btn-outline-primary"
52
+
53
+ <!-- SHOW BUTTON -->
54
+ <td class="text-center">
55
+ <a class="btn btn-sm btn-outline-primary px-3"
44
56
  href="<%= sidekiq_insight.job_path(job: j[:key]) %>">
45
- View
57
+ Show
46
58
  </a>
47
59
  </td>
48
60
  </tr>
@@ -54,28 +66,32 @@
54
66
  </div>
55
67
  </div>
56
68
 
69
+
57
70
  <!-- ================= LEAK ALERTS ================= -->
58
71
  <div class="col-md-4">
59
72
  <div class="card shadow-sm h-100">
60
- <div class="card-header bg-danger text-white">
61
- <h4 class="mb-0">Leak Alerts</h4>
73
+ <div class="card-header bg-danger text-white d-flex justify-content-between align-items-center">
74
+ <h4 class="mb-0"><i class="bi bi-exclamation-triangle"></i> Leak Alerts</h4>
62
75
  </div>
63
76
 
64
77
  <div class="card-body">
78
+
65
79
  <% if @alerts.present? %>
66
- <ul class="list-group">
80
+ <ul class="list-group shadow-sm">
67
81
  <% @alerts.each do |a| %>
68
82
  <li class="list-group-item d-flex justify-content-between align-items-center">
69
83
  <strong><%= a[:job] %></strong>
70
- <span class="badge bg-danger">Leak suspected</span>
84
+ <span class="badge bg-danger px-3">⚠ Leak suspected</span>
71
85
  </li>
72
86
  <% end %>
73
87
  </ul>
74
88
  <% else %>
75
- <div class="text-center text-muted py-3">
76
- No recent leak alerts 🔍
89
+ <div class="text-center text-muted py-4">
90
+ <i class="bi bi-check2-circle fs-1 text-success"></i>
91
+ <div class="mt-2">No recent leak alerts</div>
77
92
  </div>
78
93
  <% end %>
94
+
79
95
  </div>
80
96
  </div>
81
97
  </div>
@@ -4,37 +4,11 @@
4
4
  </div>
5
5
 
6
6
  <div class="card-body">
7
- <canvas id="<%= dom_id %>" height="120"></canvas>
7
+ <%= line_chart data,
8
+ height: "260px",
9
+ points: true,
10
+ download: true,
11
+ legend: true,
12
+ curve: true %>
8
13
  </div>
9
14
  </div>
10
-
11
- <script>
12
- document.addEventListener("DOMContentLoaded", function () {
13
- const ctx = document.getElementById("<%= dom_id %>").getContext("2d");
14
-
15
- const datasets = [
16
- <% data.each do |job_key, series| %>
17
- {
18
- label: "<%= job_key %>",
19
- data: <%= series.map { |t, v| { x: t, y: v } }.to_json %>,
20
- borderWidth: 2,
21
- tension: 0.3,
22
- },
23
- <% end %>
24
- ];
25
-
26
- new Chart(ctx, {
27
- type: 'line',
28
- data: { datasets: datasets },
29
- options: {
30
- responsive: true,
31
- parsing: false,
32
- plugins: { legend: { position: "bottom" } },
33
- scales: {
34
- x: { type: 'time', time: { unit: 'minute' } },
35
- y: { beginAtZero: true }
36
- }
37
- }
38
- });
39
- });
40
- </script>
@@ -2,9 +2,7 @@
2
2
  <h2 class="mb-4 text-primary fw-bold">CPU Usage — Top Jobs</h2>
3
3
 
4
4
  <div class="card mb-4 shadow-sm">
5
- <div class="card-header bg-dark text-white">
6
- Job Statistics
7
- </div>
5
+ <div class="card-header bg-dark text-white">Job Statistics</div>
8
6
 
9
7
  <div class="card-body p-0">
10
8
  <table class="table table-striped mb-0">
@@ -15,6 +13,7 @@
15
13
  <th>Runs</th>
16
14
  </tr>
17
15
  </thead>
16
+
18
17
  <tbody>
19
18
  <% @jobs.each do |job| %>
20
19
  <tr>
@@ -28,6 +27,9 @@
28
27
  </div>
29
28
  </div>
30
29
 
31
- <%= render partial: "chart",
32
- locals: { title: "CPU Time (ms)", dom_id: "cpu_chart", data: @cpu_chart } %>
30
+ <%= render "chart",
31
+ title: "CPU Time (ms)",
32
+ dom_id: "cpu_chart",
33
+ data: @cpu_chart %>
33
34
  </div>
35
+
@@ -7,9 +7,7 @@
7
7
  </div>
8
8
  <% else %>
9
9
  <div class="card shadow-sm">
10
- <div class="card-header bg-danger text-white">
11
- Leaking Jobs
12
- </div>
10
+ <div class="card-header bg-danger text-white">Leaking Jobs</div>
13
11
 
14
12
  <div class="card-body p-0">
15
13
  <table class="table table-striped mb-0">
@@ -21,6 +19,7 @@
21
19
  <th>Runs</th>
22
20
  </tr>
23
21
  </thead>
22
+
24
23
  <tbody>
25
24
  <% @leak_jobs.each do |job| %>
26
25
  <tr class="table-danger">
@@ -1,21 +1,16 @@
1
1
  <div class="container py-4">
2
+ <h2 class="mb-4 text-success fw-bold">RSS Memory Usage — Top Jobs</h2>
2
3
 
3
- <h2 class="mb-4 text-success fw-bold">
4
- RSS Memory Usage — Top Jobs
5
- </h2>
6
-
7
- <!-- Jobs Table -->
8
4
  <div class="card mb-4 shadow-sm">
9
- <div class="card-header bg-success text-white">
10
- Job Memory Summary
11
- </div>
5
+ <div class="card-header bg-success text-white">Job Memory Summary</div>
12
6
 
13
7
  <div class="card-body p-0">
14
- <table class="table table-hover table-striped mb-0">
8
+ <table class="table table-striped table-hover mb-0">
15
9
  <thead class="table-light">
16
10
  <tr>
17
11
  <th>Job</th>
18
- <th>Average RSS (KB)</th>
12
+ <th>Avg RSS (ms)</th>
13
+ <th>Avg CPU (ms)</th>
19
14
  <th>Runs</th>
20
15
  </tr>
21
16
  </thead>
@@ -24,7 +19,8 @@
24
19
  <% @jobs.each do |job| %>
25
20
  <tr>
26
21
  <td><strong><%= job[:key] %></strong></td>
27
- <td><%= job[:avg_mem_kb].round(2) %></td>
22
+ <td><%= job[:avg_mem_kb].round(2) %> KB</td>
23
+ <td><%= job[:avg_cpu_ms].round(2) %> ms</td>
28
24
  <td><%= job[:count] %></td>
29
25
  </tr>
30
26
  <% end %>
@@ -33,10 +29,8 @@
33
29
  </div>
34
30
  </div>
35
31
 
36
- <!-- Chart -->
37
- <%= render partial: "chart",
38
- locals: { title: "RSS Memory (KB)",
39
- dom_id: "rss_chart",
40
- data: @rss_chart } %>
41
-
32
+ <%= render "chart",
33
+ title: "RSS Memory (KB)",
34
+ dom_id: "rss_chart",
35
+ data: @rss_chart %>
42
36
  </div>
@@ -1,23 +1,28 @@
1
1
  <div class="container py-4">
2
- <h2 class="mb-4 text-info fw-bold">Wall Time</h2>
2
+ <h2 class="mb-4 text-info fw-bold">Wall Time Analysis</h2>
3
3
 
4
- <div class="card mb-3 shadow-sm">
4
+ <div class="card mb-4 shadow-sm">
5
5
  <div class="card-header bg-info text-white">
6
- Job Wall Time Summary
6
+ Wall Time Summary (Last 200 Samples)
7
7
  </div>
8
8
 
9
9
  <div class="card-body p-0">
10
- <table class="table table-bordered mb-0">
10
+ <table class="table table-striped table-hover mb-0">
11
11
  <thead class="table-light">
12
12
  <tr>
13
13
  <th>Job</th>
14
+ <th>Avg RSS (ms)</th>
15
+ <th>Avg CPU (ms)</th>
14
16
  <th>Runs</th>
15
17
  </tr>
16
18
  </thead>
19
+
17
20
  <tbody>
18
21
  <% @jobs.each do |job| %>
19
22
  <tr>
20
23
  <td><strong><%= job[:key] %></strong></td>
24
+ <td><%= job[:avg_mem_kb].round(2) %> KB</td>
25
+ <td><%= job[:avg_cpu_ms].round(2) %> ms</td>
21
26
  <td><%= job[:count] %></td>
22
27
  </tr>
23
28
  <% end %>
@@ -26,6 +31,8 @@
26
31
  </div>
27
32
  </div>
28
33
 
29
- <%= render partial: "chart",
30
- locals: { title: "Wall Time (ms)", dom_id: "wall_chart", data: @wall_chart } %>
34
+ <%= render "chart",
35
+ title: "Wall Time Trend (ms)",
36
+ dom_id: "wall_chart",
37
+ data: @wall_chart %>
31
38
  </div>
@@ -7,19 +7,22 @@ module SidekiqInsight
7
7
  end
8
8
 
9
9
  def call(env)
10
- pm_before = GetProcessMem.new
11
- rss_before = pm_before.mb * 1024.0
12
- cpu_before = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
13
- t_before = Process.clock_gettime(Process::CLOCK_MONOTONIC)
10
+ # ---- BEFORE REQUEST ----
11
+ pm_before = GetProcessMem.new
12
+ rss_before = pm_before.mb * 1024.0
13
+ cpu_before = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
14
+ t_before = Process.clock_gettime(Process::CLOCK_MONOTONIC)
14
15
 
15
16
  status, headers, body = @app.call(env)
16
17
 
17
- t_after = Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
+ # ---- AFTER REQUEST ----
19
+ t_after = Process.clock_gettime(Process::CLOCK_MONOTONIC)
18
20
  cpu_after = Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID)
19
- pm_after = GetProcessMem.new
21
+ pm_after = GetProcessMem.new
20
22
  rss_after = pm_after.mb * 1024.0
21
23
 
22
24
  req = Rack::Request.new(env)
25
+
23
26
  sample = {
24
27
  path: req.path,
25
28
  method: req.request_method,
@@ -30,6 +33,7 @@ module SidekiqInsight
30
33
  rss_kb: (rss_after - rss_before)
31
34
  }
32
35
 
36
+ # 🚨 FIX — separate HTTP bucket
33
37
  SidekiqInsight.storage.push_sample("__http__#{req.path}", sample)
34
38
 
35
39
  [status, headers, body]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SidekiqInsight
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq_insight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - mrmalvi