prometheus_reporter 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df4a4042a47ad2b5b0308c03754b85e747a40c21
4
+ data.tar.gz: '088e89c9d7735eebfa3ffd6a0b41017fcc292937'
5
+ SHA512:
6
+ metadata.gz: 4e4a88947ef124cfacd3639dd72747075a612eeb68d33e03d34d754ab4bc44ffa2663ed3a057e775bf10a2cf6ba63b98fa37d553f50467284d4d073532478390
7
+ data.tar.gz: 1de01a6cb0e01149796bb821ab149cf58b7861b7420abd023ac4959520b494cb2ffbc7a7e2c0a530899b24825da8b6c2a212f2d02978a079feb634daba217a07
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ coverage/
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --order rand
3
+ --format doc
4
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+
4
+ Style/NumericLiterals:
5
+ Enabled: false
6
+
7
+ Metrics/BlockLength:
8
+ ExcludedMethods:
9
+ - 'describe'
10
+ - 'context'
11
+ - 'let'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ dist: trusty
3
+
4
+ rvm:
5
+ - 2.4.3
6
+ - 2.5.0
7
+ - ruby-head
8
+
9
+ script:
10
+ - bundle exec rspec
11
+ - bundle exec rubocop
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 1.0.0 (2018-01-11)
2
+
3
+ The very first version released.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Oleksii Kuznietsov (@nattfodd)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ [![Gem Version](https://badge.fury.io/rb/prometheus_reporter.svg)](https://badge.fury.io/rb/prometheus_reporter)
2
+ [![Build Status](https://travis-ci.org/nattfodd/prometheus_reporter.svg?branch=master)](https://travis-ci.org/nattfodd/prometheus_reporter)
3
+ [![Dependency Status](https://gemnasium.com/nattfodd/prometheus_reporter.svg)](https://gemnasium.com/nattfodd/prometheus_reporter)
4
+ [![Maintainability Status](https://img.shields.io/codeclimate/maintainability/nattfodd/prometheus_reporter.svg)](https://codeclimate.com/github/nattfodd/prometheus_reporter)
5
+
6
+ ## Prometheus Reporter
7
+ `prometheus_reporter` helps you to generate [Prometheus](https://prometheus.io) metric reports using its [text format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-format-details). Official client helps you to monitor internal web-server guts, while `prometheus_reporter` can help you representing any data you want.
8
+
9
+ ### Overview
10
+ ```ruby
11
+ require 'prometheus_reporter'
12
+
13
+ f = PrometheusReporter::TextFormatter.new
14
+ f.entry(:my_metric, value: 144, labels: { source: 'api', module: 'chat' })
15
+ f.to_s
16
+ # => 'my_metric{souce="api",module="chat"} 144'
17
+ ```
18
+
19
+ ## Installation
20
+ Installation is pretty standard:
21
+ ```bash
22
+ $ gem install prometheus_reporter
23
+ ```
24
+ or using `Gemfile`:
25
+ ```ruby
26
+ gem 'prometheus_reporter', '~> 1.0'
27
+ ```
28
+ ## Configuration
29
+
30
+ You may want to have metric keys application prefix to distinguish those metrics
31
+ from metric keys reported by other applications.
32
+
33
+ By default, `prefix` isn't used.
34
+
35
+ ```ruby
36
+ PrometheusReporter.configure do |config|
37
+ config.prefix = 'my_web_app'
38
+ end
39
+
40
+ f = PrometheusReporter::TextFormatter.new
41
+ f.entry(:emails_count, value: 567)
42
+ f.to_s # => "my_web_app_emails_count 567\n"
43
+ ```
44
+
45
+ You can overwrite it passing another prefix to a new `TextFormatter` instance:
46
+
47
+ ```ruby
48
+ f = PrometheusReporter::TextFormatter.new(prefix: 'facebook_clone')
49
+ f.entry(:emails_count, value: 987)
50
+ f.to_s # => "facebook_clone_emails_count 987\n"
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### Creating text report for Prometheus:
56
+
57
+ ```ruby
58
+ require 'prometheus_reporter'
59
+
60
+ f = PrometheusReporter::TextFormatter.new
61
+ f.help(:emails_today, 'Events created from the beginning of the day')
62
+ f.type(:emails_today, :counter)
63
+ f.entry(:emails_today,
64
+ value: 10,
65
+ labels: { type: 'notify_for_inactivity' },
66
+ timestamp: Time.now.to_i)
67
+ f.entry(:emails_today,
68
+ value: 18,
69
+ labels: { type: 'registration_confirmation' },
70
+ timestamp: Time.now.to_i)
71
+ f.to_s
72
+ ```
73
+
74
+ Produces the following output:
75
+
76
+ ```
77
+ # HELP facebook_clone_emails_today Amount of emails sent from the beginning of the day
78
+ # TYPE facebook_clone_emails_today counter
79
+ facebook_clone_emails_today{type="notify_for_inactivity"} 10 1515681885
80
+ facebook_clone_emails_today{type="registration_confirmation"} 18 1515681886
81
+ ```
82
+
83
+ ## Usefull links
84
+ - Official Prometheus [ruby client](https://github.com/prometheus/client_ruby)
85
+ - [Prometheus](https://github.com/prometheus) itself
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusReporter
4
+ module Configuration
5
+ CONFIG_KEYS = %i[prefix].freeze
6
+
7
+ def config
8
+ @config ||= OpenStruct.new
9
+ end
10
+
11
+ def configure
12
+ yield(config)
13
+ config.to_h.each_key do |key|
14
+ raise(UnknownConfig, key) unless CONFIG_KEYS.include?(key)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusReporter
4
+ class BaseError < StandardError; end
5
+
6
+ class UnknownConfig < BaseError
7
+ def initialize(key)
8
+ @key = key
9
+ end
10
+
11
+ def message
12
+ "Unknown configuration option: #{@key}"
13
+ end
14
+ end
15
+
16
+ class MissingMetricValue < BaseError
17
+ def intialize(key)
18
+ @key = key
19
+ end
20
+
21
+ def message
22
+ "Missing value for #{@key} metric"
23
+ end
24
+ end
25
+
26
+ class UnknownMetricType < BaseError
27
+ def initialize(key_type)
28
+ @type = key_type
29
+ end
30
+
31
+ def message
32
+ "Unknown key type: #{@type}. "\
33
+ "Valid ones: #{TextFormatter::TYPES.join(', ')}"
34
+ end
35
+ end
36
+
37
+ class TypeAlreadySpecified < BaseError
38
+ def initialize(key)
39
+ @key = key
40
+ end
41
+
42
+ def message
43
+ "Can't overwrite type for the #{@key} metric (type is already specified)"
44
+ end
45
+ end
46
+
47
+ class HelpAlreadySpecified < BaseError
48
+ def initialize(key)
49
+ @key = key
50
+ end
51
+
52
+ def message
53
+ "Can't overwrite help for the #{@key} metric (help is already specified)"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusReporter
4
+ class TextFormatter
5
+ TYPES = %i[counter gauge histogram summary untyped].freeze
6
+ LABELESS = :labeless
7
+ HELP_TOKEN = :help
8
+ TYPE_TOKEN = :type
9
+ DEFAULTS = { HELP_TOKEN => nil, TYPE_TOKEN => nil }.freeze
10
+ TOKENS = [HELP_TOKEN, TYPE_TOKEN].freeze
11
+ SEPARATOR = "\n"
12
+
13
+ def initialize(prefix: PrometheusReporter.config.prefix)
14
+ @data = {}
15
+ @prefix = prefix
16
+ end
17
+
18
+ def help(key, description)
19
+ set_token_value!(HELP_TOKEN, key, description)
20
+ end
21
+
22
+ def type(key, type)
23
+ type = type.downcase.to_sym
24
+ raise(UnknownMetricType, type) unless TYPES.include?(type)
25
+
26
+ set_token_value!(TYPE_TOKEN, key, type)
27
+ end
28
+
29
+ def entry(key, value:, labels: {}, timestamp: nil)
30
+ @data[prefixed(key)] ||= DEFAULTS.dup
31
+
32
+ label_key = labels.any? ? labels : LABELESS
33
+ entry_data =
34
+ {
35
+ value: value,
36
+ timestamp: timestamp
37
+ }.compact
38
+
39
+ @data[prefixed(key)][label_key] = entry_data
40
+ end
41
+
42
+ def to_s
43
+ metrics_data = []
44
+
45
+ @data.each do |prometheus_key_name, prometheus_key_values|
46
+ prometheus_key_values = prometheus_key_values.compact
47
+ validate_data!(prometheus_key_name, prometheus_key_values)
48
+ metrics_data << metric_entry(prometheus_key_name, prometheus_key_values)
49
+ end
50
+
51
+ metrics_data.join(SEPARATOR)
52
+ end
53
+
54
+ private
55
+
56
+ def set_token_value!(token, key, value)
57
+ prefixed_key = prefixed(key)
58
+ @data[prefixed_key] ||= DEFAULTS.dup
59
+
60
+ if @data[prefixed_key][token]
61
+ err_class = "PrometheusReporter::#{token.capitalize}AlreadySpecified"
62
+ raise(Object.const_get(err_class), key)
63
+ end
64
+
65
+ @data[prefixed_key][token] = value
66
+ end
67
+
68
+ def prefixed(key)
69
+ @prefix ? "#{@prefix}_#{key}" : key
70
+ end
71
+
72
+ def validate_data!(key_name, key_data)
73
+ return if TOKENS.include?(key_name)
74
+ return if key_data.any? { |k, _| k.is_a?(Hash) || k == LABELESS }
75
+
76
+ raise(MissingMetricValue, key_name)
77
+ end
78
+
79
+ def metric_entry(prometheus_key_name, prometheus_key_values)
80
+ metric_lines = []
81
+ prometheus_key_values.each do |label_or_token, value|
82
+ metric_lines << text_entry(prometheus_key_name, label_or_token, value)
83
+ end
84
+ (metric_lines << nil).join(SEPARATOR)
85
+ end
86
+
87
+ def text_entry(prometheus_key_name, label_or_token, value)
88
+ case label_or_token
89
+ when HELP_TOKEN
90
+ "# HELP #{prometheus_key_name} #{value}"
91
+ when TYPE_TOKEN
92
+ "# TYPE #{prometheus_key_name} #{value}"
93
+ else
94
+ entry_to_string(prometheus_key_name, label_or_token, value)
95
+ end
96
+ end
97
+
98
+ def entry_to_string(prometheus_key_name, label_or_token, value_hash)
99
+ entry = "#{prometheus_key_name}#{labels_to_str(label_or_token)} "\
100
+ "#{value_hash[:value]}"
101
+ entry += " #{value_hash[:timestamp]}" if value_hash[:timestamp]
102
+ entry
103
+ end
104
+
105
+ def labels_to_str(label_or_token)
106
+ return if label_or_token == LABELESS || label_or_token.empty?
107
+
108
+ labels_str =
109
+ label_or_token.map do |l_name, l_value|
110
+ "#{l_name}=\"#{l_value}\""
111
+ end.join(',')
112
+
113
+ "{#{labels_str}}"
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusReporter
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ require_relative 'prometheus_reporter/errors'
6
+ require_relative 'prometheus_reporter/configuration'
7
+
8
+ module PrometheusReporter
9
+ extend Configuration
10
+ end
11
+
12
+ require_relative 'prometheus_reporter/version'
13
+ require_relative 'prometheus_reporter/text_formatter'
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/prometheus_reporter/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'prometheus_reporter'
7
+ s.version = PrometheusReporter::VERSION
8
+ s.summary = 'Generates & parses prometheus text reporting format'
9
+ s.authors = ['Oleksii Kuznietsov (nattfodd)']
10
+ s.email = ['nattfodd.pp.ua@gmail.com']
11
+ s.homepage = 'https://github.com/nattfodd/prometheus_reporter'
12
+ s.licenses = ['MIT']
13
+ s.files = `git ls-files -z`.split("\x0")
14
+ .reject do |f|
15
+ f.match(%r{^(test|spec|features)/})
16
+ end
17
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ s.required_ruby_version = '~> 2.4'
19
+
20
+ s.require_paths = ['lib']
21
+
22
+ s.add_development_dependency 'pry', '~> 0.11'
23
+ s.add_development_dependency 'rspec', '~> 3.7'
24
+ s.add_development_dependency 'rubocop', '~> 0.52'
25
+ s.add_development_dependency 'simplecov', '~> 0.15'
26
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prometheus_reporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Oleksii Kuznietsov (nattfodd)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.52'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.52'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.15'
69
+ description:
70
+ email:
71
+ - nattfodd.pp.ua@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".rubocop.yml"
79
+ - ".ruby-version"
80
+ - ".travis.yml"
81
+ - CHANGELOG.md
82
+ - Gemfile
83
+ - LICENCE
84
+ - README.md
85
+ - lib/prometheus_reporter.rb
86
+ - lib/prometheus_reporter/configuration.rb
87
+ - lib/prometheus_reporter/errors.rb
88
+ - lib/prometheus_reporter/text_formatter.rb
89
+ - lib/prometheus_reporter/version.rb
90
+ - prometheus_reporter.gemspec
91
+ homepage: https://github.com/nattfodd/prometheus_reporter
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.4'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.6.13
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Generates & parses prometheus text reporting format
115
+ test_files: []