johnf-fnordmetric 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -0
- data/Rakefile +9 -0
- data/fnordmetric.gemspec +41 -0
- data/lib/fnordmetric/acceptors/acceptor.rb +42 -0
- data/lib/fnordmetric/acceptors/amqp_acceptor.rb +56 -0
- data/lib/fnordmetric/acceptors/fyrehose_acceptor.rb +43 -0
- data/lib/fnordmetric/acceptors/stomp_acceptor.rb +71 -0
- data/lib/fnordmetric/acceptors/tcp_acceptor.rb +58 -0
- data/lib/fnordmetric/acceptors/udp_acceptor.rb +37 -0
- data/lib/fnordmetric/api.rb +46 -0
- data/lib/fnordmetric/cache.rb +20 -0
- data/lib/fnordmetric/context.rb +96 -0
- data/lib/fnordmetric/defaults.rb +22 -0
- data/lib/fnordmetric/enterprise/compatibility_handler.rb +42 -0
- data/lib/fnordmetric/ext.rb +75 -0
- data/lib/fnordmetric/gauge.rb +98 -0
- data/lib/fnordmetric/gauge_calculations.rb +106 -0
- data/lib/fnordmetric/gauge_modifiers.rb +144 -0
- data/lib/fnordmetric/gauge_rendering.rb +40 -0
- data/lib/fnordmetric/gauge_validations.rb +15 -0
- data/lib/fnordmetric/gauges/distribution_gauge.rb +87 -0
- data/lib/fnordmetric/gauges/server_health_gauge.rb +13 -0
- data/lib/fnordmetric/gauges/timeseries_gauge.rb +138 -0
- data/lib/fnordmetric/gauges/toplist_gauge.rb +44 -0
- data/lib/fnordmetric/histogram.rb +64 -0
- data/lib/fnordmetric/logger.rb +63 -0
- data/lib/fnordmetric/namespace.rb +208 -0
- data/lib/fnordmetric/session.rb +139 -0
- data/lib/fnordmetric/standalone.rb +20 -0
- data/lib/fnordmetric/timeseries.rb +79 -0
- data/lib/fnordmetric/toplist.rb +61 -0
- data/lib/fnordmetric/udp_client.rb +22 -0
- data/lib/fnordmetric/util.rb +25 -0
- data/lib/fnordmetric/version.rb +3 -0
- data/lib/fnordmetric/web/app.rb +63 -0
- data/lib/fnordmetric/web/app_helpers.rb +42 -0
- data/lib/fnordmetric/web/dashboard.rb +40 -0
- data/lib/fnordmetric/web/event.rb +99 -0
- data/lib/fnordmetric/web/reactor.rb +127 -0
- data/lib/fnordmetric/web/web.rb +59 -0
- data/lib/fnordmetric/web/websocket.rb +41 -0
- data/lib/fnordmetric/widget.rb +82 -0
- data/lib/fnordmetric/widgets/bars_widget.rb +44 -0
- data/lib/fnordmetric/widgets/html_widget.rb +28 -0
- data/lib/fnordmetric/widgets/numbers_widget.rb +80 -0
- data/lib/fnordmetric/widgets/pie_widget.rb +23 -0
- data/lib/fnordmetric/widgets/timeseries_widget.rb +65 -0
- data/lib/fnordmetric/widgets/toplist_widget.rb +68 -0
- data/lib/fnordmetric/worker.rb +89 -0
- data/lib/fnordmetric/zero_config_gauge.rb +138 -0
- data/lib/fnordmetric.rb +149 -0
- data/run_specs.sh +11 -0
- data/spec/api_spec.rb +49 -0
- data/spec/context_spec.rb +42 -0
- data/spec/dashboard_spec.rb +38 -0
- data/spec/event_spec.rb +170 -0
- data/spec/ext_spec.rb +14 -0
- data/spec/fnordmetric_spec.rb +56 -0
- data/spec/gauge_like_shared.rb +56 -0
- data/spec/gauge_modifiers_spec.rb +583 -0
- data/spec/gauge_spec.rb +230 -0
- data/spec/namespace_spec.rb +114 -0
- data/spec/session_spec.rb +231 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/tcp_acceptor_spec.rb +35 -0
- data/spec/timeseries_gauge_spec.rb +56 -0
- data/spec/udp_acceptor_spec.rb +35 -0
- data/spec/util_spec.rb +46 -0
- data/spec/widget_spec.rb +113 -0
- data/spec/worker_spec.rb +40 -0
- data/web/.gitignore +4 -0
- data/web/build.sh +34 -0
- data/web/css/fnordmetric.core.css +868 -0
- data/web/fnordmetric-core.css +1409 -0
- data/web/fnordmetric-core.js +3420 -0
- data/web/fnordmetric-ui.css +282 -0
- data/web/fnordmetric-ui.js +12032 -0
- data/web/haml/app.haml +20 -0
- data/web/haml/distribution_gauge.haml +118 -0
- data/web/haml/timeseries_gauge.haml +80 -0
- data/web/haml/toplist_gauge.haml +194 -0
- data/web/img/head.png +0 -0
- data/web/img/list.png +0 -0
- data/web/img/list_active.png +0 -0
- data/web/img/list_hover.png +0 -0
- data/web/img/loader.gif +0 -0
- data/web/img/loader_white.gif +0 -0
- data/web/img/navbar.png +0 -0
- data/web/img/navbar_btn.png +0 -0
- data/web/img/picto_gauge.png +0 -0
- data/web/js/fnordmetric.bars_widget.js +178 -0
- data/web/js/fnordmetric.dashboard_view.js +99 -0
- data/web/js/fnordmetric.gauge_explorer.js +173 -0
- data/web/js/fnordmetric.gauge_view.js +260 -0
- data/web/js/fnordmetric.html_widget.js +21 -0
- data/web/js/fnordmetric.js +315 -0
- data/web/js/fnordmetric.numbers_widget.js +122 -0
- data/web/js/fnordmetric.overview_view.js +35 -0
- data/web/js/fnordmetric.pie_widget.js +118 -0
- data/web/js/fnordmetric.realtime_timeline_widget.js +175 -0
- data/web/js/fnordmetric.session_view.js +342 -0
- data/web/js/fnordmetric.timeline_widget.js +333 -0
- data/web/js/fnordmetric.timeseries_widget.js +405 -0
- data/web/js/fnordmetric.toplist_widget.js +119 -0
- data/web/js/fnordmetric.ui.js +91 -0
- data/web/js/fnordmetric.util.js +248 -0
- data/web/vendor/font-awesome/css/font-awesome-ie7.min.css +22 -0
- data/web/vendor/font-awesome/css/font-awesome.css +540 -0
- data/web/vendor/font-awesome/css/font-awesome.min.css +33 -0
- data/web/vendor/font-awesome/font/FontAwesome.otf +0 -0
- data/web/vendor/font-awesome/font/fontawesome-webfont.eot +0 -0
- data/web/vendor/font-awesome/font/fontawesome-webfont.svg +284 -0
- data/web/vendor/font-awesome/font/fontawesome-webfont.ttf +0 -0
- data/web/vendor/font-awesome/font/fontawesome-webfont.woff +0 -0
- data/web/vendor/jquery-1.6.2.min.js +18 -0
- data/web/vendor/jquery-ui.min.js +6 -0
- data/web/vendor/jquery.combobox.js +129 -0
- data/web/vendor/jquery.maskedinput.js +252 -0
- metadata +444 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
data/fnordmetric.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "date"
|
4
|
+
require "fnordmetric/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "johnf-fnordmetric"
|
8
|
+
s.version = FnordMetric::VERSION
|
9
|
+
s.date = Date.today.to_s
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["Paul Asmuth"]
|
12
|
+
s.email = ["paul@paulasmuth.com"]
|
13
|
+
s.homepage = "http://github.com/paulasmuth/fnordmetric"
|
14
|
+
s.summary = %q{FnordMetric is a Ruby Event-Tracking gem on steroids}
|
15
|
+
s.description = %q{FnordMetric is a Ruby Event-Tracking gem on steroids}
|
16
|
+
s.licenses = ["MIT"]
|
17
|
+
|
18
|
+
s.add_dependency "sinatra", ">= 1.2.6"
|
19
|
+
s.add_dependency "redis", ">= 2.2.2"
|
20
|
+
s.add_dependency "eventmachine"
|
21
|
+
s.add_dependency "websocket-rack", "0.4.0"
|
22
|
+
s.add_dependency "em-hiredis", ">= 0.1.1"
|
23
|
+
s.add_dependency "json"
|
24
|
+
s.add_dependency "i18n"
|
25
|
+
s.add_dependency "haml"
|
26
|
+
s.add_dependency "rack"
|
27
|
+
s.add_dependency "rack-test"
|
28
|
+
s.add_dependency "yajl-ruby"
|
29
|
+
s.add_dependency "thin", ">= 1.3.0"
|
30
|
+
s.add_dependency "activesupport"
|
31
|
+
|
32
|
+
s.add_development_dependency "delorean"
|
33
|
+
s.add_development_dependency "rspec", "~> 2.8.0"
|
34
|
+
s.add_development_dependency "shoulda"
|
35
|
+
|
36
|
+
s.files = `git ls-files`.split("\n") - [".gitignore", ".rspec", ".travis.yml"]
|
37
|
+
s.files += ["web/fnordmetric-ui.js", "web/fnordmetric-ui.css", "web/fnordmetric-core.js", "web/fnordmetric-core.css"]
|
38
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
39
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class FnordMetric::Acceptor
|
2
|
+
|
3
|
+
def initialize(opts)
|
4
|
+
@opts = opts
|
5
|
+
|
6
|
+
FnordMetric.register(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialized
|
10
|
+
inbound_class = if @opts[:protocol] == :udp
|
11
|
+
FnordMetric::UDPAcceptor
|
12
|
+
elsif @opts[:protocol] == :tcp
|
13
|
+
FnordMetric::TCPAcceptor
|
14
|
+
elsif @opts[:protocol] == :fyrehose
|
15
|
+
FnordMetric::FyrehoseAcceptor
|
16
|
+
elsif @opts[:protocol] == :amqp
|
17
|
+
FnordMetric::AMQPAcceptor
|
18
|
+
elsif @opts[:protocol] == :stomp
|
19
|
+
FnordMetric::STOMPAcceptor
|
20
|
+
else
|
21
|
+
raise "unknown protocol: #{@opts[:protocol]}"
|
22
|
+
end
|
23
|
+
|
24
|
+
@opts[:listen] = [
|
25
|
+
@opts[:host] || "0.0.0.0",
|
26
|
+
@opts[:port] || 2323
|
27
|
+
]
|
28
|
+
|
29
|
+
begin
|
30
|
+
inbound_stream = inbound_class.start(@opts)
|
31
|
+
if inbound_class.respond_to?(:outbound?) && inbound_class.outbound?
|
32
|
+
FnordMetric.log "connected to #{@opts[:protocol]}://#{@opts[:listen][0..1].join(":")}"
|
33
|
+
else
|
34
|
+
FnordMetric.log "listening on #{@opts[:protocol]}://#{@opts[:listen][0..1].join(":")}"
|
35
|
+
end
|
36
|
+
rescue Exception => e
|
37
|
+
raise e if ENV["FNORDMETRIC_ENV"] == "dev"
|
38
|
+
FnordMetric.log "cant start #{inbound_class.name} on #{@opts[:protocol]}://#{@opts[:listen][0..1].join(":")}. port in use?"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class FnordMetric::AMQPAcceptor
|
2
|
+
|
3
|
+
def self.start(opts)
|
4
|
+
begin
|
5
|
+
require "amqp"
|
6
|
+
rescue LoadError
|
7
|
+
FnordMetric.error("require 'amqp' failed, you need the amqp gem")
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
new(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(opts)
|
15
|
+
amqp = AMQP.connect(:host => 'firehose')
|
16
|
+
amqp_channel = AMQP::Channel.new(amqp)
|
17
|
+
|
18
|
+
msg_handler = lambda do |channel, data|
|
19
|
+
event = begin
|
20
|
+
JSON.parse(data)
|
21
|
+
rescue
|
22
|
+
FnordMetric.log("[AMQP] received invalid JSON: #{data[0..60]}")
|
23
|
+
end
|
24
|
+
|
25
|
+
if event
|
26
|
+
event["_type"] ||= channel
|
27
|
+
events << event
|
28
|
+
push_next_event
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
opts[:channels].each do |channel|
|
33
|
+
queue = amqp_channel.queue(channel, :auto_delete => true)
|
34
|
+
queue.subscribe{ |data| msg_handler[channel, data] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def push_next_event
|
39
|
+
return true if events.empty?
|
40
|
+
api.event(@events.pop)
|
41
|
+
EM.next_tick(&method(:push_next_event))
|
42
|
+
end
|
43
|
+
|
44
|
+
def events
|
45
|
+
@events ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def api
|
49
|
+
@api ||= FnordMetric::API.new(FnordMetric.options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.outbound?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class FnordMetric::FyrehoseAcceptor
|
2
|
+
|
3
|
+
def self.start(opts)
|
4
|
+
require "fyrehose"
|
5
|
+
require "fyrehose/reactor"
|
6
|
+
|
7
|
+
new(opts)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(opts)
|
11
|
+
reactor = EM.connect(opts[:host], opts[:port], Fyrehose::Reactor)
|
12
|
+
|
13
|
+
reactor.on_message do |channel, data|
|
14
|
+
event = JSON.parse(data)
|
15
|
+
event["_type"] ||= channel
|
16
|
+
events << event
|
17
|
+
push_next_event
|
18
|
+
end
|
19
|
+
|
20
|
+
opts[:channels].each do |channel|
|
21
|
+
reactor.subscribe(channel)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def push_next_event
|
26
|
+
return true if events.empty?
|
27
|
+
api.event(@events.pop)
|
28
|
+
EM.next_tick(&method(:push_next_event))
|
29
|
+
end
|
30
|
+
|
31
|
+
def events
|
32
|
+
@events ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def api
|
36
|
+
@api ||= FnordMetric::API.new(FnordMetric.options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.outboud?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class FnordMetric::STOMPAcceptor
|
2
|
+
|
3
|
+
def self.start(opts)
|
4
|
+
begin
|
5
|
+
require "stomp"
|
6
|
+
rescue LoadError
|
7
|
+
FnordMetric.error("require 'stomp' failed, you need the stomp gem")
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
new(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(opts)
|
15
|
+
@mutex = Mutex.new
|
16
|
+
|
17
|
+
client = Stomp::Client.new(:hosts => [{
|
18
|
+
:host => opts[:host],
|
19
|
+
:port => opts[:port],
|
20
|
+
:passcode => opts[:password],
|
21
|
+
:login => opts[:username]}])
|
22
|
+
|
23
|
+
msg_handler = lambda do |topic, msg|
|
24
|
+
data = msg.body
|
25
|
+
|
26
|
+
event = begin
|
27
|
+
JSON.parse(data)
|
28
|
+
rescue
|
29
|
+
FnordMetric.log("[STOMP] received invalid JSON: #{data[0..60]}")
|
30
|
+
end
|
31
|
+
|
32
|
+
if event
|
33
|
+
event["_type"] ||= topic.gsub(/^\/topic\//, '')
|
34
|
+
@mutex.synchronize{ events << event }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
opts[:topics].each do |topic|
|
39
|
+
client.subscribe(topic){ |data| msg_handler[topic, data] }
|
40
|
+
end
|
41
|
+
|
42
|
+
Thread.new do
|
43
|
+
client.join
|
44
|
+
end
|
45
|
+
|
46
|
+
EM.next_tick(&method(:push_next_event))
|
47
|
+
end
|
48
|
+
|
49
|
+
def push_next_event
|
50
|
+
nxt = @mutex.synchronize{ events.pop }
|
51
|
+
unless nxt
|
52
|
+
EM::Timer.new(0.01, &method(:push_next_event))
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
api.event(nxt)
|
56
|
+
EM.next_tick(&method(:push_next_event))
|
57
|
+
end
|
58
|
+
|
59
|
+
def events
|
60
|
+
@events ||= []
|
61
|
+
end
|
62
|
+
|
63
|
+
def api
|
64
|
+
@api ||= FnordMetric::API.new(FnordMetric.options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.outbound?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class FnordMetric::TCPAcceptor < EventMachine::Connection
|
2
|
+
@@opts = nil
|
3
|
+
|
4
|
+
def self.start(opts)
|
5
|
+
@@opts = opts
|
6
|
+
EM.start_server(*(opts[:listen] + [self]))
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.options(opts)
|
10
|
+
@@opts = opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data(chunk)
|
14
|
+
@buffer << chunk
|
15
|
+
next_event
|
16
|
+
end
|
17
|
+
|
18
|
+
def next_event
|
19
|
+
read_next_event
|
20
|
+
push_next_event
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_next_event
|
24
|
+
while (event = @buffer.slice!(/^(.*)\n/))
|
25
|
+
@events_buffered += 1
|
26
|
+
@events << event
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def push_next_event
|
31
|
+
return true if @events.empty?
|
32
|
+
@events_buffered -= 1
|
33
|
+
api.event(@events.pop)
|
34
|
+
close_connection?
|
35
|
+
EM.next_tick(&method(:push_next_event))
|
36
|
+
end
|
37
|
+
|
38
|
+
def close_connection?
|
39
|
+
#@backend.hangup unless @streaming || (@events_buffered!=0)
|
40
|
+
end
|
41
|
+
|
42
|
+
def post_init
|
43
|
+
@events_buffered = 0
|
44
|
+
@streaming = true
|
45
|
+
@buffer = ""
|
46
|
+
@events = []
|
47
|
+
end
|
48
|
+
|
49
|
+
def unbind
|
50
|
+
@streaming = false
|
51
|
+
close_connection?
|
52
|
+
end
|
53
|
+
|
54
|
+
def api
|
55
|
+
@api ||= FnordMetric::API.new(FnordMetric.options)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class FnordMetric::UDPAcceptor < EventMachine::Connection
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_accessor :opts
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.start(opts)
|
8
|
+
self.opts = opts
|
9
|
+
|
10
|
+
EM.open_datagram_socket(*(opts[:listen] << self << opts))
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data(event)
|
14
|
+
events << event
|
15
|
+
push_next_event
|
16
|
+
end
|
17
|
+
|
18
|
+
def push_next_event
|
19
|
+
return true if events.empty?
|
20
|
+
ev = @events.pop
|
21
|
+
api.event(ev)
|
22
|
+
EM.next_tick(&method(:push_next_event))
|
23
|
+
end
|
24
|
+
|
25
|
+
def unbind
|
26
|
+
#backend.hangup
|
27
|
+
end
|
28
|
+
|
29
|
+
def events
|
30
|
+
@events ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
def api
|
34
|
+
@api ||= FnordMetric::API.new(FnordMetric.options)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class FnordMetric::API
|
2
|
+
|
3
|
+
@@opts = nil
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
@@opts = FnordMetric.default_options(opts)
|
7
|
+
connect
|
8
|
+
end
|
9
|
+
|
10
|
+
def connect
|
11
|
+
@redis = Redis.connect(:url => @@opts[:redis_url])
|
12
|
+
end
|
13
|
+
|
14
|
+
def event(event_data)
|
15
|
+
begin
|
16
|
+
if event_data.is_a?(Hash)
|
17
|
+
event_data = event_data.to_json
|
18
|
+
else
|
19
|
+
JSON.parse(event_data) # void ;)
|
20
|
+
end
|
21
|
+
rescue JSON::ParserError
|
22
|
+
FnordMetric.log("event_lost: can't parse json")
|
23
|
+
else
|
24
|
+
push_event(get_next_uuid, event_data)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect
|
29
|
+
@redis.quit
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def push_event(event_id, event_data)
|
35
|
+
prefix = @@opts[:redis_prefix]
|
36
|
+
@redis.hincrby "#{prefix}-stats", "events_received", 1
|
37
|
+
@redis.set "#{prefix}-event-#{event_id}", event_data
|
38
|
+
@redis.lpush "#{prefix}-queue", event_id
|
39
|
+
@redis.expire "#{prefix}-event-#{event_id}", @@opts[:event_queue_ttl]
|
40
|
+
event_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_next_uuid
|
44
|
+
rand(8**32).to_s(36)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class FnordMetric::Cache
|
2
|
+
# include Mongoid::Document
|
3
|
+
|
4
|
+
# self.collection_name = 'fnordmetric_cache'
|
5
|
+
|
6
|
+
# field :cache_key, :type => String
|
7
|
+
# field :data, :type => Hash
|
8
|
+
|
9
|
+
def self.store!(cache_key, data)
|
10
|
+
data = { :value => data } unless data.is_a?(Hash)
|
11
|
+
self.create(:cache_key => cache_key, :data => data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get(cache_key)
|
15
|
+
item = self.where(:cache_key => cache_key).last
|
16
|
+
return nil unless item
|
17
|
+
item.data.keys == ["value"] ? item.data["value"] : item.data
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class FnordMetric::Context
|
2
|
+
|
3
|
+
include FnordMetric::GaugeModifiers
|
4
|
+
|
5
|
+
class Proxy
|
6
|
+
|
7
|
+
def initialize(_ref)
|
8
|
+
@ref = _ref
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
@ref.dispatch(method, *args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(opts, block)
|
18
|
+
@block = block
|
19
|
+
@opts = opts
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(event, redis, namespace)
|
23
|
+
@redis = redis
|
24
|
+
@event = event
|
25
|
+
@namespace = namespace
|
26
|
+
proxy.instance_eval(&@block)
|
27
|
+
rescue Exception => e
|
28
|
+
raise e if ENV['FNORDMETRIC_ENV'] == 'test'
|
29
|
+
puts "error: #{e.message}"
|
30
|
+
puts e.backtrace.push("").join("\n") if ENV['FNORDMETRIC_ENV'] == 'dev'
|
31
|
+
end
|
32
|
+
|
33
|
+
def proxy
|
34
|
+
@proxy ||= Proxy.new(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def namespace
|
38
|
+
@namespace
|
39
|
+
end
|
40
|
+
|
41
|
+
def dispatch(method, *args, &block)
|
42
|
+
if args.size > 0 && gauges[args[0]].try(:renderable?)
|
43
|
+
gauges[args.delete_at(0)].execute(method, *args.unshift(self), &block)
|
44
|
+
else
|
45
|
+
send(method, *args, &block)
|
46
|
+
end
|
47
|
+
rescue Exception => e
|
48
|
+
raise e if ENV['FNORDMETRIC_ENV'] == 'test'
|
49
|
+
puts "error: #{e.message}"
|
50
|
+
puts e.backtrace.push("\n").join("\n") if ENV['FNORDMETRIC_ENV'] == 'dev'
|
51
|
+
end
|
52
|
+
|
53
|
+
def redis_exec(*args)
|
54
|
+
@redis.send(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def session_key
|
60
|
+
@event[:_session_key]
|
61
|
+
end
|
62
|
+
|
63
|
+
def data
|
64
|
+
@event
|
65
|
+
end
|
66
|
+
|
67
|
+
def key(gauge)
|
68
|
+
fetch_gauge(gauge).key
|
69
|
+
end
|
70
|
+
|
71
|
+
def time
|
72
|
+
@event[:_time].to_i
|
73
|
+
end
|
74
|
+
|
75
|
+
def type
|
76
|
+
@event[:_type].to_sym
|
77
|
+
end
|
78
|
+
|
79
|
+
def gauges
|
80
|
+
@namespace.gauges
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def fetch_gauge(_gauge)
|
86
|
+
_gauge.is_a?(FnordMetric::Gauge) ? _gauge : gauges.fetch(_gauge)
|
87
|
+
rescue
|
88
|
+
error! "error: gauge '#{_gauge}' is undefined"
|
89
|
+
end
|
90
|
+
|
91
|
+
def error!(msg)
|
92
|
+
FnordMetric.error(msg)
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
FnordMetric::COLORS = ["#4572a7", "#aa4643", "#89a54e", "#80699b", "#3d96ae", "#db843d"].reverse
|
2
|
+
|
3
|
+
FnordMetric::DEFAULT_PROC = lambda{ |arg| }
|
4
|
+
|
5
|
+
FnordMetric::TICKS = lambda{ |tick, span| [tick, 60, 300, 1200, 3600, 86400]
|
6
|
+
.select{ |t| (t >= tick) && ((span/t) > 5) }
|
7
|
+
.uniq }
|
8
|
+
|
9
|
+
FnordMetric::DEFAULT_OPTIONS = {
|
10
|
+
:redis_url => "redis://localhost:6379",
|
11
|
+
:redis_prefix => "fnordmetric",
|
12
|
+
:inbound_stream => nil,
|
13
|
+
:inbound_protocol => nil,
|
14
|
+
:web_interface => ["0.0.0.0", "4242"],
|
15
|
+
:web_interface_server => "thin",
|
16
|
+
:start_worker => true,
|
17
|
+
:print_stats => 3,
|
18
|
+
:event_queue_ttl => 120,
|
19
|
+
:event_data_ttl => 3600*24*30,
|
20
|
+
:session_data_ttl => 3600*24*30,
|
21
|
+
:default_flush_interval => 10
|
22
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module FnordMetric::Enterprise
|
2
|
+
|
3
|
+
CompatibilityHandler = proc do
|
4
|
+
if data[:_cmd]
|
5
|
+
cmd = data[:_cmd].to_s
|
6
|
+
else
|
7
|
+
FnordMetric.error("missing key: _cmd")
|
8
|
+
next
|
9
|
+
end
|
10
|
+
|
11
|
+
if m = cmd.match(/^SAMPLE (.*)(delta|mean|sum)-([0-9]+) ([0-9]+)$/)
|
12
|
+
gauge_type = m[2].to_sym
|
13
|
+
gauge_tick = m[3].to_i
|
14
|
+
gauge_key = :"#{m[1]}#{gauge_type}-#{gauge_tick}"
|
15
|
+
op_value = m[4].to_i
|
16
|
+
else
|
17
|
+
FnordMetric.error("invalid _cmd")
|
18
|
+
next
|
19
|
+
end
|
20
|
+
|
21
|
+
gauge = if namespace.gauges.has_key?(gauge_key)
|
22
|
+
namespace.gauges[gauge_key]
|
23
|
+
else
|
24
|
+
namespace.opt_gauge(gauge_key,
|
25
|
+
:flush_interval => gauge_tick,
|
26
|
+
:average => (gauge_type == :mean),
|
27
|
+
:zero_config => true)
|
28
|
+
end
|
29
|
+
|
30
|
+
case gauge_type
|
31
|
+
|
32
|
+
when :delta, :sum
|
33
|
+
incr gauge, op_value
|
34
|
+
|
35
|
+
when :mean
|
36
|
+
incr_avg gauge, op_value
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Enumerable
|
2
|
+
|
3
|
+
def each_with_log(total = nil, &block)
|
4
|
+
log_every = ((total ||= self.count) / 150)
|
5
|
+
self.each_with_index do |item, index|
|
6
|
+
if index % log_every == 0
|
7
|
+
STDOUT.puts "#{index}/#{total} (#{((index/total.to_f)*100).to_i}%)"
|
8
|
+
end
|
9
|
+
block.call(item, index)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
module Haml::Filters::Gaugejs
|
16
|
+
include Haml::Filters::Base
|
17
|
+
|
18
|
+
def render(text)
|
19
|
+
"<FNORDMETRIC-GAUGEJS>#{text}</FNORDMETRIC-GAUGEJS>"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Symbol
|
24
|
+
alias :intern :to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
class Range
|
28
|
+
|
29
|
+
def size
|
30
|
+
self.last - self.first
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class Array
|
36
|
+
|
37
|
+
def mean
|
38
|
+
return 0 if empty?
|
39
|
+
inject(&:+).to_f / size
|
40
|
+
end
|
41
|
+
|
42
|
+
alias :average :mean
|
43
|
+
|
44
|
+
def median
|
45
|
+
return 0 if empty?
|
46
|
+
(_sorted = self.dup.sort)[_sorted.size/2]
|
47
|
+
end
|
48
|
+
|
49
|
+
def range
|
50
|
+
return 0 if empty?
|
51
|
+
max - min
|
52
|
+
end
|
53
|
+
|
54
|
+
def mode
|
55
|
+
return 0 if empty?
|
56
|
+
inject({}){ |h,v| h[v] = h[v].to_i+1; h }.to_a
|
57
|
+
.sort{ |a,b| b.last <=> a.last }[0][0]
|
58
|
+
end
|
59
|
+
|
60
|
+
def emtpy
|
61
|
+
self.size == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class Thin::Connection
|
67
|
+
|
68
|
+
alias :pre_process_orig :pre_process
|
69
|
+
|
70
|
+
def pre_process
|
71
|
+
@request.env['async.connection'] = self
|
72
|
+
pre_process_orig
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|