daikon 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/daikon.gemspec +2 -4
- data/lib/daikon.rb +3 -2
- data/lib/daikon/client.rb +12 -8
- data/lib/daikon/daemon.rb +35 -12
- data/lib/daikon/monitor.rb +76 -8
- data/spec/client_spec.rb +59 -51
- data/spec/daemon_spec.rb +23 -0
- data/spec/monitor_spec.rb +112 -56
- metadata +4 -6
- data/spec/daikon_spec.rb +0 -4
data/daikon.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{daikon}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nick Quaranto"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-29}
|
13
13
|
s.default_executable = %q{daikon}
|
14
14
|
s.description = %q{daikon, a radishapp.com client}
|
15
15
|
s.email = %q{nick@quaran.to}
|
@@ -40,7 +40,6 @@ Gem::Specification.new do |s|
|
|
40
40
|
"spec/client_spec.rb",
|
41
41
|
"spec/configuration_spec.rb",
|
42
42
|
"spec/daemon_spec.rb",
|
43
|
-
"spec/daikon_spec.rb",
|
44
43
|
"spec/monitor_spec.rb",
|
45
44
|
"spec/spec_helper.rb"
|
46
45
|
]
|
@@ -54,7 +53,6 @@ Gem::Specification.new do |s|
|
|
54
53
|
"spec/client_spec.rb",
|
55
54
|
"spec/configuration_spec.rb",
|
56
55
|
"spec/daemon_spec.rb",
|
57
|
-
"spec/daikon_spec.rb",
|
58
56
|
"spec/monitor_spec.rb",
|
59
57
|
"spec/spec_helper.rb"
|
60
58
|
]
|
data/lib/daikon.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
3
|
require 'logger'
|
4
|
-
require 'pp'
|
5
4
|
require 'shellwords'
|
5
|
+
require 'set'
|
6
6
|
require 'socket'
|
7
7
|
require 'stringio'
|
8
|
+
require 'thread'
|
8
9
|
|
9
10
|
require 'excon'
|
10
11
|
require 'daemons'
|
@@ -25,5 +26,5 @@ require 'daikon/monitor'
|
|
25
26
|
require 'daikon/redis_hacks'
|
26
27
|
|
27
28
|
module Daikon
|
28
|
-
VERSION = "0.
|
29
|
+
VERSION = "0.7.0"
|
29
30
|
end
|
data/lib/daikon/client.rb
CHANGED
@@ -6,7 +6,8 @@ module Daikon
|
|
6
6
|
Errno::EINVAL,
|
7
7
|
Errno::ECONNRESET,
|
8
8
|
EOFError,
|
9
|
-
JSON::ParserError
|
9
|
+
JSON::ParserError,
|
10
|
+
Excon::Errors::SocketError]
|
10
11
|
|
11
12
|
attr_accessor :redis, :logger, :config, :http, :monitor
|
12
13
|
|
@@ -38,7 +39,7 @@ module Daikon
|
|
38
39
|
options[:headers] ||= {}
|
39
40
|
options[:headers]['Authorization'] = config.api_key
|
40
41
|
|
41
|
-
log "#{options[:method]} #{config.server_prefix}
|
42
|
+
log "#{options[:method]} #{config.server_prefix}#{options[:path]}"
|
42
43
|
http.request(options)
|
43
44
|
end
|
44
45
|
|
@@ -65,16 +66,19 @@ module Daikon
|
|
65
66
|
log ex.to_s
|
66
67
|
end
|
67
68
|
|
68
|
-
def
|
69
|
-
|
69
|
+
def rotate_monitor(start, stop)
|
70
|
+
payload = monitor.rotate.merge({
|
71
|
+
"start" => start,
|
72
|
+
"stop" => stop
|
73
|
+
})
|
74
|
+
|
75
|
+
push :post, "/api/v1/summaries.json", payload
|
70
76
|
rescue *EXCEPTIONS => ex
|
71
77
|
log ex.to_s
|
72
78
|
end
|
73
79
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
push :post, "/api/v1/monitor.json", {"lines" => lines}
|
80
|
+
def report_info
|
81
|
+
push :post, "/api/v1/infos.json", redis.info
|
78
82
|
rescue *EXCEPTIONS => ex
|
79
83
|
log ex.to_s
|
80
84
|
end
|
data/lib/daikon/daemon.rb
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
module Daikon
|
2
2
|
class Daemon
|
3
|
-
|
3
|
+
INFO_INTERVAL = ENV["INFO_INTERVAL"] || 5
|
4
|
+
SUMMARY_INTERVAL = ENV["SUMMARY_INTERVAL"] || 60
|
5
|
+
|
6
|
+
def self.sleep_time=(sleep_time)
|
7
|
+
@@sleep_time = sleep_time
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.sleep_time
|
11
|
+
@@sleep_time ||= 1
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.run=(run)
|
15
|
+
@@run = run
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.run
|
19
|
+
@@run
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.start(argv, ontop = false)
|
23
|
+
self.run = true
|
4
24
|
config = Daikon::Configuration.new(argv)
|
5
25
|
|
6
26
|
if argv.include?("-v") || argv.include?("--version")
|
@@ -8,31 +28,34 @@ module Daikon
|
|
8
28
|
return
|
9
29
|
end
|
10
30
|
|
11
|
-
Daemons.run_proc("daikon", :log_output => true, :backtrace => true) do
|
31
|
+
Daemons.run_proc("daikon", :ARGV => argv, :log_output => true, :backtrace => true, :ontop => ontop) do
|
12
32
|
if argv.include?("run")
|
13
33
|
logger = Logger.new(STDOUT)
|
14
34
|
else
|
15
35
|
logger = Logger.new("/tmp/radish.log")
|
16
36
|
end
|
17
37
|
|
18
|
-
|
19
|
-
|
38
|
+
rotated_at = Time.now
|
39
|
+
reported_at = Time.now
|
40
|
+
client = Daikon::Client.new
|
41
|
+
|
20
42
|
client.setup(config, logger)
|
21
43
|
client.start_monitor
|
22
44
|
|
23
|
-
|
24
|
-
|
45
|
+
while self.run do
|
46
|
+
now = Time.now
|
47
|
+
|
48
|
+
if now - reported_at >= sleep_time * INFO_INTERVAL.to_i
|
25
49
|
client.report_info
|
50
|
+
reported_at = now
|
26
51
|
end
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
client.rotate_monitor
|
53
|
+
if now - rotated_at >= sleep_time * SUMMARY_INTERVAL.to_i
|
54
|
+
client.rotate_monitor(rotated_at, now)
|
55
|
+
rotated_at = now
|
32
56
|
end
|
33
57
|
|
34
|
-
|
35
|
-
sleep 1
|
58
|
+
sleep sleep_time
|
36
59
|
end
|
37
60
|
end
|
38
61
|
end
|
data/lib/daikon/monitor.rb
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
module Daikon
|
2
2
|
class Monitor
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
NO_ARG_COMMANDS = ["BGREWRITEAOF", "BGSAVE", "CONFIG RESETSTAT", "DBSIZE", "DEBUG SEGFAULT", "DISCARD", "EXEC", "FLUSHALL", "FLUSHDB", "INFO", "LASTSAVE", "MONITOR", "MULTI", "PING", "QUIT", "RANDOMKEY", "SAVE", "SHUTDOWN", "SYNC", "UNWATCH"]
|
6
|
+
READ_COMMANDS = ["EXISTS", "GET", "GETBIT", "GETRANGE", "HEXISTS", "HGET", "HGETALL", "HKEYS", "HLEN", "HMGET", "HVALS", "KEYS", "LINDEX", "LLEN", "LRANGE", "MGET", "SCARD", "SDIFF", "SINTER", "SISMEMBER", "SMEMBERS", "SORT", "SRANDMEMBER", "STRLEN", "SUNION", "TTL", "TYPE", "ZCARD", "ZCOUNT", "ZRANGE", "ZRANGEBYSCORE", "ZRANK", "ZREVRANGE", "ZREVRANGEBYSCORE", "ZREVRANK", "ZSCORE"].to_set
|
7
|
+
WRITE_COMMANDS = ["APPEND", "BLPOP", "BRPOP", "BRPOPLPUSH", "DECR", "DECRBY", "DEL", "GETSET", "EXPIRE", "EXPIREAT", "HDEL", "HINCRBY", "HMSET", "HSET", "HSETNX", "INCR", "INCRBY", "LINSERT", "LPOP", "LPUSH", "LPUSHX", "LREM", "LSET", "LTRIM", "MOVE", "MSET", "MSETNX", "PERSIST", "RENAME", "RENAMENX", "RPOP", "RPOPLPUSH", "RPUSH", "RPUSHX", "SADD", "SDIFFSTORE", "SET", "SETBIT", "SETEX", "SETNX", "SETRANGE", "SINTERSTORE", "SMOVE", "SPOP", "SREM", "SUNIONSTORE", "ZADD", "ZINCRBY", "ZINTERSTORE", "ZREM", "ZREMRANGEBYRANK", "ZREMRANGEBYSCORE", "ZUNIONSTORE"].to_set
|
8
|
+
OTHER_COMMANDS = ["AUTH", "BGREWRITEAOF", "BGSAVE", "CONFIG GET", "CONFIG SET", "CONFIG RESETSTAT", "DBSIZE", "DEBUG OBJECT", "DEBUG SEGFAULT", "DISCARD", "ECHO", "EXEC", "FLUSHALL", "FLUSHDB", "INFO", "LASTSAVE", "MONITOR", "MULTI", "PING", "PSUBSCRIBE", "PUBLISH", "PUNSUBSCRIBE", "QUIT", "RANDOMKEY", "SAVE", "SELECT", "SHUTDOWN", "SUBSCRIBE", "SYNC", "UNSUBSCRIBE", "UNWATCH", "WATCH"].to_set
|
9
|
+
ALL_COMMANDS = READ_COMMANDS + WRITE_COMMANDS + OTHER_COMMANDS
|
4
10
|
|
5
11
|
NEW_FORMAT = /^\+?(\d+\.\d+)( "[A-Z]+".*)/i
|
6
|
-
OLD_SINGLE_FORMAT = /^(
|
12
|
+
OLD_SINGLE_FORMAT = /^(#{NO_ARG_COMMANDS.join('|')})$/i
|
7
13
|
OLD_MORE_FORMAT = /^[A-Z]+ .*$/i
|
8
14
|
|
9
15
|
def initialize(redis = nil, logger = nil)
|
10
|
-
@
|
16
|
+
@data = data_hash
|
11
17
|
@redis = redis
|
12
18
|
@logger = logger
|
19
|
+
@mutex = Mutex.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def data_hash
|
23
|
+
{"commands" => Hash.new(0),
|
24
|
+
"keys" => Hash.new(0),
|
25
|
+
"namespaces" => Hash.new(0),
|
26
|
+
"totals" => Hash.new(0)}
|
13
27
|
end
|
14
28
|
|
15
29
|
def start
|
@@ -20,20 +34,74 @@ module Daikon
|
|
20
34
|
end
|
21
35
|
end
|
22
36
|
|
37
|
+
def lock(&block)
|
38
|
+
@mutex.synchronize(&block)
|
39
|
+
end
|
40
|
+
|
23
41
|
def rotate
|
24
|
-
|
42
|
+
this_data = nil
|
43
|
+
lock do
|
44
|
+
this_data = self.data.dup
|
45
|
+
self.data.replace(data_hash)
|
46
|
+
end
|
47
|
+
this_data["keys"] = Hash[*this_data["keys"].sort_by(&:last).reverse[0..99].flatten]
|
48
|
+
this_data
|
25
49
|
end
|
26
50
|
|
27
51
|
def parse(line)
|
28
52
|
if line =~ NEW_FORMAT
|
29
|
-
push(
|
53
|
+
push($2)
|
30
54
|
elsif line =~ OLD_SINGLE_FORMAT || line =~ OLD_MORE_FORMAT
|
31
|
-
push(
|
55
|
+
push(line)
|
32
56
|
end
|
33
57
|
end
|
34
58
|
|
35
|
-
def push(
|
36
|
-
|
59
|
+
def push(raw_command)
|
60
|
+
command, key, *rest = raw_command.strip.gsub('"', '').split
|
61
|
+
command.upcase!
|
62
|
+
lock do
|
63
|
+
incr_command(command)
|
64
|
+
incr_total(command)
|
65
|
+
if key
|
66
|
+
incr_key(key)
|
67
|
+
incr_namespace(key)
|
68
|
+
else
|
69
|
+
incr_global_namespace
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def incr_namespace(key)
|
75
|
+
namespace, has_namespace = key.split(/(?::|-)/, 2)
|
76
|
+
if has_namespace
|
77
|
+
data["namespaces"][namespace] += 1
|
78
|
+
else
|
79
|
+
incr_global_namespace
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def incr_global_namespace
|
84
|
+
data["namespaces"]["global"] += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
def incr_command(command)
|
88
|
+
data["commands"][command] += 1 if ALL_COMMANDS.member?(command)
|
89
|
+
end
|
90
|
+
|
91
|
+
def incr_key(key)
|
92
|
+
data["keys"][key.gsub(".", "{PERIOD}").gsub("$", "{DOLLAR}")] += 1
|
93
|
+
end
|
94
|
+
|
95
|
+
def incr_total(command)
|
96
|
+
data["totals"]["all"] += 1
|
97
|
+
|
98
|
+
if READ_COMMANDS.member?(command)
|
99
|
+
data["totals"]["read"] += 1
|
100
|
+
elsif WRITE_COMMANDS.member?(command)
|
101
|
+
data["totals"]["write"] += 1
|
102
|
+
elsif OTHER_COMMANDS.member?(command)
|
103
|
+
data["totals"]["other"] += 1
|
104
|
+
end
|
37
105
|
end
|
38
106
|
end
|
39
107
|
end
|
data/spec/client_spec.rb
CHANGED
@@ -134,32 +134,47 @@ describe Daikon::Client, "when it returns bad json" do
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
-
shared_examples_for "a
|
137
|
+
shared_examples_for "a summary api consumer" do
|
138
138
|
it "shoots the results back to radish" do
|
139
139
|
headers = {
|
140
140
|
"Authorization" => api_key,
|
141
|
-
"Content-Length" =>
|
141
|
+
"Content-Length" => payload.to_json.size.to_s,
|
142
142
|
"Content-Type" => "application/json"
|
143
143
|
}
|
144
144
|
|
145
145
|
http.should have_received(:request).with(
|
146
146
|
:method => "POST",
|
147
|
-
:path => "/api/v1/
|
148
|
-
:body =>
|
147
|
+
:path => "/api/v1/summaries.json",
|
148
|
+
:body => payload.to_json,
|
149
149
|
:headers => headers)
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
describe Daikon::Client, "
|
154
|
-
subject
|
155
|
-
let(:
|
156
|
-
let(:
|
157
|
-
let(:
|
153
|
+
describe Daikon::Client, "rotate monitor" do
|
154
|
+
subject { Daikon::Client.new }
|
155
|
+
let(:http) { stub("http", :request => Excon::Response.new) }
|
156
|
+
let(:now) { "2011-01-19T18:23:55-05:00" }
|
157
|
+
let(:past) { "2011-01-19T18:23:54-05:00" }
|
158
|
+
let(:payload) do
|
159
|
+
data.merge("start" => past, "stop" => now)
|
160
|
+
end
|
161
|
+
let(:data) do
|
162
|
+
{"commands" => {"GET" => 42},
|
163
|
+
"keys" => {"foo" => 42},
|
164
|
+
"namespaces" => {"a" => 42, "global" => 42},
|
165
|
+
"totals" => {"all" => 42, "read" => 42}}
|
166
|
+
end
|
158
167
|
|
159
168
|
before do
|
160
|
-
|
169
|
+
Timecop.freeze DateTime.parse(now)
|
170
|
+
subject.stubs(:http => http)
|
161
171
|
subject.setup(config)
|
162
|
-
subject.
|
172
|
+
subject.monitor = stub("monitor", :rotate => data)
|
173
|
+
subject.rotate_monitor(DateTime.parse(past), DateTime.parse(now))
|
174
|
+
end
|
175
|
+
|
176
|
+
after do
|
177
|
+
Timecop.return
|
163
178
|
end
|
164
179
|
|
165
180
|
context "with default configuration" do
|
@@ -167,7 +182,7 @@ describe Daikon::Client, "report info" do
|
|
167
182
|
let(:server) { "https://radish.heroku.com" }
|
168
183
|
let(:config) { Daikon::Configuration.new }
|
169
184
|
|
170
|
-
it_should_behave_like "a
|
185
|
+
it_should_behave_like "a summary api consumer"
|
171
186
|
end
|
172
187
|
|
173
188
|
context "with custom settings" do
|
@@ -175,43 +190,57 @@ describe Daikon::Client, "report info" do
|
|
175
190
|
let(:server) { "http://localhost:9999" }
|
176
191
|
let(:config) { Daikon::Configuration.new(["-k", api_key, "-s", "http://localhost:9999"]) }
|
177
192
|
|
178
|
-
it_should_behave_like "a
|
193
|
+
it_should_behave_like "a summary api consumer"
|
179
194
|
end
|
180
195
|
end
|
181
196
|
|
182
|
-
|
183
|
-
|
184
|
-
|
197
|
+
describe Daikon::Client, "pretty printing results" do
|
198
|
+
subject { Daikon::Client.new }
|
199
|
+
let(:body) { {"13" => "LRANGE foo 0 -1"}.to_json }
|
200
|
+
let(:list) { %w[apples bananas carrots] }
|
201
|
+
let(:server) { "https://radish.heroku.com" }
|
202
|
+
let(:config) { Daikon::Configuration.new }
|
203
|
+
let(:http) { stub("http", :request => Excon::Response.new(:body => body)) }
|
185
204
|
|
205
|
+
before do
|
206
|
+
subject.stubs(:evaluate_redis => list, :http => http)
|
207
|
+
subject.setup(config)
|
208
|
+
subject.fetch_commands
|
209
|
+
end
|
210
|
+
|
211
|
+
it "returns pretty printed results" do
|
212
|
+
http.should have_received(:request).with(has_entry(
|
213
|
+
:body => {"response" => "[\"apples\", \"bananas\", \"carrots\"]"}.to_json
|
214
|
+
))
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
shared_examples_for "a info api consumer" do
|
219
|
+
it "shoots the results back to radish" do
|
186
220
|
headers = {
|
187
221
|
"Authorization" => api_key,
|
188
|
-
"Content-Length" =>
|
222
|
+
"Content-Length" => results.to_json.size.to_s,
|
189
223
|
"Content-Type" => "application/json"
|
190
224
|
}
|
191
225
|
|
192
226
|
http.should have_received(:request).with(
|
193
227
|
:method => "POST",
|
194
|
-
:path => "/api/v1/
|
195
|
-
:body =>
|
228
|
+
:path => "/api/v1/infos.json",
|
229
|
+
:body => results.to_json,
|
196
230
|
:headers => headers)
|
197
231
|
end
|
198
232
|
end
|
199
233
|
|
200
|
-
describe Daikon::Client, "
|
234
|
+
describe Daikon::Client, "report info" do
|
201
235
|
subject { Daikon::Client.new }
|
202
|
-
let(:results) {
|
236
|
+
let(:results) { {"connected_clients"=>"1", "used_cpu_sys_childrens"=>"0.00"} }
|
203
237
|
let(:redis) { stub("redis instance", :info => results) }
|
204
238
|
let(:http) { stub("http", :request => Excon::Response.new) }
|
205
|
-
let(:lines) do
|
206
|
-
[{"at" => 1290289048.96581, "command" => "info"},
|
207
|
-
{"at" => 1290289053.568815, "command" => "info"}]
|
208
|
-
end
|
209
239
|
|
210
240
|
before do
|
211
|
-
subject.stubs(:http => http)
|
241
|
+
subject.stubs(:redis => redis, :http => http)
|
212
242
|
subject.setup(config)
|
213
|
-
subject.
|
214
|
-
subject.rotate_monitor
|
243
|
+
subject.report_info
|
215
244
|
end
|
216
245
|
|
217
246
|
context "with default configuration" do
|
@@ -219,7 +248,7 @@ describe Daikon::Client, "rotate monitor" do
|
|
219
248
|
let(:server) { "https://radish.heroku.com" }
|
220
249
|
let(:config) { Daikon::Configuration.new }
|
221
250
|
|
222
|
-
it_should_behave_like "a
|
251
|
+
it_should_behave_like "a info api consumer"
|
223
252
|
end
|
224
253
|
|
225
254
|
context "with custom settings" do
|
@@ -227,27 +256,6 @@ describe Daikon::Client, "rotate monitor" do
|
|
227
256
|
let(:server) { "http://localhost:9999" }
|
228
257
|
let(:config) { Daikon::Configuration.new(["-k", api_key, "-s", "http://localhost:9999"]) }
|
229
258
|
|
230
|
-
it_should_behave_like "a
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
describe Daikon::Client, "pretty printing results" do
|
235
|
-
subject { Daikon::Client.new }
|
236
|
-
let(:body) { {"13" => "LRANGE foo 0 -1"}.to_json }
|
237
|
-
let(:list) { %w[apples bananas carrots] }
|
238
|
-
let(:server) { "https://radish.heroku.com" }
|
239
|
-
let(:config) { Daikon::Configuration.new }
|
240
|
-
let(:http) { stub("http", :request => Excon::Response.new(:body => body)) }
|
241
|
-
|
242
|
-
before do
|
243
|
-
subject.stubs(:evaluate_redis => list, :http => http)
|
244
|
-
subject.setup(config)
|
245
|
-
subject.fetch_commands
|
246
|
-
end
|
247
|
-
|
248
|
-
it "returns pretty printed results" do
|
249
|
-
http.should have_received(:request).with(has_entry(
|
250
|
-
:body => {"response" => "[\"apples\", \"bananas\", \"carrots\"]"}.to_json
|
251
|
-
))
|
259
|
+
it_should_behave_like "a info api consumer"
|
252
260
|
end
|
253
261
|
end
|
data/spec/daemon_spec.rb
CHANGED
@@ -1,6 +1,29 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Daikon::Daemon do
|
4
|
+
let(:client) { stub("client") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
Daikon::Client.stubs(:new => client)
|
8
|
+
Daikon::Daemon.sleep_time = 0.05
|
9
|
+
client.stubs(:setup => true,
|
10
|
+
:start_monitor => true,
|
11
|
+
:rotate_monitor => true,
|
12
|
+
:report_info => true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "submits the last minute of data" do
|
16
|
+
thread = Thread.new do
|
17
|
+
Daikon::Daemon.start(["run", "--", "-k", "1234"], true)
|
18
|
+
end
|
19
|
+
sleep 3.1
|
20
|
+
Daikon::Daemon.run = false
|
21
|
+
client.should have_received(:rotate_monitor)
|
22
|
+
client.should have_received(:report_info).times(12)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Daikon::Daemon, "flags" do
|
4
27
|
%w[-v --version].each do |flag|
|
5
28
|
it "shows the daikon version with #{flag}" do
|
6
29
|
old_stdout = $stdout
|
data/spec/monitor_spec.rb
CHANGED
@@ -1,15 +1,52 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Daikon::Monitor, "#rotate" do
|
4
|
-
|
4
|
+
before do
|
5
5
|
subject.parse("INCR foo")
|
6
6
|
subject.parse("DECR foo")
|
7
|
+
subject.parse("DECR baz")
|
8
|
+
subject.parse("HGETALL faz")
|
9
|
+
subject.parse("PING")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "clears out current data" do
|
13
|
+
subject.rotate
|
14
|
+
subject.data["commands"].size.should be_zero
|
15
|
+
subject.data["totals"].size.should be_zero
|
16
|
+
subject.data["keys"].size.should be_zero
|
17
|
+
end
|
18
|
+
|
19
|
+
it "only saves the top 100 key listings" do
|
20
|
+
150.times { |n| subject.parse("INCR foo#{n}") }
|
21
|
+
150.times { |n| subject.parse("DECR foo#{n}") }
|
22
|
+
100.times { |n| subject.parse("DEL foo#{n}") }
|
23
|
+
data = subject.rotate
|
24
|
+
data["keys"].size.should == 100
|
25
|
+
data["keys"].values.all? { |n| n == 3 }.should be_true
|
26
|
+
end
|
7
27
|
|
28
|
+
it "santizes key names" do
|
29
|
+
subject.parse("INCR $foo.zomg")
|
8
30
|
data = subject.rotate
|
9
|
-
data
|
10
|
-
data
|
11
|
-
|
12
|
-
|
31
|
+
data["keys"]["$foo.zomg"].should be_nil
|
32
|
+
data["keys"]["{DOLLAR}foo{PERIOD}zomg"].should == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
it "increments each command type" do
|
36
|
+
subject.data["commands"]["INCR"].should == 1
|
37
|
+
subject.data["commands"]["DECR"].should == 2
|
38
|
+
end
|
39
|
+
|
40
|
+
it "keeps track of key accesses" do
|
41
|
+
subject.data["keys"]["foo"].should == 2
|
42
|
+
subject.data["keys"]["baz"].should == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
it "tallies up totals of commands" do
|
46
|
+
subject.data["totals"]["all"].should == 5
|
47
|
+
subject.data["totals"]["read"].should == 1
|
48
|
+
subject.data["totals"]["write"].should == 3
|
49
|
+
subject.data["totals"]["other"].should == 1
|
13
50
|
end
|
14
51
|
end
|
15
52
|
|
@@ -19,7 +56,10 @@ describe Daikon::Monitor, "#parse with new format" do
|
|
19
56
|
|
20
57
|
it "parses the log into json" do
|
21
58
|
subject.parse(line)
|
22
|
-
subject.
|
59
|
+
subject.data["commands"]["DECRBY"].should == 1
|
60
|
+
subject.data["keys"]["fooz"].should == 1
|
61
|
+
subject.data["totals"]["all"].should == 1
|
62
|
+
subject.data["totals"]["write"].should == 1
|
23
63
|
end
|
24
64
|
end
|
25
65
|
|
@@ -29,78 +69,94 @@ describe Daikon::Monitor, "#parse with new format that has reply byte" do
|
|
29
69
|
|
30
70
|
it "parses the log into json" do
|
31
71
|
subject.parse(line)
|
32
|
-
subject.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
describe Daikon::Monitor, "#parse with multiple inputs" do
|
37
|
-
subject { Daikon::Monitor.new }
|
38
|
-
before { Timecop.freeze }
|
39
|
-
after { Timecop.return }
|
40
|
-
|
41
|
-
it "queues up multiple lines" do
|
42
|
-
subject.parse("+OK")
|
43
|
-
subject.parse("INCR foo")
|
44
|
-
subject.parse("INCR fooz")
|
45
|
-
subject.parse("info")
|
46
|
-
|
47
|
-
subject.queue.size.should == 3
|
48
|
-
subject.queue.should include({:at => Time.now.to_f, :command => 'INCR foo'})
|
49
|
-
subject.queue.should include({:at => Time.now.to_f, :command => 'INCR fooz'})
|
50
|
-
subject.queue.should include({:at => Time.now.to_f, :command => 'info'})
|
72
|
+
subject.data["commands"]["DECRBY"].should == 1
|
73
|
+
subject.data["keys"]["fooz"].should == 1
|
74
|
+
subject.data["totals"]["all"].should == 1
|
75
|
+
subject.data["totals"]["write"].should == 1
|
51
76
|
end
|
52
77
|
end
|
53
78
|
|
54
79
|
describe Daikon::Monitor, "#parse with old multi line input" do
|
55
80
|
subject { Daikon::Monitor.new }
|
56
|
-
before { Timecop.freeze }
|
57
|
-
after { Timecop.return }
|
58
81
|
|
59
|
-
it "parses
|
82
|
+
it "parses logs" do
|
60
83
|
subject.parse("incr foo")
|
61
84
|
subject.parse("sismember project-13897-global-error-classes 17")
|
62
85
|
subject.parse("incrApiParameterError")
|
63
86
|
subject.parse("decr foo")
|
64
87
|
|
65
|
-
subject.
|
66
|
-
subject.
|
67
|
-
subject.
|
68
|
-
subject.
|
88
|
+
subject.data["commands"]["DECR"].should == 1
|
89
|
+
subject.data["commands"]["INCR"].should == 1
|
90
|
+
subject.data["commands"]["SISMEMBER"].should == 1
|
91
|
+
subject.data["keys"]["foo"].should == 2
|
92
|
+
subject.data["keys"]["project-13897-global-error-classes"].should == 1
|
93
|
+
subject.data["totals"]["all"].should == 3
|
94
|
+
subject.data["totals"]["write"].should == 2
|
95
|
+
subject.data["totals"]["read"].should == 1
|
69
96
|
end
|
70
97
|
end
|
71
98
|
|
72
|
-
describe Daikon::Monitor, "#parse with
|
99
|
+
describe Daikon::Monitor, "#parse with old input" do
|
73
100
|
subject { Daikon::Monitor.new }
|
74
|
-
before { Timecop.freeze }
|
75
|
-
after { Timecop.return }
|
76
101
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
102
|
+
shared_examples_for "a valid parser" do
|
103
|
+
it "parses the given commands properly" do
|
104
|
+
subject.data["commands"]["DECR"].should == 1
|
105
|
+
subject.data["commands"]["INCR"].should == 1
|
106
|
+
subject.data["commands"]["SET"].should == 1
|
107
|
+
subject.data["keys"]["foo"].should == 2
|
108
|
+
subject.data["keys"]["g:2470920:mrn"].should == 1
|
109
|
+
subject.data["totals"]["all"].should == 3
|
110
|
+
subject.data["totals"]["write"].should == 3
|
111
|
+
end
|
112
|
+
end
|
82
113
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
114
|
+
context "with a bulk input that is a number" do
|
115
|
+
before do
|
116
|
+
subject.parse("incr foo")
|
117
|
+
subject.parse("set g:2470920:mrn 9")
|
118
|
+
subject.parse("554079885")
|
119
|
+
subject.parse("decr foo")
|
120
|
+
end
|
121
|
+
it_should_behave_like "a valid parser"
|
122
|
+
end
|
123
|
+
|
124
|
+
context "with a bulk input that is a number" do
|
125
|
+
before do
|
126
|
+
subject.parse("incr foo")
|
127
|
+
subject.parse("set g:2470920:mrn 9")
|
128
|
+
subject.parse("46fdcf77c1bb2108e6191602c2f5f9ae")
|
129
|
+
subject.parse("decr foo")
|
130
|
+
end
|
131
|
+
it_should_behave_like "a valid parser"
|
87
132
|
end
|
88
133
|
end
|
89
134
|
|
90
|
-
describe Daikon::Monitor, "#
|
91
|
-
|
92
|
-
|
93
|
-
|
135
|
+
describe Daikon::Monitor, "#rotate with a bad command name" do
|
136
|
+
before do
|
137
|
+
subject.parse("gmail foo")
|
138
|
+
end
|
94
139
|
|
95
|
-
it "
|
96
|
-
subject.
|
140
|
+
it "clears out current data" do
|
141
|
+
data = subject.rotate
|
142
|
+
data["commands"].size.should be_zero
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe Daikon::Monitor, "#rotate that collects namespaces" do
|
147
|
+
before do
|
97
148
|
subject.parse("set g:2470920:mrn 9")
|
98
|
-
subject.parse("
|
99
|
-
subject.parse("
|
149
|
+
subject.parse("get g:2470914:mrn")
|
150
|
+
subject.parse("incr s3-queue-key")
|
151
|
+
subject.parse("info")
|
152
|
+
subject.parse("decr somehorriblynamespacedkey")
|
153
|
+
subject.parse("flushdb")
|
154
|
+
end
|
100
155
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
156
|
+
it "keeps track of namespace accesses" do
|
157
|
+
data = subject.rotate
|
158
|
+
data["namespaces"]["g"].should == 2
|
159
|
+
data["namespaces"]["global"].should == 3
|
160
|
+
data["namespaces"]["s3"].should == 1
|
105
161
|
end
|
106
162
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: daikon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 7
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.7.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nick Quaranto
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-29 00:00:00 -05:00
|
19
19
|
default_executable: daikon
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -183,7 +183,6 @@ files:
|
|
183
183
|
- spec/client_spec.rb
|
184
184
|
- spec/configuration_spec.rb
|
185
185
|
- spec/daemon_spec.rb
|
186
|
-
- spec/daikon_spec.rb
|
187
186
|
- spec/monitor_spec.rb
|
188
187
|
- spec/spec_helper.rb
|
189
188
|
has_rdoc: true
|
@@ -226,6 +225,5 @@ test_files:
|
|
226
225
|
- spec/client_spec.rb
|
227
226
|
- spec/configuration_spec.rb
|
228
227
|
- spec/daemon_spec.rb
|
229
|
-
- spec/daikon_spec.rb
|
230
228
|
- spec/monitor_spec.rb
|
231
229
|
- spec/spec_helper.rb
|
data/spec/daikon_spec.rb
DELETED