drone 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ pkg/*
6
+ coverage/
7
+ gems/
8
+ doc/
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private lib/**/*.rb - README.md LICENSE
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in drone.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011-2011 Julien Ammous
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,138 @@
1
+
2
+ # What is this ?
3
+
4
+ Drone is a monitoring library designed to collect data from your application and export them
5
+ to virtually any monitoring tool.
6
+ Its core is heavily based on the impressive works of Coda Hale on the metrics java library.
7
+
8
+ A fully working example is included in examples/simple, to run it
9
+ (I suppose you already cloned the repository and have a prompt at the root of it):
10
+
11
+ gem install bundler
12
+ bundle
13
+ ruby examples/simple.rb
14
+
15
+ The example will output the collected statistics directly on the console every second.
16
+
17
+ # How is it done
18
+
19
+ The library is split in different parts
20
+
21
+ - the core
22
+ it contains all the API used to declare which data to collect and how as well as the storage for them
23
+
24
+ - the metrics
25
+ that is all the metrics type the library know.
26
+
27
+ - the interfaces
28
+ those are the parts which will decides how the stored data are made available.
29
+
30
+ - the schedulers
31
+ this is where the timers are scheduled, currently there is only one scheduler: eventmachine
32
+
33
+ ## Constraints
34
+
35
+ - the name of each metric can be formatted how it pleases you (note that output interfaces may expect some format)
36
+ but the name is expected to be unique or you could end up reusing the same metric without wanting it.
37
+ (this only applies to monitor_time and monitor_rate helpers but could apply anywhere else as needed)
38
+
39
+
40
+ # Supported Runtimes
41
+
42
+ - MRI 1.8.7+
43
+ - Rubinius 1.2.2+
44
+
45
+
46
+ # Status
47
+ - Most of the features I wanted in are:
48
+ - timing method calls
49
+ - method calls rate
50
+ - counters
51
+ - gauges
52
+
53
+ - Decent test coverage (Simplecov report ~ 87% for what is worth)
54
+
55
+ # Usage
56
+
57
+ I try to keep things as simple as possible, there is currently two ways to use
58
+ this library:
59
+
60
+ - the first one is to just instantiate metrics by hand and use them directly
61
+
62
+ require 'drone'
63
+ Drone::init_drone()
64
+ @counter = Drone::Metris::Counter.new('my_counter')
65
+
66
+ def some_method
67
+ @counter.inc()
68
+ end
69
+
70
+ - the other way is to instrument a class:
71
+
72
+ require 'drone'
73
+ Drone::init_drone()
74
+
75
+ class User
76
+ include Drone::Monitoring
77
+
78
+ monitor_rate("users/new")
79
+ def initialize(login, pass); end
80
+
81
+ monitor_time("users/rename")
82
+ def rename(new_login); end
83
+
84
+ end
85
+
86
+ This code will create three metrics:
87
+ - "users/new" : how many users are created each second
88
+ - "users/rename" : how much time renaming a user takes and how many users are renamed
89
+ each second
90
+
91
+
92
+ Once you have your data you need to add a way to serve them, each lives in a separate
93
+ gem to limit the core's dependencies so the only one in core is:
94
+
95
+ - console output (puts), mainly for debug:
96
+
97
+ require 'drone'
98
+ Drone::init_drone()
99
+ Drone::add_output(:console, 1)
100
+
101
+ The values will be printed on the console at the inter
102
+
103
+ # Goals
104
+
105
+ My goal is to be able to serve stats efficiently from any ruby 1.9 application built
106
+ on top of eventmachine and fibers but I built the library to allow non eventmachine uses too, for
107
+ now the only part where eventmachine is required is the scheduler.
108
+
109
+ Implementing a scheduler based on a background Thread is possible but before that work
110
+ needs to be done to ensure thread safety, Actually the library is not thread safe.
111
+
112
+ if someone wants to implements it I am not against it but I prefer it to be added as an
113
+ optional part instead of in the core. There should not be any problem to implements it
114
+ in an includable module not included as default (it may requires some modifications in the core):
115
+
116
+ require 'drone'
117
+ require 'drone/threadsafe'
118
+
119
+ [...]
120
+
121
+
122
+ # Development
123
+
124
+ Installing the development environment is pretty simple thanks to bundler:
125
+
126
+ gem install bundler
127
+ bundle
128
+
129
+ ## Running specs
130
+
131
+ The specs are written with bacon, mocha and em-spec, they can be ran with:
132
+
133
+ rake spec
134
+
135
+ ## Build the doc
136
+ You will need the gems: yard and bluecloth and then run:
137
+
138
+ rake doc
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'bundler'
2
+
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ task :spec do
6
+ ENV['COVERAGE'] = "1"
7
+ Dir.chdir( File.dirname(__FILE__) ) do
8
+ Dir["specs/**/*_spec.rb"].each do |path|
9
+ load(path)
10
+ end
11
+ end
12
+ end
13
+
14
+ begin
15
+ require 'yard'
16
+ require 'bluecloth'
17
+ YARD::Rake::YardocTask.new(:doc)
18
+ rescue
19
+
20
+ end
data/drone.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "drone/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "drone"
7
+ s.version = Drone::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Julien Ammous"]
10
+ s.email = []
11
+ s.homepage = ""
12
+ s.summary = %q{Drone is a monitoring library}
13
+ s.description = %q{Drone is a monitoring library based on the metrics java library}
14
+
15
+ s.rubyforge_project = "drone"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency("eventmachine", "~> 0.12.10")
23
+
24
+ s.add_development_dependency("mocha")
25
+ s.add_development_dependency("bacon")
26
+ s.add_development_dependency("schmurfy-em-spec")
27
+ s.add_development_dependency("delorean")
28
+ s.add_development_dependency("simplecov")
29
+ end
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
5
+ require 'drone'
6
+
7
+ Drone::init_drone()
8
+ Drone::register_gauge("cpu:0/user"){ rand(200) }
9
+
10
+ class User
11
+ include Drone::Monitoring
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+
17
+ monitor_rate("users:rename")
18
+ def rename(new_name)
19
+ @name = new_name
20
+ end
21
+
22
+ monitor_time("users:do_something")
23
+ def do_something
24
+ # just eat some cpu
25
+ 0.upto(rand(2000)) do |n|
26
+ str = "a"
27
+ 200.times{ str << "b" }
28
+ end
29
+ end
30
+ end
31
+
32
+ Drone::add_output(:console, 1)
33
+
34
+ EM::run do
35
+ Drone::start_monitoring()
36
+
37
+ counter1 = Drone::register_counter("something_counted")
38
+ counter1.increment()
39
+
40
+ a = User.new("bob")
41
+
42
+ EM::add_periodic_timer(2) do
43
+ rand(100).times{|n| a.rename("user#{n}") }
44
+ counter1.increment()
45
+ end
46
+
47
+ EM::add_periodic_timer(1) do
48
+ a.do_something()
49
+ end
50
+ end
data/lib/drone.rb ADDED
@@ -0,0 +1,23 @@
1
+
2
+ module Drone
3
+ def self.require_lib(path)
4
+ require File.expand_path("../#{path}", __FILE__)
5
+ end
6
+
7
+ require_lib("drone/version")
8
+
9
+ require_lib("drone/monitoring")
10
+
11
+ # Schedulers
12
+ require_lib("drone/schedulers/eventmachine")
13
+
14
+ # Metrics
15
+ require_lib("drone/metrics/counter")
16
+ require_lib("drone/metrics/gauge")
17
+ require_lib("drone/metrics/histogram")
18
+ require_lib("drone/metrics/meter")
19
+ require_lib("drone/metrics/timer")
20
+
21
+ # Output
22
+ require_lib("drone/interfaces/console")
23
+ end
data/lib/drone/core.rb ADDED
@@ -0,0 +1,125 @@
1
+
2
+ require File.expand_path('../schedulers/eventmachine', __FILE__)
3
+
4
+ module Drone
5
+ ##
6
+ # This module contains all the metrics you can use to collect data
7
+ #
8
+ module Metrics; end
9
+
10
+
11
+ ##
12
+ # This module contains all the interfaces to the outside world,
13
+ # they are the only way to communicate with external applications
14
+ #
15
+ module Interface; end
16
+
17
+
18
+ ##
19
+ # This module contains the class used for scheduling timers
20
+ #
21
+ module Schedulers; end
22
+
23
+ class <<self
24
+
25
+ def init_drone(scheduler = Schedulers::EMScheduler)
26
+ @meters = []
27
+ @scheduler = scheduler
28
+ @monitored_classes = []
29
+ @output_modules = []
30
+ end
31
+
32
+ ##
33
+ # Start monitoring.
34
+ # This method needs to be called when the timers can be started
35
+ # In the case of eventmachine scheduler it needs to be called
36
+ # in the EM::run block
37
+ #
38
+ def start_monitoring
39
+ @scheduler.start()
40
+ end
41
+
42
+ def each_metric
43
+ raise "Block expected" unless block_given?
44
+ @meters.each{|m| yield(m) }
45
+ end
46
+
47
+
48
+ ##
49
+ # Fetch a metric by its name
50
+ #
51
+ # @param [String] name The mtric's name
52
+ #
53
+ def find_metric(name)
54
+ @meters.detect{|m| m.name == name }
55
+ end
56
+
57
+ ##
58
+ # Instantiate an output module.
59
+ #
60
+ # @param [String,Symbol] type Class name in lowercase
61
+ # @param [Array] args additional parameters will be sent to thh
62
+ # class constructor
63
+ #
64
+ def add_output(type, *args)
65
+ class_name = type.to_s.capitalize
66
+ klass = Drone::Interfaces.const_get(class_name)
67
+ @output_modules << klass.new(*args)
68
+ end
69
+
70
+ ##
71
+ # Register a monitored class.
72
+ # @private
73
+ #
74
+ def register_monitored_class(klass)
75
+ @monitored_classes << klass
76
+ end
77
+
78
+ ##
79
+ # Register a new counter
80
+ # @param [String] type Name of this metric
81
+ # @api public
82
+ #
83
+ def register_counter(type)
84
+ register_meter( Drone::Metrics::Counter.new(type) )
85
+ end
86
+
87
+ ##
88
+ # Register a new gauge
89
+ # @param [String] type Name of this metric
90
+ # @api public
91
+ #
92
+ def register_gauge(type, &block)
93
+ register_meter( Drone::Metrics::Gauge.new(type, &block) )
94
+ end
95
+
96
+ ##
97
+ # Register a new meter
98
+ # This method can be used bu the user but the prefered
99
+ # way is to use the register_counter / register_gauge methods
100
+ #
101
+ # @param [Meter] meter The Meter to register
102
+ # @api private
103
+ # @private
104
+ #
105
+ def register_meter(meter)
106
+ @meters << meter
107
+ meter
108
+ end
109
+
110
+ ##
111
+ # (see EMScheduler#schedule_periodic)
112
+ #
113
+ def schedule_periodic(*args, &block)
114
+ @scheduler.schedule_periodic(*args, &block)
115
+ end
116
+
117
+ ##
118
+ # (see EMScheduler#schedule_once)
119
+ #
120
+ def schedule_once(*args, &block)
121
+ @scheduler.schedule_once(*args, &block)
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,17 @@
1
+ module Drone
2
+ module Interfaces
3
+
4
+ class Base
5
+ def initialize(period)
6
+ @period = period
7
+ Drone::schedule_periodic(period){ output() }
8
+ end
9
+
10
+ def output
11
+ raise "Uninmplemented"
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end