fnordmetric 0.5.5 → 0.5.6

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