daikon 0.4.0 → 0.5.0

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/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