drone 0.0.1

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