redmon 0.0.4 → 0.0.5
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.
- data/.gitignore +5 -4
- data/.travis.yml +9 -1
- data/README.md +11 -9
- data/bin/redmon +9 -3
- data/lib/redmon.rb +15 -19
- data/lib/redmon/app.rb +14 -5
- data/lib/redmon/config.rb +4 -3
- data/lib/redmon/helpers.rb +1 -3
- data/lib/redmon/public/redmon.css +15 -1
- data/lib/redmon/public/redmon.js +88 -80
- data/lib/redmon/public/vendor/bootstrap.min.css +1 -1
- data/lib/redmon/public/vendor/d3.v3.min.js +4 -0
- data/lib/redmon/public/vendor/nv.d3.css +656 -0
- data/lib/redmon/public/vendor/nv.d3.min.js +5 -0
- data/lib/redmon/redis.rb +1 -1
- data/lib/redmon/version.rb +1 -1
- data/lib/redmon/views/app.haml +9 -3
- data/redmon.gemspec +0 -1
- data/spec/app_spec.rb +7 -1
- data/spec/config_spec.rb +3 -3
- metadata +8 -22
- data/lib/redmon/public/vendor/jquery.flot.js +0 -2651
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Redmon
|
2
2
|
|
3
|
-
** Work in progress in the very early stages of dev **
|
4
|
-
|
5
3
|
Simple sinatra based dashbord for redis. After seeing the [fnordmetric](https://github.com/paulasmuth/fnordmetric)
|
6
4
|
project I was inspired to write this. Some of the ideas there have be carried over here.
|
7
5
|
|
8
6
|
[  ](http://travis-ci.org/steelThread/redmon)
|
9
7
|
|
8
|
+
[](https://codeclimate.com/github/steelThread/redmon)
|
9
|
+
|
10
10
|
----
|
11
11
|
|
12
12
|
Watch your redis server live.
|
@@ -38,17 +38,18 @@ gem install redmon
|
|
38
38
|
## Usage
|
39
39
|
|
40
40
|
```bash
|
41
|
-
$
|
42
|
-
|
43
|
-
Usage: bin/redmon (options)
|
41
|
+
$ redmon -h
|
42
|
+
Usage: /Users/sean/codez/steelThread/redmon/vendor/ruby/1.9.1/bin/redmon (options)
|
44
43
|
-a, --address ADDRESS The thin bind address for the app (default: 0.0.0.0)
|
45
44
|
-n, --namespace NAMESPACE The root Redis namespace (default: redmon)
|
46
45
|
-i, --interval SECS Poll interval in secs for the worker (default: 10)
|
47
46
|
-p, --port PORT The thin bind port for the app (default: 4567)
|
48
47
|
-r, --redis URL The Redis url for monitor (default: redis://127.0.0.1:6379)
|
48
|
+
-s, --secure CREDENTIALS Use basic auth. Colon separated credentials, eg admin:admin.
|
49
49
|
--no-app Do not run the web app to present stats
|
50
50
|
--no-worker Do not run a worker to collect the stats
|
51
|
-
|
51
|
+
|
52
|
+
$ redmon
|
52
53
|
>> Thin web server (v1.3.1 codename Triple Espresso)
|
53
54
|
>> Maximum connections set to 1024
|
54
55
|
>> Listening on 0.0.0.0:4567, CTRL+C to stop
|
@@ -63,7 +64,6 @@ $ ruby load_sim.rb
|
|
63
64
|
|
64
65
|
Open your browser to 0.0.0.0:4567
|
65
66
|
|
66
|
-
|
67
67
|
## Using in a Rails application
|
68
68
|
|
69
69
|
Add to Gemfile:
|
@@ -85,12 +85,14 @@ You can configure the Redmon using an initializer config/initializers/redmon.rb:
|
|
85
85
|
Redmon.configure do |config|
|
86
86
|
config.redis_url = 'redis://127.0.0.1:6379'
|
87
87
|
config.namespace = 'redmon'
|
88
|
-
config.poll_interval = 10
|
89
88
|
end
|
90
89
|
```
|
91
90
|
|
92
91
|
This will mount the Redmon application to the /redmon/ path. The trailing slash
|
93
|
-
is important.
|
92
|
+
is important. The worker that gathers the redis info stats will not be started
|
93
|
+
when Redmon is mounted. In order to get a worker running inside of your Rails
|
94
|
+
app you can try this [Railtie](https://github.com/steelThread/redmon/pull/19#issuecomment-7273659)
|
95
|
+
based approach.
|
94
96
|
|
95
97
|
## License
|
96
98
|
|
data/bin/redmon
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'rubygems'
|
3
4
|
require 'mixlib/cli'
|
4
5
|
require 'redmon'
|
5
6
|
|
6
7
|
class RedmonCLI
|
7
8
|
include Mixlib::CLI
|
8
9
|
|
9
|
-
to_i
|
10
|
+
to_i = lambda { |s| s.to_i }
|
10
11
|
|
11
12
|
option :address,
|
12
13
|
:short => '-a ADDRESS',
|
@@ -33,6 +34,11 @@ class RedmonCLI
|
|
33
34
|
:default => 'redmon',
|
34
35
|
:description => 'The root Redis namespace (default: redmon)'
|
35
36
|
|
37
|
+
option :secure,
|
38
|
+
:short => '-s CREDENTIALS',
|
39
|
+
:long => '--secure CREDENTIALS',
|
40
|
+
:description => "Use basic auth. Colon separated credentials, eg admin:admin."
|
41
|
+
|
36
42
|
option :poll_interval,
|
37
43
|
:short => '-i SECS',
|
38
44
|
:long => '--interval SECS',
|
@@ -56,10 +62,10 @@ class RedmonCLI
|
|
56
62
|
|
57
63
|
def run
|
58
64
|
parse_options
|
59
|
-
config[:web_interface] = [config[:address], config[:port]]
|
65
|
+
config[:web_interface] = [config[:address], config[:port]] if config[:app]
|
60
66
|
config
|
61
67
|
end
|
62
68
|
|
63
69
|
end
|
64
70
|
|
65
|
-
Redmon.run RedmonCLI.new.run
|
71
|
+
Redmon.run RedmonCLI.new.run
|
data/lib/redmon.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
|
-
require 'redmon/config'
|
2
|
-
require 'active_support/core_ext'
|
3
1
|
require 'eventmachine'
|
4
|
-
require '
|
5
|
-
require 'redis'
|
6
|
-
require 'sinatra/base'
|
7
|
-
require 'thin'
|
2
|
+
require 'active_support/core_ext'
|
8
3
|
|
9
4
|
module Redmon
|
10
5
|
extend self
|
@@ -13,8 +8,11 @@ module Redmon
|
|
13
8
|
config.apply opts
|
14
9
|
start_em
|
15
10
|
rescue Exception => e
|
16
|
-
|
17
|
-
|
11
|
+
unless e.is_a?(SystemExit)
|
12
|
+
log "!!! Redmon has shit the bed, restarting... #{e.message}"
|
13
|
+
sleep(1)
|
14
|
+
run(opts)
|
15
|
+
end
|
18
16
|
end
|
19
17
|
|
20
18
|
def start_em
|
@@ -27,8 +25,10 @@ module Redmon
|
|
27
25
|
end
|
28
26
|
|
29
27
|
def start_app
|
30
|
-
|
31
|
-
Thin::Server.start(*config.web_interface
|
28
|
+
require 'thin'
|
29
|
+
Thin::Server.start(*config.web_interface) do
|
30
|
+
run Redmon::App.new
|
31
|
+
end
|
32
32
|
log "listening on http##{config.web_interface.join(':')}"
|
33
33
|
rescue Exception => e
|
34
34
|
log "Can't start Redmon::App. port in use? Error #{e}"
|
@@ -46,14 +46,10 @@ module Redmon
|
|
46
46
|
puts "[#{Time.now.strftime('%y-%m-%d %H:%M:%S')}] #{msg}"
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
config.send :option
|
52
|
-
end
|
49
|
+
require 'redmon/config'
|
50
|
+
require 'redmon/redis'
|
53
51
|
|
54
|
-
|
52
|
+
autoload :App, 'redmon/app'
|
53
|
+
autoload :Worker, 'redmon/worker'
|
55
54
|
|
56
|
-
|
57
|
-
require 'redmon/helpers'
|
58
|
-
require 'redmon/app'
|
59
|
-
require 'redmon/worker'
|
55
|
+
end
|
data/lib/redmon/app.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'sinatra/base'
|
1
2
|
require 'redmon/helpers'
|
3
|
+
require 'haml'
|
2
4
|
|
3
5
|
module Redmon
|
4
6
|
class App < Sinatra::Base
|
@@ -6,17 +8,24 @@ module Redmon
|
|
6
8
|
helpers Redmon::Helpers
|
7
9
|
|
8
10
|
use Rack::Static, {
|
9
|
-
urls
|
10
|
-
root
|
11
|
-
cache_control
|
11
|
+
:urls => [/\.css$/, /\.js$/],
|
12
|
+
:root => "#{root}/public",
|
13
|
+
:cache_control => 'public, max-age=3600'
|
12
14
|
}
|
13
15
|
|
16
|
+
if Redmon.config.secure
|
17
|
+
use Rack::Auth::Basic do |username, password|
|
18
|
+
[username, password] == Redmon.config.secure.split(':')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
get '/' do
|
15
23
|
haml :app
|
16
24
|
end
|
17
25
|
|
18
26
|
get '/cli' do
|
19
|
-
args = params[:command].split
|
27
|
+
args = params[:command].split(/ *"(.*?)" *| *'(.*?)' *| /)
|
28
|
+
args.reject!(&:blank?)
|
20
29
|
@cmd = args.shift.downcase.intern
|
21
30
|
begin
|
22
31
|
raise RuntimeError unless supported? @cmd
|
@@ -44,4 +53,4 @@ module Redmon
|
|
44
53
|
end
|
45
54
|
|
46
55
|
end
|
47
|
-
end
|
56
|
+
end
|
data/lib/redmon/config.rb
CHANGED
@@ -14,7 +14,8 @@ module Redmon
|
|
14
14
|
:app => true,
|
15
15
|
:worker => true,
|
16
16
|
:web_interface => ['0.0.0.0', 4567],
|
17
|
-
:poll_interval => 10
|
17
|
+
:poll_interval => 10,
|
18
|
+
:secure => false
|
18
19
|
}
|
19
20
|
|
20
21
|
attr_accessor(*DEFAULTS.keys)
|
@@ -24,8 +25,8 @@ module Redmon
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def apply(opts)
|
27
|
-
opts.each {|k,v| send("#{k}=", v) if respond_to? k}
|
28
|
+
opts.each { |k,v| send("#{k}=", v) if respond_to? k }
|
28
29
|
end
|
29
30
|
|
30
31
|
end
|
31
|
-
end
|
32
|
+
end
|
data/lib/redmon/helpers.rb
CHANGED
@@ -108,7 +108,21 @@ body {
|
|
108
108
|
|
109
109
|
#terminal input:focus{ box-shadow:none; }
|
110
110
|
|
111
|
-
.chart {
|
111
|
+
.chart-container {
|
112
|
+
position: relative;
|
112
113
|
width:880px;
|
113
114
|
height:225px;
|
115
|
+
margin-top: 10px;
|
116
|
+
}
|
117
|
+
|
118
|
+
.label {
|
119
|
+
padding: none;
|
120
|
+
font-size: inherit;
|
121
|
+
font-weight: bold;
|
122
|
+
color: white;
|
123
|
+
text-transform: uppercase;
|
124
|
+
background-color: #404040;
|
125
|
+
-webkit-border-radius: none;
|
126
|
+
-moz-border-radius: none;
|
127
|
+
border-radius: none;
|
114
128
|
}
|
data/lib/redmon/public/redmon.js
CHANGED
@@ -52,17 +52,20 @@ var Redmon = (function() {
|
|
52
52
|
}
|
53
53
|
|
54
54
|
function formatDate(date) {
|
55
|
-
var d = new Date(parseInt(
|
55
|
+
var d = new Date(parseInt(date));
|
56
56
|
return d.getMonth()+1+'/'+d.getDate()+' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds();
|
57
57
|
}
|
58
58
|
|
59
59
|
function formatNumber(num) {
|
60
|
-
return
|
60
|
+
return d3.format(',.0f')(d3.round(num, 2));
|
61
|
+
}
|
62
|
+
|
63
|
+
function formatMetric(num) {
|
64
|
+
return d3.format('s')(num);
|
61
65
|
}
|
62
66
|
|
63
67
|
function formatTime(time) {
|
64
|
-
|
65
|
-
return d.getHours()+':'+d.getMinutes();
|
68
|
+
return d3.time.format('%H:%S')(new Date(time));
|
66
69
|
}
|
67
70
|
|
68
71
|
/**
|
@@ -70,10 +73,10 @@ var Redmon = (function() {
|
|
70
73
|
*/
|
71
74
|
function base1024(arg) {
|
72
75
|
var y = arg;
|
73
|
-
if (y >= 1073741824) { return (y / 1073741824).toFixed(2) + "
|
74
|
-
else if (y >= 1048576) { return (y / 1048576).toFixed(1) + "
|
75
|
-
else if (y >= 1024) { return (y / 1024).toFixed(0) + "
|
76
|
-
else if (y < 1 && y > 0) { return y.toFixed(0) }
|
76
|
+
if (y >= 1073741824) { return (y / 1073741824).toFixed(2) + "Gb" }
|
77
|
+
else if (y >= 1048576) { return (y / 1048576).toFixed(1) + "Mb" }
|
78
|
+
else if (y >= 1024) { return (y / 1024).toFixed(0) + "Kb" }
|
79
|
+
else if (y < 1 && y > 0) { return y.toFixed(0) + "b"}
|
77
80
|
else { return y }
|
78
81
|
}
|
79
82
|
|
@@ -146,57 +149,57 @@ var Redmon = (function() {
|
|
146
149
|
//////////////////////////////////////////////////////////////////////
|
147
150
|
// encapsulate the keyspace chart
|
148
151
|
var memoryWidget = (function() {
|
149
|
-
var
|
150
|
-
, dataset;
|
152
|
+
var chart
|
153
|
+
, dataset = [];
|
151
154
|
|
152
155
|
function render(data) {
|
153
156
|
dataset = points(data);
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
}
|
170
|
-
}
|
171
|
-
});
|
157
|
+
chart = nv.models.lineChart()
|
158
|
+
.x(function(d) { return d.x })
|
159
|
+
.y(function(d) { return d.y })
|
160
|
+
.margin({top : 10, right : 20, bottom : 20, left : 60})
|
161
|
+
.showLegend(false);
|
162
|
+
|
163
|
+
chart.xAxis
|
164
|
+
.tickFormat(formatTime);
|
165
|
+
|
166
|
+
chart.yAxis
|
167
|
+
.tickFormat(base1024);
|
168
|
+
|
169
|
+
update();
|
170
|
+
|
171
|
+
events.bind('data', onData);
|
172
172
|
}
|
173
173
|
|
174
174
|
function points(data) {
|
175
|
-
|
175
|
+
if (data.length)
|
176
|
+
return data.map(point);
|
177
|
+
|
178
|
+
return [];
|
176
179
|
}
|
177
180
|
|
178
181
|
function point(info) {
|
179
|
-
return
|
180
|
-
parseInt(info.time),
|
181
|
-
parseInt(info.used_memory)
|
182
|
-
|
182
|
+
return !info ? {} : {
|
183
|
+
x : parseInt(info.time),
|
184
|
+
y : parseInt(info.used_memory)
|
185
|
+
}
|
183
186
|
}
|
184
187
|
|
185
188
|
function onData(ev, data) {
|
186
|
-
if (
|
187
|
-
|
188
|
-
dataset.shift()
|
189
|
-
}
|
190
|
-
|
191
|
-
dataset.push(point(data));
|
192
|
-
plot.setData([dataset]);
|
193
|
-
plot.setupGrid();
|
194
|
-
plot.draw();
|
189
|
+
if (dataset.length >= 200) {
|
190
|
+
dataset.shift()
|
195
191
|
}
|
192
|
+
|
193
|
+
dataset.push(point(data));
|
194
|
+
update();
|
196
195
|
}
|
197
196
|
|
198
|
-
|
199
|
-
|
197
|
+
function update() {
|
198
|
+
d3.select('#memory-chart svg')
|
199
|
+
.datum([{key : '', values: dataset}])
|
200
|
+
.transition()
|
201
|
+
.ease("linear").call(chart);
|
202
|
+
}
|
200
203
|
|
201
204
|
return {
|
202
205
|
render: render
|
@@ -206,47 +209,45 @@ var Redmon = (function() {
|
|
206
209
|
//////////////////////////////////////////////////////////////////////
|
207
210
|
// encapsulate the keyspace chart
|
208
211
|
var keyspaceWidget = (function() {
|
209
|
-
var
|
212
|
+
var chart
|
210
213
|
, dataset = {
|
211
|
-
hits:
|
212
|
-
misses: {
|
214
|
+
hits: {key: 'Hits' , values: []},
|
215
|
+
misses: {key: 'Misses' , values: []},
|
213
216
|
load: function(data) {
|
214
217
|
var self = this;
|
215
218
|
points(data).forEach(function(point) {self.push(point)});
|
216
|
-
return self
|
219
|
+
return self;
|
217
220
|
},
|
218
221
|
append: function(data) {
|
219
|
-
if (this.hits.length >= 100) this.shift();
|
222
|
+
if (this.hits.values.length >= 100) this.shift();
|
220
223
|
this.push(point(data));
|
221
|
-
return this;
|
222
224
|
},
|
223
225
|
push: function(point) {
|
224
|
-
this.hits.
|
225
|
-
this.misses.
|
226
|
+
this.hits.values.push(point[0]);
|
227
|
+
this.misses.values.push(point[1]);
|
226
228
|
},
|
227
229
|
shift: function() {
|
228
|
-
this.hits.
|
229
|
-
this.misses.
|
230
|
-
},
|
231
|
-
series: function() {
|
232
|
-
return [this.hits, this.misses]
|
230
|
+
this.hits.values.shift();
|
231
|
+
this.misses.values.shift();
|
233
232
|
}
|
234
233
|
};
|
235
234
|
|
236
235
|
function render(data) {
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
236
|
+
dataset.load(data);
|
237
|
+
chart = nv.models.lineChart()
|
238
|
+
.x(function(d) { return d.x })
|
239
|
+
.y(function(d) { return d.y })
|
240
|
+
.margin({top : 10, right : 20, bottom : 20, left : 60});
|
241
|
+
|
242
|
+
chart.xAxis
|
243
|
+
.tickFormat(formatTime);
|
244
|
+
|
245
|
+
chart.yAxis
|
246
|
+
.tickFormat(formatMetric);
|
247
|
+
|
248
|
+
update();
|
249
|
+
|
250
|
+
events.bind('data', onData);
|
250
251
|
}
|
251
252
|
|
252
253
|
function points(data) {
|
@@ -256,21 +257,27 @@ var Redmon = (function() {
|
|
256
257
|
function point(info) {
|
257
258
|
var time = parseInt(info.time);
|
258
259
|
return [
|
259
|
-
|
260
|
-
|
260
|
+
{x: time, y: parseInt(info.keyspace_hits)},
|
261
|
+
{x: time, y: parseInt(info.keyspace_misses)}
|
261
262
|
];
|
262
263
|
}
|
263
264
|
|
264
265
|
function onData(ev, data) {
|
265
|
-
|
266
|
-
|
267
|
-
plot.setupGrid();
|
268
|
-
plot.draw();
|
269
|
-
}
|
266
|
+
dataset.append(data);
|
267
|
+
update();
|
270
268
|
}
|
271
269
|
|
272
|
-
|
273
|
-
|
270
|
+
function update() {
|
271
|
+
var data = [
|
272
|
+
{key : dataset.hits.key, values : dataset.hits.values, color : '#0000FF'},
|
273
|
+
{key : dataset.misses.key, values : dataset.misses.values, color : '#FF0000'}
|
274
|
+
];
|
275
|
+
|
276
|
+
d3.select('#keyspace-chart svg')
|
277
|
+
.datum(data)
|
278
|
+
.transition()
|
279
|
+
.ease("linear").call(chart);
|
280
|
+
}
|
274
281
|
|
275
282
|
return {
|
276
283
|
render: render
|
@@ -281,7 +288,8 @@ var Redmon = (function() {
|
|
281
288
|
// encapsulate the info widget
|
282
289
|
var infoWidget = (function() {
|
283
290
|
function render(data) {
|
284
|
-
|
291
|
+
if (data && data.length)
|
292
|
+
updateTable(data[data.length-1]);
|
285
293
|
}
|
286
294
|
|
287
295
|
function onData(ev, data) {
|