meter 0.1.7 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4fd53f330e039e19bf426396f41ef53246f47aff
4
- data.tar.gz: 077c2be0e237a1f06af9aafd846d536027c6eaf8
3
+ metadata.gz: c7251d39b8598a821768f8b5194d091a4cf567da
4
+ data.tar.gz: 2a901ecbdbbdd2ba0f1e663b09b526dd2d485e8c
5
5
  SHA512:
6
- metadata.gz: 8d31866e279376f65636be45c9cfbc5056907e559bd5eaaa3cc7106d11e37a8e44b630881286456d7edd04dced7aa4e13fa1642cf5131f012f27761050e21712
7
- data.tar.gz: 01fdf3b7af762fece7e79eacf40bdc66914daaa4363d0936f7eea85259e0c6bc0ca2f146479719bb18a32fecd89e7fede96afb745a7ebf13c33bbf5a8bcf55ae
6
+ metadata.gz: 6f681d9e84b87ed9ae215a54149a4a7ffe8e376b9452f67641cb8c46ddbcde141761e886dde90821fd3bbd7fd23c51caded9b12455dec197230e5c6f74a5ed55
7
+ data.tar.gz: 193dddf526ebbe275b2278bc1f69f8af1b4805e05845b11b2c21d519713f29f0e14d792fa9355f3fd32025221ff97e62ac01f4d740f35eaac401b660d938ccc8
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2013 Bukowskis
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Meter
2
+
3
+ A generic abstraction layer for tracking metrics and other data via configurable backends.
4
+
5
+ # Installation
6
+
7
+ ```bash
8
+ gem install meter
9
+ ````
10
+
11
+ # Usage
12
+
13
+ #### Configuration
14
+
15
+ ```ruby
16
+ Meter.configure do |config|
17
+ config.namespace = 'my_app'
18
+ config.backends << Meter::Backends::Datadog.new
19
+ config.backends << Meter::Backends::JsonLog.new
20
+ end
21
+
22
+ #### Syntax
23
+
24
+ ```ruby
25
+ Meter.increment key, sample_rate: 0.5, tags: {}, data: {}
26
+ ````
27
+
28
+ #### Examples
29
+
30
+ ```ruby
31
+ Meter.increment 'my.key'
32
+ Meter.increment 'my.key', 5
33
+ Meter.increment 'my.key', 5, sample_rate: 0.25
34
+
35
+ Meter.gauge 'my.gauge.key', 20
36
+ ```
data/lib/meter.rb CHANGED
@@ -1,36 +1,58 @@
1
- require 'meter/backends'
1
+ require 'meter/backends/base'
2
+ require 'meter/backends/udp'
3
+ require 'meter/backends/statsd'
4
+ require 'meter/backends/datadog'
5
+ require 'meter/backends/json_log'
2
6
  require 'meter/configure'
7
+ require 'meter/mdc'
8
+ require 'meter/metric/base'
9
+ require 'meter/metric/counter'
10
+ require 'meter/metric/gauge'
11
+ require 'meter/metric/histogram'
12
+ require 'meter/metric/timing'
13
+ require 'meter/rails/middleware'
14
+ require "meter/rails/railtie" if defined?(Rails::Railtie)
3
15
 
4
16
  module Meter
5
17
 
6
- def self.increment(key, options = {})
7
- id = options.delete(:id)
8
- backends.datadog.increment key, options
9
- backends.counter.increment("#{::Meter.config.namespace}.#{key}.#{id}", options) if id
10
-
11
- rescue => exception
12
- ::Meter.config.logger.error exception.inspect
13
- end
14
-
15
- def self.gauge(key, value, options = {})
16
- backends.datadog.gauge key, value, options
17
-
18
- rescue => exception
19
- ::Meter.config.logger.error exception.inspect
18
+ class << self
19
+ def increment(key, value: 1, sample_rate: 1, tags: {}, data: {})
20
+ metric = Metric::Counter.new(name: key, value: value, sample_rate: sample_rate, tags: tags, data: data)
21
+ send_metric_to_backends(metric)
22
+ rescue => exception
23
+ ::Meter.config.logger.error exception.inspect
24
+ end
25
+ alias :track :increment
26
+
27
+ def gauge(key, value, sample_rate: 1, tags: {}, data: {})
28
+ metric = Metric::Gauge.new(name: key, value: value, sample_rate: sample_rate, tags: tags, data: data)
29
+ send_metric_to_backends(metric)
30
+ rescue => exception
31
+ ::Meter.config.logger.error exception.inspect
32
+ end
33
+
34
+ def histogram(key, value, sample_rate: 1, tags: {}, data: {})
35
+ metric = Metric::Histogram.new(name: key, value: value, sample_rate: sample_rate, tags: tags, data: data)
36
+ send_metric_to_backends(metric)
37
+ rescue => exception
38
+ ::Meter.config.logger.error exception.inspect
39
+ end
40
+
41
+ def timing(key, value, sample_rate: 1, tags: {}, data: {})
42
+ metric = Metric::Timing.new(name: key, value: value, sample_rate: sample_rate, tags: tags, data: data)
43
+ send_metric_to_backends(metric)
44
+ rescue => exception
45
+ ::Meter.config.logger.error exception.inspect
46
+ end
47
+
48
+ def send_metric_to_backends(metric)
49
+ return unless should_sample? metric.sample_rate
50
+ config.backends.each {|backend| backend.emit_metric(metric)}
51
+ metric
52
+ end
53
+
54
+ def should_sample?(sample_rate = 1)
55
+ rand < sample_rate
56
+ end
20
57
  end
21
-
22
- def self.histogram(key, value, options = {})
23
- backends.datadog.histogram key, value, options
24
-
25
- rescue => exception
26
- ::Meter.config.logger.error exception.inspect
27
- end
28
-
29
- def self.log(key, data)
30
- backends.heka.log key, data
31
-
32
- rescue => exception
33
- ::Meter.config.logger.error exception.inspect
34
- end
35
-
36
58
  end
@@ -0,0 +1,27 @@
1
+ module Meter
2
+ module Backends
3
+ class Base
4
+
5
+ def self.supported_metrics
6
+ []
7
+ end
8
+
9
+ def supported_metric?(metric)
10
+ self.class.supported_metrics.include? metric.type
11
+ end
12
+
13
+ def emit_metric(metric)
14
+ return unless supported_metric? metric
15
+ metric_data = convert_to_backend_format(metric)
16
+ output_data(metric_data)
17
+ end
18
+
19
+ def convert_to_backend_format(metric)
20
+ end
21
+
22
+ def output_data(data)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'socket'
2
+
3
+ module Meter
4
+ module Backends
5
+ class Datadog < ::Meter::Backends::Statsd
6
+
7
+ def self.supported_metrics
8
+ [:counter, :gauge, :timing, :histogram]
9
+ end
10
+
11
+ def convert_to_backend_format(metric)
12
+ rate = "|@#{metric.sample_rate}" unless metric.sample_rate == 1
13
+ tags = "|##{convert_tags_to_datadog_format(metric.tags)}" unless metric.tags.empty?
14
+ "#{metric.name}:#{metric.value}|#{statsd_type(metric)}#{rate}#{tags}"
15
+ end
16
+
17
+ def convert_tags_to_datadog_format(tags = {})
18
+ tags.map{|key,val| "#{key}:#{val}"}.join(',')
19
+ end
20
+
21
+ def statsd_type(metric)
22
+ types = {counter: ':c', gauge: ':g', timing: ':ms', histogram: ':h'}
23
+ types[metric.type]
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ require 'json'
2
+
3
+ module Meter
4
+ module Backends
5
+ class JsonLog < ::Meter::Backends::Base
6
+
7
+ def self.supported_metrics
8
+ [:counter, :gauge, :timing, :histogram]
9
+ end
10
+
11
+ def convert_to_backend_format(metric)
12
+ {statname: metric.name, metric_type: metric.type, metric_value: metric.value}.merge(metric.data).merge(metric.tags)
13
+ end
14
+
15
+ def output_data(data)
16
+ log_file.open('a') { |f| f.puts(JSON.dump data) }
17
+ end
18
+
19
+ private
20
+
21
+ def log_file
22
+ ::Meter.config.log_dir.join('application.json.log')
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'socket'
2
+
3
+ module Meter
4
+ module Backends
5
+ class Statsd < ::Meter::Backends::Udp
6
+
7
+ def self.supported_metrics
8
+ [:counter, :gauge, :timing]
9
+ end
10
+
11
+ def convert_to_backend_format(metric)
12
+ rate = "|@#{metric.sample_rate}" unless metric.sample_rate == 1
13
+ "#{metric.name}:#{metric.value}|#{statsd_type(metric)}#{rate}"
14
+ end
15
+
16
+ def statsd_type(metric)
17
+ types = {counter: ':c', gauge: ':g', timing: ':ms'}
18
+ types[metric.type]
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require 'json'
2
+ require 'socket'
3
+
4
+ module Meter
5
+ module Backends
6
+ class Udp < ::Meter::Backends::Base
7
+
8
+ attr_reader :host, :port
9
+
10
+ def initialize(host: '127.0.0.1', port: 8125)
11
+ @host, @port = host, port
12
+ end
13
+
14
+ def output_data(data)
15
+ socket.send data, 0, self.host, self.port
16
+ end
17
+
18
+ def socket
19
+ Thread.current["meter_socket_#{self.class.name}"] ||= UDPSocket.new
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -1,6 +1,5 @@
1
1
  require 'logger'
2
2
  require 'pathname'
3
- require 'meter/backend'
4
3
 
5
4
  module Meter
6
5
  class Configuration
@@ -39,6 +38,11 @@ module Meter
39
38
  end
40
39
  attr_writer :hostname
41
40
 
41
+ def backends
42
+ @backends ||= []
43
+ end
44
+ attr_writer :backends
45
+
42
46
  private
43
47
 
44
48
  def default_logger
data/lib/meter/mdc.rb ADDED
@@ -0,0 +1,18 @@
1
+ # stolen from https://github.com/steveklabnik/request_store
2
+ module Meter
3
+ module MDC
4
+ def self.tags
5
+ Thread.current[:meter_mdc_tags] ||= {}
6
+ end
7
+
8
+ def self.data
9
+ Thread.current[:meter_mdc_data] ||= {}
10
+ end
11
+
12
+ def self.clear!
13
+ Thread.current[:meter_mdc_tags] = {}
14
+ Thread.current[:meter_mdc_data] = {}
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ module Meter
2
+ module Metric
3
+ class Base
4
+
5
+ attr_reader :name
6
+ attr_reader :tags
7
+ attr_reader :data
8
+ attr_accessor :value
9
+ attr_accessor :sample_rate
10
+
11
+ def type
12
+ end
13
+
14
+ def initialize(name:, value: default_value, sample_rate: 1, tags: {}, data: {})
15
+ self.name = name
16
+ self.value = value
17
+ self.sample_rate = sample_rate
18
+ self.tags = tags
19
+ self.data = data
20
+ end
21
+
22
+ def name=(new_name)
23
+ # Replace Ruby module scoping with '.' and reserved chars (: | @) with underscores.
24
+ @name = new_name.to_s.gsub('::', '.').tr(':|@', '_')
25
+ end
26
+
27
+ def tags=(new_tags)
28
+ @tags = default_tags.merge(new_tags)
29
+ end
30
+
31
+ def data=(new_data)
32
+ @data = default_data.merge(new_data)
33
+ end
34
+
35
+ private
36
+
37
+ def default_value
38
+ end
39
+
40
+ def default_tags
41
+ {
42
+ app: ::Meter.config.namespace,
43
+ environment: ::Meter.config.environment
44
+ }.merge(::Meter::MDC.tags)
45
+ end
46
+
47
+ def default_data
48
+ {
49
+ timestamp: Time.now.strftime("%FT%H:%M:%S%:z"),
50
+ host: ::Meter.config.hostname,
51
+ }.merge(::Meter::MDC.data)
52
+ end
53
+
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ module Meter
2
+ module Metric
3
+ class Counter < Meter::Metric::Base
4
+
5
+ def type
6
+ :counter
7
+ end
8
+
9
+ private
10
+
11
+ def default_value
12
+ 1
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Meter
2
+ module Metric
3
+ class Gauge < Meter::Metric::Base
4
+
5
+ def type
6
+ :gauge
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Meter
2
+ module Metric
3
+ class Histogram < Meter::Metric::Base
4
+
5
+ def type
6
+ :histogram
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module Meter
2
+ module Metric
3
+ class Timing < Meter::Metric::Base
4
+
5
+ def type
6
+ :timing
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Meter
2
+ module Rails
3
+ class Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ @app.call(env)
10
+ ensure
11
+ Meter::MDC.clear!
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Meter
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie
4
+ initializer "meter.insert_middleware" do |app|
5
+ if ActionDispatch.const_defined? :RequestId
6
+ app.config.middleware.insert_after ActionDispatch::RequestId, Meter::Rails::Middleware
7
+ else
8
+ app.config.middleware.insert_after Rack::MethodOverride, Meter::Rails::Middleware
9
+ end
10
+
11
+ if ActionDispatch.const_defined?(:Reloader) && ActionDispatch::Reloader.respond_to?(:to_cleanup)
12
+ ActionDispatch::Reloader.to_cleanup do
13
+ Meter::MDC.clear!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/meter/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Meter
2
2
  module VERSION #:nodoc:
3
- MAJOR = 0
4
- MINOR = 1
5
- TINY = 7
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bukowskis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-04 00:00:00.000000000 Z
11
+ date: 2015-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -73,11 +73,24 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
+ - LICENSE
77
+ - README.md
76
78
  - lib/meter.rb
77
- - lib/meter/backend.rb
78
- - lib/meter/backends.rb
79
+ - lib/meter/backends/base.rb
80
+ - lib/meter/backends/datadog.rb
81
+ - lib/meter/backends/json_log.rb
82
+ - lib/meter/backends/statsd.rb
83
+ - lib/meter/backends/udp.rb
79
84
  - lib/meter/configuration.rb
80
85
  - lib/meter/configure.rb
86
+ - lib/meter/mdc.rb
87
+ - lib/meter/metric/base.rb
88
+ - lib/meter/metric/counter.rb
89
+ - lib/meter/metric/gauge.rb
90
+ - lib/meter/metric/histogram.rb
91
+ - lib/meter/metric/timing.rb
92
+ - lib/meter/rails/middleware.rb
93
+ - lib/meter/rails/railtie.rb
81
94
  - lib/meter/version.rb
82
95
  homepage: https://github.com/bukowskis/meter
83
96
  licenses:
data/lib/meter/backend.rb DELETED
@@ -1,57 +0,0 @@
1
- require 'json'
2
- require 'socket'
3
-
4
- module Meter
5
- class Backend
6
-
7
- attr_reader :host, :port
8
-
9
- def initialize(host = '127.0.0.1', port = 8125)
10
- @host, @port = host, port
11
-
12
- @socket = UDPSocket.new
13
- end
14
-
15
- def increment(stat, options = {})
16
- send_stats stat, 1, :c, options
17
- end
18
-
19
- def gauge(stat, value, options = {})
20
- send_stats stat, value, :g, options
21
- end
22
-
23
- def histogram(stat, value, options = {})
24
- send_stats stat, value, :h, options
25
- end
26
-
27
- def log(stat, data = {})
28
- data = { environment: ::Meter.config.environment, Timestamp: Time.now.strftime("%FT%H:%M:%S%:z") }.merge data
29
- data.merge! app: ::Meter.config.namespace, host: ::Meter.config.hostname, statname: stat
30
- ::Meter.config.logger.debug { "Logging #{log_file} - #{data}"}
31
- log_file.open('a') { |f| f.puts(JSON.dump data) }
32
- end
33
-
34
- private
35
-
36
- def log_file
37
- ::Meter.config.log_dir.join('application.json.log')
38
- end
39
-
40
- # See https://github.com/DataDog/dogstatsd-ruby/blob/master/lib/statsd.rb
41
- def send_stats(stat, delta, type, options = {})
42
- sample_rate = options[:sample_rate] || 1
43
- return unless sample_rate == 1 or rand < sample_rate
44
- stat = stat.to_s.gsub('::', '.').tr(':|@', '_')
45
- rate = "|@#{sample_rate}" unless sample_rate == 1
46
- app_tag = ["app:#{::Meter.config.namespace}"]
47
- tags = "|##{(Array(options[:tags]) + app_tag).join(",")}"
48
- send_to_socket "#{stat}:#{delta}|#{type}#{rate}#{tags}"
49
- end
50
-
51
- def send_to_socket(message)
52
- ::Meter.config.logger.debug { "UDP #{self.host}:#{self.port} - #{message}" }
53
- @socket.send message, 0, self.host, self.port
54
- end
55
-
56
- end
57
- end
@@ -1,16 +0,0 @@
1
- require 'meter/backend'
2
- require 'ostruct'
3
-
4
- module Meter
5
-
6
- # Holds the currently known backends.
7
- #
8
- def self.backends
9
- @backends ||= ::OpenStruct.new(
10
- datadog: ::Meter::Backend.new('127.0.0.1', 8125),
11
- counter: ::Meter::Backend.new('127.0.0.1', 3333),
12
- heka: ::Meter::Backend.new('127.0.0.1', 8128),
13
- )
14
- end
15
-
16
- end