statue19 0.2.7

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: 68ddfa3744c63889bf94581ac7fb3d3faaf76ada
4
+ data.tar.gz: 8b3313832255195944ca2e4fd6bc4249a6c4b21b
5
+ SHA512:
6
+ metadata.gz: 2685204941aed389de449b449eb19acbba4f267edfd7f5e2c3870164ee850a318afd331da2adba2307bcadb2b80ecc9ff25b696daac7ac44c0d3dce4ab8952cb
7
+ data.tar.gz: ab37ee18ac4173ba6f774724d8ad292983123440626499602df0d7e10adf9609110d700eef9214024d8a16dd06dca65c93e55686b7a3556c3010487c809ece8c
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ /.bundle/
2
+ /Gemfile.lock
3
+ /tmp/
4
+ *.bundle
5
+ pkg/
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## [0.2.0] - 2015-11-17
2
+ ### Added
3
+ - Add support for multithreaded applications
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in statue.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Juan Barreneche
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Statue
2
+
3
+ / ___)(_ _)/ _\(_ _)/ )( \( __)
4
+ \___ \ )( / \ )( ) \/ ( ) _)
5
+ (____/ (__)\_/\_/(__) \____/(____)
6
+ Rock solid metrics report...
7
+
8
+ Easily track application metrics into [Statsite](https://github.com/armon/statsite) (Statsd compatible).
9
+
10
+ ## Configuration
11
+
12
+ The library has different backends, one to be used for production environment (ie. actually
13
+ sending metrics using the Statsd protocol), and the others for testing or developing.
14
+
15
+ The available backends are:
16
+
17
+ `Statue::UDPBackend` -> this is the one that actually sends metrics to the Statsd.
18
+
19
+ eg.
20
+ ```ruby
21
+ Statue.backend = Statue::UDPBackend.new(statsd_host, statsd_port)
22
+ ```
23
+
24
+ `Statue::NullBackend`, this backend discards all metrics (useful for test environment, if you
25
+ aren't interested in testing which metrics are sent).
26
+
27
+ `Statue::CaptureBackend`, this backend collects all metrics (useful for test environment, if you
28
+ arent interested in testing which metrics are sent). You can check the metrics with `Statue.backend.captures`
29
+ and reset this array with `Statue.backend.captures.clear` or by setting a new instance before each test.
30
+
31
+ `Statue::LoggerBackend`, this backend logs the received metrics to a logger (useful for development purposes)
32
+
33
+ eg.
34
+ ```ruby
35
+ Statue.backend = Statue::LoggerBackend.new(Rails.logger)
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### Common meassurments
41
+
42
+ `Statue.report_increment('metric.name')` -> send to Statsd an increment to the counter `metric.name`
43
+
44
+ `Statue.report_gauge('metric.name', value)` -> send to Statsd the gauge value for `metric.name`
45
+
46
+ `Statue.report_duration('metric.name') { some_operation } # => some_operation_result` -> send to Statsd the
47
+ measure for the block duration in `metric.name`
48
+
49
+ `Statue.report_success_or_failure('metric.name') { some_operation } # => some_operation_result` -> checks the
50
+ result of the block, if its a `truthy` value, then increments `metric.name.success`, else it increments
51
+ `metric.name.failure`.
52
+
53
+ ### Stopwatch
54
+
55
+ The stopwatch provides an easy way to track the duration of a long process with multiple phases.
56
+
57
+ ```ruby
58
+ stopwatch = Statue.stopwatch("metric") # => Starts tracking time
59
+
60
+ while something do
61
+ do_something
62
+ stopwatch.partial # => reports duration from last partial until now as: "metric.runtime.partial"
63
+ end
64
+
65
+ stopwatch.stop # => reports duration from start until now as: "metric.runtime.total"
66
+ ```
67
+
68
+ ### Rack Integration
69
+
70
+ We provide a middleware to track basic request metrics, see: Statue::RackStatistics
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it ( https://github.com/restorando/statue/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,15 @@
1
+ module Statue
2
+ class CaptureBackend
3
+ attr_reader :captures
4
+
5
+ def initialize
6
+ @captures = []
7
+ end
8
+
9
+ def collect_metric(metric)
10
+ @captures << metric
11
+ end
12
+ alias :<< :collect_metric
13
+
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Statue
2
+ class LoggerBackend < Struct.new(:logger)
3
+
4
+ def collect_metric(metric)
5
+ logger.info("Statue") { metric.to_s }
6
+ end
7
+ alias :<< :collect_metric
8
+
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Statue
2
+ class NullBackend
3
+
4
+ def collect_metric(metric)
5
+ end
6
+ alias :<< :collect_metric
7
+
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ require 'socket'
2
+
3
+ module Statue
4
+ class UDPBackend
5
+ attr_reader :host, :port
6
+
7
+ def initialize(host = nil, port = nil)
8
+ @host = host
9
+ @port = port
10
+ end
11
+
12
+ def collect_metric(metric)
13
+ if metric.sample_rate == 1 || rand < metric.sample_rate
14
+ send_to_socket metric.to_s
15
+ end
16
+ end
17
+ alias :<< :collect_metric
18
+
19
+ private
20
+
21
+ def socket
22
+ Thread.current[:statue_socket] ||= begin
23
+ socket = UDPSocket.new(Addrinfo.ip(host).afamily)
24
+ socket.connect(host, port)
25
+ socket
26
+ end
27
+ end
28
+
29
+ def send_to_socket(message)
30
+ Statue.debug(message)
31
+ socket.send(message, 0)
32
+ rescue => e
33
+ Statue.error("#{e.class} #{e}")
34
+ nil
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ module Statue
2
+ autoload :LoggerBackend, "statue/backends/logger"
3
+ autoload :CaptureBackend, "statue/backends/capture"
4
+ autoload :NullBackend, "statue/backends/null"
5
+ autoload :UDPBackend, "statue/backends/udp"
6
+ end
@@ -0,0 +1,53 @@
1
+ module Statue
2
+ class Metric
3
+ TYPES = {
4
+ c: 'increment',
5
+ ms: 'measure',
6
+ g: 'gauge',
7
+ kv: 'key/value',
8
+ s: 'set'
9
+ }
10
+
11
+ attr_accessor :type, :name, :value, :sample_rate
12
+
13
+ def self.counter(name, value = 1, options = {})
14
+ new(options.merge(type: :c, value: value, name: name))
15
+ end
16
+
17
+ def self.gauge(name, value, options = {})
18
+ new(options.merge(type: :g, value: value, name: name))
19
+ end
20
+
21
+ def self.measure(name, options = {}, &block)
22
+ duration = options.delete(:duration)
23
+ value = duration || Statue.duration(&block)
24
+ new(options.merge(type: :ms, value: value, name: name))
25
+ end
26
+
27
+ def initialize(options = {})
28
+ @type = options.fetch(:type)
29
+ @name = options.fetch(:name)
30
+ @value = options.fetch(:value)
31
+ @sample_rate = options[:sample_rate] || 1.0
32
+ end
33
+
34
+ def to_s
35
+ str = "#{full_name}:#{value}|#{type}"
36
+ str << "|@#{sample_rate}" if sample_rate != 1.0
37
+ str
38
+ end
39
+
40
+ def inspect
41
+ "#<StatsD::Instrument::Metric #{full_name} #{TYPES[type]}(#{value})@#{sample_rate}>"
42
+ end
43
+
44
+ def full_name
45
+ if Statue.namespace
46
+ "#{Statue.namespace}.#{@name}"
47
+ else
48
+ @name
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,117 @@
1
+ require 'statue'
2
+
3
+ module Statue
4
+ # Middleware to send metrics about rack requests
5
+ #
6
+ # this middleware reports metrics with the following pattern:
7
+ # `request.{env['REQUEST_METHOD']}.{path_name}
8
+ #
9
+ # where `path_name` can be configured when inserting the middleware like this:
10
+ # `use RackStatistics, path_name: ->(env) { ... build the path name ... }`
11
+ # You can build the path using the environment information in the lambda or
12
+ # you can delegate that logic to your app stack and later fetching it from
13
+ # the env, Eg:
14
+ # `use RackStatistics, path_name: ->(env) { env['route.path_name'] }`
15
+ #
16
+ # This middleware will report the following metrics
17
+ #
18
+ # Counters:
19
+ #
20
+ # * <key>.status-XXX (where XXX is the status code)
21
+ # * <key>.success (on any status 2XX)
22
+ # * <key>.unmodified (on status 304)
23
+ # * <key>.redirect (on any status 3XX)
24
+ # * <key>.failure (on any status 4xx)
25
+ # * <key>.error (on any status 5xx or when an exception is raised)
26
+ #
27
+ # Timers (all measured from the middleware perspective):
28
+ #
29
+ # * <key> (request time)
30
+ # * request.queue (queue time, depends on HTTP_X_QUEUE_START header)
31
+ #
32
+ # To get accurate timers, the middleware should be as higher as
33
+ # possible in your rack stack
34
+ #
35
+ class RackStatistics
36
+ DEFAULT_PATH_NAME = lambda do |env|
37
+ # Remove duplicate and trailing '/'
38
+ path = env['REQUEST_PATH'].squeeze('/').chomp('/')
39
+ if path == ''
40
+ 'root'
41
+ else
42
+ # Skip leading '/'
43
+ env['REQUEST_PATH'][1..-1].tr('/,|', '-')
44
+ end
45
+ end
46
+
47
+ def initialize(app, options = {})
48
+ @app = app
49
+ @path_name = options[:path_name] || DEFAULT_PATH_NAME
50
+ end
51
+
52
+ def call(env)
53
+ report_header_metrics(env)
54
+
55
+ response = nil
56
+ duration = Statue.duration do
57
+ response = @app.call(env)
58
+ end
59
+
60
+ report_response_metrics(env, response, duration)
61
+
62
+ response
63
+ rescue => e
64
+ report_exception(env, e) and raise
65
+ end
66
+
67
+ private
68
+
69
+ def report_header_metrics(env)
70
+ if start_header = env['HTTP_X_QUEUE_START']
71
+ queue_start = start_header[/t=([\d\.]+)/, 1].to_f
72
+ Statue.report_duration 'request.queue', Time.now.to_f - queue_start
73
+ end
74
+ end
75
+
76
+ def report_response_metrics(env, response, duration)
77
+ metric_name = metric_name(env)
78
+ status, _headers, _body = response
79
+
80
+ Statue.report_duration metric_name, duration
81
+
82
+ Statue.report_increment "#{metric_name}.status-#{status}"
83
+ Statue.report_increment "#{metric_name}.#{status_group(status)}"
84
+ end
85
+
86
+ def report_exception(env, _exception)
87
+ Statue.report_increment "#{metric_name(env)}.error"
88
+ end
89
+
90
+ def metric_name(env)
91
+ "request.#{env['REQUEST_METHOD']}.#{@path_name.call(env)}"
92
+ end
93
+
94
+ # success: ok (2XX)
95
+ # failure: client error (4xx)
96
+ # error: server error (5xx)
97
+ # unmodified: (304)
98
+ # redirect: (3XX)
99
+ def status_group(status)
100
+ case status.to_i
101
+ when 100..299
102
+ 'success'
103
+ when 304
104
+ 'unmodified'
105
+ when 300..399
106
+ 'redirect'
107
+ when 400..499
108
+ 'failure'
109
+ when 500..599
110
+ 'error'
111
+ else
112
+ 'invalid-status'
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,44 @@
1
+ module Statue
2
+ class Stopwatch
3
+
4
+ def initialize(name, options = {})
5
+ @name = name
6
+ @reporter = options[:reporter] || Statue
7
+ @start = @partial = options[:now] || clock_now
8
+ end
9
+
10
+ def partial(options = {})
11
+ suffix = options.delete(:suffix)
12
+ now = options.delete(:now) || clock_now
13
+ previous, @partial = @partial, now
14
+
15
+ @reporter.report_duration(metric_name(suffix || "runtime.partial"), @partial - previous, options)
16
+ end
17
+
18
+ def stop(options = {})
19
+ suffix = options.delete(:suffix)
20
+ now = options.delete(:now) || clock_now
21
+ report_partial = options.delete(:report_partial) || false
22
+
23
+ partial(options.merge(now: now, suffix: report_partial.is_a?(String) ? report_partial : nil)) if report_partial
24
+
25
+ previous, @start = @start, now
26
+
27
+ @reporter.report_duration(metric_name(suffix || "runtime.total"), @start - previous, options)
28
+ end
29
+
30
+ def reset(options = {})
31
+ @start = @partial = options[:now] || clock_now
32
+ end
33
+
34
+ private
35
+
36
+ def metric_name(suffix)
37
+ "#{@name}.#{suffix}"
38
+ end
39
+
40
+ def clock_now
41
+ Statue.clock_now
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Statue
2
+ VERSION = '0.2.7'
3
+ end
data/lib/statue.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'statue/version'
2
+
3
+ require 'statue/backends'
4
+ require 'statue/metric'
5
+ require 'statue/stopwatch'
6
+
7
+ module Statue
8
+ extend self
9
+
10
+ attr_accessor :namespace, :logger
11
+
12
+ attr_accessor :backend
13
+
14
+ def report_duration(metric_name, duration = nil, options = {}, &block)
15
+ result = nil
16
+ backend << Metric.measure(metric_name, options.merge(duration: duration)) do
17
+ result = block.call
18
+ end
19
+ result
20
+ end
21
+
22
+ def report_increment(metric_name, value = 1, options = {})
23
+ backend << Metric.counter(metric_name, value, options)
24
+ end
25
+
26
+ def report_gauge(metric_name, value, options = {})
27
+ backend << Metric.gauge(metric_name, value, options)
28
+ end
29
+
30
+ def report_success_or_failure(metric_name, options = {}, &block)
31
+ success_method = options.delete(:success_method)
32
+ result = block.call
33
+ success = success_method ? result.public_send(success_method) : result
34
+
35
+ if success
36
+ report_increment("#{metric_name}.success", 1, options)
37
+ else
38
+ report_increment("#{metric_name}.failure", 1, options)
39
+ end
40
+
41
+ result
42
+ rescue
43
+ report_increment("#{metric_name}.failure", 1, options)
44
+ raise
45
+ end
46
+
47
+ def stopwatch(metric_name)
48
+ Stopwatch.new(metric_name, reporter: self)
49
+ end
50
+
51
+ def backend
52
+ @backend ||= UDPBackend.new
53
+ end
54
+
55
+ def duration
56
+ start = clock_now
57
+ yield
58
+ clock_now - start
59
+ end
60
+
61
+ def debug(text, &block)
62
+ logger.debug(text, &block) if logger
63
+ end
64
+
65
+ def error(text, &block)
66
+ logger.error(text, &block) if logger
67
+ end
68
+
69
+ if defined?(Process::CLOCK_MONOTONIC)
70
+ def clock_now
71
+ Process.clock_gettime Process::CLOCK_MONOTONIC
72
+ end
73
+ elsif RUBY_PLATFORM == 'java'
74
+ def clock_now
75
+ java.lang.System.nanoTime() / 1_000_000_000.0
76
+ end
77
+ else
78
+ def clock_now
79
+ Time.now.to_f
80
+ end
81
+ end
82
+
83
+ end
data/statue19.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'statue/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "statue19"
8
+ spec.version = Statue::VERSION
9
+ spec.authors = ["Juan Barreneche"]
10
+ spec.email = ["devs@restorando.com"]
11
+ spec.summary = %q{Easily track application metrics into Statsie}
12
+ spec.description = %q{Track application metrics to Statsie (compat with ruby 1.9)}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "minitest"
23
+ end
@@ -0,0 +1,114 @@
1
+ require 'test_helper'
2
+
3
+ describe Statue do
4
+ after do
5
+ Statue.backend.captures.clear
6
+ end
7
+
8
+ describe ".report_increment" do
9
+ it "Adds a counter metric to the backend" do
10
+ Statue.report_increment("some.counter")
11
+
12
+ assert_equal 1, Statue.backend.captures.size
13
+ assert_equal "some.counter:1|c", Statue.backend.captures.first.to_s
14
+ end
15
+
16
+ it "Allows to increment by a custom value" do
17
+ Statue.report_increment("some.counter", 5)
18
+
19
+ assert_equal 1, Statue.backend.captures.size
20
+ assert_equal "some.counter:5|c", Statue.backend.captures.first.to_s
21
+ end
22
+ end
23
+
24
+ describe ".report_duration" do
25
+
26
+ it "adds a measure metric to the backend using the block call duration" do
27
+ Statue.stub(:duration, 1.5) do
28
+ result = Statue.report_duration("some.timer") { nil }
29
+
30
+ assert_equal 1, Statue.backend.captures.size
31
+ assert_equal "some.timer:1.5|ms", Statue.backend.captures.first.to_s
32
+ end
33
+ end
34
+
35
+ it "returns the block result" do
36
+ result = Statue.report_duration("some.timer") { 42 }
37
+
38
+ assert_equal 42, result
39
+ end
40
+
41
+ it "adds a measure metric to the backend using the fixed value" do
42
+ result = Statue.report_duration("some.timer", 2.5)
43
+
44
+ assert_equal 1, Statue.backend.captures.size
45
+ assert_equal "some.timer:2.5|ms", Statue.backend.captures.first.to_s
46
+ end
47
+
48
+ it "doesn't report duration if an exception is thrown" do
49
+ begin
50
+ Statue.report_duration("some.timer") { raise "error" }
51
+ rescue => e
52
+ assert_equal "error", e.message
53
+ assert_empty Statue.backend.captures
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ describe ".report_gauge" do
60
+
61
+ it "adds a gauge metric to the backend using the fixed value" do
62
+ result = Statue.report_gauge("some.gauge", 23)
63
+
64
+ assert_equal 1, Statue.backend.captures.size
65
+ assert_equal "some.gauge:23|g", Statue.backend.captures.first.to_s
66
+ end
67
+
68
+ end
69
+
70
+ describe ".report_success_or_failure" do
71
+
72
+ it "Adds a counter metric to the backend with .success suffix with a truthy result" do
73
+ Statue.report_success_or_failure("some.event") { 42 }
74
+
75
+ assert_equal 1, Statue.backend.captures.size
76
+ assert_equal "some.event.success:1|c", Statue.backend.captures.first.to_s
77
+ end
78
+
79
+ it "Adds a counter metric to the backend with .failure suffix with a falsey result" do
80
+ Statue.report_success_or_failure("some.event") { nil }
81
+
82
+ assert_equal 1, Statue.backend.captures.size
83
+ assert_equal "some.event.failure:1|c", Statue.backend.captures.first.to_s
84
+ end
85
+
86
+ it "returns the block result" do
87
+ result = Statue.report_success_or_failure("some.event") { 42 }
88
+
89
+ assert_equal 42, result
90
+ end
91
+
92
+ it "Adds a counter metric to the backend with .failure suffix when an exception ocurrs" do
93
+ begin
94
+ Statue.report_success_or_failure("some.event") { raise "error" }
95
+ rescue => e
96
+ assert_equal "error", e.message
97
+ assert_equal 1, Statue.backend.captures.size
98
+ assert_equal "some.event.failure:1|c", Statue.backend.captures.first.to_s
99
+ end
100
+ end
101
+
102
+ it "allows to use a symbol for a custom success_method" do
103
+ Statue.report_success_or_failure("some.zero_event", success_method: :zero?) { 0 }
104
+ Statue.report_success_or_failure("some.life_event", success_method: :zero?) { 42 }
105
+ zero_event, life_event = Statue.backend.captures
106
+
107
+ assert_equal 2, Statue.backend.captures.size
108
+ assert_equal "some.zero_event.success:1|c", zero_event.to_s
109
+ assert_equal "some.life_event.failure:1|c", life_event.to_s
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,85 @@
1
+ require 'test_helper'
2
+
3
+ describe Statue::Stopwatch do
4
+ after do
5
+ Statue.backend.captures.clear
6
+ end
7
+
8
+ describe "#partial" do
9
+ it "reports the duration between start and the partial call" do
10
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
11
+ stopwatch.partial(now: 42)
12
+
13
+ assert_equal 1, Statue.backend.captures.size
14
+ assert_equal "my_watch.runtime.partial:42|ms", Statue.backend.captures.first.to_s
15
+ end
16
+
17
+ it "can report multiple partials" do
18
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
19
+ (1..20).each { |now| stopwatch.partial(now: now) }
20
+
21
+ assert_equal 20, Statue.backend.captures.size
22
+ Statue.backend.captures.each do |metric|
23
+ assert_equal "my_watch.runtime.partial:1|ms", metric.to_s
24
+ end
25
+ end
26
+
27
+ it "tracks time correctly" do
28
+ stopwatch = Statue::Stopwatch.new("my_watch")
29
+ stopwatch.partial
30
+
31
+ assert Statue.backend.captures.first.value > 0, "partial metric time should be greater than zero"
32
+ end
33
+ end
34
+
35
+ describe "#stop" do
36
+ it "reports the duration between start and the stop call" do
37
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
38
+ stopwatch.stop(now: 42)
39
+
40
+ assert_equal 1, Statue.backend.captures.size
41
+ assert_equal "my_watch.runtime.total:42|ms", Statue.backend.captures.first.to_s
42
+ end
43
+
44
+ it "is not affected by partials" do
45
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
46
+ (1..20).each { |now| stopwatch.partial(now: now) }
47
+ stopwatch.stop(now: 21)
48
+
49
+ assert_equal 21, Statue.backend.captures.size
50
+ *partials, total = Statue.backend.captures
51
+ assert_equal "my_watch.runtime.total:21|ms", total.to_s
52
+ end
53
+
54
+ it "can send the last partial duration" do
55
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
56
+ (1..20).each { |now| stopwatch.partial(now: now) }
57
+ stopwatch.stop(now: 21, report_partial: true)
58
+
59
+ assert_equal 22, Statue.backend.captures.size
60
+ *partials, total = Statue.backend.captures
61
+ assert_equal "my_watch.runtime.total:21|ms", total.to_s
62
+ partials.each do |metric|
63
+ assert_equal "my_watch.runtime.partial:1|ms", metric.to_s
64
+ end
65
+ end
66
+
67
+ it "can send the last partial duration with a special name" do
68
+ stopwatch = Statue::Stopwatch.new("my_watch", now: 0)
69
+ (1..20).each { |now| stopwatch.partial(now: now) }
70
+ stopwatch.stop(now: 21, report_partial: "runtime.final_lap")
71
+
72
+ assert_equal 22, Statue.backend.captures.size
73
+ *partials, special_partial, total = Statue.backend.captures
74
+ assert_equal "my_watch.runtime.final_lap:1|ms", special_partial.to_s
75
+ end
76
+
77
+ it "tracks time correctly" do
78
+ stopwatch = Statue::Stopwatch.new("my_watch")
79
+ stopwatch.stop
80
+
81
+ assert Statue.backend.captures.first.value > 0, "partial metric time should be greater than zero"
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,6 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/spec'
3
+
4
+ require 'statue'
5
+
6
+ Statue.backend = Statue::CaptureBackend.new
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statue19
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.7
5
+ platform: ruby
6
+ authors:
7
+ - Juan Barreneche
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Track application metrics to Statsie (compat with ruby 1.9)
56
+ email:
57
+ - devs@restorando.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - CHANGELOG.md
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/rake
69
+ - lib/statue.rb
70
+ - lib/statue/backends.rb
71
+ - lib/statue/backends/capture.rb
72
+ - lib/statue/backends/logger.rb
73
+ - lib/statue/backends/null.rb
74
+ - lib/statue/backends/udp.rb
75
+ - lib/statue/metric.rb
76
+ - lib/statue/rack_statistics.rb
77
+ - lib/statue/stopwatch.rb
78
+ - lib/statue/version.rb
79
+ - statue19.gemspec
80
+ - test/statue_test.rb
81
+ - test/stopwatch_test.rb
82
+ - test/test_helper.rb
83
+ homepage: ''
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.8
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Easily track application metrics into Statsie
107
+ test_files:
108
+ - test/statue_test.rb
109
+ - test/stopwatch_test.rb
110
+ - test/test_helper.rb