fnordmetric 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 0.6.2
data/fnordmetric.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{fnordmetric}
8
- s.version = "0.6.1"
8
+ s.version = "0.6.2"
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 = %q{2012-01-12}
12
+ s.date = %q{2012-01-13}
13
13
  s.description = %q{FnordMetric is a Ruby Event-Tracking gem on steroids}
14
14
  s.email = %q{paul@paulasmuth.com}
15
15
  s.extra_rdoc_files = [
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "haml/app.haml",
31
31
  "haml/widget.haml",
32
32
  "lib/fnordmetric.rb",
33
+ "lib/fnordmetric/api.rb",
33
34
  "lib/fnordmetric/app.rb",
34
35
  "lib/fnordmetric/average_metric.rb",
35
36
  "lib/fnordmetric/bars_widget.rb",
@@ -64,6 +65,7 @@ Gem::Specification.new do |s|
64
65
  "pub/sprite.png",
65
66
  "pub/vendor/highcharts.js",
66
67
  "pub/vendor/jquery-1.6.1.min.js",
68
+ "spec/api_spec.rb",
67
69
  "spec/app_spec.rb",
68
70
  "spec/context_spec.rb",
69
71
  "spec/dashboard_spec.rb",
@@ -79,9 +81,10 @@ Gem::Specification.new do |s|
79
81
  s.homepage = %q{http://github.com/paulasmuth/fnordmetric}
80
82
  s.licenses = ["MIT"]
81
83
  s.require_paths = ["lib"]
82
- s.rubygems_version = %q{1.5.2}
84
+ s.rubygems_version = %q{1.6.2}
83
85
  s.summary = %q{FnordMetric is a Ruby Event-Tracking gem on steroids}
84
86
  s.test_files = [
87
+ "spec/api_spec.rb",
85
88
  "spec/app_spec.rb",
86
89
  "spec/context_spec.rb",
87
90
  "spec/dashboard_spec.rb",
data/lib/fnordmetric.rb CHANGED
@@ -12,8 +12,8 @@ module FnordMetric
12
12
 
13
13
  @@namespaces = {}
14
14
 
15
- def self.namespace(key=nil, &block)
16
- @@namespaces[key] = block
15
+ def self.namespace(key=nil, &block)
16
+ @@namespaces[key] = block
17
17
  end
18
18
 
19
19
  def self.server_configuration=(configuration)
@@ -23,7 +23,7 @@ module FnordMetric
23
23
  def self.default_options(opts)
24
24
 
25
25
  opts[:redis_url] ||= "redis://localhost:6379"
26
- opts[:redis_prefix] ||= "fnordmetric"
26
+ opts[:redis_prefix] ||= "fnordmetric"
27
27
 
28
28
  opts[:inbound_stream] ||= ["0.0.0.0", "1337"]
29
29
  opts[:web_interface] ||= ["0.0.0.0", "4242"]
@@ -38,8 +38,8 @@ module FnordMetric
38
38
  opts[:event_data_ttl] ||= 3600*24*30
39
39
 
40
40
  # session data is kept for one month
41
- opts[:session_data_ttl] ||= 3600*24*30
42
-
41
+ opts[:session_data_ttl] ||= 3600*24*30
42
+
43
43
  opts
44
44
  end
45
45
 
@@ -49,25 +49,10 @@ module FnordMetric
49
49
  trap("TERM", &method(:shutdown))
50
50
  trap("INT", &method(:shutdown))
51
51
 
52
- opts = default_options(opts)
53
-
54
- if opts[:start_worker]
55
- worker = Worker.new(@@namespaces.clone, opts)
56
- worker.ready!
57
- end
58
-
59
- if opts[:inbound_stream]
60
- begin
61
- inbound_stream = InboundStream.start(opts)
62
- log "listening on tcp##{opts[:inbound_stream].join(":")}"
63
- rescue
64
- log "cant start FnordMetric::InboundStream. port in use?"
65
- end
66
- end
52
+ app = embedded(opts)
67
53
 
68
54
  if opts[:web_interface]
69
- begin
70
- app = FnordMetric::App.new(@@namespaces.clone, opts)
55
+ begin
71
56
  Thin::Server.start(*opts[:web_interface], app)
72
57
  log "listening on http##{opts[:web_interface].join(":")}"
73
58
  rescue Exception => e
@@ -75,14 +60,7 @@ module FnordMetric
75
60
  end
76
61
  end
77
62
 
78
- if opts[:print_stats]
79
- redis = connect_redis(opts[:redis_url])
80
- EM::PeriodicTimer.new(opts[:print_stats]) do
81
- print_stats(opts, redis)
82
- end
83
- end
84
-
85
- end
63
+ end
86
64
  end
87
65
 
88
66
  def self.log(msg)
@@ -113,21 +91,60 @@ module FnordMetric
113
91
 
114
92
  def self.print_stats(opts, redis) # FIXME: refactor this mess
115
93
  keys = [:events_received, :events_processed]
116
- redis.llen("#{opts[:redis_prefix]}-queue") do |queue_length|
94
+ redis.llen("#{opts[:redis_prefix]}-queue") do |queue_length|
117
95
  redis.hmget("#{opts[:redis_prefix]}-stats", *keys) do |data|
118
96
  data_human = keys.size.times.map{|n|"#{keys[n]}: #{data[n]}"}
119
97
  log "#{data_human.join(", ")}, queue_length: #{queue_length}"
120
- end
98
+ end
121
99
  end
122
100
  end
123
101
 
124
- def self.standalone
102
+ def self.standalone
125
103
  require "fnordmetric/logger"
126
104
  require "fnordmetric/standalone"
127
105
  end
128
106
 
107
+ # returns a Rack app which can be mounted under any path.
108
+ # `:start_worker` starts a worker
109
+ # `:inbound_stream` starts the TCP interface
110
+ # `:print_stats` periodicaly prints worker stats
111
+ def self.embedded(opts={})
112
+ opts = default_options(opts)
113
+ app = nil
114
+
115
+ if opts[:rack_app] or opts[:web_interface]
116
+ app = FnordMetric::App.new(@@namespaces.clone, opts)
117
+ end
118
+
119
+ EM.next_tick do
120
+ if opts[:start_worker]
121
+ worker = Worker.new(@@namespaces.clone, opts)
122
+ worker.ready!
123
+ end
124
+
125
+ if opts[:inbound_stream]
126
+ begin
127
+ inbound_stream = InboundStream.start(opts)
128
+ log "listening on tcp##{opts[:inbound_stream].join(":")}"
129
+ rescue
130
+ log "cant start FnordMetric::InboundStream. port in use?"
131
+ end
132
+ end
133
+
134
+ if opts[:print_stats]
135
+ redis = connect_redis(opts[:redis_url])
136
+ EM::PeriodicTimer.new(opts[:print_stats]) do
137
+ print_stats(opts, redis)
138
+ end
139
+ end
140
+ end
141
+
142
+ app
143
+ end
144
+
129
145
  end
130
146
 
147
+ require "fnordmetric/api"
131
148
  require "fnordmetric/inbound_stream"
132
149
  require "fnordmetric/worker"
133
150
  require "fnordmetric/widget"
@@ -0,0 +1,38 @@
1
+ require 'securerandom'
2
+ class FnordMetric::API
3
+ @@opts = nil
4
+ def initialize opts
5
+ @@opts = FnordMetric.default_options(opts)
6
+ connect
7
+ end
8
+
9
+ def connect
10
+ @redis = @@opts[:redis] if @@opts[:redis]
11
+ @redis = Redis.connect(:url => @@opts[:redis_url])
12
+ end
13
+
14
+ def event(event_data)
15
+ event_data = event_data.to_json if event_data.is_a?(Hash)
16
+ push_event(get_next_uuid, event_data)
17
+ end
18
+
19
+ def disconnect
20
+ @redis.quit
21
+ end
22
+
23
+ private
24
+
25
+ def push_event(event_id, event_data)
26
+ prefix = @@opts[:redis_prefix]
27
+ @redis.hincrby "#{prefix}-testdata", "events_received", 1
28
+ @redis.hincrby "#{prefix}-stats", "events_received", 1
29
+ @redis.set "#{prefix}-event-#{event_id}", event_data
30
+ @redis.lpush "#{prefix}-queue", event_id
31
+ @redis.expire "#{prefix}-event-#{event_id}", @@opts[:event_queue_ttl]
32
+ event_id
33
+ end
34
+
35
+ def get_next_uuid
36
+ SecureRandom.uuid
37
+ end
38
+ end
@@ -1,18 +1,18 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class FnordMetric::App < Sinatra::Base
4
-
4
+
5
5
  @@sessions = Hash.new
6
-
6
+
7
7
  Encoding.default_external = Encoding::UTF_8
8
8
 
9
9
  #use Rack::Reloader, 0
10
-
10
+
11
11
  enable :session
12
12
 
13
- set :haml, :format => :html5
13
+ set :haml, :format => :html5
14
14
  set :views, ::File.expand_path('../../../haml', __FILE__)
15
- set :public, ::File.expand_path('../../../pub', __FILE__)
15
+ set :public_folder, ::File.expand_path('../../../pub', __FILE__)
16
16
 
17
17
  def initialize(namespaces, opts)
18
18
  @namespaces = {}
@@ -25,7 +25,7 @@ class FnordMetric::App < Sinatra::Base
25
25
  end
26
26
  super(nil)
27
27
  end
28
-
28
+
29
29
  helpers do
30
30
  include Rack::Utils
31
31
  alias_method :h, :escape_html
@@ -38,7 +38,7 @@ class FnordMetric::App < Sinatra::Base
38
38
  @namespaces
39
39
  end
40
40
 
41
- def current_namespace
41
+ def current_namespace
42
42
  @namespaces[@namespaces.keys.detect{ |k|
43
43
  k.to_s == params[:namespace]
44
44
  }.try(:intern)]
@@ -47,7 +47,7 @@ class FnordMetric::App < Sinatra::Base
47
47
  end
48
48
 
49
49
  if ENV['RACK_ENV'] == "test"
50
- set :raise_errors, true
50
+ set :raise_errors, true
51
51
  end
52
52
 
53
53
  get '/' do
@@ -78,7 +78,7 @@ class FnordMetric::App < Sinatra::Base
78
78
  params[:sum] ? { :sum => _values.values.compact.map(&:to_i).sum } : _values
79
79
  else
80
80
  { (_t = gauge.tick_at(Time.now.to_i-gauge.tick)) => gauge.value_at(_t) }
81
- end
81
+ end
82
82
 
83
83
  data.to_json
84
84
  end
@@ -88,14 +88,14 @@ class FnordMetric::App < Sinatra::Base
88
88
  sessions = current_namespace.sessions(:all, :limit => 100).map do |session|
89
89
  session.fetch_data!
90
90
  session.to_json
91
- end
91
+ end
92
92
 
93
93
  { :sessions => sessions }.to_json
94
94
  end
95
95
 
96
96
  get '/:namespace/events' do
97
97
 
98
- events = if params[:type]
98
+ events = if params[:type]
99
99
  current_namespace.events(:by_type, :type => params[:type])
100
100
  elsif params[:session_key]
101
101
  current_namespace.events(:by_session_key, :session_key => params[:session_key])
@@ -122,7 +122,7 @@ class FnordMetric::App < Sinatra::Base
122
122
  end
123
123
 
124
124
  post '/events' do
125
- halt 400, 'please specify the event_type (_type)' unless params["_type"]
125
+ halt 400, 'please specify the event_type (_type)' unless params["_type"]
126
126
  track_event((8**32).to_s(36), parse_params(params))
127
127
  end
128
128
 
@@ -130,7 +130,7 @@ private
130
130
 
131
131
  def parse_params(hash)
132
132
  hash.tap do |h|
133
- h.keys.each{ |k| h[k] = parse_param(h[k]) }
133
+ h.keys.each{ |k| h[k] = parse_param(h[k]) }
134
134
  end
135
135
  end
136
136
 
@@ -1,6 +1,4 @@
1
- require 'securerandom'
2
1
  class FnordMetric::InboundStream < EventMachine::Connection
3
-
4
2
  @@opts = nil
5
3
 
6
4
  def self.start(opts)
@@ -13,18 +11,6 @@ class FnordMetric::InboundStream < EventMachine::Connection
13
11
  EM.defer{ next_event }
14
12
  end
15
13
 
16
- def push_event(event_id, event_data)
17
- prefix = @@opts[:redis_prefix]
18
-
19
- @redis.hincrby "#{prefix}-stats", "events_received", 1
20
- @redis.set "#{prefix}-event-#{event_id}", event_data
21
- @redis.lpush "#{prefix}-queue", event_id
22
- @redis.expire "#{prefix}-event-#{event_id}", @@opts[:event_queue_ttl]
23
-
24
- @events_buffered -= 1
25
- close_connection?
26
- end
27
-
28
14
  def next_event
29
15
  read_next_event
30
16
  push_next_event
@@ -39,20 +25,18 @@ class FnordMetric::InboundStream < EventMachine::Connection
39
25
 
40
26
  def push_next_event
41
27
  return true if @events.empty?
42
- push_event(get_next_uuid, @events.pop)
28
+ @events_buffered -= 1
29
+ @api.event(@events.pop)
30
+ close_connection?
43
31
  EM.next_tick(&method(:push_next_event))
44
32
  end
45
33
 
46
- def get_next_uuid
47
- SecureRandom.uuid
48
- end
49
-
50
34
  def close_connection?
51
- @redis.quit unless @streaming || (@events_buffered!=0)
35
+ @api.disconnect unless @streaming || (@events_buffered!=0)
52
36
  end
53
37
 
54
38
  def post_init
55
- @redis = Redis.connect(:url => @@opts[:redis_url])
39
+ @api = FnordMetric::API.new(@@opts)
56
40
  @events_buffered = 0
57
41
  @streaming = true
58
42
  @buffer = ""
@@ -63,5 +47,4 @@ class FnordMetric::InboundStream < EventMachine::Connection
63
47
  @streaming = false
64
48
  close_connection?
65
49
  end
66
-
67
50
  end
data/pub/fnordmetric.css CHANGED
@@ -4,7 +4,7 @@ body{ background:#3b3e45; color:#333; margin:0; padding:0; overflow-y:scroll; fo
4
4
  .shown{ display: block; }
5
5
  .hidden{ display: none; }
6
6
 
7
- .topbar{ height:38px; background:#24272c; position:fixed; top:0px; width:100%;}
7
+ .topbar{ height:38px; background:#24272c; position:fixed; top:0px; width:100%; z-index: 1}
8
8
  .topbar ul { list-style-type:none; margin:0; }
9
9
  .topbar ul li { padding: 5px 10px 5px 10px; background-color:#3b3e45; display:inline; height:38px; line-height:38px; border-radius:3px; margin-right:5px;}
10
10
  .topbar ul li a { color:#ccc; font-size:13px; text-decoration:none; }
@@ -122,7 +122,7 @@ ul.session_list li .history:hover{ color:#333; text-decoration:underline; }
122
122
  .sessions_feed ul.feed_inner li .message{ font-size:12px; line-height:19px; padding-top:9px; display:block; }
123
123
  .sessions_feed ul.feed_inner li .properties{ margin-left:50px; font-size:10px; display:block; color:#555; }
124
124
  .sessions_feed ul.feed_inner li .time{ font-size:10px; line-height:20px; padding-top:19px; padding-right:10px; display:block; color:#999; float:right; font-style:italic; }
125
- .sessions_feed ul.feed_inner li .picture{ height:40px; overflow:hidden; width:40px; float:left; background:#333; margin:7px 10px 0 0; }
125
+ .sessions_feed ul.feed_inner li .picture{ height:40px; overflow:hidden; width:40px; float:left; background:#333; margin:7px 10px 0 0; cursor:pointer; }
126
126
  .sessions_sidebar{ min-height:1200px; float:right; width:250px; border-left:1px solid #C7C9CC; }
127
127
  .events_sidebar{ min-height:1200px; float:left; width:200px; border-right:1px solid #C7C9CC; }
128
128
 
data/pub/fnordmetric.js CHANGED
@@ -959,8 +959,6 @@ var FnordMetric = (function(){
959
959
 
960
960
  listElem.append(
961
961
  $('<li class="session"></li>').append(
962
- $('<input type="checkbox" />').click(function(){ updateEventFilter(); })
963
- ).append(
964
962
  $('<div class="picture"></div>').html(session_picture)
965
963
  ).append(
966
964
  $('<span class="name"></span>').html(session_name)
@@ -969,9 +967,9 @@ var FnordMetric = (function(){
969
967
  ).append(
970
968
  $('<span class="history"></span>').html('history')
971
969
  .click(function(){
972
- setCheckboxesCheckedState(true, false);
973
- $('input', $(this).parent()).attr('checked', true);
974
- updateEventFilter(); loadEventHistory({session_key: session_data["session_key"]});
970
+ setCheckboxesCheckedState(true, false);
971
+ updateEventFilter();
972
+ loadEventHistory({session_key: session_data["session_key"]});
975
973
  })
976
974
  ).attr('data-session', session_data["session_key"])
977
975
  );
@@ -1002,16 +1000,22 @@ var FnordMetric = (function(){
1002
1000
  event_time.html(formatTimeOfDay(event_data._time));
1003
1001
 
1004
1002
  if(event_data._session_key && event_data._session_key.length > 0){
1003
+ var __session_key = event_data._session_key;
1004
+ var load_usersession = (function(){
1005
+ loadEventHistory({session_key: __session_key});
1006
+ });
1005
1007
  if(session_data=sessionData[event_data._session_key]){
1006
1008
  if(session_data._name){
1007
1009
  event_props.append(
1008
- $('<strong></strong>').html(session_data._name)
1010
+ $('<strong></strong>').html(session_data._name).css({
1011
+ 'cursor': 'pointer'
1012
+ }).click(load_usersession)
1009
1013
  );
1010
1014
  }
1011
1015
  if(session_data._picture){
1012
1016
  event_picture.append(
1013
1017
  $('<img width="40" />').attr('src', session_data._picture)
1014
- )
1018
+ ).click(load_usersession);
1015
1019
  }
1016
1020
  }
1017
1021
  }
data/spec/api_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require ::File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ describe FnordMetric::Event 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-test-ns1234-api"
13
+ @timeline = "#{@namespace}-timeline"
14
+
15
+ @opts = {
16
+ :namespace_prefix => "#{@namespace}",
17
+ :redis_prefix => "fnordmetric-test",
18
+ :redis => @redis
19
+ }
20
+ @api = API.new @opts
21
+ end
22
+
23
+ describe "creating events using API" do
24
+
25
+ before(:each) do
26
+ @redis.keys("fnordmetric-test-*").each { |k| @redis.del(k) }
27
+ end
28
+
29
+ it "should create an event from a (json-)string" do
30
+ json_string = {
31
+ :_type => "Fn0rd123",
32
+ :_time => @now
33
+ }.to_json
34
+ event_id = @api.event(json_string)
35
+ event = Event.find(event_id, @opts)
36
+ event.type.should == "Fn0rd123"
37
+ end
38
+
39
+ it "should create an event from a hash" do
40
+ event_id = @api.event(
41
+ :_type => "Fn0rd234",
42
+ :_time => @now
43
+ )
44
+ event = Event.find(event_id, @opts)
45
+ event.type.should == "Fn0rd234"
46
+ end
47
+
48
+ end
49
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: fnordmetric
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.1
5
+ version: 0.6.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Paul Asmuth
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-01-12 00:00:00 +01:00
13
+ date: 2012-01-13 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -234,6 +234,7 @@ files:
234
234
  - haml/app.haml
235
235
  - haml/widget.haml
236
236
  - lib/fnordmetric.rb
237
+ - lib/fnordmetric/api.rb
237
238
  - lib/fnordmetric/app.rb
238
239
  - lib/fnordmetric/average_metric.rb
239
240
  - lib/fnordmetric/bars_widget.rb
@@ -268,6 +269,7 @@ files:
268
269
  - pub/sprite.png
269
270
  - pub/vendor/highcharts.js
270
271
  - pub/vendor/jquery-1.6.1.min.js
272
+ - spec/api_spec.rb
271
273
  - spec/app_spec.rb
272
274
  - spec/context_spec.rb
273
275
  - spec/dashboard_spec.rb
@@ -303,11 +305,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
303
305
  requirements: []
304
306
 
305
307
  rubyforge_project:
306
- rubygems_version: 1.5.2
308
+ rubygems_version: 1.6.2
307
309
  signing_key:
308
310
  specification_version: 3
309
311
  summary: FnordMetric is a Ruby Event-Tracking gem on steroids
310
312
  test_files:
313
+ - spec/api_spec.rb
311
314
  - spec/app_spec.rb
312
315
  - spec/context_spec.rb
313
316
  - spec/dashboard_spec.rb