met_on_the_middle 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e57a849172420e708be4f89acaf2722b6e41aa6f
4
+ data.tar.gz: b90079be8aae22ba88cfe564e66a8a2466eeeb3b
5
+ SHA512:
6
+ metadata.gz: 129067d4d81f42928fa08028a7f2bf5331291bb0bf64365c8d7587e42326421b32bc66f870f2641d19c41b9d0e4579c3d364b9207d7bb19c4f2b229699b637b1
7
+ data.tar.gz: f4564a9a5308193f305efce34e341ed62a6d10de580b1814f109bcded89c814d89f4abe6f50ef447c7dbbbdfbf0e21e42114ada35d13ed08cdb40f447d53c19d
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.14.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in met_on_the_middle.gemspec
4
+ gemspec
@@ -0,0 +1,52 @@
1
+ # MetOnTheMiddle
2
+
3
+ Gemma per l'estrapolazione di dati da inviare poi a vari
4
+ sistemi di analisi
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'met_on_the_middle', source: 'http://XXXXXX:XXXXXX@gem.archimedianet.it'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Installare l'inizializzatore con le varie opzioni dell'applicativo
19
+
20
+ $ bundle exec rails g met_on_the_middle:install
21
+
22
+
23
+
24
+ ## Usage
25
+
26
+ Per implementare nuove metriche:
27
+
28
+ creare una nuova classe derivata da
29
+
30
+ MetOnTheMiddle::Readers::Base
31
+
32
+ come quelle già presenti, leggere direttamente la documentazione
33
+ su tale classe
34
+
35
+ Nel caso non esistesse un ActiveSupport::Notifications che fa al caso vostro
36
+ registratelo come definito nella
37
+ [documentazione](http://guides.rubyonrails.org/active_support_instrumentation.html)
38
+ e registrate quindi la vostra classe per quell'evento
39
+
40
+ Ricordardarsi di aggiungere la classe corretta nell'array dei readers nella
41
+ configurazione dell'inizializer
42
+
43
+
44
+ Aggiungere quindi su zabbix all'host interessato il Template Rails preparato.
45
+ Ho estraolato anche il template e posizionato in doc/zbx_export_templates.xml
46
+
47
+ ## Development
48
+
49
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
50
+
51
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
52
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "met_on_the_middle"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
9
+
10
+ cd spec/dummy
11
+ bundle install
12
+ bundle exec rake db:migrate
13
+ bundle exec rake db:seed
@@ -0,0 +1,42 @@
1
+ module MetOnTheMiddle
2
+
3
+ end
4
+
5
+ require 'met_on_the_middle/readers/base'
6
+ require 'met_on_the_middle/readers/database_time'
7
+ require 'met_on_the_middle/readers/request_count'
8
+ require 'met_on_the_middle/readers/total_time'
9
+ require 'met_on_the_middle/readers/view_time'
10
+
11
+ require 'met_on_the_middle/senders/base'
12
+ require 'met_on_the_middle/senders/file_system'
13
+ require 'met_on_the_middle/senders/zabbix'
14
+
15
+ require 'met_on_the_middle/version'
16
+ require 'met_on_the_middle/root_middleware'
17
+ require 'met_on_the_middle/configuration'
18
+ require 'met_on_the_middle/request'
19
+ require 'met_on_the_middle/collector'
20
+ require 'met_on_the_middle/tracker'
21
+ require 'met_on_the_middle/worker'
22
+
23
+
24
+ module MetOnTheMiddle
25
+ # Your code goes here...
26
+
27
+ class << self
28
+ attr_writer :configuration
29
+ end
30
+
31
+ def self.configuration
32
+ @configuration ||= Configuration.new
33
+ end
34
+
35
+ def self.configure
36
+ yield(configuration)
37
+ end
38
+
39
+ end
40
+
41
+
42
+ require 'met_on_the_middle/railtie' if defined?(Rails)
@@ -0,0 +1,35 @@
1
+ module MetOnTheMiddle
2
+ class Collector
3
+
4
+ MAX_QUEUE_SIZE_AUTOCLEAR = 100
5
+ attr_accessor :sender, :data
6
+
7
+ delegate :clear, :size, :empty?, to: :data
8
+
9
+ def initialize(sender)
10
+ self.sender = sender
11
+ @data = Hash.new
12
+ end
13
+
14
+ def add(request)
15
+ @data[request.time]=request
16
+ end
17
+
18
+
19
+ def submit
20
+ Marshal.load(Marshal.dump(@data)).each do |k, v|
21
+ self.sender.write(v)
22
+ end
23
+ self.clear
24
+ rescue Exception => e
25
+ if self.size>MAX_QUEUE_SIZE_AUTOCLEAR
26
+
27
+ pre_clear = self.keys[self.size/2]
28
+
29
+ self.reject! {|k, v| k<pre_clear}
30
+
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,69 @@
1
+ module MetOnTheMiddle
2
+ class Configuration
3
+
4
+ ##
5
+ # Regular expression per selezionare l'event matcher
6
+ attr_accessor :notifications_event_matcher
7
+
8
+ # type of timer to use, valid options are
9
+ # :sleep (default), :eventmachine, or :synchrony
10
+ attr_accessor :event_mode
11
+
12
+ ##
13
+ # Tempo in secondi ogni quanto eseguire un flush dei dati
14
+ attr_accessor :flush_interval
15
+
16
+ ##
17
+ # Enviroments dove installare il middleware
18
+ # default a sola produzione
19
+ attr_accessor :mounting_enviroments
20
+
21
+ ##
22
+ # Definisce la classe da utilizzare per spedire/registrazione le informazioni
23
+ # Default a MetOnTheMiddle::Senders::FileSystem configurato per scrivere
24
+ # in un file dentro alla cartella log dell'applicativo Rails
25
+ attr_accessor :sender
26
+
27
+ attr_accessor :logger
28
+
29
+
30
+ attr_reader :tracker
31
+
32
+ def initialize
33
+ @notifications_event_matcher = /.*/
34
+ @event_mode= :sleep
35
+ @tracker = nil
36
+ @logger = Rails.logger
37
+ @sender = Senders::FileSystem.new(Rails.root.join('log/met_on_the_middle.log'))
38
+ @flush_interval = 60
39
+ @mounting_enviroments = [:production]
40
+ self.readers = [:RequestCount, :TotalTime, :DatabaseTime, :ViewTime]
41
+ end
42
+
43
+
44
+ def dump
45
+
46
+ {
47
+ notifications_event_matcher: :notifications_event_matcher,
48
+ event_mode: :event_mode
49
+ }
50
+
51
+ end
52
+
53
+
54
+ def readers=(array)
55
+ @_readers = array.collect {|c|
56
+ if c.is_a?(Symbol)
57
+ Readers.const_get(c)
58
+ else
59
+ c
60
+ end
61
+ }
62
+ end
63
+
64
+ def readers
65
+ @_readers
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,10 @@
1
+ module MetOnTheMiddle::Generators
2
+ class InstallGenerator < Rails::Generators::Base
3
+
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def create_initializer_file
7
+ copy_file "initializer.rb", "config/initializers/met_on_the_middle.rb"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ MetOnTheMiddle.configure do |cfg|
2
+
3
+ #cfg.notifications_event_matcher = /.*/
4
+
5
+ # type of timer to use, valid options are
6
+ # :sleep (default), :eventmachine, or :synchrony
7
+ # cfg.event_mode = :sleep
8
+
9
+ # Default Logger
10
+ #cfg.logger = Rails.logger
11
+
12
+ # Tempo in secondi ogni quanto eseguire un flush dei dati
13
+ # cfg.flush_interval = 60
14
+
15
+ # Elenco Classi da Utilizzare per estrapolare le informazioni
16
+ # Può essere anche una classe oltre ad un symbol
17
+ #
18
+ #cfg.readers = [:RequestCount, :TotalTime, :DatabaseTime, :ViewTime]
19
+
20
+ ##
21
+ # Definisce la classe da utilizzare per spedire/registrazione le informazioni
22
+ # Default a MetOnTheMiddle::Senders::FileSystem configurato per scrivere
23
+ # in un file dentro alla cartella log dell'applicativo Rails
24
+ #cfg.sender = MetOnTheMiddle::Senders::FileSystem.new(Rails.root.join('log/met_on_the_middle.log'))
25
+
26
+ ##
27
+ # Configurazione per Zabbix
28
+ #
29
+ # cfg.sender = MetOnTheMiddle::Senders::Zabbix.new(
30
+ # server_host: '127.0.0.1',
31
+ # port: 10051,
32
+ # identify_host:'nome.mio.host'
33
+ # )
34
+
35
+ ##
36
+ # Configurazione per definire in quali enviroments montare il middleware
37
+ # cfg.mounting_enviroments = [:production]
38
+
39
+
40
+
41
+ end
@@ -0,0 +1,45 @@
1
+ module MetOnTheMiddle
2
+ class Railtie < Rails::Railtie
3
+
4
+
5
+ generators do
6
+ require File.expand_path('../../../lib/met_on_the_middle/generators/install_generator', __FILE__)
7
+ end
8
+
9
+ initializer "met_on_the_middle.append_root_middleware" do |app|
10
+ if MetOnTheMiddle.configuration.mounting_enviroments.include?(Rails.env.to_sym)
11
+ app.middleware.insert_before(Rack::Sendfile, 'MetOnTheMiddle::RootMiddleware')
12
+ else
13
+ Rails.logger.info {
14
+ "MetOnTheMiddle::RootMiddleware non installato
15
+ per configurazione dell'enviroment: #{Rails.env.to_sym.inspect}->
16
+ #{MetOnTheMiddle.configuration.mounting_enviroments.inspect}"
17
+ }
18
+ end
19
+ end
20
+
21
+
22
+ =begin
23
+ initializer "met_on_the_middle.define_notification_subscription" do
24
+
25
+ variabile_mia = '123123'
26
+
27
+ ActiveSupport::Notifications.subscribe MetOnTheMiddle.configuration.notifications_event_matcher do |*args|
28
+ event = ActiveSupport::Notifications::Event.new *args
29
+
30
+ raise variabile_mia.inspect
31
+
32
+ event.name # => "process_action.action_controller"
33
+ event.duration # => 10 (in milliseconds)
34
+ event.payload # => {:extra=>information}
35
+
36
+ # Rails.logger.info "#{event.inspect} Received!"
37
+ # Rails.logger.info "----------"
38
+ end
39
+
40
+ end
41
+ =end
42
+
43
+
44
+ end
45
+ end
@@ -0,0 +1,56 @@
1
+ module MetOnTheMiddle::Readers
2
+ ##
3
+ # Classe base del reader, la quale deve rispondere con la chiave da
4
+ # utilizzare per dare un label al dato inviato (chiave per zabbix)
5
+ #
6
+ class Base
7
+ include Singleton
8
+ extend SingleForwardable
9
+
10
+ def_delegators :instance, :parse, :to_key_value, :register_subscription
11
+
12
+ attr_accessor :key
13
+
14
+ def initialize(key)
15
+ @key = key
16
+ end
17
+
18
+ ##
19
+ # Regular expression to match notifications
20
+ # Serve per attivare la subscription a determinati eventi di
21
+ # ActiveSupport::Notifications
22
+ def match_subscription
23
+ ''
24
+ end
25
+
26
+ #
27
+ # metodo da utilizzare per parsare i dati
28
+ #
29
+ # @author Bonetti Marino
30
+ #
31
+ #
32
+ # @param [ActiveSupport::Notifications::Event] event proveniente dalla subscription
33
+ # @option opts [MetOnTheMiddle::Request] :request oggetto che raggruppa la richiesta, utile se si
34
+ # devono sommare dati
35
+ #
36
+ # @return il valore da salvare ed inviare nei provider di registrazione
37
+ def parse(event, request: nil)
38
+ event.name
39
+ end
40
+
41
+ def to_key_value(event, request: nil)
42
+ return @key, parse(event, request: request)
43
+ end
44
+
45
+
46
+ def register_subscription(tracker)
47
+ ActiveSupport::Notifications.subscribe match_subscription do |*args|
48
+ if not tracker.nil? and not tracker.actual_request.nil?
49
+ event = ActiveSupport::Notifications::Event.new *args
50
+ tracker.add *self.to_key_value(event, request: tracker.actual_request)
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ module MetOnTheMiddle::Readers
2
+ class DatabaseTime < Base
3
+
4
+ def initialize
5
+ super :database_time
6
+ end
7
+
8
+ def match_subscription
9
+ 'sql.active_record'
10
+ end
11
+
12
+ def parse(e, request: nil)
13
+ return e.duration if request.nil?
14
+ (e.duration+(request.get(key)||0)).round(2)
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module MetOnTheMiddle::Readers
2
+ class RequestCount < Base
3
+
4
+ def initialize
5
+ super(:request_count)
6
+ end
7
+
8
+ def match_subscription
9
+ 'met_on_the_middle.total_time'
10
+ end
11
+
12
+ def parse(event, request: nil)
13
+ 1
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module MetOnTheMiddle::Readers
2
+ class TotalTime < Base
3
+
4
+ def initialize
5
+ super(:total_time)
6
+ end
7
+
8
+ def match_subscription
9
+ 'met_on_the_middle.total_time'
10
+ end
11
+
12
+ def parse(event,request:nil)
13
+ event.duration.round(2)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module MetOnTheMiddle::Readers
2
+ class ViewTime < Base
3
+
4
+ def initialize
5
+ super :view_time
6
+ end
7
+
8
+ def match_subscription
9
+ /render_(partial|template).action_view/
10
+ end
11
+
12
+ def parse(e, request: nil)
13
+ path = e.payload[:identifier].split('/views/', 2)
14
+
15
+ duration = 0
16
+ duration = e.duration if path[1]
17
+
18
+ (duration+(request.get(key)||0)).round(2)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module MetOnTheMiddle
2
+ ##
3
+ # Classe che si occupa di raggruppare le informazioni per richiesta,
4
+ # se la chiave è già presente, il valore viene sommato
5
+ class Request
6
+
7
+ attr_accessor :time
8
+ attr_reader :data
9
+
10
+ def initialize(time=Time.new)
11
+ @time = time
12
+ @data = {}
13
+ end
14
+
15
+ def add(key, value)
16
+ @data[key]=value
17
+ end
18
+
19
+ def get(key)
20
+ @data[key]
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ module MetOnTheMiddle
2
+ class RootMiddleware
3
+
4
+
5
+ attr_reader :tracker
6
+
7
+ def initialize(app)
8
+ @app = app
9
+
10
+ generate_tracker
11
+
12
+ if defined?(PhusionPassenger)
13
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
14
+ if forked
15
+ generate_tracker
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ def generate_tracker
23
+ @tracker = Tracker.new(MetOnTheMiddle.configuration)
24
+ @tracker.start!
25
+
26
+ ##
27
+ # TODO trovare sistema migliore per individuare se siamo lanciati tramite
28
+ # rake oppure tramite server
29
+ unless File.basename($0) == 'rake'
30
+ register_subscriptions
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Serve per essere multy trade safe
36
+ def call(env)
37
+ dup._call(env)
38
+ end
39
+
40
+ def _call(env)
41
+ response = nil
42
+
43
+ @tracker.request_block do
44
+
45
+ ActiveSupport::Notifications.instrument "met_on_the_middle.total_time" do
46
+ response = @app.call(env)
47
+ end
48
+
49
+ end
50
+
51
+
52
+ response
53
+ end
54
+
55
+
56
+ def register_subscriptions
57
+ MetOnTheMiddle.configuration.readers.each do |r|
58
+ r.register_subscription(@tracker)
59
+ end
60
+ end
61
+
62
+
63
+ end
64
+ end
@@ -0,0 +1,16 @@
1
+ module MetOnTheMiddle::Senders
2
+ ##
3
+ # Classe base per la registrazione delle informazioni
4
+ class Base
5
+
6
+ # Metodo per scrivere la richiesta
7
+ #
8
+ # @author Bonetti Marino
9
+ #
10
+ # @param [Request] reuqest
11
+ def write(request)
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ module MetOnTheMiddle::Senders
2
+ class FileSystem < Base
3
+
4
+ attr_accessor :logger
5
+
6
+ def initialize(path)
7
+ self.logger = ::Logger.new(path, 2)
8
+ end
9
+
10
+
11
+ def write(request)
12
+ self.logger.info request.time.to_s do
13
+ keys = []
14
+ request.data.each do |k, v|
15
+ keys<<"#{k}:#{v}"
16
+ end
17
+ keys.join("|")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,45 @@
1
+ require 'socket'
2
+ require 'zabbix_protocol'
3
+
4
+ SERVER_PORT = 10051
5
+
6
+
7
+ module MetOnTheMiddle::Senders
8
+ class Zabbix < Base
9
+
10
+
11
+ attr_accessor :server_host, :port, :identify_host
12
+
13
+ ##
14
+ # Inizializzatore, configurazioni:
15
+ #
16
+ # server_host: host del server
17
+ # port: porta del server
18
+ # identify_host: nome dell'host da cui partono i dati
19
+ def initialize(cfgs)
20
+ self.server_host= cfgs[:server_host]
21
+ self.port= cfgs[:port]
22
+ self.identify_host= cfgs[:identify_host]
23
+ end
24
+
25
+ def write(request)
26
+ TCPSocket.open(server_host, port) do |sock|
27
+ datas = []
28
+ request.data.each do |k, v|
29
+ datas<<{"key":k,"value":v}
30
+ end
31
+
32
+ data = {
33
+ "request" => "sender data",
34
+ "data" =>datas.collect{|d| d.merge("host" => identify_host)}
35
+ }
36
+
37
+ sock.print ZabbixProtocol.dump(data)
38
+ #p ZabbixProtocol.load(sock.read)
39
+ #=> {"response"=>"success", "info"=>"Processed 0 Failed 1 Total 1 Seconds spent 0.000018"}
40
+ end
41
+ end
42
+
43
+
44
+ end
45
+ end
@@ -0,0 +1,118 @@
1
+ module MetOnTheMiddle
2
+ class Tracker
3
+ extend Forwardable
4
+
5
+ def_delegators :actual_request, :add
6
+
7
+ attr_reader :config
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ # check to see if we should start a worker for this process.
14
+ # if you are using this externally, use #start! instead as this
15
+ # method may change
16
+ def check_worker
17
+ return if @worker # already running
18
+ return unless start_worker?
19
+ logger.debug {"config: #{config.dump}"}
20
+ @pid = $$
21
+ logger.debug {">> starting up worker for pid #{@pid}..."}
22
+
23
+ @worker = Worker.new(timer: config.event_mode)
24
+ @worker.run_periodically(config.flush_interval) do
25
+ flush
26
+ end
27
+
28
+ end
29
+
30
+ # primary collector object used by this tracker
31
+ def collector
32
+ @collector ||= MetOnTheMiddle::Collector.new(@config.sender)
33
+ end
34
+
35
+
36
+ # send all current data to Metrics
37
+ def flush
38
+ logger.debug {"flushing pid #{@pid} (#{Time.now}).."}
39
+ start = Time.now
40
+ # thread safety is handled internally for stores
41
+ # queue = build_flush_queue(collector)
42
+ # queue.submit unless queue.empty?
43
+ unless collector.empty?
44
+ logger.debug {"logica di invio dati - #{collector.inspect}"}
45
+
46
+ collector.submit
47
+
48
+ logger.debug { "#### #{collector.size} - GarbageCollector - #{GC.stat.inspect}" }
49
+
50
+ end
51
+
52
+ logger.info {"flushed pid #{@pid} in #{(Time.now - start)*1000.to_f}ms"}
53
+ rescue Exception => error
54
+ logger.error {"submission failed permanently: #{error}"}
55
+ end
56
+
57
+ # given current state, should the tracker start a reporter thread?
58
+ def should_start?
59
+ true
60
+ end
61
+
62
+ # start worker thread, one per process.
63
+ # if this process has been forked from an one with an active
64
+ # worker thread we don't need to worry about cleanup, the worker
65
+ # thread will not pass with the fork
66
+ def start!
67
+ check_worker if should_start?
68
+ end
69
+
70
+ def actual_request
71
+ @_actual_request
72
+ end
73
+
74
+ ##
75
+ # Si occupa di ragruppare per richiesta
76
+ def request_block
77
+ @_actual_request = Request.new
78
+ yield
79
+ collector.add @_actual_request
80
+ end
81
+
82
+ private
83
+
84
+ # access to client instance
85
+ def client
86
+ @client ||= prepare_client
87
+ end
88
+
89
+ # use custom faraday adapter if running in evented context
90
+ def custom_adapter
91
+ case config.event_mode
92
+ when :eventmachine
93
+ :em_http
94
+ when :synchrony
95
+ :em_synchrony
96
+ else
97
+ nil
98
+ end
99
+ end
100
+
101
+ def logger
102
+ Rails.logger
103
+ end
104
+
105
+ # should we spin up a worker? wrap this in a process check
106
+ # so we only actually check once per process. this allows us
107
+ # to check again if the process forks.
108
+ def start_worker?
109
+ if @pid_checked == $$
110
+ false
111
+ else
112
+ @pid_checked = $$
113
+ should_start?
114
+ end
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,3 @@
1
+ module MetOnTheMiddle
2
+ VERSION = '0.1.6'
3
+ end
@@ -0,0 +1,94 @@
1
+ module MetOnTheMiddle
2
+ class Worker
3
+ attr_reader :timer
4
+
5
+ # available options:
6
+ # * timer - type of timer to use, valid options are
7
+ # :sleep (default), :eventmachine, or :synchrony
8
+ # * sync - try to synchronize timer executions to whole
9
+ # minutes or subdivisions thereof
10
+ def initialize(options={})
11
+ @interrupt = false
12
+ @timer = (options[:timer] || :sleep).to_sym
13
+ @sync = options[:sync] || false
14
+ end
15
+
16
+ # run the given block every <period> seconds, looping
17
+ # infinitely unless @interrupt becomes true.
18
+ #
19
+ def run_periodically(period, &block)
20
+ @proc = block # store
21
+
22
+ if [:eventmachine, :synchrony].include?(timer)
23
+ compensated_repeat(period) # threading is already handled
24
+ else
25
+ @thread = Thread.new {compensated_repeat(period)}
26
+ end
27
+ end
28
+
29
+ # Give some structure to worker start times so when possible
30
+ # they will be in sync.
31
+ #
32
+ def start_time(period)
33
+ if @sync
34
+ earliest = Time.now + period
35
+ # already on a whole minute
36
+ return earliest if earliest.sec == 0
37
+ if period > 30
38
+ # bump to whole minute
39
+ earliest + (60-earliest.sec)
40
+ else
41
+ # ensure sync to whole minute if minute is evenly divisible
42
+ earliest + (period-(earliest.sec%period))
43
+ end
44
+ else
45
+ if period > 30
46
+ # ensure some wobble in start times,
47
+ # trade a slightly irregular first period for a more even
48
+ # distribution for network requests between processes
49
+ start = Time.now
50
+ start + (60-start.sec) + rand(60)
51
+ else
52
+ Time.now + period
53
+ end
54
+ end
55
+ end
56
+
57
+ # stop worker loop at the beginning of the next round
58
+ # of execution
59
+ def stop!
60
+ @interrupt = true
61
+ end
62
+
63
+ private
64
+
65
+ # run continuous loop executing every <period>, will start
66
+ # at <first_run> if set otherwise will auto-determine
67
+ # appropriate time for first run
68
+ def compensated_repeat(period, first_run = nil)
69
+ next_run = first_run || start_time(period)
70
+ until @interrupt do
71
+ now = Time.now
72
+ if now >= next_run
73
+ @proc.call
74
+
75
+ while next_run <= now
76
+ next_run += period # schedule future run
77
+ end
78
+ end
79
+
80
+ interval = next_run - now
81
+ case timer
82
+ when :eventmachine
83
+ EM.add_timer(interval) {compensated_repeat(period, next_run)}
84
+ break
85
+ when :synchrony
86
+ EM::Synchrony.sleep(interval)
87
+ else
88
+ sleep(next_run - now)
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'met_on_the_middle/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "met_on_the_middle"
8
+ spec.version = MetOnTheMiddle::VERSION
9
+ spec.authors = ["MarinoBonetti"]
10
+ spec.email = ["marinobonetti@gmail.com"]
11
+
12
+ spec.summary = 'Metric on the middle'
13
+ spec.description = 'Little app to send configurable datas to various destinations'
14
+ spec.homepage = "http://www.archimedianet.it"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features|doc)/})
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f)}
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "zabbix_protocol", "~> 0.1.5"
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.14"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ spec.add_development_dependency "curb", "~> 0.9.3"
38
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: met_on_the_middle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - MarinoBonetti
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zabbix_protocol
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: curb
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.3
83
+ description: Little app to send configurable datas to various destinations
84
+ email:
85
+ - marinobonetti@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - lib/met_on_the_middle.rb
99
+ - lib/met_on_the_middle/collector.rb
100
+ - lib/met_on_the_middle/configuration.rb
101
+ - lib/met_on_the_middle/generators/install_generator.rb
102
+ - lib/met_on_the_middle/generators/templates/initializer.rb
103
+ - lib/met_on_the_middle/railtie.rb
104
+ - lib/met_on_the_middle/readers/base.rb
105
+ - lib/met_on_the_middle/readers/database_time.rb
106
+ - lib/met_on_the_middle/readers/request_count.rb
107
+ - lib/met_on_the_middle/readers/total_time.rb
108
+ - lib/met_on_the_middle/readers/view_time.rb
109
+ - lib/met_on_the_middle/request.rb
110
+ - lib/met_on_the_middle/root_middleware.rb
111
+ - lib/met_on_the_middle/senders/base.rb
112
+ - lib/met_on_the_middle/senders/file_system.rb
113
+ - lib/met_on_the_middle/senders/zabbix.rb
114
+ - lib/met_on_the_middle/tracker.rb
115
+ - lib/met_on_the_middle/version.rb
116
+ - lib/met_on_the_middle/worker.rb
117
+ - met_on_the_middle.gemspec
118
+ homepage: http://www.archimedianet.it
119
+ licenses: []
120
+ metadata:
121
+ allowed_push_host: https://rubygems.org
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.6.7
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Metric on the middle
142
+ test_files: []
143
+ has_rdoc: