marlowe 2.1 → 3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +55 -0
- data/.standard.yml +4 -0
- data/History.md +7 -0
- data/Licence.md +11 -13
- data/Manifest.txt +5 -0
- data/README.rdoc +60 -34
- data/Rakefile +3 -3
- data/lib/marlowe/config.rb +116 -0
- data/lib/marlowe/faraday.rb +20 -0
- data/lib/marlowe/middleware.rb +17 -48
- data/lib/marlowe/rails.rb +7 -9
- data/lib/marlowe/simple_formatter.rb +2 -0
- data/lib/marlowe.rb +34 -1
- data/test/test_marlowe.rb +2 -1
- data/test/test_marlowe_config.rb +109 -0
- metadata +20 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a38beee250c02c545fa9a16e99a7e32702f4e027e2d9b87bfee4911210d6f7e
|
4
|
+
data.tar.gz: 3c4b0d3dddec1f5706076a36036fe1b1fcc26a4a662a7ce134fc4e7df7fa5579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1adb8585c41f04ae645af9080f97ae115d2977ed1e0fa0d2bace7b6b896b6dc3e9c10fca4938a40dcd7727a98a552312be9a514e883d48a0dd9058986b7b85ac
|
7
|
+
data.tar.gz: 80bfa345158bcf410251861067b071bbef0362cccac7866c50febbf6b9db557498a6b561aed47b1e62ea0de5988d15a510c896ceca017cc8193c386b1f0a5cdd
|
@@ -0,0 +1,55 @@
|
|
1
|
+
name: Ruby CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- main
|
8
|
+
- master
|
9
|
+
workflow_dispatch:
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
ruby-ci:
|
13
|
+
name: Ruby ${{ matrix.ruby }} - ${{ matrix.os }}
|
14
|
+
|
15
|
+
strategy:
|
16
|
+
fail-fast: true
|
17
|
+
matrix:
|
18
|
+
os:
|
19
|
+
- ubuntu-20.04
|
20
|
+
ruby:
|
21
|
+
- '2.7'
|
22
|
+
- '3.0'
|
23
|
+
- '3.1'
|
24
|
+
- head
|
25
|
+
- jruby
|
26
|
+
- jruby-head
|
27
|
+
- truffleruby
|
28
|
+
- truffleruby-head
|
29
|
+
- truffleruby+graalvm
|
30
|
+
- truffleruby+graalvm-head
|
31
|
+
include:
|
32
|
+
- ruby: head
|
33
|
+
continue-on-error: true
|
34
|
+
- ruby: jruby-head
|
35
|
+
continue-on-error: true
|
36
|
+
- os: ubuntu-22.04
|
37
|
+
ruby: head
|
38
|
+
- os: ubuntu-22.04
|
39
|
+
ruby: '3.1'
|
40
|
+
|
41
|
+
runs-on: ${{ matrix.os }}
|
42
|
+
|
43
|
+
continue-on-error: ${{ matrix.continue-on-error || false }}
|
44
|
+
|
45
|
+
steps:
|
46
|
+
- uses: actions/checkout@v3
|
47
|
+
- uses: ruby/setup-ruby@v1
|
48
|
+
with:
|
49
|
+
ruby-version: ${{ matrix.ruby }}
|
50
|
+
bundler-cache: true
|
51
|
+
|
52
|
+
- run: bundle exec ruby -S rake test --trace
|
53
|
+
- run: bundle exec ruby -S appraisal install
|
54
|
+
- run: bundle exec ruby -S appraisal rake test
|
55
|
+
- run: bundle exec standardrb
|
data/.standard.yml
ADDED
data/History.md
CHANGED
data/Licence.md
CHANGED
@@ -2,26 +2,24 @@
|
|
2
2
|
|
3
3
|
This software is available under an MIT-style licence.
|
4
4
|
|
5
|
-
|
5
|
+
- Copyright 2015–2022 Kinetic Cafe
|
6
6
|
|
7
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
8
8
|
this software and associated documentation files (the "Software"), to deal in
|
9
9
|
the Software without restriction, including without limitation the rights to
|
10
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
11
|
-
|
12
|
-
|
10
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
11
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
12
|
+
subject to the following conditions:
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
permission.
|
14
|
+
- The names of its contributors may not be used to endorse or promote products
|
15
|
+
derived from this software without specific prior written permission.
|
17
16
|
|
18
17
|
The above copyright notice and this permission notice shall be included in all
|
19
18
|
copies or substantial portions of the Software.
|
20
19
|
|
21
20
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
SOFTWARE.
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
22
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
23
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
24
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
.github/workflows/ruby.yml
|
2
|
+
.standard.yml
|
1
3
|
Contributing.md
|
2
4
|
History.md
|
3
5
|
Licence.md
|
@@ -5,9 +7,12 @@ Manifest.txt
|
|
5
7
|
README.rdoc
|
6
8
|
Rakefile
|
7
9
|
lib/marlowe.rb
|
10
|
+
lib/marlowe/config.rb
|
11
|
+
lib/marlowe/faraday.rb
|
8
12
|
lib/marlowe/formatter.rb
|
9
13
|
lib/marlowe/middleware.rb
|
10
14
|
lib/marlowe/rails.rb
|
11
15
|
lib/marlowe/simple_formatter.rb
|
12
16
|
test/minitest_config.rb
|
13
17
|
test/test_marlowe.rb
|
18
|
+
test/test_marlowe_config.rb
|
data/README.rdoc
CHANGED
@@ -14,7 +14,41 @@ correlation across multiple services.
|
|
14
14
|
When using Rails, Marlowe automatically adds itself to the middleware before
|
15
15
|
<tt>Rails::Rack::Logger</tt>.
|
16
16
|
|
17
|
-
|
17
|
+
As of Marlowe 3.0, a Faraday middleware is provided (<tt>require 'marlowe/faraday'</tt>).
|
18
|
+
|
19
|
+
=== Upgrading
|
20
|
+
|
21
|
+
==== from Marlowe 2.0
|
22
|
+
|
23
|
+
In Marlowe 2.0, configuration was entirely specified in Rails configuration or
|
24
|
+
in the Rack +use+ clause. With the addition of a Faraday middleware for use with
|
25
|
+
Marlowe, centralization of the configuration is advisable.
|
26
|
+
|
27
|
+
The existing configuration mechanisms will work (and create instance
|
28
|
+
configurations if provided), but it is instead recommended to use the global
|
29
|
+
configuration:
|
30
|
+
|
31
|
+
# Compatibility with Marlowe 1.0
|
32
|
+
Marlowe.configure do |config|
|
33
|
+
config.header = 'Correlation-ID'
|
34
|
+
config.handler = :simple
|
35
|
+
config.return = false
|
36
|
+
end
|
37
|
+
|
38
|
+
Configuration is static for Marlowe::Middleware or Marlowe::Faraday instances
|
39
|
+
and changes to Marlowe configuration only affect *new* instances. If the
|
40
|
+
configuration options are provided in the middleware +use+ clause, they will
|
41
|
+
override the configured values.
|
42
|
+
|
43
|
+
Marlowe.configure do |config|
|
44
|
+
config.header = 'Correlation-ID'
|
45
|
+
config.handler = :simple
|
46
|
+
config.return = false
|
47
|
+
end
|
48
|
+
use Marlowe::Middleware, handler: :clean
|
49
|
+
# The handler will be the :clean handler for the used middleware.
|
50
|
+
|
51
|
+
==== from Marlowe 1.x
|
18
52
|
|
19
53
|
In Marlowe 1.0, the correlation header was called <tt>Correlation-Id</tt>;
|
20
54
|
since then, Rails 5 and other tools and frameworks (such as Phoenix) have
|
@@ -109,7 +143,7 @@ The correlation id can be accessed throughout the application by accessing the
|
|
109
143
|
|
110
144
|
For a Rails application, you simply need to change the log formatter to one of
|
111
145
|
the provided ones. Correlated versions of both the SimpleFormatter and
|
112
|
-
Formatter are included.
|
146
|
+
Formatter are included.
|
113
147
|
|
114
148
|
# config/environments/development.rb
|
115
149
|
Rails.application.configure do
|
@@ -134,7 +168,7 @@ As {lograge}[https://github.com/roidrage/lograge] supplies its own formatter,
|
|
134
168
|
you will need to do something a little different:
|
135
169
|
|
136
170
|
# config/application.rb
|
137
|
-
|
171
|
+
|
138
172
|
class Application < Rails::Application
|
139
173
|
config.before_initialize do
|
140
174
|
...
|
@@ -146,47 +180,39 @@ you will need to do something a little different:
|
|
146
180
|
end
|
147
181
|
end
|
148
182
|
|
149
|
-
|
150
|
-
|
151
|
-
Catching and creating the correlation ID is a great all on its own, but to
|
152
|
-
really take advantage of the correlation in a service based architecture you'll
|
153
|
-
need to pass the request ID to the next service in the change.
|
154
|
-
|
155
|
-
Here's an example of a {Hurley}[https://github.com/lostisland/hurley] client:
|
183
|
+
=== SemanticLogger
|
156
184
|
|
157
|
-
|
185
|
+
As {semantic_logger}[https://github.com/rocketjob/semantic_logger] provides its
|
186
|
+
own formatters this should be added as a tag. The best way that I can see to do
|
187
|
+
this is to capture the +correlation_id+ in an +on_log+ event:
|
158
188
|
|
159
|
-
|
160
|
-
require 'request_store'
|
189
|
+
# config/initializers/semantic_logger.rb
|
161
190
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
header['X-Request-Id'] = ::RequestStore.store[:correlation_id]
|
191
|
+
SemanticLogger.on_log do |log|
|
192
|
+
if RequestStore[:correlation_id]
|
193
|
+
log.named_tags[:correlation_id] = RequestStore[:correlation_id]
|
166
194
|
end
|
167
195
|
end
|
168
196
|
|
169
|
-
|
170
|
-
{callback machanism}[https://github.com/lostisland/hurley#client-callbacks] to
|
171
|
-
add the outgoing headers:
|
197
|
+
== Clients
|
172
198
|
|
173
|
-
|
174
|
-
|
175
|
-
|
199
|
+
Catching and creating the correlation ID is a great all on its own, but to
|
200
|
+
really take advantage of the correlation in a service based architecture you'll
|
201
|
+
need to pass the request ID to the next service in the change.
|
176
202
|
|
177
|
-
|
178
|
-
|
179
|
-
class Correlator
|
180
|
-
def name
|
181
|
-
:correlator
|
182
|
-
end
|
203
|
+
Here's an example with {Faraday}[https://github.com/lostisland/faraday]:
|
183
204
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
205
|
+
require 'faraday'
|
206
|
+
require 'faraday_middleware'
|
207
|
+
require 'marlowe/faraday'
|
208
|
+
|
209
|
+
conn = Faraday.new(url: 'https://example.org/') do |conn|
|
210
|
+
conn.request :marlowe
|
211
|
+
conn.request :json
|
188
212
|
|
189
|
-
|
213
|
+
conn.response :json
|
214
|
+
conn.adapter Faraday.default_adapter
|
215
|
+
end
|
190
216
|
|
191
217
|
== Install
|
192
218
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,7 @@ spec = Hoe.spec "marlowe" do
|
|
23
23
|
require_ruby_version ">= 2.0", "< 4"
|
24
24
|
|
25
25
|
extra_deps << ["request_store", "~> 1.2"]
|
26
|
-
extra_deps << ["rack", ">=
|
26
|
+
extra_deps << ["rack", ">= 1", "< 4"]
|
27
27
|
|
28
28
|
extra_dev_deps << ["appraisal", "~> 2.1"]
|
29
29
|
extra_dev_deps << ["hoe-doofus", "~> 1.0"]
|
@@ -35,7 +35,7 @@ spec = Hoe.spec "marlowe" do
|
|
35
35
|
extra_dev_deps << ["minitest-bonus-assertions", "~> 3.0"]
|
36
36
|
extra_dev_deps << ["minitest-focus", "~> 1.1"]
|
37
37
|
extra_dev_deps << ["minitest-moar", "~> 0.0"]
|
38
|
-
extra_dev_deps << ["rack-test", "~>
|
38
|
+
extra_dev_deps << ["rack-test", "~> 2.0"]
|
39
39
|
extra_dev_deps << ["rake", ">= 10.0", "< 14"]
|
40
40
|
extra_dev_deps << ["rdoc", ">= 4.2"]
|
41
41
|
extra_dev_deps << ["standard", "~> 1.0"]
|
@@ -45,7 +45,7 @@ end
|
|
45
45
|
|
46
46
|
ENV["RUBYOPT"] = "-W0"
|
47
47
|
|
48
|
-
module Hoe::Publish
|
48
|
+
module Hoe::Publish # :nodoc:
|
49
49
|
alias_method :__make_rdoc_cmd__marlowe__, :make_rdoc_cmd
|
50
50
|
|
51
51
|
def make_rdoc_cmd(*extra_args) # :nodoc:
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configuration object for Marlowe.
|
4
|
+
class Marlowe::Config
|
5
|
+
# The name of the default header to look for and put the correlation id in.
|
6
|
+
CORRELATION_HEADER = "X-Request-Id" # :nodoc:
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# The global Marlowe configuration.
|
10
|
+
def global
|
11
|
+
@global ||= new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override the global Marlowe configuration.
|
15
|
+
def override(opts)
|
16
|
+
new(global, opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure(&block) # :nodoc:
|
20
|
+
@global = new(global, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def clear_global!
|
26
|
+
@global = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The name of the header to inspect. Defaults to 'X-Request-Id'.
|
31
|
+
attr_accessor :header
|
32
|
+
# The HTTP formatted version of the header name to inspect. Defaults to
|
33
|
+
# 'HTTP_X_REQUEST_ID'.
|
34
|
+
attr_reader :http_header
|
35
|
+
# The handler for request correlation IDs. Defaults to sanitizing provided
|
36
|
+
# request IDs or generating a UUID. If <tt>:simple</tt> is provided, provided
|
37
|
+
# request IDs will not be sanitized. A callable (expecting a single input of
|
38
|
+
# any possible existing request ID) may be provided to introduce more complex
|
39
|
+
# request ID handling.
|
40
|
+
attr_accessor :handler
|
41
|
+
# If +true+ (the default), the request correlation ID will be returned as
|
42
|
+
# part of the response headers. Only affects Marlowe::Middleware.
|
43
|
+
attr_accessor :return
|
44
|
+
# If +true+, Marlowe will add code to behave like
|
45
|
+
# <tt>ActionDispatch::RequestId</tt>. Depends on
|
46
|
+
# <tt>ActionDispatch::Request</tt>. Only affects Marlowe::Middleware.
|
47
|
+
attr_accessor :action_dispatch
|
48
|
+
|
49
|
+
# === Option Values
|
50
|
+
#
|
51
|
+
# <tt>:header</tt>:: The name of the header to inspect. Defaults to
|
52
|
+
# 'X-Request-Id'. Also available as
|
53
|
+
# <tt>:correlation_header</tt>.
|
54
|
+
# <tt>:handler</tt>:: The handler for request correlation IDs. Defaults to
|
55
|
+
# sanitizing provided request IDs or generating a UUID.
|
56
|
+
# If <tt>:simple</tt> is provided, provided request IDs
|
57
|
+
# will not be sanitized. A callable (expecting a single
|
58
|
+
# input of any possible existing request ID) may be
|
59
|
+
# provided to introduce more complex request ID
|
60
|
+
# handling.
|
61
|
+
# <tt>:return</tt>:: If +true+ (the default), the request correlation ID
|
62
|
+
# will be returned as part of the response headers.
|
63
|
+
# <tt>:action_dispatch</tt>:: If +true+, Marlowe will add code to behave
|
64
|
+
# like <tt>ActionDispatch::RequestId</tt>.
|
65
|
+
# Depends on <tt>ActionDispatch::Request</tt>.
|
66
|
+
def initialize(base = nil, opts = nil) # :yields: self
|
67
|
+
opts =
|
68
|
+
if base.nil? && opts.nil?
|
69
|
+
{}
|
70
|
+
elsif base.nil? && opts.is_a?(Hash)
|
71
|
+
opts
|
72
|
+
elsif base.is_a?(Hash) && opts.nil?
|
73
|
+
base
|
74
|
+
elsif base.is_a?(self.class) && opts.nil?
|
75
|
+
base.to_hash
|
76
|
+
elsif (base.is_a?(Hash) || base.is_a?(self.class)) && opts.is_a?(Hash)
|
77
|
+
hash =
|
78
|
+
if base.is_a?(self.class)
|
79
|
+
base.to_hash
|
80
|
+
else
|
81
|
+
base
|
82
|
+
end
|
83
|
+
hash.update(opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
@header, @http_header = format_header_name(
|
87
|
+
opts[:header] || opts[:correlation_header] || CORRELATION_HEADER
|
88
|
+
)
|
89
|
+
|
90
|
+
@handler = opts.fetch(:handler, :clean)
|
91
|
+
@return = opts.fetch(:return, true)
|
92
|
+
@action_dispatch = opts.fetch(:action_dispatch, false)
|
93
|
+
|
94
|
+
yield self if block_given?
|
95
|
+
|
96
|
+
freeze
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_hash
|
100
|
+
{
|
101
|
+
header: header,
|
102
|
+
handler: handler,
|
103
|
+
return: self.return,
|
104
|
+
action_dispatch: action_dispatch
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def format_header_name(header)
|
111
|
+
[
|
112
|
+
header.to_s.tr("_", "-").freeze,
|
113
|
+
"HTTP_#{header.to_s.tr("-", "_").upcase}"
|
114
|
+
]
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "marlowe"
|
4
|
+
|
5
|
+
# Marlowe correlation ID middleware for Faraday. Including this into your
|
6
|
+
# request middleware stack will use the captured correlation ID.
|
7
|
+
class Marlowe::Faraday < Faraday::Middleware
|
8
|
+
def initialize(app, opts = {})
|
9
|
+
super(app)
|
10
|
+
@config = Marlowe::Config.override(opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
env[:request_headers][@config.header] =
|
15
|
+
Marlowe.make_request_id(RequestStore[:correlation_id], @config)
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Faraday::Request.register_middleware marlowe: -> { Marlowe::Faraday }
|
data/lib/marlowe/middleware.rb
CHANGED
@@ -6,11 +6,11 @@ require "securerandom"
|
|
6
6
|
|
7
7
|
module Marlowe
|
8
8
|
# Marlowe correlation id middleware. Including this into your middleware
|
9
|
-
# stack will add a correlation id header
|
10
|
-
# that id in a request session variable.
|
9
|
+
# stack will capture or add a correlation id header on an incoming request,
|
10
|
+
# and save that id in a request session variable.
|
11
11
|
class Middleware
|
12
12
|
# The name of the default header to look for and put the correlation id in.
|
13
|
-
CORRELATION_HEADER =
|
13
|
+
CORRELATION_HEADER = Marlowe::Config::CORRELATION_HEADER # :nodoc:
|
14
14
|
|
15
15
|
# Configure the Marlowe middleware to call +app+ with options +opts+.
|
16
16
|
#
|
@@ -31,67 +31,36 @@ module Marlowe
|
|
31
31
|
# <tt>:action_dispatch</tt>:: If +true+, Marlowe will add code to behave
|
32
32
|
# like <tt>ActionDispatch::RequestId</tt>.
|
33
33
|
# Depends on <tt>ActionDispatch::Request</tt>.
|
34
|
-
def initialize(app, opts =
|
34
|
+
def initialize(app, opts = nil)
|
35
35
|
@app = app
|
36
|
-
@
|
37
|
-
opts[:header] || opts[:correlation_header] || CORRELATION_HEADER
|
38
|
-
)
|
39
|
-
@handler = opts.fetch(:handler, :clean)
|
40
|
-
@return = opts.fetch(:return, true)
|
41
|
-
@action_dispatch = opts.fetch(:action_dispatch, false)
|
36
|
+
@config = Marlowe::Config.override(opts)
|
42
37
|
end
|
43
38
|
|
44
39
|
# Stores the incoming correlation id from the +env+ hash. If the correlation
|
45
40
|
# id has not been sent, a new UUID is generated and the +env+ is modified.
|
46
41
|
def call(env)
|
47
|
-
req_id = make_request_id(env[
|
48
|
-
RequestStore.store[:correlation_id] = env[
|
42
|
+
req_id = Marlowe.make_request_id(env[config.http_header], config)
|
43
|
+
RequestStore.store[:correlation_id] = env[config.http_header] = req_id
|
49
44
|
|
50
|
-
if
|
45
|
+
if config.action_dispatch
|
51
46
|
req = ActionDispatch::Request.new(env)
|
52
47
|
req.request_id = req_id
|
53
48
|
end
|
54
49
|
|
55
|
-
|
56
|
-
if
|
57
|
-
headers[
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
50
|
+
app.call(env).tap { |_status, headers, _body|
|
51
|
+
if config.return
|
52
|
+
headers[config.header] =
|
53
|
+
if config.action_dispatch
|
54
|
+
req.request_id
|
55
|
+
else
|
56
|
+
RequestStore.store[:correlation_id]
|
57
|
+
end
|
62
58
|
end
|
63
59
|
}
|
64
60
|
end
|
65
61
|
|
66
62
|
private
|
67
63
|
|
68
|
-
|
69
|
-
[
|
70
|
-
header.to_s.tr("_", "-").freeze,
|
71
|
-
"HTTP_#{header.to_s.tr("-", "_").upcase}"
|
72
|
-
]
|
73
|
-
end
|
74
|
-
|
75
|
-
def make_request_id(request_id)
|
76
|
-
if @handler == :simple
|
77
|
-
simple(request_id)
|
78
|
-
elsif @handler.is_a?(Proc)
|
79
|
-
simple(@handler.call(request_id))
|
80
|
-
else
|
81
|
-
clean(request_id)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def clean(request_id)
|
86
|
-
simple(request_id).gsub(/[^\w\-]/, "")[0, 255]
|
87
|
-
end
|
88
|
-
|
89
|
-
def simple(request_id)
|
90
|
-
if request_id && !request_id.empty? && request_id !~ /\A[[:space]]*\z/
|
91
|
-
request_id
|
92
|
-
else
|
93
|
-
SecureRandom.uuid
|
94
|
-
end
|
95
|
-
end
|
64
|
+
attr_reader :app, :config
|
96
65
|
end
|
97
66
|
end
|
data/lib/marlowe/rails.rb
CHANGED
@@ -6,23 +6,21 @@ module Marlowe
|
|
6
6
|
config = app.config
|
7
7
|
|
8
8
|
opts = {
|
9
|
-
header: config
|
10
|
-
handler: config
|
11
|
-
return: config
|
12
|
-
action_dispatch: config
|
9
|
+
header: config&.marlowe_header || config&.marlowe_correlation_header,
|
10
|
+
handler: config&.marlowe_request_id_handler,
|
11
|
+
return: config&.marlowe_return_request_id,
|
12
|
+
action_dispatch: config&.marlowe_replace_action_dispatch_request_id
|
13
13
|
}.compact
|
14
14
|
|
15
15
|
if opts[:action_dispatch]
|
16
|
-
app.middleware.insert_before ActionDispatch::RequestId,
|
17
|
-
Marlowe::Middleware, opts
|
16
|
+
app.middleware.insert_before ActionDispatch::RequestId, Marlowe::Middleware, opts
|
18
17
|
app.middleware.delete ActionDispatch::RequestId
|
19
18
|
else
|
20
|
-
app.middleware.insert_before Rails::Rack::Logger, Marlowe::Middleware,
|
21
|
-
opts
|
19
|
+
app.middleware.insert_before Rails::Rack::Logger, Marlowe::Middleware, opts
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
25
|
-
def app
|
23
|
+
def app # :nodoc:
|
26
24
|
Rails.application
|
27
25
|
end
|
28
26
|
end
|
data/lib/marlowe.rb
CHANGED
@@ -2,11 +2,44 @@
|
|
2
2
|
|
3
3
|
# Marlowe, a correlation id injector.
|
4
4
|
module Marlowe
|
5
|
-
VERSION = "
|
5
|
+
VERSION = "3.0" # :nodoc:
|
6
6
|
|
7
|
+
require "marlowe/config"
|
7
8
|
require "marlowe/middleware"
|
8
9
|
require "marlowe/rails" if defined? Rails::Railtie
|
9
10
|
|
10
11
|
autoload :Formatter, "marlowe/formatter"
|
11
12
|
autoload :SimpleFormatter, "marlowe/simple_formatter"
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Configure Marlowe
|
16
|
+
def configure(&block)
|
17
|
+
Marlowe::Config.configure(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Make a Marlowe request ID
|
21
|
+
def make_request_id(request_id, config = Marlowe::Config.global)
|
22
|
+
if config.handler == :simple
|
23
|
+
simple(request_id)
|
24
|
+
elsif config.handler.is_a?(Proc)
|
25
|
+
simple(config.handler.call(request_id))
|
26
|
+
else
|
27
|
+
clean(request_id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def clean(request_id)
|
34
|
+
simple(request_id).gsub(/[^\w\-]/, "")[0, 255]
|
35
|
+
end
|
36
|
+
|
37
|
+
def simple(request_id)
|
38
|
+
if request_id && !request_id.empty? && request_id !~ /\A[[:space]]*\z/
|
39
|
+
request_id
|
40
|
+
else
|
41
|
+
SecureRandom.uuid
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
12
45
|
end
|
data/test/test_marlowe.rb
CHANGED
@@ -9,6 +9,7 @@ class TestMarlowe < Minitest::Test
|
|
9
9
|
|
10
10
|
def setup
|
11
11
|
@marlowe_options = {}
|
12
|
+
Marlowe::Config.send(:clear_global!)
|
12
13
|
end
|
13
14
|
|
14
15
|
def app
|
@@ -85,7 +86,7 @@ class TestMarlowe < Minitest::Test
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def test_handler_config_with_proc_handler_returning_nil
|
88
|
-
marlowe_options[:handler] = ->(
|
89
|
+
marlowe_options[:handler] = ->(_item) {}
|
89
90
|
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
90
91
|
assert last_response.header.key?("X-Request-Id")
|
91
92
|
refute_empty last_response.header["X-Request-Id"]
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest_config"
|
4
|
+
|
5
|
+
class TestMarloweConfig < Minitest::Test
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
attr_reader :marlowe_options
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@marlowe_options = {}
|
12
|
+
Marlowe::Config.send(:clear_global!)
|
13
|
+
end
|
14
|
+
|
15
|
+
def app
|
16
|
+
Marlowe.configure do |config|
|
17
|
+
marlowe_options.each do |k, v|
|
18
|
+
config.send(:"#{k}=", v) if config.respond_to?(:"#{k}=")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
options = marlowe_options
|
23
|
+
Rack::Builder.new do
|
24
|
+
use Marlowe::Middleware, options
|
25
|
+
|
26
|
+
run lambda { |_env|
|
27
|
+
[
|
28
|
+
200,
|
29
|
+
{"Content-Type" => "text/plain"},
|
30
|
+
[RequestStore[:correlation_id]]
|
31
|
+
]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_default_config_no_header_value
|
37
|
+
get "/"
|
38
|
+
assert last_response.header.key?("X-Request-Id")
|
39
|
+
refute_empty last_response.header["X-Request-Id"]
|
40
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_default_config_with_header_value
|
44
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "testvalue"}
|
45
|
+
assert last_response.header.key?("X-Request-Id")
|
46
|
+
refute_empty last_response.header["X-Request-Id"]
|
47
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
48
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_header_config_no_header_value
|
52
|
+
marlowe_options[:header] = "Correlation-Id"
|
53
|
+
get "/"
|
54
|
+
assert last_response.header.key?("Correlation-Id")
|
55
|
+
refute_empty last_response.header["Correlation-Id"]
|
56
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_header_config_no_header_with_header_value
|
60
|
+
marlowe_options[:header] = "Correlation-Id"
|
61
|
+
get "/", {}, {"HTTP_CORRELATION_ID" => "testvalue"}
|
62
|
+
assert last_response.header.key?("Correlation-Id")
|
63
|
+
refute_empty last_response.header["Correlation-Id"]
|
64
|
+
assert_equal last_response.header["Correlation-Id"], last_response.body
|
65
|
+
assert_equal "testvalue", last_response.header["Correlation-Id"]
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_handler_config_default_handler
|
69
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
70
|
+
assert last_response.header.key?("X-Request-Id")
|
71
|
+
refute_empty last_response.header["X-Request-Id"]
|
72
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
73
|
+
assert_equal "testvalue", last_response.header["X-Request-Id"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_handler_config_with_simple_handler
|
77
|
+
marlowe_options[:handler] = :simple
|
78
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
79
|
+
assert last_response.header.key?("X-Request-Id")
|
80
|
+
refute_empty last_response.header["X-Request-Id"]
|
81
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
82
|
+
assert_equal "test+value", last_response.header["X-Request-Id"]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_handler_config_with_proc_handler
|
86
|
+
marlowe_options[:handler] = ->(item) { item && item.reverse || SecureRandom.uuid }
|
87
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
88
|
+
assert last_response.header.key?("X-Request-Id")
|
89
|
+
refute_empty last_response.header["X-Request-Id"]
|
90
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
91
|
+
assert_equal "eulav+tset", last_response.header["X-Request-Id"]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_handler_config_with_proc_handler_returning_nil
|
95
|
+
marlowe_options[:handler] = ->(_item) {}
|
96
|
+
get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
|
97
|
+
assert last_response.header.key?("X-Request-Id")
|
98
|
+
refute_empty last_response.header["X-Request-Id"]
|
99
|
+
assert_equal last_response.header["X-Request-Id"], last_response.body
|
100
|
+
assert_match(/\A[-\w]+\z/, last_response.header["X-Request-Id"])
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_return_config_false
|
104
|
+
marlowe_options[:return] = false
|
105
|
+
get "/"
|
106
|
+
refute last_response.header.key?("X-Request-Id")
|
107
|
+
assert_equal RequestStore[:correlation_id], last_response.body
|
108
|
+
end
|
109
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marlowe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '3.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trevor Oke
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-09-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: request_store
|
@@ -31,34 +31,34 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '1'
|
35
35
|
- - "<"
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '4'
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
42
|
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '
|
44
|
+
version: '1'
|
45
45
|
- - "<"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '4'
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: minitest
|
50
50
|
requirement: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '5.
|
54
|
+
version: '5.16'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
57
|
version_requirements: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '5.
|
61
|
+
version: '5.16'
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: appraisal
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,14 +191,14 @@ dependencies:
|
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: '
|
194
|
+
version: '2.0'
|
195
195
|
type: :development
|
196
196
|
prerelease: false
|
197
197
|
version_requirements: !ruby/object:Gem::Requirement
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: '
|
201
|
+
version: '2.0'
|
202
202
|
- !ruby/object:Gem::Dependency
|
203
203
|
name: rake
|
204
204
|
requirement: !ruby/object:Gem::Requirement
|
@@ -281,14 +281,14 @@ dependencies:
|
|
281
281
|
requirements:
|
282
282
|
- - "~>"
|
283
283
|
- !ruby/object:Gem::Version
|
284
|
-
version: '3.
|
284
|
+
version: '3.25'
|
285
285
|
type: :development
|
286
286
|
prerelease: false
|
287
287
|
version_requirements: !ruby/object:Gem::Requirement
|
288
288
|
requirements:
|
289
289
|
- - "~>"
|
290
290
|
- !ruby/object:Gem::Version
|
291
|
-
version: '3.
|
291
|
+
version: '3.25'
|
292
292
|
description: |-
|
293
293
|
{Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that
|
294
294
|
extracts or creates a request ID using a pre-defined header, permitting request
|
@@ -296,6 +296,8 @@ description: |-
|
|
296
296
|
|
297
297
|
When using Rails, Marlowe automatically adds itself to the middleware before
|
298
298
|
<tt>Rails::Rack::Logger</tt>.
|
299
|
+
|
300
|
+
As of Marlowe 3.0, a Faraday middleware is provided (<tt>require 'marlowe/faraday'</tt>).
|
299
301
|
email:
|
300
302
|
- toke@kineticcafe.com
|
301
303
|
- dev@kineticcafe.com
|
@@ -308,6 +310,8 @@ extra_rdoc_files:
|
|
308
310
|
- Manifest.txt
|
309
311
|
- README.rdoc
|
310
312
|
files:
|
313
|
+
- ".github/workflows/ruby.yml"
|
314
|
+
- ".standard.yml"
|
311
315
|
- Contributing.md
|
312
316
|
- History.md
|
313
317
|
- Licence.md
|
@@ -315,12 +319,15 @@ files:
|
|
315
319
|
- README.rdoc
|
316
320
|
- Rakefile
|
317
321
|
- lib/marlowe.rb
|
322
|
+
- lib/marlowe/config.rb
|
323
|
+
- lib/marlowe/faraday.rb
|
318
324
|
- lib/marlowe/formatter.rb
|
319
325
|
- lib/marlowe/middleware.rb
|
320
326
|
- lib/marlowe/rails.rb
|
321
327
|
- lib/marlowe/simple_formatter.rb
|
322
328
|
- test/minitest_config.rb
|
323
329
|
- test/test_marlowe.rb
|
330
|
+
- test/test_marlowe_config.rb
|
324
331
|
homepage: https://github.com/KineticCafe/marlowe/
|
325
332
|
licenses:
|
326
333
|
- MIT
|
@@ -347,7 +354,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
347
354
|
- !ruby/object:Gem::Version
|
348
355
|
version: '0'
|
349
356
|
requirements: []
|
350
|
-
rubygems_version: 3.
|
357
|
+
rubygems_version: 3.3.7
|
351
358
|
signing_key:
|
352
359
|
specification_version: 4
|
353
360
|
summary: "{Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that
|