rack-timer-statsd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c37d1a7913f5c6009167bac186bd10b9f63adb70
4
+ data.tar.gz: c49d42f1953330b053a9454370ba6b7884f8ef08
5
+ SHA512:
6
+ metadata.gz: 2b52c0bdcb832756f9010b035a14f7c3aa426aea9fdce6b890580173947da3f394128ea5b9a697d727d05b30524d6eaebac2eecd6d147e9b19448780b325abce
7
+ data.tar.gz: 5bc50340e16679bf804bae8a31229c052336cd2ddb8fbdca1e5b87b1133559b2639e94f8d1e37e498b34520c2897281016d32d2fd0bad6cd99743206657ad12f
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-timer.gemspec
4
+ gemspec
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-timer (0.0.1)
5
+ rack
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.0)
11
+ diff-lcs (1.2.5)
12
+ method_source (0.8.2)
13
+ pry (0.9.12.6)
14
+ coderay (~> 1.0)
15
+ method_source (~> 0.8)
16
+ slop (~> 3.4)
17
+ rack (1.5.2)
18
+ rake (10.1.1)
19
+ rspec (2.14.1)
20
+ rspec-core (~> 2.14.0)
21
+ rspec-expectations (~> 2.14.0)
22
+ rspec-mocks (~> 2.14.0)
23
+ rspec-core (2.14.8)
24
+ rspec-expectations (2.14.5)
25
+ diff-lcs (>= 1.1.3, < 2.0)
26
+ rspec-mocks (2.14.6)
27
+ slop (3.4.7)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ bundler (~> 1.5)
34
+ pry
35
+ rack-timer!
36
+ rake
37
+ rspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 HouseTrip Ltd
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.
@@ -0,0 +1,91 @@
1
+ # rack-timer
2
+
3
+ Are you spending too much time in Rack middlewares?
4
+
5
+ Is one of your Rack middlewares misbehaving in production?
6
+
7
+ Add `RackTimer::Middleware` to the to of your middleware stack and figure out.
8
+
9
+ [![Build Status](https://travis-ci.org/mezis/rack-timer.png?branch=master)](https://travis-ci.org/mezis/rack-timer)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'rack-timer'
16
+
17
+ Add the middleware to your stack. In your `config.ru`, add this before any other
18
+ middleware:
19
+
20
+ require 'rack-timer'
21
+ use RackTimer::Middleware
22
+
23
+ When your app runs, it will output timing information to standard error.
24
+ If you want to change that, you can tell `rack-timer` to send data to another
25
+ file-y object, for instance:
26
+
27
+ RackTimer.output = $stdout
28
+
29
+ If you want to send execution time to StatsD as well, you can specify a client:
30
+
31
+ RackTimer.statsd = $statsd
32
+
33
+ ## Usage
34
+
35
+ Run your app normally after installation, wait a while, and download your logs.
36
+
37
+ They will report the middleware starting up:
38
+
39
+ [rack-timer] assimilating: Rack::MiddlewareTimer
40
+ [rack-timer] assimilating: Rack::Lock
41
+ [rack-timer] assimilating: Airbrake::UserInformer
42
+ [rack-timer] assimilating: Rack::DetectCrawler
43
+
44
+ then timing each request:
45
+
46
+ [rack-timer] Proc took 3398135 us
47
+ [rack-timer] Rack::Cors took 54 us
48
+ [rack-timer] Rack::OutOfBandGC took 51 us
49
+ ...
50
+ [rack-timer] Airbrake::UserInformer took 74 us
51
+ [rack-timer] Rack::Lock took 78 us
52
+ [rack-timer] Rack::MiddlewareTimer took 80 us
53
+ [rack-timer] queued for 5256 us
54
+
55
+ Besides middleware timing, two bits of extra information are reported:
56
+
57
+ - `Proc` (the first entry for each request) typically is your app itself
58
+ (excluding middleware).
59
+ - `queued for...` will be present if your web frontend adds the `X-Request-Start`
60
+ HTTP header. If using Nginx+Unicorn or Apache+Passenger, this will report the
61
+ queueing time in Unicorn or Passenger.
62
+
63
+ ## An example
64
+
65
+ We developed this because of wierd issues with queuing time (was too high
66
+ without an obvious reason).
67
+
68
+ We graphed the middleware timings:
69
+
70
+ ![](http://cl.ly/image/460a3z060F3B/capture%202014-03-12%20at%2016.51.53.png)
71
+
72
+ And the queueing timings:
73
+
74
+ ![](http://cl.ly/image/2D2336390628/capture%202014-03-12%20at%2013.25.43.png)
75
+
76
+ (horizontally: log10 of the queuing time in microseconds, ie. 3 is 1ms and 6 is
77
+ 1 second)
78
+
79
+ The conlusion was that something what causing queuing in some cases. It turned
80
+ out our out-of-band garbage colleciton hack was no longer compatible with
81
+ Passenger, and removing it solved the issue:
82
+
83
+ ![](http://cl.ly/image/3z0V40291P46/capture%202014-03-12%20at%2014.18.55.png)
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,2 @@
1
+ require 'rack-timer/version'
2
+ require 'rack-timer/middleware'
@@ -0,0 +1,104 @@
1
+
2
+ require 'rack'
3
+
4
+ module RackTimer
5
+
6
+ module ModuleMethods
7
+ def log(message)
8
+ (@_output || $stderr).puts(message)
9
+ end
10
+
11
+ def measure(key, duration)
12
+ if @_statsd
13
+ @_statsd.timing("rack-timer.time.#{key}", duration)
14
+ end
15
+ end
16
+
17
+ def output=(io)
18
+ @_output = io
19
+ end
20
+
21
+ def statsd=(client)
22
+ @_statsd = client
23
+ end
24
+ end
25
+ extend ModuleMethods
26
+
27
+
28
+ class Middleware
29
+
30
+ def initialize(app)
31
+ @app = app
32
+ self.extend(Borg)
33
+ _log "started borg collective: #{self.class.name}"
34
+ end
35
+
36
+ def call(env)
37
+ @app.call(env)
38
+ end
39
+
40
+ module Borg
41
+ def self.extended(object)
42
+ object.singleton_class.class_eval do
43
+ alias_method :call_without_timing, :call
44
+ alias_method :call, :call_with_timing
45
+ public :call
46
+ end
47
+
48
+ object.instance_eval do
49
+ _log "assimilating: #{object.class.name}"
50
+ recursive_borg
51
+ end
52
+ end
53
+
54
+ def borged?
55
+ true
56
+ end
57
+
58
+ private
59
+
60
+ def recursive_borg
61
+ return if @app.nil?
62
+ return if @app.respond_to?(:borged?)
63
+ return unless @app.respond_to?(:call)
64
+ @app.extend(Borg)
65
+ end
66
+
67
+ def call_with_timing(env)
68
+ time_before = _current_ticks
69
+ result = call_without_timing(env)
70
+ time_delta = _current_ticks - time_before
71
+
72
+ if time_inner = env['rack-timer.time']
73
+ time_inner = time_inner.to_i
74
+ time_self = time_delta - time_inner
75
+ else
76
+ time_self = time_delta
77
+ end
78
+ _log "#{self.class.name} took #{time_self} us"
79
+ _measure(self.class.name, time_self / 1000)
80
+
81
+ if (request_start = env['HTTP_X_REQUEST_START']) && kind_of?(RackTimer::Middleware)
82
+ time_queue_start = request_start.gsub('t=', '').to_i
83
+ time_in_queue = time_before - time_queue_start
84
+ _log "queued for #{time_in_queue} us"
85
+ end
86
+
87
+ env['rack-timer.time'] = time_delta
88
+ return result
89
+ end
90
+
91
+ def _log(message)
92
+ RackTimer.log "[rack-timer] #{message}"
93
+ end
94
+
95
+ def _measure(class_name, duration)
96
+ RackTimer.measure(class_name.downcase.gsub('::', '_'), duration)
97
+ end
98
+
99
+ def _current_ticks
100
+ (Time.now.to_f * 1e6).to_i
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module RackTimer
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack-timer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-timer-statsd"
8
+ spec.version = RackTimer::VERSION
9
+ spec.authors = ["Julien Letessier"]
10
+ spec.email = ["julien.letessier@gmail.com"]
11
+ spec.summary = %q{Measure time spent in your Rack middlewares}
12
+ spec.description = %q{Measure time spent in your Rack middlewares}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry"
25
+
26
+ spec.add_dependency "rack"
27
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'rack-timer'
3
+
4
+ describe RackTimer::Middleware do
5
+ let(:buffer) { StringIO.new }
6
+ let(:output) { buffer.rewind ; buffer.read }
7
+ let(:statsd) { double(timing: true) }
8
+ let(:app) { build_stack *stack }
9
+ let(:stack) { [Test::App, Test::FastMiddleware, RackTimer::Middleware] }
10
+
11
+ before do
12
+ RackTimer.output = buffer
13
+ RackTimer.statsd = statsd
14
+ end
15
+
16
+ def build_stack(app_class, *middlewares)
17
+ app = app_class.new
18
+ middlewares.each do |m|
19
+ app = m.new(app)
20
+ end
21
+ app
22
+ end
23
+
24
+ describe 'assimilation' do
25
+ it 'gets logged' do
26
+ app
27
+ output.should =~ /assimilating: RackTimer::Middleware/
28
+ output.should =~ /assimilating: Test::FastMiddleware/
29
+ end
30
+ end
31
+
32
+ describe 'middleware timing' do
33
+ it 'logs app timing' do
34
+ app.call({})
35
+ output.should =~ /Test::App took \d+ us/
36
+ end
37
+
38
+ it 'logs middleware timing' do
39
+ statsd.should_receive(:timing).with("rack-timer.time.test_app", kind_of(Integer))
40
+ statsd.should_receive(:timing).with("rack-timer.time.test_fastmiddleware", kind_of(Integer))
41
+ statsd.should_receive(:timing).with("rack-timer.time.racktimer_middleware", kind_of(Integer))
42
+ app.call({})
43
+ output.should =~ /Test::FastMiddleware took \d+ us/
44
+ end
45
+
46
+ context 'with a slow middleware' do
47
+ let(:stack) { [Test::App, Test::SlowMiddleware, Test::FastMiddleware, RackTimer::Middleware] }
48
+
49
+ it 'times the slow middleware' do
50
+ app.call({})
51
+ output.should =~ /Test::SlowMiddleware took \d+ us/
52
+ end
53
+
54
+ it 'does not include the slow middleware time in the outer middleware' do
55
+ app.call({})
56
+ time_slow = /Test::SlowMiddleware took (?<time>\d+) us/.match(output)[:time].to_i
57
+ time_fast = /Test::FastMiddleware took (?<time>\d+) us/.match(output)[:time].to_i
58
+ time_slow.should > 250_000
59
+ time_fast.should < time_slow
60
+ end
61
+ end
62
+ end
63
+
64
+ describe 'queue timing' do
65
+ let(:timestamp) { (Time.now.to_f * 1e6).to_i }
66
+ let(:env) {{ 'HTTP_X_REQUEST_START' => "t=#{timestamp}" }}
67
+
68
+ it 'adds queue time when HTTP_X_REQUEST_START present' do
69
+ app.call(env)
70
+ output.should =~ /queued for \d+ us/
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,50 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ #
8
+ # lib = File.expand_path('../../lib', __FILE__)
9
+ # $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
10
+
11
+ require 'rack'
12
+ require 'pry'
13
+
14
+ module Test
15
+ class App
16
+ def call(env)
17
+ [200, {}, 'ok']
18
+ end
19
+ end
20
+
21
+ class FastMiddleware
22
+ def initialize(app)
23
+ @app = app
24
+ end
25
+
26
+ def call(env)
27
+ @app.call(env)
28
+ end
29
+ end
30
+
31
+ class SlowMiddleware < FastMiddleware
32
+ def call(env)
33
+ sleep 250e-3
34
+ super
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ RSpec.configure do |config|
41
+ config.treat_symbols_as_metadata_keys_with_true_values = true
42
+ config.run_all_when_everything_filtered = true
43
+ config.filter_run :focus
44
+
45
+ # Run specs in random order to surface order dependencies. If you find an
46
+ # order dependency and want to debug it, you can fix the order by providing
47
+ # the seed, which is printed after each run.
48
+ # --seed 1234
49
+ config.order = 'random'
50
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-timer-statsd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Julien Letessier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-27 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.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
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
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Measure time spent in your Rack middlewares
84
+ email:
85
+ - julien.letessier@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - lib/rack-timer.rb
99
+ - lib/rack-timer/middleware.rb
100
+ - lib/rack-timer/version.rb
101
+ - rack-timer.gemspec
102
+ - spec/rack-timer/middleware_spec.rb
103
+ - spec/spec_helper.rb
104
+ homepage: ''
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.4.5
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Measure time spent in your Rack middlewares
128
+ test_files:
129
+ - spec/rack-timer/middleware_spec.rb
130
+ - spec/spec_helper.rb