request_tagger 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +90 -0
- data/LICENSE +7 -0
- data/README.md +111 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/lib/request_tagger/active_record_magic.rb +65 -0
- data/lib/request_tagger/errors.rb +5 -0
- data/lib/request_tagger/http_magic.rb +45 -0
- data/lib/request_tagger/setup.rb +108 -0
- data/lib/request_tagger/tag_requests.rb +18 -0
- data/lib/request_tagger/version.rb +5 -0
- data/lib/request_tagger.rb +31 -0
- data/locales/en.yml +3 -0
- data/request_tagger.gemspec +35 -0
- metadata +180 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 06c2d273d68e0f7dbd1cb32279e35000fdd8fb9e
|
4
|
+
data.tar.gz: 648802a660492cf0afe173232071984c025bd2be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1a7c5d2d9a9eccaf19018205754c797578f4446a19a4afa41842c56f39c00f5f93a81961af0e8bc6a4154f36bf98c2551399c500f97eb7531ed1c0fae714a808
|
7
|
+
data.tar.gz: b6bb72a6402418213bd60c0368246a91addb2e0945fd8a8c40cb1ecaf08359994cf009fbf1b9af813e974c2b14b62136d963820fec61fe6e6d13328285a67853
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Don't require top-level comment for all modules and classes
|
2
|
+
Style/Documentation:
|
3
|
+
Enabled: false
|
4
|
+
|
5
|
+
Metrics/BlockLength:
|
6
|
+
Exclude:
|
7
|
+
- 'spec/**/*'
|
8
|
+
|
9
|
+
# We don't care about the auto-generated dummy Rails app
|
10
|
+
AllCops:
|
11
|
+
Exclude:
|
12
|
+
- 'bin/**/*'
|
13
|
+
- 'db/schema.rb'
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
request_tagger (0.1.0)
|
5
|
+
i18n (~> 1.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (5.2.1)
|
11
|
+
activesupport (= 5.2.1)
|
12
|
+
activerecord (5.2.1)
|
13
|
+
activemodel (= 5.2.1)
|
14
|
+
activesupport (= 5.2.1)
|
15
|
+
arel (>= 9.0)
|
16
|
+
activesupport (5.2.1)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (>= 0.7, < 2)
|
19
|
+
minitest (~> 5.1)
|
20
|
+
tzinfo (~> 1.1)
|
21
|
+
addressable (2.5.2)
|
22
|
+
public_suffix (>= 2.0.2, < 4.0)
|
23
|
+
arel (9.0.0)
|
24
|
+
ast (2.4.0)
|
25
|
+
byebug (10.0.2)
|
26
|
+
concurrent-ruby (1.0.5)
|
27
|
+
crack (0.4.3)
|
28
|
+
safe_yaml (~> 1.0.0)
|
29
|
+
diff-lcs (1.3)
|
30
|
+
hashdiff (0.3.7)
|
31
|
+
i18n (1.1.0)
|
32
|
+
concurrent-ruby (~> 1.0)
|
33
|
+
jaro_winkler (1.5.1)
|
34
|
+
minitest (5.11.3)
|
35
|
+
parallel (1.12.1)
|
36
|
+
parser (2.5.1.2)
|
37
|
+
ast (~> 2.4.0)
|
38
|
+
powerpack (0.1.2)
|
39
|
+
public_suffix (3.0.3)
|
40
|
+
rainbow (3.0.0)
|
41
|
+
rake (10.5.0)
|
42
|
+
rspec (3.8.0)
|
43
|
+
rspec-core (~> 3.8.0)
|
44
|
+
rspec-expectations (~> 3.8.0)
|
45
|
+
rspec-mocks (~> 3.8.0)
|
46
|
+
rspec-core (3.8.0)
|
47
|
+
rspec-support (~> 3.8.0)
|
48
|
+
rspec-expectations (3.8.2)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.8.0)
|
51
|
+
rspec-mocks (3.8.0)
|
52
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
53
|
+
rspec-support (~> 3.8.0)
|
54
|
+
rspec-support (3.8.0)
|
55
|
+
rubocop (0.59.2)
|
56
|
+
jaro_winkler (~> 1.5.1)
|
57
|
+
parallel (~> 1.10)
|
58
|
+
parser (>= 2.5, != 2.5.1.1)
|
59
|
+
powerpack (~> 0.1)
|
60
|
+
rainbow (>= 2.2.2, < 4.0)
|
61
|
+
ruby-progressbar (~> 1.7)
|
62
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
63
|
+
ruby-progressbar (1.10.0)
|
64
|
+
safe_yaml (1.0.4)
|
65
|
+
sqlite3 (1.3.13)
|
66
|
+
thread_safe (0.3.6)
|
67
|
+
tzinfo (1.2.5)
|
68
|
+
thread_safe (~> 0.1)
|
69
|
+
unicode-display_width (1.4.0)
|
70
|
+
webmock (3.4.2)
|
71
|
+
addressable (>= 2.3.6)
|
72
|
+
crack (>= 0.3.2)
|
73
|
+
hashdiff
|
74
|
+
|
75
|
+
PLATFORMS
|
76
|
+
ruby
|
77
|
+
|
78
|
+
DEPENDENCIES
|
79
|
+
activerecord (~> 5.2)
|
80
|
+
bundler (~> 1.16)
|
81
|
+
byebug
|
82
|
+
rake (~> 10.0)
|
83
|
+
request_tagger!
|
84
|
+
rspec (~> 3.0)
|
85
|
+
rubocop (~> 0.59.2)
|
86
|
+
sqlite3 (~> 1.3)
|
87
|
+
webmock (~> 3.4)
|
88
|
+
|
89
|
+
BUNDLED WITH
|
90
|
+
1.16.4
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2018 Robert Farrell
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# RequestTagger
|
2
|
+
|
3
|
+
Inject a request ID tag into all _ActiveRecord_ queries and HTTP requests made within your [_Rails_] application.
|
4
|
+
|
5
|
+
Any web service requests or database queries your application makes in a given request can be tied together by coalescing your log files in your favourite log aggregator, giving a full picture of every request on your system.
|
6
|
+
|
7
|
+
An incoming HTTP header is used as the ID for all subsequent requests.
|
8
|
+
|
9
|
+
SQL queries are prepended with a comment that will look something like this:
|
10
|
+
|
11
|
+
```sql
|
12
|
+
/* request-id: abc123 */ SELECT * FROM ...
|
13
|
+
```
|
14
|
+
|
15
|
+
HTTP requests will include an extra header:
|
16
|
+
|
17
|
+
```
|
18
|
+
X-Request-Id: abc123
|
19
|
+
```
|
20
|
+
|
21
|
+
The implementation has borrowed ideas from _RSpec's_ `allow_any_instance_of` and _webmock's_ `stub_request`. Their source code was used as an invaluable reference during development. Thanks to the developers of both libraries for their hard work.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'request_tagger'
|
29
|
+
```
|
30
|
+
|
31
|
+
And then rebuild your bundle:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
$ bundle install
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
The only things you need to do are create an initializer:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
# config/initializers/request_tagger.rb
|
43
|
+
RequestTagger.start
|
44
|
+
```
|
45
|
+
|
46
|
+
and include the `RequestTagger::TagRequests` module in your base controller:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class ApplicationController < ActionController::Base
|
50
|
+
include RequestTagger::TagRequests
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
You can pass the following options to `RequestTagger.start` (values shown are the defaults):
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
RequestTagger.start(
|
58
|
+
tag_sql: true, # Tag all ActiveRecord SQL queries
|
59
|
+
tag_http: true, # Tag all HTTP requests
|
60
|
+
http_tag_name: 'X-Request-Id', # Header to use for outbound requests
|
61
|
+
sql_tag_name: 'request-id', # Identifier to use in SQL tags
|
62
|
+
header: 'HTTP_X_REQUEST_ID' # Header to watch for inbound requests*
|
63
|
+
)
|
64
|
+
```
|
65
|
+
|
66
|
+
\* Note that an inbound HTTP header e.g. `X-Request-Id` will be transformed by Rack to `HTTP_X_REQUEST_ID` so take this into account when setting the `header` option.
|
67
|
+
|
68
|
+
An example usage would be the `$request_id` variable provided by _nginx_:
|
69
|
+
|
70
|
+
```
|
71
|
+
location / {
|
72
|
+
proxy_set_header X-Request-Id $request_id;
|
73
|
+
}
|
74
|
+
```
|
75
|
+
|
76
|
+
### Setting a request ID manually
|
77
|
+
|
78
|
+
If you want to manually assign the request ID to be used in tags, just add overwrite the following method in your base controller:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
private
|
82
|
+
|
83
|
+
def __request_tagger__set_request_id__
|
84
|
+
RequestTagger.request_id = 'my-custom-request-id'
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Caveats
|
89
|
+
|
90
|
+
- Only web requests made by _Net::HTTP_ are intercepted. Most popular HTTP libraries use this at their core, including _Faraday_ and _HTTParty_ so this should cover the vast majority of cases but feel free to submit a pull request to add more drivers.
|
91
|
+
- Since _Net::HTTP_ and _ActiveRecord_ are monkeypatched in similar ways to how _RSpec_ and _webmock_ operate, you probably do not want to enable _RequestTagger_ in your test environment. Use `unless Rails.env.test?` in your initializer.
|
92
|
+
|
93
|
+
## Development
|
94
|
+
|
95
|
+
Clone the repository and submit a pull requests to fix any bugs or add any new features.
|
96
|
+
|
97
|
+
Write tests for any new code you write and ensure all tests pass before submitting:
|
98
|
+
|
99
|
+
```bash
|
100
|
+
$ bin/rspec
|
101
|
+
```
|
102
|
+
|
103
|
+
Please also run _Rubocop_ and fix any issues before making a pull request:
|
104
|
+
|
105
|
+
```bash
|
106
|
+
$ bin/rubocop
|
107
|
+
```
|
108
|
+
|
109
|
+
## License
|
110
|
+
|
111
|
+
_RequestTagger_ is licensed under the MIT license. Do whatever you like with the code, just give credit where it's due.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "request_tagger"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/setup
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This code is heavily inspired by RSpec's `allow_any_instance_of` and the
|
4
|
+
# RSpec source code was used as reference material during development.
|
5
|
+
# https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/any_instance/recorder.rb
|
6
|
+
#
|
7
|
+
# Thanks, RSpec devs.
|
8
|
+
# https://github.com/rspec/rspec/blob/master/LICENSE.md
|
9
|
+
module RequestTagger
|
10
|
+
class ActiveRecordMagic
|
11
|
+
WRAPPED_METHODS = %i[exec_query execute].freeze
|
12
|
+
|
13
|
+
def initialize(target = ActiveRecord::Base.connection)
|
14
|
+
@target_class = target.class
|
15
|
+
end
|
16
|
+
|
17
|
+
def wrap
|
18
|
+
WRAPPED_METHODS.each { |method_name| wrap_method(method_name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def restore
|
22
|
+
WRAPPED_METHODS.each { |method_name| restore_method(method_name) }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def wrap_method(method_name)
|
28
|
+
copy_original_method(method_name)
|
29
|
+
|
30
|
+
create_proxy_method(method_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy_original_method(method_name)
|
34
|
+
alias_name = alias_for(method_name)
|
35
|
+
|
36
|
+
@target_class.class_exec do
|
37
|
+
# Note that [the badly-named] `alias_method` actually copies the
|
38
|
+
# method, so we can use it like `cp` but for methods instead of files.
|
39
|
+
alias_method alias_name, method_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_proxy_method(method_name)
|
44
|
+
alias_name = alias_for(method_name)
|
45
|
+
|
46
|
+
@target_class.__send__(:define_method, method_name) do |sql, *args|
|
47
|
+
tagged_sql = "#{RequestTagger::Setup.sql_tag} #{sql}"
|
48
|
+
__send__(alias_name, tagged_sql, *args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def restore_method(method_name)
|
53
|
+
alias_name = alias_for(method_name)
|
54
|
+
@target_class.class_exec do
|
55
|
+
remove_method method_name
|
56
|
+
alias_method method_name, alias_name
|
57
|
+
remove_method alias_name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def alias_for(method_name)
|
62
|
+
:"__request_tagger_original__#{method_name}__"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
# This code is heavily inspired by webmock's NetHttpAdapter
|
6
|
+
# webmock source code was used as reference material during development.
|
7
|
+
# https://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_adapters/net_http.rb
|
8
|
+
# Thanks, webmock devs.
|
9
|
+
# https://github.com/bblimke/webmock/blob/master/LICENSE
|
10
|
+
module RequestTagger
|
11
|
+
class HttpMagic
|
12
|
+
def initialize
|
13
|
+
@original_net_http = original_net_http
|
14
|
+
@magic_net_http = magic_net_http
|
15
|
+
end
|
16
|
+
|
17
|
+
def wrap
|
18
|
+
Net.send(:remove_const, :HTTP)
|
19
|
+
Net.send(:const_set, :HTTP, @magic_net_http)
|
20
|
+
end
|
21
|
+
|
22
|
+
def restore
|
23
|
+
Net.send(:remove_const, :HTTP)
|
24
|
+
Net.send(:const_set, :HTTP, @original_net_http)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def magic_net_http
|
30
|
+
Class.new(Net::HTTP) do
|
31
|
+
def request(request, body = nil, &block)
|
32
|
+
http_tag = RequestTagger::Setup.http_tag
|
33
|
+
if request.get_fields(http_tag[:field]).nil?
|
34
|
+
request.add_field(http_tag[:field], http_tag[:value])
|
35
|
+
end
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def original_net_http
|
42
|
+
Net::HTTP
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RequestTagger
|
4
|
+
class Setup
|
5
|
+
@initialized = false
|
6
|
+
|
7
|
+
def self.start(options = {})
|
8
|
+
raise AlreadyStartedError, I18n.t('errors.start_once') if @initialized
|
9
|
+
|
10
|
+
@options = options
|
11
|
+
|
12
|
+
wrap_active_record
|
13
|
+
wrap_http
|
14
|
+
|
15
|
+
@initialized = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.stop
|
19
|
+
return unless @initialized
|
20
|
+
|
21
|
+
restore_active_record
|
22
|
+
restore_http
|
23
|
+
|
24
|
+
@options = {}
|
25
|
+
@initialized = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.inbound_header
|
29
|
+
@options[:header] || 'HTTP_X_REQUEST_ID'
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.sql_tag
|
33
|
+
"/* #{sql_tag_identifier}: #{sql_sanitize(request_id)} */"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.http_tag
|
37
|
+
{ field: http_tag_identifier, value: request_id }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.request_id=(val)
|
41
|
+
# I would prefer not to use `Thread.current` for storage but, since we
|
42
|
+
# can't control what kind of Rails server is in use, we don't have much
|
43
|
+
# choice.
|
44
|
+
Thread.current[:__request_tagger__request_id__] = val
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.request_id
|
48
|
+
Thread.current[:__request_tagger__request_id__] || uninitialized
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
private
|
53
|
+
|
54
|
+
def wrap_active_record
|
55
|
+
return unless @options.fetch(:tag_sql, true)
|
56
|
+
|
57
|
+
@ar_magic = ActiveRecordMagic.new(active_record_connection)
|
58
|
+
@ar_magic.wrap
|
59
|
+
end
|
60
|
+
|
61
|
+
def wrap_http
|
62
|
+
return unless @options.fetch(:tag_http, true)
|
63
|
+
|
64
|
+
@http_magic = HttpMagic.new
|
65
|
+
@http_magic.wrap
|
66
|
+
end
|
67
|
+
|
68
|
+
def restore_active_record
|
69
|
+
return unless @options.fetch(:tag_sql, true)
|
70
|
+
|
71
|
+
@ar_magic.restore
|
72
|
+
end
|
73
|
+
|
74
|
+
def restore_http
|
75
|
+
return unless @options.fetch(:tag_http, true)
|
76
|
+
|
77
|
+
@http_magic.wrap
|
78
|
+
end
|
79
|
+
|
80
|
+
def sql_tag_identifier
|
81
|
+
@options[:sql_tag_name] || 'request-id'
|
82
|
+
end
|
83
|
+
|
84
|
+
def http_tag_identifier
|
85
|
+
@options[:http_tag_name] || 'X-Request-Id'
|
86
|
+
end
|
87
|
+
|
88
|
+
def uninitialized
|
89
|
+
'[not initialized]'
|
90
|
+
end
|
91
|
+
|
92
|
+
def active_record_connection
|
93
|
+
@options[:active_record_connection] || ActiveRecord::Base.connection
|
94
|
+
end
|
95
|
+
|
96
|
+
def sql_sanitize(val)
|
97
|
+
val.chars.select { |char| sql_whitelist.include?(char) }.join
|
98
|
+
end
|
99
|
+
|
100
|
+
def sql_whitelist
|
101
|
+
# It could be enough to just blacklist '*' but I'd rather be extra
|
102
|
+
# cautious here.
|
103
|
+
alphanum = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
104
|
+
alphanum + %w[[ ] { } ( ) _ -] + [' ']
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RequestTagger
|
4
|
+
module TagRequests
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
before_action :__request_tagger__set_request_id__
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def __request_tagger__set_request_id__
|
14
|
+
field = RequestTagger::Setup.inbound_header
|
15
|
+
RequestTagger::Setup.request_id = request.headers[field]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'i18n'
|
4
|
+
|
5
|
+
require 'request_tagger/version'
|
6
|
+
require 'request_tagger/errors'
|
7
|
+
require 'request_tagger/setup'
|
8
|
+
require 'request_tagger/active_record_magic'
|
9
|
+
require 'request_tagger/http_magic'
|
10
|
+
|
11
|
+
module RequestTagger
|
12
|
+
def self.start(options = {})
|
13
|
+
Setup.start(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.stop
|
17
|
+
Setup.stop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
locales_path = File.expand_path('../locales/*.yml', __dir__)
|
22
|
+
|
23
|
+
I18n.load_path += Dir[locales_path]
|
24
|
+
|
25
|
+
# Avoid i18n conflicts when using as a gem in a Rails application
|
26
|
+
unless Gem.loaded_specs.key?('rails')
|
27
|
+
I18n.backend.load_translations
|
28
|
+
I18n.config.available_locales = :en
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'request_tagger/tag_requests' if Gem.loaded_specs.key?('rails')
|
data/locales/en.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'request_tagger/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'request_tagger'
|
9
|
+
spec.version = RequestTagger::VERSION
|
10
|
+
spec.authors = ['Bob Farrell']
|
11
|
+
spec.email = ['robertanthonyfarrell@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'Tag all ActiveRecord and HTTP traffic with a request ID'
|
14
|
+
spec.description = 'Inject a tracking tag into all SQL and HTTP requests'
|
15
|
+
spec.homepage = 'https://github.com/bobf/request_tagger'
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
spec.bindir = 'bin'
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
|
26
|
+
spec.add_dependency 'i18n', '~> 1.1'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'activerecord', '~> 5.2'
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'rubocop', '~> 0.59.2'
|
33
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3'
|
34
|
+
spec.add_development_dependency 'webmock', '~> 3.4'
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: request_tagger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bob Farrell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.59.2
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.59.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.4'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.4'
|
125
|
+
description: Inject a tracking tag into all SQL and HTTP requests
|
126
|
+
email:
|
127
|
+
- robertanthonyfarrell@gmail.com
|
128
|
+
executables:
|
129
|
+
- console
|
130
|
+
- rspec
|
131
|
+
- rubocop
|
132
|
+
- setup
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files: []
|
135
|
+
files:
|
136
|
+
- ".gitignore"
|
137
|
+
- ".rspec"
|
138
|
+
- ".rubocop.yml"
|
139
|
+
- Gemfile
|
140
|
+
- Gemfile.lock
|
141
|
+
- LICENSE
|
142
|
+
- README.md
|
143
|
+
- Rakefile
|
144
|
+
- bin/console
|
145
|
+
- bin/rspec
|
146
|
+
- bin/rubocop
|
147
|
+
- bin/setup
|
148
|
+
- lib/request_tagger.rb
|
149
|
+
- lib/request_tagger/active_record_magic.rb
|
150
|
+
- lib/request_tagger/errors.rb
|
151
|
+
- lib/request_tagger/http_magic.rb
|
152
|
+
- lib/request_tagger/setup.rb
|
153
|
+
- lib/request_tagger/tag_requests.rb
|
154
|
+
- lib/request_tagger/version.rb
|
155
|
+
- locales/en.yml
|
156
|
+
- request_tagger.gemspec
|
157
|
+
homepage: https://github.com/bobf/request_tagger
|
158
|
+
licenses: []
|
159
|
+
metadata: {}
|
160
|
+
post_install_message:
|
161
|
+
rdoc_options: []
|
162
|
+
require_paths:
|
163
|
+
- lib
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 2.5.2.2
|
177
|
+
signing_key:
|
178
|
+
specification_version: 4
|
179
|
+
summary: Tag all ActiveRecord and HTTP traffic with a request ID
|
180
|
+
test_files: []
|