fnordmetric 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,335 @@
1
+ FnordMetric
2
+ ===========
3
+
4
+ FnordMetric is a highly configurable (and pretty fast) realtime app/event tracking thing based on ruby eventmachine and redis. You define your own plotting and counting functions as ruby blocks!
5
+
6
+ [ ![Build status - Travis-ci](https://secure.travis-ci.org/paulasmuth/fnordmetric.png) ](http://travis-ci.org/paulasmuth/fnordmetric)
7
+
8
+ [SCREENCAST](http://www.screenr.com/KiJs): the FnordMetric-instance we use to track our social dating app.
9
+
10
+ ----
11
+
12
+ FnordMetric keeps track of your data and draws nice timeline plots.
13
+
14
+ [ ![Nice timeline plots](https://raw.github.com/paulasmuth/fnordmetric/master/doc/preview1.png) ](https://raw.github.com/paulasmuth/fnordmetric/master/doc/preview1.png)
15
+
16
+ FnordMetric gives you a live dashboard, that shows who is using your app in realtime. You can select a single user and follow them step by step.
17
+
18
+ [ ![Live dashboard](https://raw.github.com/paulasmuth/fnordmetric/master/doc/preview2.png) ](https://raw.github.com/paulasmuth/fnordmetric/master/doc/preview2.png)
19
+
20
+
21
+ Getting Started
22
+ ---------------
23
+
24
+ Copy `doc/ulm_stats.rb` (that's the configuration from the screenshots and screencast) or the simple example from below to `my_stats_app.rb`.
25
+
26
+ Simple Example: this will listen for json-events with `type=unicorn_seen` and render a timeline-plot showing the number of received events per hour.
27
+
28
+ ```ruby
29
+ require "fnordmetric"
30
+
31
+ FnordMetric.namespace :myapp do
32
+
33
+ # numeric (delta) gauge, 1-hour tick
34
+ gauge :unicorns_seen_per_hour,
35
+ :tick => 1.hour.to_i,
36
+ :title => "Unicorns seenper Hour"
37
+
38
+ # on every event like { _type: 'unicorn_seen' }
39
+ event(:unicorn_seen) do
40
+ # increment the unicorns_seen_per_hour gauge by 1
41
+ incr :unicorns_seen_per_hour
42
+ end
43
+
44
+ # draw a timeline showing the gauges value, auto-refresh every 2s
45
+ widget 'Overview', {
46
+ :title => "Unicorn-Sightings per Hour",
47
+ :type => :timeline,
48
+ :gauges => :unicorns_seen_per_hour,
49
+ :include_current => true,
50
+ :autoupdate => 2
51
+ }
52
+
53
+ end
54
+
55
+ FnordMetric.standalone
56
+ ```
57
+
58
+ Start the app (requires ruby >= 1.9.2):
59
+
60
+ $ ruby my_stats_app.rb run
61
+
62
+ Log all incoming events:
63
+
64
+ $ ruby my_stats_app.rb log DUMP_FILE=/tmp/foo.json
65
+
66
+ This is the easiest way to submit an event:
67
+
68
+ echo "{\"_type\": \"unicorn_seen\"}" | nc localhost 1337
69
+
70
+
71
+ Installation
72
+ ------------
73
+
74
+ gem install fnordmetric
75
+
76
+ or in your Gemfile:
77
+
78
+ gem 'fnordmetric', '~> 0.5'
79
+
80
+
81
+ Documentation
82
+ -------------
83
+
84
+ ### Sending Events ###
85
+
86
+ The slow way: HTTP-Post the json event to the fnordmetric webinterface.
87
+
88
+ POST http://localhost:2323/events _type=unicorn_seen
89
+
90
+ curl -X POST -d "_type=unicorn_seen" http://localhost:4242/events
91
+
92
+ The easy way: Stream one ore more newline-seperated json encoded events through a tcp connection.
93
+
94
+ echo "\{\"_type\": \"unicorn_seen\"\}\n" | nc localhost 2323
95
+
96
+ The fast way: Add your event directly to the redis-based queue.
97
+
98
+ ```ruby
99
+ uuid = (8**32).to_s(36)
100
+ event = { :_type => "unicorn_seen" }.to_json
101
+
102
+ redis.set("fnordmetric-event-#{my_uuid}", event)
103
+ redis.expire("fnordmetric-event-#{my_uuid}", 60)
104
+ redis.lpush("fnordmetric-queue", uuid)
105
+ ```
106
+
107
+ ----
108
+
109
+ ### Special Events ###
110
+
111
+ ```js
112
+ // track a pageview
113
+ { "_type": "_pageview", "url": "/blob/my_super_seo_article", "_session": "mysessiontoken" }
114
+
115
+ // set the user name
116
+ { "_type": "_set_name", "name": "Tingle Tangle Bob", "_session": "mysessiontoken" }
117
+
118
+ // set the user picture
119
+ { "_type": "_set_picture", "url": "http://myhost/123.jpg", "_session": "mysessiontoken" }
120
+ ```
121
+
122
+ ----
123
+
124
+ ### Event Handlers ###
125
+
126
+ Call these methods from the event-handler block
127
+
128
+ incr(gauge_name, value=1):
129
+ Increment the given (two-dimensional) gauge by value at the tick specified by event-time
130
+
131
+ incr_field(gauge_name, field_name, value=1):
132
+ Increment the given field on a three-dimensional gauge by value at the tick specified by event-time
133
+
134
+ set_value(gauge_name, value)
135
+ Set the given (two-dimensional) to value at the tick specified by event-time (overwrite existing value)
136
+
137
+ set_field(gauge_name, field_name, value)
138
+ Set the given field on a three-dimensional gauge to value at the tick specified by event-time (overwrite existing value)
139
+
140
+ ----
141
+
142
+ ### Options: Gauges ###
143
+
144
+ ----
145
+
146
+ ### Options: Widgets ###
147
+
148
+ + `[autoupdate]` auto-refresh the timeline every n secs (0 turns autoupdate off)
149
+
150
+ TimelineWidget
151
+
152
+ + `[plot_style]` one of: line, areaspline
153
+ + `[include_current]` show the current tick?
154
+ + `[ticks]` number of ticks to show (defaults to 24/30)
155
+
156
+ BarsWidget
157
+
158
+ + `[plot_style]` one of: vertical, horizontal
159
+ + `[order_by]`: order bars/columns by: value, field
160
+
161
+
162
+ ----
163
+
164
+ ### JSON API ###
165
+
166
+
167
+ Examples
168
+ --------
169
+
170
+ + doc/ulm_stats.rb
171
+
172
+
173
+ ## Full Example ##
174
+
175
+ ```ruby
176
+ require "fnordmetric"
177
+
178
+ FnordMetric.namespace :myapp do
179
+
180
+ # numeric (delta) gauge, 1-hour tick
181
+ gauge :messages_sent,
182
+ :tick => 1.hour.to_i,
183
+ :title => "Messages (sent) per Hour"
184
+
185
+ # numeric (delta) gauge, 1-hour tick
186
+ gauge :messages_read,
187
+ :tick => 1.hour.to_i,
188
+ :title => "Messages (read) per Hour"
189
+
190
+ # numeric (progressive) gauge, 1-hour tick
191
+ gauge :events_total,
192
+ :tick => 1.day.to_i,
193
+ :progressive => true,
194
+ :title => "Events (total)"
195
+
196
+ # numeric (delta) gauge, increments uniquely by session_key
197
+ gauge :pageviews_daily_unique,
198
+ :tick => 1.day.to_i,
199
+ :unique => true,
200
+ :title => "Unique Visits (Daily)"
201
+
202
+ # numeric (delta) gauge, increments uniquely by session_key, returns average
203
+ gauge :avg_age_per_session,
204
+ :tick => 1.day.to_i,
205
+ :unique => true,
206
+ :average => true,
207
+ :title => "Avg. User Age"
208
+
209
+ # three-dimensional (delta) gauge (time->key->value)
210
+ gauge :pageviews_per_url_daily,
211
+ :tick => 1.day.to_i,
212
+ :title => "Daily Pageviews per URL",
213
+ :three_dimensional => true
214
+
215
+
216
+ # on every event like { "_type": "message_sent" }
217
+ event(:message_sent) do
218
+ # increment the messages_sent gauge by 1
219
+ incr :messages_sent
220
+ end
221
+
222
+ # on every event like { "_type": "message_read" }
223
+ event(:message_read) do
224
+ # increment the messages_read gauge by 1
225
+ incr :messages_read
226
+ end
227
+
228
+ # on _every_ event
229
+ event :"*" do
230
+ # increment the events_total gauge by 1
231
+ incr :events_total
232
+ end
233
+
234
+ # on every event like { "_type": "_pageview", "_session": "sbz7jset", "url": "/page2" }
235
+ event :_pageview do
236
+ # increment the daily_uniques gauge by 1 if session_key hasn't been seen in this tick yet
237
+ incr :pageviews_daily_unique
238
+ # increment the pageviews_per_url_daily gauge by 1 where key = 'page2'
239
+ incr_field :pageviews_per_url_daily, data[:url]
240
+ end
241
+
242
+ # on every event like { "_type": "_my_set_age", "my_age_field": "23" }
243
+ event(:my_set_age) do
244
+ # add the value of my_set_age to the avg_age_per_session gauge if session_key
245
+ # hasn't been seen in this tick yet
246
+ incr :avg_age_per_session, data[:my_age_field]
247
+ end
248
+
249
+ # draw a timeline showing the pageviews_daily_unique, auto-refresh every 30s
250
+ widget 'Overview', {
251
+ :title => "Unique Visits per Day",
252
+ :type => :timeline,
253
+ :width => 70,
254
+ :gauges => :pageviews_daily_unique,
255
+ :include_current => true,
256
+ :autoupdate => 30
257
+ }
258
+
259
+ # draw the values of the messages_sent and messages_read gauge at the current tick, three ticks ago, and
260
+ # the sum of the last 10 ticks, auto-refresh every 20s
261
+ widget 'Overview', {
262
+ :title => "Messages Sent / Read",
263
+ :type => :numbers,
264
+ :width => 30,
265
+ :autoupdate => 20,
266
+ :offsets => [0,3,"10s"],
267
+ :gauges => [ :messages_sent, :messages_read ]
268
+ }
269
+
270
+ # draw a list of the most visited urls (url, visits + percentage), auto-refresh every 20s
271
+ widget 'Overview', {
272
+ :title => "Top Pages",
273
+ :type => :toplist,
274
+ :autoupdate => 20,
275
+ :gauges => [ :pageviews_per_url_daily ]
276
+ }
277
+
278
+ end
279
+
280
+ FnordMetric.standalone
281
+ ```
282
+
283
+ Contributors
284
+ ------------
285
+
286
+ + Simon Menke (http://github.com/fd)
287
+ + Bruno Michel (http://github.com/nono)
288
+ + Marco Borromeo (http://github.com/mborromeo)
289
+ + Leo Lou (http://github.com/l4u)
290
+ + Andy Lindeman (http://github.com/alindeman)
291
+
292
+ To contribute, please fork this repository, make your changes and run the specs, commit them to your github repository and send me a pull request.
293
+
294
+
295
+ License
296
+ -------
297
+
298
+ Copyright (c) 2011 Paul Asmuth
299
+
300
+ Permission is hereby granted, free of charge, to any person obtaining
301
+ a copy of this software and associated documentation files (the
302
+ "Software"), to use, copy and modify copies of the Software, subject
303
+ to the following conditions:
304
+
305
+ The above copyright notice and this permission notice shall be
306
+ included in all copies or substantial portions of the Software.
307
+
308
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
309
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
310
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
311
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
312
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
313
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
314
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
315
+
316
+
317
+ Todos
318
+ -----
319
+
320
+ * funnel-widget
321
+ * combine/calculation gauges via opts_gauge({}, &block) (+calculate ctr)
322
+ * timeline_widget: 'compare mode': compate gauge to yesterday
323
+ * numbers_widget: handle decreasing vals
324
+ * make listen-ports configurable
325
+ * referal tracking fu (parse googlequeries)
326
+ * trend detection
327
+ * opt_event options: :increment => gauge_name
328
+ * preconfigured default-dashboard (like google analytics)
329
+ * pagview+ref-tracking via js-tracking-pixel
330
+ * table/gauge-list-widget (with mini-stats!)
331
+ * prune the namespace-sessions-timline (remove event_ids older than x)
332
+ * prune the namespace-event-types-list (trim to max items)
333
+ * timelinewidget + numberswidget => should use redis hmget
334
+ * get multiple metrics in a single http get
335
+ * `{ _namespace: myns }` field
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.5
1
+ 0.5.6
data/fnordmetric.gemspec CHANGED
@@ -5,17 +5,21 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "fnordmetric"
8
- s.version = "0.5.5"
8
+ s.version = "0.5.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Paul Asmuth"]
12
- s.date = "2011-12-28"
12
+ s.date = "2012-01-03"
13
13
  s.description = "FnordMetric is a Ruby Event-Tracking gem on steroids"
14
14
  s.email = "paul@paulasmuth.com"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
15
18
  s.files = [
16
19
  ".travis.yml",
17
20
  "Gemfile",
18
21
  "Gemfile.lock",
22
+ "README.md",
19
23
  "Rakefile",
20
24
  "VERSION",
21
25
  "doc/preview1.png",
@@ -60,7 +64,6 @@ Gem::Specification.new do |s|
60
64
  "pub/sprite.png",
61
65
  "pub/vendor/highcharts.js",
62
66
  "pub/vendor/jquery-1.6.1.min.js",
63
- "readme.rdoc",
64
67
  "spec/app_spec.rb",
65
68
  "spec/context_spec.rb",
66
69
  "spec/dashboard_spec.rb",
@@ -76,7 +79,7 @@ Gem::Specification.new do |s|
76
79
  s.homepage = "http://github.com/paulasmuth/fnordmetric"
77
80
  s.licenses = ["MIT"]
78
81
  s.require_paths = ["lib"]
79
- s.rubygems_version = "1.8.11"
82
+ s.rubygems_version = "1.8.10"
80
83
  s.summary = "FnordMetric is a Ruby Event-Tracking gem on steroids"
81
84
  s.test_files = [
82
85
  "spec/app_spec.rb",
data/haml/app.haml CHANGED
@@ -14,7 +14,11 @@
14
14
  FnordMetric.p = '#{path_prefix}';
15
15
 
16
16
  %body
17
- .topbar
17
+ .topbar{:class => namespaces.count > 1 ? 'shown' : 'hidden'}
18
+ %ul
19
+ -namespaces.each do |key,namespace|
20
+ %li{:class => namespace.token == current_namespace.token ? 'active' : nil}
21
+ %a{:href=>"/#{namespace.token}"}=h namespace.token
18
22
 
19
23
  #wrap
20
24
  #tabs
@@ -44,6 +48,7 @@
44
48
 
45
49
  $('#tabs li.sessions').click(function(){
46
50
  FnordMetric.renderSessionView();
51
+ window.location.hash = '';
47
52
  });
48
53
 
49
54
  $('#tabs li').click(function(){
data/lib/fnordmetric.rb CHANGED
@@ -18,7 +18,7 @@ module FnordMetric
18
18
 
19
19
  def self.default_options(opts)
20
20
 
21
- opts[:redis_uri] = "redis://localhost:6379"
21
+ opts[:redis_url] ||= "redis://localhost:6379"
22
22
  opts[:redis_prefix] ||= "fnordmetric"
23
23
 
24
24
  opts[:inbound_stream] ||= ["0.0.0.0", "1337"]
@@ -16,7 +16,7 @@ class FnordMetric::App < Sinatra::Base
16
16
 
17
17
  def initialize(namespaces, opts)
18
18
  @namespaces = {}
19
- @redis = Redis.new
19
+ @redis = Redis.connect(:url => opts[:redis_url])
20
20
  @opts = opts
21
21
  namespaces.each do |key, block|
22
22
  @namespaces[key] = FnordMetric::Namespace.new(key, opts.clone)
@@ -34,6 +34,10 @@ class FnordMetric::App < Sinatra::Base
34
34
  request.env["SCRIPT_NAME"]
35
35
  end
36
36
 
37
+ def namespaces
38
+ @namespaces
39
+ end
40
+
37
41
  def current_namespace
38
42
  @namespaces[@namespaces.keys.detect{ |k|
39
43
  k.to_s == params[:namespace]
@@ -51,7 +51,7 @@ class FnordMetric::InboundStream < EventMachine::Connection
51
51
  end
52
52
 
53
53
  def post_init
54
- @redis = Redis.new
54
+ @redis = Redis.connect(:url => opts[:redis_url])
55
55
  @events_buffered = 0
56
56
  @streaming = true
57
57
  @buffer = ""
@@ -7,7 +7,7 @@ class FnordMetric::Worker
7
7
  end
8
8
 
9
9
  def ready!
10
- @redis = EM::Hiredis.connect(@opts[:redis_uri])
10
+ @redis = EM::Hiredis.connect(@opts[:redis_url])
11
11
  tick
12
12
  end
13
13