protor 0.0.3

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: 537a3afb0cc18e89d25b042061ca0ddebb5b5db6
4
+ data.tar.gz: 4470654d6e40c9554e88c4b6f652d96ea3fb11ed
5
+ SHA512:
6
+ metadata.gz: 8c745d4dfe654901355e910ef80411451a04029c33383e92a8c066be490f1fa20591ac221504ff18463a7aec663c47c92218a372be5299aece418312dbc9f5c8
7
+ data.tar.gz: ba8dd56a6c69e65af5da3f84352ef3de50baff2b1f51c881d9ffc65e559819b0dbada0384da225e3034a50a600735a7cea65c349e223d25f740b8d538b367795
@@ -0,0 +1,53 @@
1
+ # protor-ruby
2
+ [Prometheus aggregator](https://github.com/rolandhawk/prometheus-aggregator) client for ruby
3
+
4
+ ## Installation
5
+ In Gemfile
6
+ ````ruby
7
+ gem 'protor'
8
+ ````
9
+ Then run
10
+ ````shell
11
+ bundle install
12
+ ````
13
+
14
+ ## Usage
15
+ ### Counter
16
+ It automatically aggregate value
17
+
18
+ ````ruby
19
+ Protor.counter(:counter, 1, {label1: 1}) # value => 1
20
+ Protor.counter(:counter, 1, {label1: 1}) # value => 2
21
+ ````
22
+ ### Gauge
23
+ It automatically replace value
24
+ ````ruby
25
+ Protor.gauge(:gauge, 50) # value 50
26
+ Protor.gauge(:gauge, 20) # value 20
27
+ ````
28
+
29
+ ### Histogram
30
+ It save all observed values
31
+ ````ruby
32
+ Protor.histogram(:histogram, 10, {label1: 1}, [1,2,3,4]) # observed value [10]
33
+ Protor.histogram(:histogram, 2, {label1: 1}, [1,2,3,4]( # observed value [10,2]
34
+ ````
35
+
36
+ ### Publish
37
+ To publish all saved metrics to aggregator
38
+ ````ruby
39
+ Protor.publish
40
+ ````
41
+
42
+ ## Configuration
43
+ To configure protor:
44
+ ````ruby
45
+ Protor.configure do |config|
46
+ config.host = 'localhost' # aggregator host, default to localhost
47
+ config.port = 10601 # aggregator port, default to 10601
48
+ config.max_packet_size = 56607 # max udp packet buffer, default to 56607
49
+ end
50
+ ````
51
+
52
+ ## Contributing
53
+ [Fork the project](https://github.com/rolandhawk/protor-ruby) and send pull requests.
@@ -0,0 +1,49 @@
1
+ require 'monitor'
2
+
3
+ require_relative 'protor/configuration'
4
+ require_relative 'protor/client'
5
+ require_relative 'protor/registry'
6
+ require_relative 'protor/version'
7
+
8
+ module Protor
9
+ extend MonitorMixin
10
+ end
11
+
12
+ class << Protor
13
+ def configuration
14
+ @configuration ||= Protor::Configuration.new
15
+ end
16
+
17
+ def configure
18
+ yield(configuration)
19
+ end
20
+
21
+ def publish
22
+ synchronize do
23
+ unless @registry.empty?
24
+ client.publish(registry)
25
+ reset
26
+ end
27
+ end
28
+ end
29
+
30
+ [:counter, :gauge, :histogram].each do |method|
31
+ define_method method do |*args, &block|
32
+ synchronize{ registry.public_send(method, *args, &block) }
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def client
39
+ @client ||= Protor::Client.new(configuration)
40
+ end
41
+
42
+ def registry
43
+ @registry || reset
44
+ end
45
+
46
+ def reset
47
+ @registry = Protor::Registry.new(configuration)
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ module Protor
2
+ class Accumulator
3
+ def initialize(type)
4
+ @data = {}
5
+ @type = type
6
+ end
7
+
8
+ def inc(metric_name, value, labels = {})
9
+ data[metric_name] ||= Hash.new(0)
10
+ data[metric_name][labels] += value
11
+ end
12
+
13
+ def set(metric_name, value, labels = {})
14
+ data[metric_name] ||= Hash.new(0)
15
+ data[metric_name][labels] = value
16
+ end
17
+
18
+ def to_a
19
+ array = []
20
+
21
+ data.each do |metric_name, values|
22
+ values.each do |labels, value|
23
+ array << { metric_name: metric_name, labels: labels, value: value, type: type }
24
+ end
25
+ end
26
+
27
+ return array
28
+ end
29
+
30
+ def empty?
31
+ data.empty?
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :type, :data
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'socket'
2
+
3
+ module Protor
4
+ class Client
5
+ def initialize(configuration)
6
+ @options = configuration
7
+ end
8
+
9
+ def publish(payload)
10
+ established do |connection|
11
+ payload.each{ |str| connection.send(str, 0) }
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :options
18
+
19
+ def established
20
+ socket = UDPSocket.new
21
+ socket.connect(options.host, options.port)
22
+ yield socket
23
+ rescue Errno::ECONNREFUSED => exception
24
+ # omit error, it caused by aggregate server being down
25
+ exception
26
+ ensure
27
+ socket.close unless socket.closed?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module Protor
2
+ class Configuration
3
+ attr_writer :host, :port, :max_packet_size
4
+
5
+ def host
6
+ @host ||= 'localhost'
7
+ end
8
+
9
+ def port
10
+ @port ||= 10601
11
+ end
12
+
13
+ def max_packet_size
14
+ @max_packet_size ||= 56_607
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ module Protor
2
+ class Observer
3
+ def initialize(type)
4
+ @data = {}
5
+ @type = type
6
+ end
7
+
8
+ def observe(metric_name, value, labels = {}, additional = [])
9
+ data[metric_name] ||= { values: {} }
10
+ data[metric_name][:values][labels] ||= []
11
+
12
+ data[metric_name][:additional] = additional
13
+ data[metric_name][:values][labels] << value
14
+ end
15
+
16
+ def to_a
17
+ array = []
18
+
19
+ data.each do |metric_name, values|
20
+ first = true
21
+ values[:values].each do |labels, value|
22
+ value.each do |val|
23
+ array << {
24
+ metric_name: metric_name,
25
+ labels: labels,
26
+ value: val,
27
+ additional: values[:additional],
28
+ first: first,
29
+ type: type
30
+ }
31
+ first = false
32
+ end
33
+ end
34
+ end
35
+
36
+ return array
37
+ end
38
+
39
+ def empty?
40
+ data.empty?
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :data, :type
46
+ end
47
+ end
@@ -0,0 +1,100 @@
1
+ module Protor
2
+ class Payload
3
+
4
+ Lf = "\n".freeze
5
+
6
+
7
+ def initialize(max_packet_size)
8
+ @max_packet_size = max_packet_size
9
+ @lines = []
10
+ @total_size = 0
11
+ end
12
+
13
+ def add(data)
14
+ data = data.dup
15
+ data[:first] = true if lines.empty?
16
+
17
+ return false if packet_overflow?(data)
18
+
19
+ self.common_labels = data[:labels]
20
+ @total_size += data_size(data)
21
+ @lines << data
22
+ end
23
+
24
+ def to_s
25
+ str = first_line
26
+ lines.each do |data|
27
+ line = template(data)
28
+ str << "#{line}#{Lf}"
29
+ end
30
+
31
+ return str
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :common_labels, :lines, :total_size, :max_packet_size
37
+
38
+ def data_size(data)
39
+ size = 3
40
+ [:metric_name, :value].each do |key|
41
+ size += data[key].to_s.size
42
+ end
43
+
44
+ size += labels_string(data[:labels]).size + 1 if data[:labels]
45
+
46
+ size += additional_string(data[:additional]).size + 1 if data[:first] && data[:additional]
47
+
48
+ return size
49
+ end
50
+
51
+ def common_labels=(labels = {})
52
+ @common_labels ||= labels.dup
53
+ intersect(common_labels, labels)
54
+ @common_labels.keep_if{ |k, v| labels.key? k }
55
+ end
56
+
57
+ def first_line
58
+ if !common_labels || common_labels.empty?
59
+ ""
60
+ else
61
+ "#{labels_string(common_labels)}#{Lf}"
62
+ end
63
+ end
64
+
65
+ def template(data)
66
+ labels = exclude_common(data[:labels])
67
+
68
+ "#{ data[:metric_name] }|#{ data[:type][0] }|".tap do |str|
69
+ str << "#{ additional_string(data[:additional]) }|" if data[:first] && data[:additional]
70
+ str << "#{ labels_string(labels) }|" unless labels.empty?
71
+ str << "#{ data[:value] }"
72
+ end
73
+ end
74
+
75
+ def labels_string(labels)
76
+ labels.map{ |k, v| "#{k}=#{v}" }.join(';')
77
+ end
78
+
79
+ def additional_string(additional)
80
+ additional.join(';')
81
+ end
82
+
83
+ def intersect(a, b)
84
+ a.keep_if{ |k, v| b.key? k }
85
+ end
86
+
87
+ def exclude_common(labels)
88
+ labels.keep_if{ |k, v| !common_labels[k] }
89
+ end
90
+
91
+ def packet_overflow?(data)
92
+ backup = common_labels && common_labels.dup || data[:labels]
93
+ intersect(backup, data[:labels])
94
+ size = data_size(data)
95
+
96
+ total_size + size - (labels_string(backup).size * lines.size) > max_packet_size
97
+ end
98
+ end
99
+ end
100
+
@@ -0,0 +1,63 @@
1
+ require_relative 'payload'
2
+ require_relative 'accumulator'
3
+ require_relative 'observer'
4
+
5
+ module Protor
6
+ class Registry
7
+ HistogramDefaultBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10].freeze
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
13
+ def counter(metric_name, value, labels = {})
14
+ counter_data.inc(metric_name, value, labels)
15
+ end
16
+
17
+ def gauge(metric_name, value, labels = {})
18
+ gauge_data.set(metric_name, value, labels)
19
+ end
20
+
21
+ def histogram(metric_name, value, labels = {}, buckets = HistogramDefaultBuckets)
22
+ histogram_data.observe(metric_name, value, labels, buckets)
23
+ end
24
+
25
+ def each
26
+ payload = Payload.new options.max_packet_size
27
+ all_data.each do |data|
28
+ unless payload.add(data)
29
+ yield payload.to_s
30
+
31
+ payload = Payload.new options.max_packet_size
32
+ payload.add(data)
33
+ end
34
+ end
35
+
36
+ yield payload.to_s
37
+ end
38
+
39
+ def empty?
40
+ [@counter, @gauge, @histogram].all?{ |metrics| metrics && metrics.empty? }
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :options
46
+
47
+ def counter_data
48
+ @counter ||= Accumulator.new(:counter)
49
+ end
50
+
51
+ def gauge_data
52
+ @gauge ||= Accumulator.new(:gauge)
53
+ end
54
+
55
+ def histogram_data
56
+ @histogram ||= Observer.new(:histogram)
57
+ end
58
+
59
+ def all_data
60
+ [counter_data.to_a, gauge_data.to_a, histogram_data.to_a].flatten
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module Protor
2
+ VERSION = '0.0.3'
3
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: protor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Roland Rinfandi Utama
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - roland_hawk@yahoo.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - lib/protor.rb
22
+ - lib/protor/accumulator.rb
23
+ - lib/protor/client.rb
24
+ - lib/protor/configuration.rb
25
+ - lib/protor/observer.rb
26
+ - lib/protor/payload.rb
27
+ - lib/protor/registry.rb
28
+ - lib/protor/version.rb
29
+ homepage: https://github.com/rolandhawk/protor-ruby
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 2.5.1
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: Prometheus aggregator client for ruby.
53
+ test_files: []