redis_dashboard 0.1.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +13 -19
- data/README.md +17 -15
- data/lib/redis_dashboard/application.rb +65 -21
- data/lib/redis_dashboard/client.rb +33 -8
- data/lib/redis_dashboard/public/ariato.css +739 -0
- data/lib/redis_dashboard/public/style.css +185 -0
- data/lib/redis_dashboard/views/application.scss +2 -10
- data/lib/redis_dashboard/views/clients.erb +18 -11
- data/lib/redis_dashboard/views/config.erb +6 -7
- data/lib/redis_dashboard/views/index.erb +10 -10
- data/lib/redis_dashboard/views/info.erb +6 -7
- data/lib/redis_dashboard/views/key/hash.erb +12 -0
- data/lib/redis_dashboard/views/key/list.erb +11 -0
- data/lib/redis_dashboard/views/key/metadata.erb +47 -0
- data/lib/redis_dashboard/views/key/set.erb +11 -0
- data/lib/redis_dashboard/views/key/string.erb +3 -0
- data/lib/redis_dashboard/views/key/unsupported.erb +2 -0
- data/lib/redis_dashboard/views/key/zset.erb +16 -0
- data/lib/redis_dashboard/views/key.erb +5 -0
- data/lib/redis_dashboard/views/keys.erb +26 -0
- data/lib/redis_dashboard/views/keyspace.erb +23 -0
- data/lib/redis_dashboard/views/layout.erb +35 -29
- data/lib/redis_dashboard/views/memory.erb +25 -0
- data/lib/redis_dashboard/views/slowlog.erb +25 -18
- data/lib/redis_dashboard/views/stats.erb +19 -15
- data/lib/redis_dashboard.rb +1 -1
- data/redis_dashboard.gemspec +2 -2
- data/screenshot.jpg +0 -0
- metadata +19 -13
- data/lib/redis_dashboard/views/stylesheets/base.scss +0 -17
- data/lib/redis_dashboard/views/stylesheets/colors.scss +0 -6
- data/lib/redis_dashboard/views/stylesheets/link.scss +0 -20
- data/lib/redis_dashboard/views/stylesheets/page.scss +0 -69
- data/lib/redis_dashboard/views/stylesheets/server-card.scss +0 -37
- data/lib/redis_dashboard/views/stylesheets/table.scss +0 -49
- data/lib/redis_dashboard/views/stylesheets/typography.scss +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9642ce8486ef1fc0d995d2c06380537aa7918335b1f90e8cbade6983e3a84940
|
4
|
+
data.tar.gz: 5221f10a927a48865bd0f598cddac86f9601a942b800d85543e336754c2682e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1865b8f9ef769bf860987722807e5c341d32c0303e5c115e9c09a9f8c97b91bf9a2de46bd8c2c17dc5b676f379ac1a96142c747f2a1d134611d51a101bff8389
|
7
|
+
data.tar.gz: be609cbe14b267d8421006103528db4afafbd99de6f7995bffc5880daf0f067b83d27a7230e8ed97e0fc014c4e76af53c6c2ef317587d688e309a8c82f0a9503
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
## 0.3.0
|
2
|
+
|
3
|
+
* Add keyspace explorer
|
4
|
+
* Use friendly URLs with server host name
|
5
|
+
* Mute Redis error when server is too old to support memory command
|
6
|
+
* Use Ariato CSS framework https://ariato.org
|
7
|
+
* Remove sassc dependency to switch to plain CSS
|
8
|
+
|
9
|
+
## 0.2.0
|
10
|
+
|
11
|
+
* Add memory stats
|
12
|
+
* Replace deprecated sass by sassc
|
13
|
+
|
14
|
+
## 0.1.6
|
15
|
+
|
16
|
+
* Switch to erubi
|
17
|
+
* Escape HTML by default
|
18
|
+
* Fix cache hit ratio
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,34 +1,28 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
5
|
-
mustermann (1.
|
6
|
-
|
7
|
-
rack
|
4
|
+
erubi (1.10.0)
|
5
|
+
mustermann (1.1.1)
|
6
|
+
ruby2_keywords (~> 0.0.1)
|
7
|
+
rack (2.2.3)
|
8
|
+
rack-protection (2.1.0)
|
8
9
|
rack
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
redis (4.1.2)
|
13
|
-
sass (3.7.4)
|
14
|
-
sass-listen (~> 4.0.0)
|
15
|
-
sass-listen (4.0.0)
|
16
|
-
rb-fsevent (~> 0.9, >= 0.9.4)
|
17
|
-
rb-inotify (~> 0.9, >= 0.9.7)
|
18
|
-
sinatra (2.0.5)
|
10
|
+
redis (4.5.1)
|
11
|
+
ruby2_keywords (0.0.5)
|
12
|
+
sinatra (2.1.0)
|
19
13
|
mustermann (~> 1.0)
|
20
|
-
rack (~> 2.
|
21
|
-
rack-protection (= 2.0
|
14
|
+
rack (~> 2.2)
|
15
|
+
rack-protection (= 2.1.0)
|
22
16
|
tilt (~> 2.0)
|
23
|
-
tilt (2.0.
|
17
|
+
tilt (2.0.10)
|
24
18
|
|
25
19
|
PLATFORMS
|
26
20
|
ruby
|
27
21
|
|
28
22
|
DEPENDENCIES
|
23
|
+
erubi
|
29
24
|
redis
|
30
|
-
sass
|
31
25
|
sinatra
|
32
26
|
|
33
27
|
BUNDLED WITH
|
34
|
-
1.
|
28
|
+
2.1.4
|
data/README.md
CHANGED
@@ -20,38 +20,40 @@ You can run it in standalone or inside your Rails app.
|
|
20
20
|
|
21
21
|
## Installation inside a Rails app
|
22
22
|
|
23
|
-
Add
|
24
|
-
```ruby
|
25
|
-
gem "redis_dashboard"
|
26
|
-
```
|
27
|
-
|
28
|
-
In your terminal run the following command:
|
29
|
-
```shell
|
30
|
-
bundle install
|
31
|
-
```
|
23
|
+
Add to your Gemfile `gem "redis_dashboard"` and run `bundle install`.
|
32
24
|
|
33
25
|
Then mount the app from `config/routes.rb`:
|
34
26
|
```ruby
|
35
|
-
mount RedisDashboard::Application, at: "
|
27
|
+
mount RedisDashboard::Application, at: "redis"
|
36
28
|
```
|
37
29
|
|
38
|
-
|
30
|
+
By default Redis dashboard tries to connect to `REDIS_URL` environment variable or to `localhost`. You can specify any other URL by adding an initializer in `config/initializers/redis_dashboard.rb` :
|
39
31
|
```ruby
|
40
|
-
RedisDashboard.urls = ["redis://localhost"]
|
32
|
+
RedisDashboard.urls = [ENV["REDIS_URL"] || "redis://localhost"]
|
41
33
|
```
|
42
34
|
|
43
|
-
Finally visit http://localhost/
|
35
|
+
Finally visit http://localhost:3000/redis.
|
44
36
|
|
45
|
-
## Authentication
|
37
|
+
## Authentication and permissions
|
46
38
|
|
47
|
-
To protect your dashboard you can setup a basic HTTP authentication:
|
39
|
+
To protect your dashboard you can setup a basic HTTP authentication :
|
48
40
|
|
49
41
|
```ruby
|
42
|
+
# config/initializers/redis_dashboard.rb
|
50
43
|
RedisDashboard::Application.use(Rack::Auth::Basic) do |user, password|
|
51
44
|
user == "USER" && password == "PASSWORD"
|
52
45
|
end
|
53
46
|
```
|
54
47
|
|
48
|
+
In case you handle authentication with Devise, you can perform the permission verification directly from the routes :
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# config/routes.rb
|
52
|
+
authenticate :user, -> (u) { u.admin? } do # Supposing there is a User#admin? method
|
53
|
+
mount RedisDashboard::Application, at: "redis"
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
55
57
|
## MIT License
|
56
58
|
|
57
59
|
Made by [Base Secrète](https://basesecrete.com).
|
@@ -1,40 +1,64 @@
|
|
1
1
|
require "sinatra/base"
|
2
|
+
require "erubi"
|
2
3
|
require "redis"
|
3
4
|
require "uri"
|
4
5
|
|
5
6
|
class RedisDashboard::Application < Sinatra::Base
|
7
|
+
set :erb, escape_html: true
|
8
|
+
|
6
9
|
after { close_clients }
|
7
10
|
|
8
11
|
get "/" do
|
9
12
|
erb(:index, locals: {clients: clients})
|
10
13
|
end
|
11
14
|
|
12
|
-
get "/
|
13
|
-
erb(:info, locals: {info: client.info})
|
14
|
-
end
|
15
|
-
|
16
|
-
get "/config" do
|
15
|
+
get "/:server/config" do
|
17
16
|
erb(:config, locals: {config: client.config})
|
18
17
|
end
|
19
18
|
|
20
|
-
get "/clients" do
|
19
|
+
get "/:server/clients" do
|
21
20
|
erb(:clients, locals: {clients: client.clients})
|
22
21
|
end
|
23
22
|
|
24
|
-
get "/stats" do
|
23
|
+
get "/:server/stats" do
|
25
24
|
erb(:stats, locals: {stats: client.stats})
|
26
25
|
end
|
27
26
|
|
28
|
-
get "/slowlog" do
|
27
|
+
get "/:server/slowlog" do
|
29
28
|
erb(:slowlog, locals: {client: client, commands: client.slow_commands})
|
30
29
|
end
|
31
30
|
|
32
|
-
get "/
|
33
|
-
|
31
|
+
get "/:server/memory" do
|
32
|
+
stats = mute_redis_command_error { client.memory_stats } || {}
|
33
|
+
erb(:memory, locals: {client: client, stats: stats })
|
34
|
+
end
|
35
|
+
|
36
|
+
get "/:server/keyspace" do
|
37
|
+
erb(:keyspace, locals: {keyspace: client.keyspace})
|
38
|
+
end
|
39
|
+
|
40
|
+
get "/:server/keyspace/:db" do
|
41
|
+
client.connection.select(params[:db].sub(/^db/, ""))
|
42
|
+
erb(:keys, locals: {client: client, keys: client.keys(params[:query])})
|
43
|
+
end
|
44
|
+
|
45
|
+
get "/:server/keyspace/:db/*" do
|
46
|
+
params[:key] = params[:splat].first
|
47
|
+
client.connection.select(params[:db].sub(/^db/, ""))
|
48
|
+
erb(:key, locals: {client: client})
|
49
|
+
end
|
50
|
+
|
51
|
+
get "/:server" do
|
52
|
+
erb(:info, locals: {info: client.info})
|
34
53
|
end
|
35
54
|
|
36
55
|
def client
|
37
|
-
@client
|
56
|
+
return @client if @client
|
57
|
+
if url = RedisDashboard.urls.find { |url| URI(url).host == params[:server] }
|
58
|
+
@client ||= RedisDashboard::Client.new(url)
|
59
|
+
else
|
60
|
+
raise Sinatra::NotFound
|
61
|
+
end
|
38
62
|
end
|
39
63
|
|
40
64
|
def clients
|
@@ -57,12 +81,12 @@ class RedisDashboard::Application < Sinatra::Base
|
|
57
81
|
Time.at(epoch).strftime("%b %d %H:%M")
|
58
82
|
end
|
59
83
|
|
60
|
-
def
|
61
|
-
|
84
|
+
def active_page_css(path)
|
85
|
+
request.path_info == path && "active"
|
62
86
|
end
|
63
87
|
|
64
|
-
def
|
65
|
-
request.path_info
|
88
|
+
def active_path_css(path)
|
89
|
+
request.path_info.start_with?(path) && "active"
|
66
90
|
end
|
67
91
|
|
68
92
|
def format_impact_percentage(percentage)
|
@@ -74,16 +98,34 @@ class RedisDashboard::Application < Sinatra::Base
|
|
74
98
|
end
|
75
99
|
|
76
100
|
def compute_cache_hit_ratio(info)
|
77
|
-
|
78
|
-
|
101
|
+
hits = info["keyspace_hits"].to_i
|
102
|
+
misses = info["keyspace_misses"].to_i
|
103
|
+
if (total = hits + misses) > 0
|
104
|
+
hits * 100.0 / total
|
79
105
|
else
|
80
106
|
0
|
81
107
|
end
|
82
108
|
end
|
83
109
|
|
110
|
+
def render_key_data(key)
|
111
|
+
type = client.connection.type(params[:key])
|
112
|
+
erb(:"key/#{type}", locals: {key: key})
|
113
|
+
rescue Errno::ENOENT
|
114
|
+
erb(:"key/unsupported", locals: {key: key})
|
115
|
+
end
|
116
|
+
|
117
|
+
def mute_redis_command_error(&block)
|
118
|
+
block.call
|
119
|
+
rescue Redis::CommandError
|
120
|
+
end
|
121
|
+
|
122
|
+
def escape_key(key)
|
123
|
+
key.gsub("#", "%23")
|
124
|
+
end
|
125
|
+
|
84
126
|
def clients_column_description(col)
|
85
127
|
# https://redis.io/commands/client-list
|
86
|
-
{
|
128
|
+
@clients_column_description ||= {
|
87
129
|
id: "an unique 64-bit client ID (introduced in Redis 2.8.12).",
|
88
130
|
addr: "address/port of the client",
|
89
131
|
fd: "file descriptor corresponding to the socket",
|
@@ -101,12 +143,13 @@ class RedisDashboard::Application < Sinatra::Base
|
|
101
143
|
omem: "output buffer memory usage",
|
102
144
|
events: "file descriptor events (see below)",
|
103
145
|
cmd: "last command played",
|
104
|
-
}
|
146
|
+
}
|
147
|
+
@clients_column_description[col.to_sym]
|
105
148
|
end
|
106
149
|
|
107
150
|
def client_event_description(event)
|
108
151
|
# https://redis.io/commands/client-list
|
109
|
-
{
|
152
|
+
@client_event_description ||= {
|
110
153
|
O: "the client is a slave in MONITOR mode",
|
111
154
|
S: "the client is a normal slave server",
|
112
155
|
M: "the client is a master",
|
@@ -120,7 +163,8 @@ class RedisDashboard::Application < Sinatra::Base
|
|
120
163
|
r: "the client is in readonly mode against a cluster node",
|
121
164
|
A: "connection to be closed ASAP",
|
122
165
|
N: "no specific flag set",
|
123
|
-
}
|
166
|
+
}
|
167
|
+
@client_event_description[event.to_sym]
|
124
168
|
end
|
125
169
|
end
|
126
170
|
end
|
@@ -3,6 +3,7 @@ class RedisDashboard::Client
|
|
3
3
|
|
4
4
|
def initialize(url)
|
5
5
|
@url = url
|
6
|
+
@connection ||= Redis.new(url: url)
|
6
7
|
end
|
7
8
|
|
8
9
|
def clients
|
@@ -10,12 +11,7 @@ class RedisDashboard::Client
|
|
10
11
|
end
|
11
12
|
|
12
13
|
def config
|
13
|
-
|
14
|
-
array = connection.config("get", "*")
|
15
|
-
while (pair = array.slice!(0, 2)).any?
|
16
|
-
hash[pair.first] = pair.last
|
17
|
-
end
|
18
|
-
hash
|
14
|
+
array_reply_to_hash(connection.config("get", "*"))
|
19
15
|
end
|
20
16
|
|
21
17
|
def info
|
@@ -40,13 +36,42 @@ class RedisDashboard::Client
|
|
40
36
|
end.sort{ |left, right| right.microseconds <=> left.microseconds }
|
41
37
|
end
|
42
38
|
|
39
|
+
def memory_stats
|
40
|
+
array_reply_to_hash(connection.memory("stats"))
|
41
|
+
end
|
42
|
+
|
43
|
+
def keys(pattern)
|
44
|
+
connection.keys(pattern)
|
45
|
+
end
|
46
|
+
|
43
47
|
def close
|
44
48
|
connection.close if connection
|
45
49
|
end
|
46
50
|
|
51
|
+
def host
|
52
|
+
URI(url).host
|
53
|
+
end
|
54
|
+
|
55
|
+
def keyspace
|
56
|
+
connection.info("KEYSPACE").inject({}) do |hash, space|
|
57
|
+
db, str = space
|
58
|
+
hash[db] = str.split(",").inject({}) do |h, s|
|
59
|
+
k, v = s.split("=")
|
60
|
+
h[k] = v
|
61
|
+
h
|
62
|
+
end
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
47
67
|
private
|
48
68
|
|
49
|
-
|
50
|
-
|
69
|
+
# Array reply is a Redis format which is translated into a hash for convenience.
|
70
|
+
def array_reply_to_hash(array)
|
71
|
+
hash = {}
|
72
|
+
while (pair = array.slice!(0, 2)).any?
|
73
|
+
hash[pair.first] = pair.last.is_a?(Array) ? array_reply_to_hash(pair.last) : pair.last
|
74
|
+
end
|
75
|
+
hash
|
51
76
|
end
|
52
77
|
end
|