met_on_the_middle 0.1.6

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.
@@ -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: