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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +52 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +13 -0
- data/lib/met_on_the_middle.rb +42 -0
- data/lib/met_on_the_middle/collector.rb +35 -0
- data/lib/met_on_the_middle/configuration.rb +69 -0
- data/lib/met_on_the_middle/generators/install_generator.rb +10 -0
- data/lib/met_on_the_middle/generators/templates/initializer.rb +41 -0
- data/lib/met_on_the_middle/railtie.rb +45 -0
- data/lib/met_on_the_middle/readers/base.rb +56 -0
- data/lib/met_on_the_middle/readers/database_time.rb +18 -0
- data/lib/met_on_the_middle/readers/request_count.rb +17 -0
- data/lib/met_on_the_middle/readers/total_time.rb +17 -0
- data/lib/met_on_the_middle/readers/view_time.rb +22 -0
- data/lib/met_on_the_middle/request.rb +24 -0
- data/lib/met_on_the_middle/root_middleware.rb +64 -0
- data/lib/met_on_the_middle/senders/base.rb +16 -0
- data/lib/met_on_the_middle/senders/file_system.rb +21 -0
- data/lib/met_on_the_middle/senders/zabbix.rb +45 -0
- data/lib/met_on_the_middle/tracker.rb +118 -0
- data/lib/met_on_the_middle/version.rb +3 -0
- data/lib/met_on_the_middle/worker.rb +94 -0
- data/met_on_the_middle.gemspec +38 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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__)
|
data/bin/setup
ADDED
@@ -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 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,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:
|