fnordmetric 0.3.2 → 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.
Files changed (59) hide show
  1. data/Gemfile +6 -0
  2. data/Gemfile.lock +21 -0
  3. data/Procfile +1 -2
  4. data/VERSION +1 -1
  5. data/_spec/app_spec.rb +178 -0
  6. data/{spec → _spec}/cache_spec.rb +0 -0
  7. data/{spec → _spec}/combine_metric_spec.rb +0 -0
  8. data/{spec → _spec}/core_spec.rb +0 -0
  9. data/{spec → _spec}/count_metric_spec.rb +0 -0
  10. data/_spec/dashboard_spec.rb +67 -0
  11. data/_spec/event_spec.rb +46 -0
  12. data/{spec → _spec}/metric_spec.rb +0 -0
  13. data/{spec → _spec}/report_spec.rb +0 -0
  14. data/{spec → _spec}/sum_metric_spec.rb +0 -0
  15. data/_spec/widget_spec.rb +107 -0
  16. data/doc/import_dump.rb +26 -0
  17. data/em_runner.rb +33 -0
  18. data/fnordmetric.gemspec +59 -20
  19. data/haml/app.haml +26 -12
  20. data/lib/fnordmetric.rb +150 -15
  21. data/lib/fnordmetric/app.rb +70 -11
  22. data/lib/fnordmetric/cache.rb +4 -4
  23. data/lib/fnordmetric/context.rb +65 -0
  24. data/lib/fnordmetric/dashboard.rb +16 -12
  25. data/lib/fnordmetric/event.rb +65 -15
  26. data/lib/fnordmetric/gauge.rb +46 -0
  27. data/lib/fnordmetric/gauge_calculations.rb +43 -0
  28. data/lib/fnordmetric/gauge_modifiers.rb +43 -0
  29. data/lib/fnordmetric/inbound_stream.rb +66 -0
  30. data/lib/fnordmetric/logger.rb +38 -0
  31. data/lib/fnordmetric/namespace.rb +120 -0
  32. data/lib/fnordmetric/numbers_widget.rb +29 -11
  33. data/lib/fnordmetric/session.rb +131 -0
  34. data/lib/fnordmetric/standalone.rb +31 -0
  35. data/lib/fnordmetric/timeline_widget.rb +29 -9
  36. data/lib/fnordmetric/widget.rb +50 -45
  37. data/lib/fnordmetric/worker.rb +80 -0
  38. data/pub/fnordmetric/fnordmetric.css +76 -9
  39. data/pub/fnordmetric/fnordmetric.js +541 -42
  40. data/pub/raphael-min.js +8 -0
  41. data/pub/raphael-utils.js +221 -0
  42. data/readme.rdoc +172 -27
  43. data/server.rb +22 -0
  44. data/spec/app_spec.rb +359 -117
  45. data/spec/context_spec.rb +42 -0
  46. data/spec/dashboard_spec.rb +7 -47
  47. data/spec/event_spec.rb +114 -33
  48. data/spec/gauge_modifiers_spec.rb +276 -0
  49. data/spec/gauge_spec.rb +128 -0
  50. data/spec/namespace_spec.rb +104 -0
  51. data/spec/session_spec.rb +231 -0
  52. data/spec/spec_helper.rb +27 -4
  53. data/spec/widget_spec.rb +81 -75
  54. data/spec/worker_spec.rb +37 -0
  55. data/test_stream.sh +187 -0
  56. data/ulm_stats.rb +198 -0
  57. metadata +114 -35
  58. data/lib/fnordmetric/core.rb +0 -66
  59. data/lib/fnordmetric/engine.rb +0 -3
@@ -0,0 +1,128 @@
1
+ require ::File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe FnordMetric::Gauge do
4
+
5
+ before(:all) do
6
+ @now = Time.utc(1992,01,13,5,23,23).to_i
7
+ @redis = Redis.new
8
+ @redis_wrap = RedisWrap.new(@redis, false)
9
+ end
10
+
11
+ before(:each) do
12
+ @redis.keys("fnordmetric-myns*").each { |k| @redis.del(k) }
13
+ end
14
+
15
+ it "should remember it's own name" do
16
+ gauge = FnordMetric::Gauge.new({:key_prefix => "foo", :key => "fnordgauge"})
17
+ gauge.name.should == "fnordgauge"
18
+ end
19
+
20
+ it "should raise an error when initialize without key" do
21
+ lambda{
22
+ FnordMetric::Gauge.new({:key_prefix => "foo"})
23
+ }.should raise_error(KeyError)
24
+ end
25
+
26
+ it "should raise an error when initialize without key_prefix" do
27
+ lambda{
28
+ FnordMetric::Gauge.new({:key => "foo"})
29
+ }.should raise_error(KeyError)
30
+ end
31
+
32
+ it "should generate the correct key without append" do
33
+ gauge = FnordMetric::Gauge.new({:key_prefix => "fnordmetrics-myns", :key => "mygauge", :tick => 23})
34
+ gauge.key.should == "fnordmetrics-myns-gauge-mygauge-23"
35
+ end
36
+
37
+ it "should generate the correct key with append" do
38
+ gauge = FnordMetric::Gauge.new({:key_prefix => "fnordmetrics-myns", :key => "mygauge", :tick => 23})
39
+ gauge.key(:fnord).should == "fnordmetrics-myns-gauge-mygauge-23-fnord"
40
+ end
41
+
42
+ describe "ticks" do
43
+
44
+ it "should return the correct tick if configured"
45
+ it "should return the default tick if none configured"
46
+
47
+
48
+ it "should return the correct tick_at" do
49
+ gauge = FnordMetric::Gauge.new({:tick => 10, :key_prefix => "fnordmetrics-myns", :key => "mygauge"})
50
+ gauge.tick_at(@now).should == 695280200
51
+ gauge.tick_at(@now+6).should == 695280200
52
+ gauge.tick_at(@now+8).should == 695280210
53
+ end
54
+
55
+ end
56
+
57
+ describe "value retrival" do
58
+
59
+ before(:each) do
60
+ @gauge_key = "fnordmetric-myns-gauge-mygauge_966-10"
61
+ @redis.hset(@gauge_key, "695280200", "54")
62
+ @redis.hset(@gauge_key, "695280210", "123")
63
+ @gauge = FnordMetric::Gauge.new({
64
+ :tick => 10,
65
+ :key_prefix => "fnordmetric-myns",
66
+ :key => "mygauge_966",
67
+ :redis => @redis
68
+ })
69
+ end
70
+
71
+ it "should retrieve a gauge value at a given time" do
72
+ @gauge.value_at(@now).should == "54"
73
+ @gauge.value_at(@now+6).should == "54"
74
+ @gauge.value_at(@now+8).should == "123"
75
+ end
76
+
77
+ it "should retrieve a gauge value at the current tick"
78
+
79
+ it "should call the value calculation block and return the result" do
80
+ @gauge.value_at(@now){ |v| v.to_i + 123 }.should == 177
81
+ end
82
+
83
+ it "should return the correct value_at per session" do
84
+ @redis.set(@gauge_key+"-695280200-sessions-count", "23")
85
+ @gauge.value_at(@now, :avg_per_session => 1).should == (54.0/23.0)
86
+ end
87
+
88
+ it "should receive gauge values for multiple ticks" do
89
+ @gauge.values_at([@now, @now+8]).should == {
90
+ 695280200 => "54",
91
+ 695280210 => "123"
92
+ }
93
+ end
94
+
95
+ it "should receive gauge values per session for multiple ticks" do
96
+ @redis.set(@gauge_key+"-695280200-sessions-count", "23")
97
+ @redis.set(@gauge_key+"-695280210-sessions-count", "8")
98
+ @gauge.values_at([@now, @now+8], :avg_per_session => 1).should == {
99
+ 695280200 => (54.0/23.0),
100
+ 695280210 => (123.0/8.0)
101
+ }
102
+ end
103
+
104
+ it "should receive gauge values with custom calculation for multiple ticks" do
105
+ @gauge.values_at([@now, @now+8]){ |val|
106
+ val.to_i + 30
107
+ }.should == {
108
+ 695280200 => 84,
109
+ 695280210 => 153
110
+ }
111
+ end
112
+
113
+ it "should receive gauge values for all ticks in a given range" do
114
+ @gauge.values_in(@now..@now+8).should == {
115
+ 695280200 => "54",
116
+ 695280210 => "123"
117
+ }
118
+ @gauge.values_in(@now..@now+6).should == {
119
+ 695280200 => "54"
120
+ }
121
+ @gauge.values_in(@now+8..@now+10).should == {
122
+ 695280210 => "123"
123
+ }
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,104 @@
1
+ require ::File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe FnordMetric::Namespace do
4
+
5
+
6
+ before(:all) do
7
+ @redis = Redis.new
8
+ @redis_wrap = RedisWrap.new(@redis)
9
+ end
10
+
11
+ before(:each) do
12
+ @redis.keys("fnordmetric-myns*").each { |k| @redis.del(k) }
13
+ end
14
+
15
+ describe "instance methods" do
16
+
17
+ before(:each) do
18
+ @namespace = FnordMetric::Namespace.new(:myns_213, :redis_prefix => "fnordmetric")
19
+ end
20
+
21
+ it "should create a new dashboard if a widget is added" do
22
+ @namespace.widget("My Dash", nil)
23
+ @namespace.dashboards.keys.should == ["My Dash"]
24
+ end
25
+
26
+ it "should create a new dashboard if a widget is added and add the widget"
27
+ it "should add widget to an existing dashboard"
28
+
29
+ end
30
+
31
+
32
+ it "should generate the correct redis prefix"
33
+
34
+ it "should register a gauge"
35
+ it "should register a gauge and pass options"
36
+
37
+ describe "registering event handlers" do
38
+
39
+ before(:each) do
40
+ @namespace = FnordMetric::Namespace.new(:myns_213, :redis_prefix => "fnordmetric")
41
+ end
42
+
43
+ it "should register an event handler" do
44
+ @namespace.handlers.length.should == 0
45
+ @namespace.event(:foobar){}
46
+ @namespace.event(:fnordbar){}
47
+ @namespace.handlers["foobar"].length.should == 1
48
+ @namespace.handlers["fnordbar"].length.should == 1
49
+ @namespace.handlers.length.should == 2
50
+ end
51
+
52
+ it "should register an event handler and create a context"
53
+ it "should register an event handler and pass options"
54
+ it "should register an event handler and pass gauges"
55
+
56
+ it "should announce an event to the correct handler" do
57
+ pending("finish this")
58
+ # block_called = false
59
+ # FnordMetric::Dashboard.new(:title => 'My Dashboard') do |dash|
60
+ # block_called = true
61
+ # dash.should be_a(FnordMetric::Dashboard)
62
+ # end
63
+ # block_called.should be_true
64
+ end
65
+
66
+ it "should announce an event to multiple handlers"
67
+ it "should announce an event to the wildcard handler"
68
+
69
+ end
70
+
71
+ it "should create a new session on announce if _session is set" do
72
+ FnordMetric::Session.should_receive(:create).and_return(SessionMock.new)
73
+ FnordMetric::Namespace.new(
74
+ :myns_213,
75
+ :redis_prefix => "fnordmetric"
76
+ ).ready!(@redis_wrap).announce(
77
+ :_time => Time.now.to_i,
78
+ :_type => "foobar",
79
+ :_session => "sess213"
80
+ )
81
+ end
82
+
83
+ it "should add the event to the namespace-event-type-list" do
84
+ FnordMetric::Namespace.new(
85
+ :myns_215,
86
+ :redis_prefix => "fnordmetric"
87
+ ).ready!(@redis_wrap).announce(
88
+ :_eid => "35r2423",
89
+ :_time => Time.now.to_i,
90
+ :_type => "fnordbar",
91
+ :_session => "sess213"
92
+ )
93
+ event_ids = @redis.lrange("fnordmetric-myns_215-type-fnordbar", 0, -1)
94
+ event_ids.length.should == 1
95
+ event_ids.first.should == "35r2423"
96
+ end
97
+
98
+ class SessionMock
99
+ def session_key
100
+ "asdasd"
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,231 @@
1
+ require ::File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe FnordMetric::Session do
4
+
5
+ include FnordMetric
6
+
7
+ before(:all) do
8
+ @now = Time.utc(1992,01,13,5,23,23).to_i
9
+ @redis = Redis.new
10
+ @redis_wrap = RedisWrap.new(@redis)
11
+
12
+ @namespace = "fnordmetric-ns123"
13
+ @sessions = "#{@namespace}-session"
14
+ end
15
+
16
+ describe "creating sessions" do
17
+
18
+ before(:all) do
19
+ @event = { :_session => "sess123", :_time => @now, :_eid => "34089749" }
20
+ @md5_key = Digest::MD5.hexdigest("sess123")
21
+
22
+ @redis.del("#{@namespace}-session")
23
+ @redis.del("#{@namespace}-session-#{@md5_key}-events")
24
+ @redis.del("#{@namespace}-session-#{@md5_key}-data")
25
+ end
26
+
27
+ it "should add a new session on intialize" do
28
+ Session.create(
29
+ :namespace_prefix => @namespace,
30
+ :event => @event,
31
+ :session_data_ttl => 10,
32
+ :redis => @redis_wrap
33
+ )
34
+ @redis.zcard(@sessions).should == 1
35
+ end
36
+
37
+ it "should add a new session on intialize and hash the session token" do
38
+ Session.create(
39
+ :namespace_prefix => @namespace,
40
+ :event => @event,
41
+ :session_data_ttl => 10,
42
+ :redis => @redis_wrap
43
+ )
44
+ @redis.zrange(@sessions, 0, -1).should == [ @md5_key]
45
+ end
46
+
47
+ it "should add a new session on intialize and set the timestamp as score" do
48
+ Session.create(
49
+ :namespace_prefix => @namespace,
50
+ :event => @event,
51
+ :session_data_ttl => 10,
52
+ :redis => @redis_wrap
53
+ )
54
+ @redis.zscore(@sessions, @md5_key).to_i.should == @now
55
+ end
56
+
57
+ it "should update the timestamp on a existing session" do
58
+ @redis.zadd(@sessions, @now-10, @md5_key)
59
+ @redis.zscore(@sessions, @md5_key).to_i.should == @now-10
60
+ Session.create(
61
+ :namespace_prefix => @namespace,
62
+ :event => @event,
63
+ :session_data_ttl => 10,
64
+ :redis => @redis_wrap
65
+ )
66
+ @redis.zscore(@sessions, @md5_key).to_i.should == @now
67
+ end
68
+
69
+ it "should add the event_id to the session-event set on a new session" do
70
+ Session.create(
71
+ :namespace_prefix => @namespace,
72
+ :event => @event,
73
+ :session_data_ttl => 10,
74
+ :redis => @redis_wrap
75
+ )
76
+ events_key = "#{@namespace}-session-#{@md5_key}-events"
77
+ @redis.zrange(events_key, 0, -1).first.should == @event[:_eid]
78
+ end
79
+
80
+ it "should store a name in the session data" do
81
+ event_data = @event.merge(
82
+ :_type => "_set_name",
83
+ :name => "Horst Mayer"
84
+ )
85
+ Session.create(
86
+ :namespace_prefix => @namespace,
87
+ :event => event_data,
88
+ :session_data_ttl => 10,
89
+ :redis => @redis_wrap
90
+ )
91
+ data_key = "#{@namespace}-session-#{@md5_key}-data"
92
+ @redis.hget(data_key, "_name").should == "Horst Mayer"
93
+ end
94
+
95
+ it "should store a picture in the session data" do
96
+ event_data = @event.merge(
97
+ :_type => "_set_picture",
98
+ :url => "http://myhost/mypic.jpg"
99
+ )
100
+ Session.create(
101
+ :namespace_prefix => @namespace,
102
+ :event => event_data,
103
+ :session_data_ttl => 10,
104
+ :redis => @redis_wrap
105
+ )
106
+ data_key = "#{@namespace}-session-#{@md5_key}-data"
107
+ @redis.hget(data_key, "_picture").should == "http://myhost/mypic.jpg"
108
+ end
109
+
110
+ it "should store arbitrary data in the session data" do
111
+ event_data = @event.merge(
112
+ :_type => "_set_data",
113
+ :fnord => "blubb",
114
+ :foobar => "123"
115
+ )
116
+ Session.create(
117
+ :namespace_prefix => @namespace,
118
+ :event => event_data,
119
+ :session_data_ttl => 10,
120
+ :redis => @redis_wrap
121
+ )
122
+ data_key = "#{@namespace}-session-#{@md5_key}-data"
123
+ @redis.hget(data_key, "fnord").should == "blubb"
124
+ @redis.hget(data_key, "foobar").should == "123"
125
+ end
126
+
127
+ it "not store special attributes in the session" do
128
+ event_data = @event.merge(
129
+ :_type => "_set_data",
130
+ :fnord => "blubb",
131
+ :foobar => "123"
132
+ )
133
+ Session.create(
134
+ :namespace_prefix => @namespace,
135
+ :event => event_data,
136
+ :session_data_ttl => 10,
137
+ :redis => @redis_wrap
138
+ )
139
+ data_key = "#{@namespace}-session-#{@md5_key}-data"
140
+ @redis.hget(data_key, "_time").should be_nil
141
+ @redis.hget(data_key, "_eid").should be_nil
142
+ end
143
+
144
+ end
145
+
146
+ describe "Finding Sessions" do
147
+
148
+ before(:each) do
149
+ @redis.del("#{@namespace}-session")
150
+ @redis.keys("#{@namespace}-session-*").each { |k| @redis.del(k) }
151
+
152
+ @opts = {
153
+ :redis_prefix => "#{@namespace}-session",
154
+ :namespace_prefix => "#{@namespace}",
155
+ :redis => @redis
156
+ }
157
+ end
158
+
159
+ it "should find all sessions" do
160
+ create_session("sess533", @now, {})
161
+ create_session("sess343", @now, {})
162
+ Session.all(@opts).length.should == 2
163
+ end
164
+
165
+ it "should find all sessions and return session objects" do
166
+ create_session("sess523", @now, {})
167
+ Session.all(@opts).first.should be_a(FnordMetric::Session)
168
+ end
169
+
170
+ it "should find a session and return a session object" do
171
+ create_session("sess223", @now, {})
172
+ sess = Session.find(Digest::MD5.hexdigest("sess223"), @opts)
173
+ sess.should be_a(FnordMetric::Session)
174
+ sess.session_key.should == Digest::MD5.hexdigest("sess223")
175
+ end
176
+
177
+ it "should find a sessions and return a session object with data" do
178
+ create_session("sess123", @now, { :fnord => "blubb" })
179
+ sess = Session.find(Digest::MD5.hexdigest("sess123"), @opts)
180
+ sess.fetch_data!
181
+ sess.data(:fnord).should == "blubb"
182
+ end
183
+
184
+ it "should not include special attributes in data" do
185
+ event_data = { :_name => "Horst Mayer", :_picture => "http://myhost/mypic.jpg" }
186
+ create_session("sess173", @now, event_data)
187
+ sess = Session.find(Digest::MD5.hexdigest("sess173"), @opts)
188
+ sess.fetch_data!
189
+ sess.data(:_name).should == nil
190
+ sess.data(:_picture).should == nil
191
+ end
192
+
193
+ it "should find a session and return a session object with picture" do
194
+ event_data = { :_name => "Horst Mayer", :_picture => "http://myhost/mypic.jpg" }
195
+ create_session("sess163", @now, event_data)
196
+ sess = Session.find(Digest::MD5.hexdigest("sess163"), @opts)
197
+ sess.fetch_data!
198
+ sess.picture.should == "http://myhost/mypic.jpg"
199
+ end
200
+
201
+ it "should find a session and return a session object with name" do
202
+ event_data = { :_name => "Horst Mayer", :_picture => "http://myhost/mypic.jpg" }
203
+ create_session("sess143", @now, event_data)
204
+ sess = Session.find(Digest::MD5.hexdigest("sess143"), @opts)
205
+ sess.fetch_data!
206
+ sess.name.should == "Horst Mayer"
207
+ end
208
+
209
+ it "should find a session and return a session object with event_ids" do
210
+ sesshash = create_session("sess923", @now, {})
211
+ @redis_wrap.zadd("#{@namespace}-session-#{sesshash}-events", @now, "shmoo")
212
+ @redis_wrap.zadd("#{@namespace}-session-#{sesshash}-events", @now, "fnord")
213
+ sess = Session.find(sesshash, @opts)
214
+ sess.fetch_event_ids!
215
+ sess.event_ids[0].should == "fnord"
216
+ sess.event_ids[1].should == "shmoo"
217
+ end
218
+
219
+ def create_session(sesskey, sesstime, sessdata)
220
+ Digest::MD5.hexdigest(sesskey).tap do |sesshash|
221
+ @redis_wrap.zadd("#{@namespace}-session", sesstime, sesshash)
222
+ sessdata.each do |k,v|
223
+ @redis_wrap.hset("#{@namespace}-session-#{sesshash}-data", k, v)
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+
230
+
231
+ end