redis_dashboard 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a4b7fde8d3759d82ba885462c3f6dfeb7e696c4
4
- data.tar.gz: 81b2ba82809ec5ee6037f0b73df4559cf84248ea
3
+ metadata.gz: 578baab7856107a9e28a00022dbc4bdc75a5f860
4
+ data.tar.gz: 7335f0c7de979ac2e09a7a4fb06a34bce6ee90a2
5
5
  SHA512:
6
- metadata.gz: 658e9fbafd0a25c487dad985476ddd6215e13becd2b49d9dd56656fb28828f5a8c8e9b9c804994bf99c6e2bd94d0e1f1d8ef729645a754a6e82eb888272359c1
7
- data.tar.gz: 3c1894461af3c63c2648ab99e0cc74f7824c7ddd6b84765df12e21fd39b03f25495cfc0c4e38861ce4053dfa2e408854dd903336c1087d136003a56e666728bd
6
+ metadata.gz: fd7ee9d82db93a2cc17f9bc3f1992023524f01a8e088e97a520298f271896fd1a9044a7505bb887c08a146318d538dc1fac4f7d5abf352ed131ae7316d9f5d56
7
+ data.tar.gz: cbec9b324109fdd0b5e76618d1533093e55f7a61131b2c6ca2b76e804b7c89d11061cfaf6d347f27944aa9a683f233c081891956a48d277c0b90c46faa684019
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  .sass-cache
2
2
  **/.DS_Store
3
+ *.gem
@@ -21,6 +21,10 @@ class RedisDashboard::Application < Sinatra::Base
21
21
  erb(:clients, locals: {clients: client.clients})
22
22
  end
23
23
 
24
+ get "/stats" do
25
+ erb(:stats, locals: {stats: client.stats})
26
+ end
27
+
24
28
  get "/slowlog" do
25
29
  erb(:slowlog, locals: {commands: client.slow_commands})
26
30
  end
@@ -60,5 +64,63 @@ class RedisDashboard::Application < Sinatra::Base
60
64
  def active_page?(path='')
61
65
  request.path_info == '/' + path
62
66
  end
67
+
68
+ def format_impact_percentage(percentage)
69
+ percentage < 1 ? "< 1 <small>%</small>" : "#{percentage.round} <small>%</small>"
70
+ end
71
+
72
+ def format_usec(usec)
73
+ "#{usec}&nbsp;<small>㎲</small>"
74
+ end
75
+
76
+ def compute_cache_hit_ratio(info)
77
+ if (total = info["keyspace_hits"].to_i + info["keyspace_misses"].to_i) > 0
78
+ info["keyspace_hits"].to_f * 100
79
+ else
80
+ 0
81
+ end
82
+ end
83
+
84
+ def clients_column_description(col)
85
+ # https://redis.io/commands/client-list
86
+ {
87
+ id: "an unique 64-bit client ID (introduced in Redis 2.8.12).",
88
+ addr: "address/port of the client",
89
+ fd: "file descriptor corresponding to the socket",
90
+ age: "total duration of the connection in seconds",
91
+ idle: "idle time of the connection in seconds",
92
+ flags: "client flags (see below)",
93
+ db: "current database ID",
94
+ sub: "number of channel subscriptions",
95
+ psub: "number of pattern matching subscriptions",
96
+ multi: "number of commands in a MULTI/EXEC context",
97
+ qbuf: "query buffer length (0 means no query pending)",
98
+ 'qbuf-f': "ree: free space of the query buffer (0 means the buffer is full)",
99
+ obl: "output buffer length",
100
+ oll: "output list length (replies are queued in this list when the buffer is full)",
101
+ omem: "output buffer memory usage",
102
+ events: "file descriptor events (see below)",
103
+ cmd: "last command played",
104
+ }[col.to_sym]
105
+ end
106
+
107
+ def client_event_description(event)
108
+ # https://redis.io/commands/client-list
109
+ {
110
+ O: "the client is a slave in MONITOR mode",
111
+ S: "the client is a normal slave server",
112
+ M: "the client is a master",
113
+ x: "the client is in a MULTI/EXEC context",
114
+ b: "the client is waiting in a blocking operation",
115
+ i: "the client is waiting for a VM I/O (deprecated)",
116
+ d: "a watched keys has been modified - EXEC will fail",
117
+ c: "connection to be closed after writing entire reply",
118
+ u: "the client is unblocked",
119
+ U: "the client is connected via a Unix domain socket",
120
+ r: "the client is in readonly mode against a cluster node",
121
+ A: "connection to be closed ASAP",
122
+ N: "no specific flag set",
123
+ }[event.to_sym]
124
+ end
63
125
  end
64
126
  end
@@ -28,8 +28,11 @@ class RedisDashboard::Client
28
28
  connection.info
29
29
  end
30
30
 
31
- def close
32
- connection.close if connection
31
+ def stats
32
+ stats = connection.info("commandstats").sort { |a, b| b.last["usec"].to_i <=> a.last["usec"].to_i }
33
+ total = stats.reduce(0) { |total, stat| total += stat.last["usec"].to_i }
34
+ stats.each { |stat| stat.last["impact"] = stat.last["usec"].to_f * 100 / total }
35
+ stats
33
36
  end
34
37
 
35
38
  def slow_commands(length = 128) # 128 is the default slowlog-max-len
@@ -43,6 +46,10 @@ class RedisDashboard::Client
43
46
  end.sort{ |left, right| right.microseconds <=> left.microseconds }
44
47
  end
45
48
 
49
+ def close
50
+ connection.close if connection
51
+ end
52
+
46
53
  private
47
54
 
48
55
  def connection
@@ -2,13 +2,17 @@
2
2
  <table>
3
3
  <thead>
4
4
  <% for key in (keys = clients.first.keys) %>
5
- <th><%= key %></th>
5
+ <th title="<%= clients_column_description key %>"><%= key %></th>
6
6
  <% end %>
7
7
  </thead>
8
8
  <% for client in clients %>
9
9
  <tr>
10
10
  <% for key in keys %>
11
- <td><%= client[key] %></td>
11
+ <% if key == "events" %>
12
+ <td title="<%= client_event_description client[key] %>"><%= client[key] %></td>
13
+ <% else %>
14
+ <td><%= client[key] %></td>
15
+ <% end %>
12
16
  <% end %>
13
17
  </tr>
14
18
  <% end %>
@@ -8,3 +8,9 @@
8
8
  <% end %>
9
9
  </table>
10
10
  </div>
11
+
12
+ <a href="https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf">
13
+ <svg viewBox="0 0 24 24">
14
+ <path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z" />
15
+ </svg> Redis config documentation
16
+ </a>
@@ -18,6 +18,10 @@
18
18
  <span class="label">Commands per second</span>
19
19
  <span><%= info["instantaneous_ops_per_sec"] %></span>
20
20
  </div>
21
+ <div>
22
+ <span class="label">Cache hit ratio</span>
23
+ <span><%= format_impact_percentage compute_cache_hit_ratio(info) %></span>
24
+ </div>
21
25
  </div>
22
26
  </div>
23
27
  <% end %>
@@ -19,6 +19,7 @@
19
19
  <a class="page-menu-item <%= 'active' if active_page?('info') %>" href="<%= url("/info?id=#{redis_id}") %>">Info</a>
20
20
  <a class="page-menu-item <%= 'active' if active_page?('config') %>" href="<%= url("/config?id=#{redis_id}") %>">Config</a>
21
21
  <a class="page-menu-item <%= 'active' if active_page?('clients') %>" href="<%= url("/clients?id=#{redis_id}") %>">Clients</a>
22
+ <a class="page-menu-item <%= 'active' if active_page?('stats') %>" href="<%= url("/stats?id=#{redis_id}") %>">Stats</a>
22
23
  <a class="page-menu-item <%= 'active' if active_page?('slowlog') %>" href="<%= url("/slowlog?id=#{redis_id}") %>">Slowlog</a>
23
24
  </div>
24
25
  <% end %>
@@ -11,7 +11,7 @@
11
11
  <tr>
12
12
  <td><%= cmd.id %></td>
13
13
  <td class="date"><%= epoch_to_short_date_time cmd.timestamp %></td>
14
- <td><%= cmd.microseconds %></td>
14
+ <td class="number"><%= format_usec cmd.microseconds %></td>
15
15
  <td><%= cmd.command.join(" ") %></td>
16
16
  </tr>
17
17
  <% end %>
@@ -0,0 +1,20 @@
1
+ <div class="table">
2
+ <table>
3
+ <tr>
4
+ <th>Command</th>
5
+ <th>Total time</th>
6
+ <th>Calls</th>
7
+ <th>AVG time</th>
8
+ <th>Impact</th>
9
+ </tr>
10
+ <% for (key,hash) in stats %>
11
+ <tr>
12
+ <td class="key"><%= key %></td>
13
+ <td class="number"><%= format_usec hash["usec"] %></td>
14
+ <td class="number"><%= hash["calls"] %></td>
15
+ <td class="number"><%= format_usec hash["usec_per_call"] %></td>
16
+ <td class="number"><%= format_impact_percentage hash["impact"] %></td>
17
+ </tr>
18
+ <% end %>
19
+ </table>
20
+ </div>
@@ -19,9 +19,8 @@ td,
19
19
  th {
20
20
  border-bottom: 1px solid $color-light;
21
21
  padding: 12px 12px 11px;
22
- text-align: left;
23
22
  line-height: 24px;
24
- }
23
+ }
25
24
 
26
25
  th {
27
26
  font-weight: 400;
@@ -29,6 +28,7 @@ th {
29
28
  font-size: .694rem;
30
29
  letter-spacing: 2px;
31
30
  text-transform: uppercase;
31
+ text-align: center;
32
32
  }
33
33
 
34
34
  td.key {
@@ -41,4 +41,9 @@ td.key {
41
41
 
42
42
  td.date {
43
43
  white-space: nowrap;
44
+ text-align: right;
45
+ }
46
+
47
+ td.number {
48
+ text-align: right;
44
49
  }
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "redis_dashboard"
6
- spec.version = "0.1.2"
6
+ spec.version = "0.1.3"
7
7
  spec.authors = ["Alexis Bernard"]
8
8
  spec.email = ["alexis@bernard.io"]
9
9
  spec.summary = "Sinatra app to monitor Redis servers."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-19 00:00:00.000000000 Z
11
+ date: 2018-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -47,6 +47,7 @@ files:
47
47
  - lib/redis_dashboard/views/info.erb
48
48
  - lib/redis_dashboard/views/layout.erb
49
49
  - lib/redis_dashboard/views/slowlog.erb
50
+ - lib/redis_dashboard/views/stats.erb
50
51
  - lib/redis_dashboard/views/stylesheets/base.scss
51
52
  - lib/redis_dashboard/views/stylesheets/colors.scss
52
53
  - lib/redis_dashboard/views/stylesheets/link.scss
@@ -77,9 +78,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
78
  version: '0'
78
79
  requirements: []
79
80
  rubyforge_project:
80
- rubygems_version: 2.4.8
81
+ rubygems_version: 2.5.1
81
82
  signing_key:
82
83
  specification_version: 4
83
84
  summary: Sinatra app to monitor Redis servers.
84
85
  test_files: []
85
- has_rdoc: