request_id 0.1.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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ script: bundle exec rake
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in requestid.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eric J. Holmes
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,62 @@
1
+ # RequestId
2
+
3
+ This is a gem with a collection of middleware for easily cascading [Heroku request id's](https://devcenter.heroku.com/articles/http-request-id)
4
+ Throughout the system. It includes:
5
+
6
+ * Rack middleware, which adds the request\_id to `Thread.current[:request\_id]`
7
+ * Sidekiq Client middleware, which adds the request\_id to the message
8
+ payload.
9
+ * Sidekiq Server middleware, which adds the request\_id to
10
+ `Thread.current[:request_id]` from the request\_id in the message payload.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'request_id', github: 'remind101/request_id'
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Add the rack middleware:
23
+
24
+ ```ruby
25
+ use Rack::RequestId
26
+ ```
27
+
28
+
29
+ ### If you're using Sidekiq
30
+
31
+ Add the client middleware.
32
+
33
+ ```ruby
34
+ Sidekiq.configure_client do |config|
35
+ config.client_middleware do |chain|
36
+ chain.add Sidekiq::Middleware::Client::RequestId
37
+ end
38
+ end
39
+ ```
40
+
41
+ Add the server middleware.
42
+
43
+ ```ruby
44
+ Sidekiq.configure_server do |config|
45
+ config.client_middleware do |chain|
46
+ chain.add Sidekiq::Middleware::Client::RequestId
47
+ end
48
+
49
+ config.server_middleware do |chain|
50
+ chain.remove Sidekiq::Middleware::Server::Logging
51
+ chain.add Sidekiq::Middleware::Client::RequestId
52
+ end
53
+ end
54
+ ```
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default => [:spec]
5
+
6
+ require 'rspec/core/rake_task'
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ end
@@ -0,0 +1,31 @@
1
+ module Rack
2
+
3
+ # Public: Rack middleware that stores the Heroku-Request-Id header in a
4
+ # thread local variable.
5
+ #
6
+ # Heroku has a labs feature called request_id, which can be used to tracking
7
+ # a request through the system.
8
+ #
9
+ # app - The Rack app.
10
+ #
11
+ # Examples
12
+ #
13
+ # use Rack::LogRequestId
14
+ #
15
+ # logger.info "request_id=#{Thread.current[:request_id]} Hello world"
16
+ # # => request_id=a08a6712229fb991c0e5026c246862c7 Hello world
17
+ class RequestId
18
+ REQUEST_ID_HEADER = 'HTTP_HEROKU_REQUEST_ID'.freeze
19
+
20
+ def initialize(app, options = {})
21
+ @app = app
22
+ end
23
+
24
+ def call(env)
25
+ ::RequestId.request_id = env[REQUEST_ID_HEADER]
26
+ @app.call(env)
27
+ ensure
28
+ ::RequestId.request_id = nil
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ require 'request_id/version'
2
+
3
+ module RequestId
4
+ class << self
5
+
6
+ # Public: Retrieve the current request_id, which is generally set by the
7
+ # Rack or Sidekiq middleware.
8
+ #
9
+ # Examples
10
+ #
11
+ # RequestId.request_id
12
+ # # => "0b482498be0d6084d2b634cd6523418d"
13
+ #
14
+ # Returns the String request id.
15
+ def request_id
16
+ Thread.current[:request_id]
17
+ end
18
+
19
+ # Internal: Set the current request_id.
20
+ #
21
+ # Examples
22
+ #
23
+ # RequestId.request_id = SecureRandom.hex
24
+ # # => "2297456c027c536d0eb3eb86583fe5a9"
25
+ #
26
+ # Returns the new String request id.
27
+ def request_id=(request_id)
28
+ Thread.current[:request_id] = request_id
29
+ end
30
+
31
+ end
32
+ end
33
+
34
+ module Rack
35
+ autoload :RequestId, 'rack/request_id'
36
+ end
37
+
38
+ module Sidekiq
39
+ module Middleware
40
+ module Client
41
+ autoload :RequestId, 'sidekiq/middleware/client/request_id'
42
+ end
43
+
44
+ module Server
45
+ autoload :RequestId, 'sidekiq/middleware/server/request_id'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module RequestId
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,34 @@
1
+ module Sidekiq
2
+ module Middleware
3
+ module Client
4
+ class RequestId
5
+
6
+ def call(worker, item, queue)
7
+ item['request_id'] = request_id if request_id
8
+ log(request_id, worker, item['args'].inspect) if log_request_id?(worker)
9
+ yield
10
+ end
11
+
12
+ private
13
+
14
+ def log(request_id, worker, args)
15
+ logger.info "request_id=#{request_id} at=enqueue worker=#{worker.to_s} args=#{args}"
16
+ end
17
+
18
+ def log_request_id?(worker)
19
+ worker.respond_to?(:get_sidekiq_options) && worker.get_sidekiq_options['log_request_id']
20
+ end
21
+
22
+ def logger
23
+ Sidekiq.logger
24
+ end
25
+
26
+ def request_id
27
+ ::RequestId.request_id
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,26 @@
1
+ module Sidekiq
2
+ module Middleware
3
+ module Server
4
+ class RequestId < Logging
5
+
6
+ def call(worker, item, queue)
7
+ request_id = ::RequestId.request_id = item['request_id']
8
+ Sidekiq::Logging.with_context("request_id=#{request_id} worker=#{worker.class.to_s} jid=#{item['jid']} args=#{item['args'].inspect}") do
9
+ begin
10
+ start = Time.now
11
+ logger.info { "at=start" }
12
+ yield
13
+ logger.info { "at=done duration=#{elapsed(start)}sec" }
14
+ rescue Exception
15
+ logger.info { "at=fail duration=#{elapsed(start)}sec" }
16
+ raise
17
+ end
18
+ end
19
+ ensure
20
+ ::RequestId.request_id = nil
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'request_id/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'request_id'
8
+ spec.version = RequestId::VERSION
9
+ spec.authors = ['Eric J. Holmes']
10
+ spec.email = ['eric@ejholmes.net']
11
+ spec.description = %q{Classes for tracking request id}
12
+ spec.summary = %q{Classes for tracking request id}
13
+ spec.homepage = 'https://github.com/remind101/request_id'
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.3'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ describe Rack::RequestId do
5
+ let(:app) { double('app', call: nil) }
6
+ let(:middleware) { described_class.new app }
7
+
8
+ describe '.call' do
9
+ let(:request_id) { SecureRandom.hex }
10
+
11
+ it 'stores the request_id in a thread local' do
12
+ Thread.current.should_receive(:[]=).with(:request_id, request_id)
13
+ app.should_receive(:call)
14
+ Thread.current.should_receive(:[]=).with(:request_id, nil)
15
+ middleware.call('HTTP_HEROKU_REQUEST_ID' => request_id)
16
+ end
17
+
18
+ context 'when an exception is raised' do
19
+ it 'still sets the request_id back to nil' do
20
+ Thread.current.should_receive(:[]=).with(:request_id, request_id)
21
+ app.should_receive(:call).and_raise
22
+ Thread.current.should_receive(:[]=).with(:request_id, nil)
23
+ expect { middleware.call('HTTP_HEROKU_REQUEST_ID' => request_id) }.to raise_error
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ describe Sidekiq::Middleware::Client::RequestId do
5
+ let(:middleware) { described_class.new }
6
+
7
+ describe '#call' do
8
+ context 'when the worker is an object that responds to `get_sidekiq_options`' do
9
+ let(:worker) { double('worker', to_s: 'Worker') }
10
+
11
+ context 'when the worker is configured to log request ids' do
12
+ let(:logger) { double('Logger', info: nil) }
13
+
14
+ before { worker.stub get_sidekiq_options: { 'log_request_id' => true } }
15
+ before { Sidekiq.stub logger: logger }
16
+
17
+ it 'adds the request id to the item' do
18
+ request_id = Thread.current[:request_id] = SecureRandom.hex
19
+ item = {}
20
+ expect { middleware.call(worker, item, nil) { } }.to change { item }.from({}).to('request_id' => request_id)
21
+ end
22
+
23
+ it 'logs the request id' do
24
+ request_id = Thread.current[:request_id] = SecureRandom.hex
25
+ logger.should_receive(:info).with(
26
+ "request_id=#{request_id} at=enqueue worker=Worker args=[\"foo\"]"
27
+ )
28
+ expect { |b| middleware.call(worker, { 'args' => ['foo'] }, nil, &b) }.to yield_control
29
+ end
30
+ end
31
+
32
+ context 'when the worker is not configured to log request ids' do
33
+ before { worker.stub get_sidekiq_options: {} }
34
+
35
+ it 'does not log the request' do
36
+ expect { |b| middleware.call(worker, {}, nil, &b) }.to yield_control
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'when the worker is an object that does not respond to `get_sidekiq_options`' do
42
+ let(:worker) { 'Worker' }
43
+
44
+ it 'does not log the request' do
45
+ expect { |b| middleware.call(worker, {}, nil, &b) }.to yield_control
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ describe Sidekiq::Middleware::Server::RequestId do
5
+ let(:logger) { double('Logger', info: nil) }
6
+ let(:middleware) { described_class.new }
7
+
8
+ before { Sidekiq.stub logger: logger }
9
+
10
+ describe '#call' do
11
+ let(:worker) { double('worker') }
12
+
13
+ before { worker.stub_chain :class, to_s: 'Worker' }
14
+
15
+ context 'when the worker is configured to log request ids' do
16
+ let(:request_id) { SecureRandom.hex }
17
+ let(:job_id) { SecureRandom.hex }
18
+ let(:item) { { 'jid' => job_id, 'args' => ['foo'], 'log_request_id' => true, 'request_id' => request_id } }
19
+
20
+ it 'sets a thread local to the request id' do
21
+ Thread.current.should_receive(:[]=).with(:request_id, request_id)
22
+ Thread.current.should_receive(:[]=).with(:request_id, nil)
23
+ expect { |b| middleware.call(worker, item, nil, &b) }.to yield_control
24
+ end
25
+
26
+ it 'logs the request id' do
27
+ Sidekiq::Logging.should_receive(:with_context)
28
+ .with("request_id=#{request_id} worker=Worker jid=#{job_id} args=[\"foo\"]")
29
+ .and_yield
30
+ expect { |b| middleware.call(worker, item, nil, &b) }.to yield_control
31
+ end
32
+ end
33
+
34
+ context 'when the worker is not configured to log request ids' do
35
+ it 'does not log the request' do
36
+ expect { |b| middleware.call(worker, {}, nil, &b) }.to yield_control
37
+ end
38
+ end
39
+
40
+ context 'when an error is raised' do
41
+ it 'ensures that the thread local is set to nil, and raises the error' do
42
+ Thread.current.should_receive(:[]=).with(:request_id, nil).twice
43
+ expect { middleware.call(worker, {}, nil) { raise } }.to raise_error
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ Bundler.require :default, :test
3
+
4
+ # Requires supporting ruby files with custom matchers and macros, etc,
5
+ # in spec/support/ and its subdirectories.
6
+ Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f}
@@ -0,0 +1,33 @@
1
+ require 'logger'
2
+
3
+ module Sidekiq
4
+ def self.logger
5
+ @logger ||= ::Logger.new(STDOUT)
6
+ end
7
+ end
8
+
9
+ # https://github.com/mperham/sidekiq/blob/602d5da96d0101f47d7e89602478b2246853733e/lib/sidekiq/middleware/server/logging.rb
10
+ module Sidekiq
11
+ module Middleware
12
+ module Server
13
+ class Logging
14
+ def elapsed(start)
15
+ (Time.now - start).to_f.round(3)
16
+ end
17
+
18
+ def logger
19
+ Sidekiq.logger
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ # https://github.com/mperham/sidekiq/blob/602d5da96d0101f47d7e89602478b2246853733e/lib/sidekiq/logging.rb
27
+ module Sidekiq
28
+ module Logging
29
+ def self.with_context(msg)
30
+ yield
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: request_id
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eric J. Holmes
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Classes for tracking request id
63
+ email:
64
+ - eric@ejholmes.net
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - .travis.yml
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - lib/rack/request_id.rb
77
+ - lib/request_id.rb
78
+ - lib/request_id/version.rb
79
+ - lib/sidekiq/middleware/client/request_id.rb
80
+ - lib/sidekiq/middleware/server/request_id.rb
81
+ - request_id.gemspec
82
+ - spec/rack/request_id_spec.rb
83
+ - spec/sidekiq/middleware/client/request_id_spec.rb
84
+ - spec/sidekiq/middleware/server/request_id_spec.rb
85
+ - spec/spec_helper.rb
86
+ - spec/support/sidekiq.rb
87
+ homepage: https://github.com/remind101/request_id
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ segments:
101
+ - 0
102
+ hash: -2924657603052698808
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ segments:
110
+ - 0
111
+ hash: -2924657603052698808
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.23
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Classes for tracking request id
118
+ test_files:
119
+ - spec/rack/request_id_spec.rb
120
+ - spec/sidekiq/middleware/client/request_id_spec.rb
121
+ - spec/sidekiq/middleware/server/request_id_spec.rb
122
+ - spec/spec_helper.rb
123
+ - spec/support/sidekiq.rb