daikon 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -7,9 +7,10 @@ gem "redis", "~> 2.1.1"
7
7
  gem "SystemTimer", "~> 1.2.1"
8
8
 
9
9
  group :development do
10
- gem "rspec"
10
+ gem "bourne"
11
11
  gem "cucumber"
12
12
  gem "jeweler"
13
- gem "bourne"
13
+ gem "rspec"
14
+ gem "timecop"
14
15
  gem "webmock"
15
16
  end
data/Gemfile.lock CHANGED
@@ -39,6 +39,7 @@ GEM
39
39
  diff-lcs (~> 1.1.2)
40
40
  rspec-mocks (2.1.0)
41
41
  term-ansicolor (1.0.5)
42
+ timecop (0.3.5)
42
43
  webmock (1.6.1)
43
44
  addressable (>= 2.2.2)
44
45
  crack (>= 0.1.7)
@@ -56,4 +57,5 @@ DEPENDENCIES
56
57
  net-http-persistent (~> 1.4.1)
57
58
  redis (~> 2.1.1)
58
59
  rspec
60
+ timecop
59
61
  webmock
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.4.0"
8
+ s.version = "0.5.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{2010-12-06}
12
+ s.date = %q{2010-12-12}
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}
@@ -25,7 +25,6 @@ Gem::Specification.new do |s|
25
25
  "MIT-LICENSE",
26
26
  "README.rdoc",
27
27
  "Rakefile",
28
- "VERSION",
29
28
  "bin/daikon",
30
29
  "daikon.gemspec",
31
30
  "features/daikon.feature",
@@ -35,12 +34,14 @@ Gem::Specification.new do |s|
35
34
  "lib/daikon/client.rb",
36
35
  "lib/daikon/configuration.rb",
37
36
  "lib/daikon/daemon.rb",
37
+ "lib/daikon/monitor.rb",
38
38
  "lib/daikon/namespace_tools.rb",
39
39
  "lib/daikon/redis_hacks.rb",
40
40
  "spec/client_spec.rb",
41
41
  "spec/configuration_spec.rb",
42
42
  "spec/daemon_spec.rb",
43
43
  "spec/daikon_spec.rb",
44
+ "spec/monitor_spec.rb",
44
45
  "spec/spec_helper.rb"
45
46
  ]
46
47
  s.homepage = %q{http://github.com/qrush/daikon}
@@ -53,6 +54,7 @@ Gem::Specification.new do |s|
53
54
  "spec/configuration_spec.rb",
54
55
  "spec/daemon_spec.rb",
55
56
  "spec/daikon_spec.rb",
57
+ "spec/monitor_spec.rb",
56
58
  "spec/spec_helper.rb"
57
59
  ]
58
60
 
@@ -66,10 +68,11 @@ Gem::Specification.new do |s|
66
68
  s.add_runtime_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
67
69
  s.add_runtime_dependency(%q<redis>, ["~> 2.1.1"])
68
70
  s.add_runtime_dependency(%q<SystemTimer>, ["~> 1.2.1"])
69
- s.add_development_dependency(%q<rspec>, [">= 0"])
71
+ s.add_development_dependency(%q<bourne>, [">= 0"])
70
72
  s.add_development_dependency(%q<cucumber>, [">= 0"])
71
73
  s.add_development_dependency(%q<jeweler>, [">= 0"])
72
- s.add_development_dependency(%q<bourne>, [">= 0"])
74
+ s.add_development_dependency(%q<rspec>, [">= 0"])
75
+ s.add_development_dependency(%q<timecop>, [">= 0"])
73
76
  s.add_development_dependency(%q<webmock>, [">= 0"])
74
77
  else
75
78
  s.add_dependency(%q<daemons>, ["~> 1.1.0"])
@@ -77,10 +80,11 @@ Gem::Specification.new do |s|
77
80
  s.add_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
78
81
  s.add_dependency(%q<redis>, ["~> 2.1.1"])
79
82
  s.add_dependency(%q<SystemTimer>, ["~> 1.2.1"])
80
- s.add_dependency(%q<rspec>, [">= 0"])
83
+ s.add_dependency(%q<bourne>, [">= 0"])
81
84
  s.add_dependency(%q<cucumber>, [">= 0"])
82
85
  s.add_dependency(%q<jeweler>, [">= 0"])
83
- s.add_dependency(%q<bourne>, [">= 0"])
86
+ s.add_dependency(%q<rspec>, [">= 0"])
87
+ s.add_dependency(%q<timecop>, [">= 0"])
84
88
  s.add_dependency(%q<webmock>, [">= 0"])
85
89
  end
86
90
  else
@@ -89,10 +93,11 @@ Gem::Specification.new do |s|
89
93
  s.add_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
90
94
  s.add_dependency(%q<redis>, ["~> 2.1.1"])
91
95
  s.add_dependency(%q<SystemTimer>, ["~> 1.2.1"])
92
- s.add_dependency(%q<rspec>, [">= 0"])
96
+ s.add_dependency(%q<bourne>, [">= 0"])
93
97
  s.add_dependency(%q<cucumber>, [">= 0"])
94
98
  s.add_dependency(%q<jeweler>, [">= 0"])
95
- s.add_dependency(%q<bourne>, [">= 0"])
99
+ s.add_dependency(%q<rspec>, [">= 0"])
100
+ s.add_dependency(%q<timecop>, [">= 0"])
96
101
  s.add_dependency(%q<webmock>, [">= 0"])
97
102
  end
98
103
  end
data/lib/daikon.rb CHANGED
@@ -22,8 +22,9 @@ require 'daikon/namespace_tools'
22
22
  require 'daikon/configuration'
23
23
  require 'daikon/client'
24
24
  require 'daikon/daemon'
25
+ require 'daikon/monitor'
25
26
  require 'daikon/redis_hacks'
26
27
 
27
28
  module Daikon
28
- VERSION = "0.4.0"
29
+ VERSION = "0.5.0"
29
30
  end
data/lib/daikon/client.rb CHANGED
@@ -15,10 +15,11 @@ module Daikon
15
15
  attr_accessor :redis, :logger, :config, :http, :monitor
16
16
 
17
17
  def setup(config, logger = nil)
18
- self.config = config
19
- self.logger = logger
20
- self.redis = connect
21
- self.http = Net::HTTP::Persistent.new
18
+ self.config = config
19
+ self.logger = logger
20
+ self.redis = connect
21
+ self.http = Net::HTTP::Persistent.new
22
+ self.monitor = Monitor.new(connect, logger)
22
23
  http.headers['Authorization'] = config.api_key
23
24
 
24
25
  log "Started Daikon v#{VERSION}"
@@ -29,12 +30,7 @@ module Daikon
29
30
  end
30
31
 
31
32
  def start_monitor
32
- self.monitor = StringIO.new
33
- Thread.new do
34
- connect.monitor do |line|
35
- monitor.puts line
36
- end
37
- end
33
+ monitor.start
38
34
  end
39
35
 
40
36
  def log(message)
@@ -82,13 +78,12 @@ module Daikon
82
78
  end
83
79
 
84
80
  def rotate_monitor
85
- monitor_data = monitor.string
86
- monitor.reopen(StringIO.new)
81
+ lines = monitor.rotate
87
82
 
88
- http_request(:post, "api/v1/monitor") do |request|
89
- request.body = Gem.gzip(monitor_data)
83
+ http_request(:post, "api/v1/monitor.json") do |request|
84
+ request.body = {"lines" => lines}.to_json
90
85
  request.add_field "Content-Length", request.body.size.to_s
91
- request.add_field "Content-Type", "application/x-gzip"
86
+ request.add_field "Content-Type", "application/json"
92
87
  end
93
88
  rescue *EXCEPTIONS => ex
94
89
  log ex.to_s
@@ -0,0 +1,37 @@
1
+ module Daikon
2
+ class Monitor
3
+ attr_accessor :queue
4
+
5
+ NEW_FORMAT = /^(\d+\.\d+)( "[A-Z]+".*)/i
6
+ OLD_SINGLE_FORMAT = /^(QUIT|RANDOMKEY|DBSIZE|EXPIRE|TTL|SAVE|BGSAVE|SHUTDOWN|BGREWRITEAOF|INFO|MONITOR|SLAVEOF)$/i
7
+ OLD_MORE_FORMAT = /^[A-Z]+ .*$/i
8
+
9
+ def initialize(redis = nil, logger = nil)
10
+ @queue = []
11
+ @redis = redis
12
+ @logger = logger
13
+ end
14
+
15
+ def start
16
+ Thread.new do
17
+ @redis.monitor do |line|
18
+ parse(line)
19
+ end
20
+ end
21
+ end
22
+
23
+ def rotate
24
+ @queue.shift(@queue.size)
25
+ end
26
+
27
+ def parse(line)
28
+ if line =~ NEW_FORMAT
29
+ timestamp = $1
30
+ line = $2.strip
31
+ @queue.push({:at => Time.at(*timestamp.split('.').map(&:to_i)), :command => line})
32
+ elsif line =~ OLD_SINGLE_FORMAT || line =~ OLD_MORE_FORMAT
33
+ @queue.push({:at => Time.now, :command => line.strip})
34
+ end
35
+ end
36
+ end
37
+ end
data/spec/client_spec.rb CHANGED
@@ -18,7 +18,7 @@ describe Daikon::Client, "setup" do
18
18
  end
19
19
 
20
20
  it "sets redis to listen on the given port" do
21
- Redis.should have_received(:new).with(:host => "8.8.8.8", :port => "1234")
21
+ Redis.should have_received(:new).with(:host => "8.8.8.8", :port => "1234").twice
22
22
  subject.should have_received(:redis=).with(redis)
23
23
  end
24
24
  end
@@ -31,7 +31,7 @@ describe Daikon::Client, "setup" do
31
31
  end
32
32
 
33
33
  it "sets redis to listen on the given port" do
34
- Redis.should have_received(:new).with(:host => "127.0.0.1", :port => "6379")
34
+ Redis.should have_received(:new).with(:host => "127.0.0.1", :port => "6379").twice
35
35
  subject.should have_received(:redis=).with(redis)
36
36
  end
37
37
  end
@@ -168,16 +168,16 @@ end
168
168
 
169
169
  shared_examples_for "a monitor api consumer" do
170
170
  it "shoots the results back to radish" do
171
- zipped_data = Gem.gzip(results)
171
+ payload = {"lines" => lines}
172
172
 
173
173
  headers = {
174
174
  "Authorization" => api_key,
175
- "Content-Length" => zipped_data.size,
176
- "Content-Type" => "application/x-gzip"
175
+ "Content-Length" => payload.to_json.size,
176
+ "Content-Type" => "application/json"
177
177
  }
178
178
 
179
- WebMock.should have_requested(:post, "#{server}/api/v1/monitor").
180
- with(:body => zipped_data, :headers => headers)
179
+ WebMock.should have_requested(:post, "#{server}/api/v1/monitor.json").
180
+ with(:body => payload.to_json, :headers => headers)
181
181
  end
182
182
  end
183
183
 
@@ -185,11 +185,15 @@ describe Daikon::Client, "rotate monitor" do
185
185
  subject { Daikon::Client.new }
186
186
  let(:results) { %{1290289048.96581 "info"\n1290289053.568815 "info"} }
187
187
  let(:redis) { stub("redis instance", :info => results) }
188
+ let(:lines) do
189
+ [{"at" => Time.at(1290289048, 96581), "command" => "info"},
190
+ {"at" => Time.at(1290289053, 568815), "command" => "info"}]
191
+ end
188
192
 
189
193
  before do
190
- stub_request(:post, "#{server}/api/v1/monitor")
191
- subject.monitor = StringIO.new(results)
194
+ stub_request(:post, "#{server}/api/v1/monitor.json")
192
195
  subject.setup(config)
196
+ subject.monitor = stub("monitor", :rotate => lines)
193
197
  subject.rotate_monitor
194
198
  end
195
199
 
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daikon::Monitor, "#rotate" do
4
+ it "pops off what is on the queue" do
5
+ subject.parse("INCR foo")
6
+ subject.parse("DECR foo")
7
+
8
+ data = subject.rotate
9
+ data.first[:command].should == "INCR foo"
10
+ data.last[:command].should == "DECR foo"
11
+ data.size.should == 2
12
+ subject.queue.size.should be_zero
13
+ end
14
+ end
15
+
16
+ describe Daikon::Monitor, "#parse with new format" do
17
+ subject { Daikon::Monitor.new }
18
+ let(:line) { '1291699658.994073 "decrby" "fooz" "2000"' }
19
+
20
+ it "parses the log into json" do
21
+ subject.parse(line)
22
+ subject.queue.should include({:at => Time.at(1291699658, 994073), :command => '"decrby" "fooz" "2000"'})
23
+ end
24
+ end
25
+
26
+ describe Daikon::Monitor, "#parse with multiple inputs" do
27
+ subject { Daikon::Monitor.new }
28
+ before { Timecop.freeze }
29
+ after { Timecop.return }
30
+
31
+ it "queues up multiple lines" do
32
+ subject.parse("+OK")
33
+ subject.parse("INCR foo")
34
+ subject.parse("INCR fooz")
35
+ subject.parse("info")
36
+
37
+ subject.queue.size.should == 3
38
+ subject.queue.should include({:at => Time.now, :command => 'INCR foo'})
39
+ subject.queue.should include({:at => Time.now, :command => 'INCR fooz'})
40
+ subject.queue.should include({:at => Time.now, :command => 'info'})
41
+ end
42
+ end
43
+
44
+ describe Daikon::Monitor, "#parse with old multi line input" do
45
+ subject { Daikon::Monitor.new }
46
+ before { Timecop.freeze }
47
+ after { Timecop.return }
48
+
49
+ it "parses gzipped logs into raws" do
50
+ subject.parse("incr foo")
51
+ subject.parse("sismember project-13897-global-error-classes 17")
52
+ subject.parse("incrApiParameterError")
53
+ subject.parse("decr foo")
54
+
55
+ subject.queue.size.should == 3
56
+ subject.queue.should include({:at => Time.now, :command => 'incr foo'})
57
+ subject.queue.should include({:at => Time.now, :command => 'sismember project-13897-global-error-classes 17'})
58
+ subject.queue.should include({:at => Time.now, :command => 'decr foo'})
59
+ end
60
+ end
61
+
62
+ describe Daikon::Monitor, "#parse with multi line input with numbers" do
63
+ subject { Daikon::Monitor.new }
64
+ before { Timecop.freeze }
65
+ after { Timecop.return }
66
+
67
+ it "parses gzipped logs into raws" do
68
+ subject.parse("incr foo")
69
+ subject.parse("set g:2470920:mrn 9")
70
+ subject.parse("554079885")
71
+ subject.parse("decr foo")
72
+
73
+ subject.queue.size.should == 3
74
+ subject.queue.should include({:at => Time.now, :command => 'incr foo'})
75
+ subject.queue.should include({:at => Time.now, :command => 'set g:2470920:mrn 9'})
76
+ subject.queue.should include({:at => Time.now, :command => 'decr foo'})
77
+ end
78
+ end
79
+
80
+ describe Daikon::Monitor, "#parse with strings that may to_i to a number" do
81
+ subject { Daikon::Monitor.new }
82
+ before { Timecop.freeze }
83
+ after { Timecop.return }
84
+
85
+ it "parses gzipped logs into raws" do
86
+ subject.parse("incr foo")
87
+ subject.parse("set g:2470920:mrn 9")
88
+ subject.parse("46fdcf77c1bb2108e6191602c2f5f9ae")
89
+ subject.parse("decr foo")
90
+
91
+ subject.queue.size.should == 3
92
+ subject.queue.should include({:at => Time.now, :command => 'incr foo'})
93
+ subject.queue.should include({:at => Time.now, :command => 'set g:2470920:mrn 9'})
94
+ subject.queue.should include({:at => Time.now, :command => 'decr foo'})
95
+ end
96
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,7 @@ require 'daikon'
7
7
  # in ./support/ and its subdirectories.
8
8
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
9
 
10
+ require 'timecop'
10
11
  require 'bourne'
11
12
 
12
13
  require 'webmock/rspec'
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: 15
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
8
+ - 5
9
9
  - 0
10
- version: 0.4.0
10
+ version: 0.5.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: 2010-12-06 00:00:00 -05:00
18
+ date: 2010-12-12 00:00:00 -05:00
19
19
  default_executable: daikon
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -101,7 +101,7 @@ dependencies:
101
101
  - !ruby/object:Gem::Dependency
102
102
  prerelease: false
103
103
  type: :development
104
- name: rspec
104
+ name: bourne
105
105
  version_requirements: &id006 !ruby/object:Gem::Requirement
106
106
  none: false
107
107
  requirements:
@@ -143,7 +143,7 @@ dependencies:
143
143
  - !ruby/object:Gem::Dependency
144
144
  prerelease: false
145
145
  type: :development
146
- name: bourne
146
+ name: rspec
147
147
  version_requirements: &id009 !ruby/object:Gem::Requirement
148
148
  none: false
149
149
  requirements:
@@ -157,7 +157,7 @@ dependencies:
157
157
  - !ruby/object:Gem::Dependency
158
158
  prerelease: false
159
159
  type: :development
160
- name: webmock
160
+ name: timecop
161
161
  version_requirements: &id010 !ruby/object:Gem::Requirement
162
162
  none: false
163
163
  requirements:
@@ -168,6 +168,20 @@ dependencies:
168
168
  - 0
169
169
  version: "0"
170
170
  requirement: *id010
171
+ - !ruby/object:Gem::Dependency
172
+ prerelease: false
173
+ type: :development
174
+ name: webmock
175
+ version_requirements: &id011 !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ hash: 3
181
+ segments:
182
+ - 0
183
+ version: "0"
184
+ requirement: *id011
171
185
  description: daikon, a radishapp.com client
172
186
  email: nick@quaran.to
173
187
  executables:
@@ -184,7 +198,6 @@ files:
184
198
  - MIT-LICENSE
185
199
  - README.rdoc
186
200
  - Rakefile
187
- - VERSION
188
201
  - bin/daikon
189
202
  - daikon.gemspec
190
203
  - features/daikon.feature
@@ -194,12 +207,14 @@ files:
194
207
  - lib/daikon/client.rb
195
208
  - lib/daikon/configuration.rb
196
209
  - lib/daikon/daemon.rb
210
+ - lib/daikon/monitor.rb
197
211
  - lib/daikon/namespace_tools.rb
198
212
  - lib/daikon/redis_hacks.rb
199
213
  - spec/client_spec.rb
200
214
  - spec/configuration_spec.rb
201
215
  - spec/daemon_spec.rb
202
216
  - spec/daikon_spec.rb
217
+ - spec/monitor_spec.rb
203
218
  - spec/spec_helper.rb
204
219
  has_rdoc: true
205
220
  homepage: http://github.com/qrush/daikon
@@ -240,4 +255,5 @@ test_files:
240
255
  - spec/configuration_spec.rb
241
256
  - spec/daemon_spec.rb
242
257
  - spec/daikon_spec.rb
258
+ - spec/monitor_spec.rb
243
259
  - spec/spec_helper.rb
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.0.0