redmon 0.0.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.
@@ -0,0 +1,135 @@
1
+ // TryMongo
2
+ //
3
+ // Copyright (c) 2009 Kyle Banker
4
+ // Licensed under the MIT Licence.
5
+ // http://www.opensource.org/licenses/mit-license.php
6
+
7
+ // Readline class to handle line input.
8
+ var ReadLine = function(options) {
9
+ this.options = options || {};
10
+ this.htmlForInput = this.options.htmlForInput;
11
+ this.inputHandler = this.options.handler;
12
+ this.terminal = $(this.options.terminalId || "#terminal");
13
+ this.lineClass = this.options.lineClass || '.readLine';
14
+ this.history = [];
15
+ this.historyPtr = 0;
16
+
17
+ this.initialize();
18
+ };
19
+
20
+ ReadLine.prototype = {
21
+
22
+ initialize: function() {
23
+ this.addInputLine();
24
+ },
25
+
26
+ // Enter a new input line with proper behavior.
27
+ addInputLine: function(stackLevel) {
28
+ stackLevel = stackLevel || 0;
29
+ this.terminal.append(this.htmlForInput(stackLevel));
30
+ var ctx = this;
31
+ ctx.activeLine = $(this.lineClass + '.active');
32
+
33
+ // Bind key events for entering and navigting history.
34
+ ctx.activeLine.bind("keydown", function(ev) {
35
+ switch (ev.keyCode) {
36
+ case EnterKeyCode:
37
+ ctx.processInput(this.value);
38
+ break;
39
+ case UpArrowKeyCode:
40
+ ctx.getCommand('previous');
41
+ break;
42
+ case DownArrowKeyCode:
43
+ ctx.getCommand('next');
44
+ break;
45
+ }
46
+ });
47
+
48
+ this.activeLine.focus();
49
+ },
50
+
51
+ // Returns the 'next' or 'previous' command in this history.
52
+ getCommand: function(direction) {
53
+ if(this.history.length === 0) {
54
+ return;
55
+ }
56
+ this.adjustHistoryPointer(direction);
57
+ this.activeLine[0].value = this.history[this.historyPtr];
58
+ $(this.activeLine[0]).focus();
59
+ //this.activeLine[0].value = this.activeLine[0].value;
60
+ },
61
+
62
+ // Moves the history pointer to the 'next' or 'previous' position.
63
+ adjustHistoryPointer: function(direction) {
64
+ if(direction == 'previous') {
65
+ if(this.historyPtr - 1 >= 0) {
66
+ this.historyPtr -= 1;
67
+ }
68
+ }
69
+ else {
70
+ if(this.historyPtr + 1 < this.history.length) {
71
+ this.historyPtr += 1;
72
+ }
73
+ }
74
+ },
75
+
76
+ // Return the handler's response.
77
+ processInput: function(value) {
78
+ value = value.trim();
79
+ if (!value) {
80
+ // deactivate the line...
81
+ this.activeLine.value = "";
82
+ this.activeLine.attr({disabled: true});
83
+ this.activeLine.removeClass('active');
84
+
85
+ // and add add a new command line.
86
+ this.addInputLine();
87
+ return
88
+
89
+ } else if (value == 'clear') {
90
+ this.clear();
91
+ return
92
+ }
93
+
94
+ var me = this;
95
+ me.inputHandler(value, function(response) {
96
+ me.insertResponse(response);
97
+
98
+ // Save to the command history...
99
+ if((lineValue = value.trim()) !== "") {
100
+ me.history.push(lineValue);
101
+ me.historyPtr = me.history.length;
102
+ }
103
+
104
+ // deactivate the line...
105
+ me.activeLine.value = "";
106
+ me.activeLine.attr({disabled: true});
107
+ me.activeLine.removeClass('active');
108
+
109
+ // and add add a new command line.
110
+ me.addInputLine();
111
+ });
112
+ },
113
+
114
+ insertResponse: function(response) {
115
+ if(response.length < 3) {
116
+ this.activeLine.parent().append("<p class='response'></p>");
117
+ }
118
+ else {
119
+ this.activeLine.parent().append("<p class='response'>" + response + "</p>");
120
+ }
121
+ },
122
+
123
+ clear: function() {
124
+ this.terminal.empty();
125
+ this.addInputLine();
126
+ },
127
+
128
+ focus: function() {
129
+ this.activeLine.focus();
130
+ }
131
+ };
132
+
133
+ var EnterKeyCode = 13;
134
+ var UpArrowKeyCode = 38;
135
+ var DownArrowKeyCode = 40;
@@ -0,0 +1,63 @@
1
+ module Redmon
2
+ module Redis
3
+ extend self
4
+
5
+ UNSUPPORTED = [
6
+ :eval,
7
+ :psubscribe,
8
+ :punsubscribe,
9
+ :subscribe,
10
+ :unsubscribe,
11
+ :unwatch,
12
+ :watch
13
+ ]
14
+
15
+ def redis
16
+ @redis ||= ::Redis.connect(:url => Redmon[:redis_url])
17
+ end
18
+
19
+ def ns
20
+ Redmon[:namespace]
21
+ end
22
+
23
+ def redis_url
24
+ @redis_url ||= Redmon[:redis_url].gsub(/\w*:\w*@/, '')
25
+ end
26
+
27
+ def redis_host
28
+ redis_url.gsub('redis://', '')
29
+ end
30
+
31
+ def config
32
+ redis.config :get, '*' rescue {}
33
+ end
34
+
35
+ def unquoted
36
+ %w{string OK} << '(empty list or set)'
37
+ end
38
+
39
+ def supported?(cmd)
40
+ !UNSUPPORTED.include? cmd
41
+ end
42
+
43
+ def empty_result
44
+ '(empty list or set)'
45
+ end
46
+
47
+ def unknown(cmd)
48
+ "(error) ERR unknown command '#{cmd}'"
49
+ end
50
+
51
+ def wrong_number_of_arguments_for(cmd)
52
+ "(error) ERR wrong number of arguments for '#{cmd}' command"
53
+ end
54
+
55
+ def connection_refused
56
+ "Could not connect to Redis at #{redis_url.gsub(/\w*:\/\//, '')}: Connection refused"
57
+ end
58
+
59
+ def stats_key
60
+ "#{ns}:redis:#{redis_host}:stats"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module Redmon
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,178 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title Redmon
5
+
6
+ %script(type="text/javascript" src="vendor/jquery-1.7.1.min.js")
7
+ %script(type="text/javascript" src="vendor/jquery-effects-core.min.js")
8
+ %script(type="text/javascript" src="vendor/jquery.editinplace.js")
9
+ %script(type="text/javascript" src="vendor/jquery.flot.js")
10
+ %script(type="text/javascript" src="vendor/bootstrap-modal.js")
11
+ %script(type="text/javascript" src="vendor/terminal.js")
12
+ %script(type="text/javascript" src="redmon.js")
13
+
14
+ %link(type="text/css" rel="stylesheet" href="vendor/bootstrap.min.css")
15
+ %link(type="text/css" rel="stylesheet" href="redmon.css")
16
+
17
+ %body
18
+ .topbar
19
+ .fill
20
+ .container
21
+ %a.brand Redmon
22
+ %ul.nav
23
+ %li.active{:id => 'dashboard'}
24
+ %a Dashboard
25
+ %li{:id => 'keys'}
26
+ %a Keys
27
+ %li{:id => 'cli'}
28
+ %a CLI
29
+ %li{:id => 'config'}
30
+ %a Configuration
31
+ .pull-right
32
+ %a.brand #{redis_url}
33
+ .btns
34
+ %button.btn.primary{:id => 'flush-btn'} Flush DB
35
+ %button.btn.primary{:id => 'reset-btn'} Reset Statistics
36
+
37
+ .container.viewport
38
+ .dashboard
39
+ .content
40
+ .page-header
41
+ %h1 Dashboard
42
+ .row
43
+ .span15
44
+ .row
45
+ .span15
46
+ .widget
47
+ .headbar
48
+ %h2 Memory Usage
49
+ #memory-container.chart
50
+ .row
51
+ .span15
52
+ .widget
53
+ .headbar
54
+ %h2 Keyspace
55
+ #keyspace-container.chart
56
+ .row
57
+ .span15
58
+ .widget
59
+ .headbar
60
+ %h2 Slow Log
61
+ %div{:style => "height:225px;overflow-y:scroll;"}
62
+ %table.condensed-table{:id => "slow-tbl"}
63
+ %tbody
64
+
65
+ .span6
66
+ .widget
67
+ .headbar
68
+ %h2 Info
69
+ %table.condensed-table{:id => "info-tbl"}
70
+ %tbody
71
+ %tr
72
+ %td{:style => "width: 65%"} Version
73
+ %td{:id => "redis_version"}
74
+ %tr
75
+ %td Role
76
+ %td{:id => "role"}
77
+ %tr
78
+ %td Uptime Days
79
+ %td{:id => "uptime_in_days"}
80
+ %tr
81
+ %td Memory Used
82
+ %td{:id => "used_memory_human"}
83
+ %tr
84
+ %td Memory Peak
85
+ %td{:id => "used_memory_peak_human"}
86
+ %tr
87
+ %td Memory Fragmentation Ratio
88
+ %td{:id => "mem_fragmentation_ratio"}
89
+ %tr
90
+ %td DB Size (Keys)
91
+ %td{:id => "dbsize", :type => 'number'}
92
+ %tr
93
+ %td Keyspace Hits
94
+ %td{:id => "keyspace_hits", :type => 'number'}
95
+ %tr
96
+ %td Keyspace Misses
97
+ %td{:id => "keyspace_misses", :type => 'number'}
98
+ %tr
99
+ %td Pub/Sub Channels
100
+ %td{:id => "pubsub_channels", :type => 'number'}
101
+ %tr
102
+ %td Pub/Sub Patterns
103
+ %td{:id => "pubsub_patterns", :type => 'number'}
104
+ %tr
105
+ %td Total Connections Received
106
+ %td{:id => "total_connections_received", :type => 'number'}
107
+ %tr
108
+ %td Total Commands Processed
109
+ %td{:id => "total_commands_processed", :type => 'number'}
110
+ %tr
111
+ %td Blocked Clients
112
+ %td{:id => "blocked_clients", :type => 'number'}
113
+ %tr
114
+ %td Connected Slaves
115
+ %td{:id => "connected_slaves", :type => 'number'}
116
+ %tr
117
+ %td Last Save
118
+ %td{:id => "last_save_time", :type => 'date'}
119
+ %tr
120
+ %td Changes Since Last Save
121
+ %td{:id => "changes_since_last_save", :type => 'number'}
122
+ %tr
123
+ %td AOF Enabled
124
+ %td{:id => "aof_enabled"}
125
+ %tr
126
+ %td VM Enabled
127
+ %td{:id => "vm_enabled"}
128
+ .keys.hidden
129
+ .content
130
+ .page-header
131
+ %h1 Keys - Coming soon!
132
+ .row
133
+ .span6
134
+ .input-prepend
135
+ %span.add-on pattern
136
+ %input.span5
137
+ .span15
138
+
139
+ .cli.hidden
140
+ .content
141
+ .page-header
142
+ %h1 Command Line Interface
143
+ .row
144
+ .span21{:style => "height:620px"}
145
+ #terminal
146
+
147
+ .config.hidden
148
+ .content
149
+ .page-header
150
+ %h1 Configuration
151
+ .widget
152
+ .headbar
153
+ %h2 Parameters
154
+ %table.condensed-table{:id => "config-table"}
155
+ %tbody
156
+ - config.each do |k,v|
157
+ %tr
158
+ %td{:style => "width: 20%"}= k
159
+ %td
160
+ .editable{:id => "#{k}"}= v
161
+
162
+ #flush-confirm.modal.hide
163
+ .modal-header
164
+ %a.close x
165
+ %h3 Flush DB
166
+ .modal-body
167
+ %p This action will, without a doubt, delete all the keys of the redis server.
168
+ .modal-footer
169
+ %button.btn.secondary{:id => 'flush-cancel-btn'} Well On Second Thought
170
+ %button.btn.danger{:id => 'flush-confirm-btn'} Just Do It Already!
171
+
172
+ :javascript
173
+ $(document).ready(function() {
174
+ Redmon.init({
175
+ pollInterval : #{poll_interval},
176
+ cliPrompt : "#{prompt}"
177
+ });
178
+ });
@@ -0,0 +1,15 @@
1
+ - if @cmd == :info
2
+ - @result.each do |item|
3
+ %span= item.join ':'
4
+ %br
5
+ - elsif @result.respond_to? :each_with_index
6
+ - @result.each_with_index do |item, index|
7
+ %span= "#{index+1}) \"#{item}\""
8
+ %br
9
+ - elsif @result.is_a? Fixnum
10
+ %span= "(integer) #{@result}"
11
+ %br
12
+ - else
13
+ - @result = "\"#{@result}\"" unless unquoted.include? @result
14
+ %span= @result
15
+ %br
@@ -0,0 +1,41 @@
1
+ module Redmon
2
+ class Worker
3
+ include Redmon::Redis
4
+
5
+ def run!
6
+ EM::PeriodicTimer.new(interval) {record_stats}
7
+ end
8
+
9
+ def record_stats
10
+ redis.zadd stats_key, *stats
11
+ end
12
+
13
+ def stats
14
+ stats = redis.info.merge! \
15
+ :dbsize => redis.dbsize,
16
+ :time => ts = Time.now.to_i * 1000,
17
+ :slowlog => entries(redis.slowlog :get)
18
+ [ts, stats.to_json]
19
+ end
20
+
21
+ def entries(slowlog)
22
+ sort(slowlog).map do |entry|
23
+ {
24
+ :id => entry.shift,
25
+ :timestamp => entry.shift * 1000,
26
+ :process_time => entry.shift,
27
+ :command => entry.shift.join(' ')
28
+ }
29
+ end
30
+ end
31
+
32
+ def sort(slowlog)
33
+ slowlog.sort_by{|a| a[2]}.reverse!
34
+ end
35
+
36
+ def interval
37
+ Redmon[:poll_interval]
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,20 @@
1
+ $: << ::File.expand_path("../lib/", __FILE__)
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+ require 'redmon'
5
+ require 'redis'
6
+
7
+ redis = Redis.connect
8
+
9
+ loop do
10
+ start = rand(100000)
11
+ multi = rand(5)
12
+ start.upto(multi * start) do |i|
13
+ redis.set("key-#{i}", "abcdedghijklmnopqrstuvwxyz")
14
+ end
15
+
16
+ start.upto(multi * start) do |i|
17
+ redis.get("key-#{i}")
18
+ redis.del("key-#{i}")
19
+ end
20
+ end