marlowe 1.0.1 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 138fa6a06399a2de3f1728a7d22c6e30c2911f27
4
- data.tar.gz: ca42fe17d9f5463c905845d99a12bec35acfd96b
2
+ SHA256:
3
+ metadata.gz: 942139256c91a7b04a4549271e176b8dab519dd566df088dc818198e8d285065
4
+ data.tar.gz: 00a699c0d9ce21ada8c453ea638a0f972f64307e7572be8aa3dca7629922f598
5
5
  SHA512:
6
- metadata.gz: 40aa66da8708cecb902f47d682f73340b3e4d91421e5ec5862fb306d493b3e2440e006ff97d350373677450fc09669815ceb833c4b12e4c0ee88ec6d19f01232
7
- data.tar.gz: 25c2d9a63f75ed0de4a9d09cd8d90a5739ccf0fc7bdcddaa95b8742ca10fde6b328089d9abf2c51229576605c62743b75f2608770b8b035255d831d02fd4029c
6
+ metadata.gz: 6dfa8bd46ddc990b643db1d01925da2a694fba37466c4dfacaab2dd36c776aa8f6f26e8fa64b5e35f5e52d91095e8ad1cf368a382f5c2188defc62596be1a63d
7
+ data.tar.gz: ee94d6e57c9dc02ae7690977281dc9a62051465ef45330474aac3e2284f68d438099409736a32806ecff61da2f9191d8eaf9869300f48e6464c943ee6caf608a
data/Contributing.md ADDED
@@ -0,0 +1,71 @@
1
+ ## Contributing
2
+
3
+ We value any contribution to Marlowe you can provide: a bug report,
4
+ a feature request, or code contributions. Marlowe is reasonably complex
5
+ code, so there are a few contribution guidelines:
6
+
7
+ * Changes *will not* be accepted without tests. The test suite is written
8
+ with [Minitest][].
9
+ * Match our coding style.
10
+ * Use a thoughtfully-named topic branch that contains your change. Rebase
11
+ your commits into logical chunks as necessary.
12
+ * Use [quality commit messages][].
13
+ * Do not change the version number; when your patch is accepted and a release
14
+ is made, the version will be updated at that point.
15
+ * Submit a GitHub pull request with your changes.
16
+
17
+ ### Test Dependencies
18
+
19
+ Marlowe uses Ryan Davis’s [Hoe][] to manage the release process, which
20
+ adds a number of rake tasks. You will mostly be interested in:
21
+
22
+ $ rake
23
+
24
+ which runs the tests the same way that:
25
+
26
+ $ rake test
27
+ $ rake travis
28
+
29
+ will do.
30
+
31
+ To assist with the installation of the development dependencies for
32
+ Marlowe, I have provided the simplest possible Gemfile pointing to the
33
+ (generated) `marlowe.gemspec` file. This will permit you to do:
34
+
35
+ $ bundle install
36
+
37
+ to get the development dependencies. If you aleady have `hoe` installed, you
38
+ can accomplish the same thing with:
39
+
40
+ $ rake newb
41
+
42
+ This task will install any missing dependencies, run the tests/specs, and
43
+ generate the RDoc.
44
+
45
+ ### Workflow
46
+
47
+ Here's the most direct way to get your work merged into the project:
48
+
49
+ * Fork the project.
50
+ * Clone down your fork (`git clone
51
+ git://github.com/KineticCafe/marlowe.git`).
52
+ * Create a topic branch to contain your change (`git checkout -b
53
+ my_awesome_feature`).
54
+ * Hack away, add tests. Not necessarily in that order.
55
+ * Make sure everything still passes by running `rake`.
56
+ * If necessary, rebase your commits into logical chunks, without errors.
57
+ * Push the branch up (`git push origin my_awesome_feature`).
58
+ * Create a pull request against KineticCafe/marlowe and describe
59
+ what your change does and the why you think it should be merged.
60
+
61
+ ### Contributors
62
+
63
+ * Trevor Oke [@thefury](https://github.com/thefury) created marlowe.
64
+ * Austin Ziegler [@halostatue](https://github.com/halostatue)
65
+ * Adam Solove [@asolove](https://github.com/asolove)
66
+ * Tim Morton [@tmorton](https://github.com/tmorton)
67
+
68
+ [Minitest]: https://github.com/seattlerb/minitest
69
+ [quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
70
+ [Hoe]: https://github.com/seattlerb/hoe
71
+ [kccoc]: https://github.com/KineticCafe/code-of-conduct
data/History.md ADDED
@@ -0,0 +1,36 @@
1
+ ### 2.1 / 2021-09-08
2
+
3
+ - Allow the use of Ruby 3.
4
+ - Switch to standardruby instead of rubocop.
5
+ - Switch from Travis to Github Actions.
6
+
7
+ ### 2.0 / 2016-11-16
8
+
9
+ - Breaking change: the correlation header defaults to `X-Request-Id` instead of
10
+ `Correlation-Id`.
11
+ - Breaking change: by default, the `correlation_id` is sanitized in the same
12
+ way as `ActionDispatch::RequestId` when provided from an upstream source (only
13
+ alphanumerics with dashes up to 255 characters in length).
14
+ - Breaking change: by default, the `correlation_id` is returned to the client as
15
+ part of the response.
16
+ - Marlowe is more configurable now.
17
+
18
+ ### 1.0.3 / 2016-01-15
19
+
20
+ - Update Readme example of using available formatted subclass.
21
+ - Make the correlation header name configurable
22
+
23
+ ### 1.0.2 / 2015-11-24
24
+
25
+ - Add documentation for using Marlowe with [lograge][].
26
+
27
+ ### 1.0.1 / 2015-10-20
28
+
29
+ - Update gemspec with homepage
30
+ - Update Rakefile
31
+
32
+ ### 1.0.0 / 2015-10-16
33
+
34
+ - Initial Commit
35
+
36
+ [lograge]: https://github.com/roidrage/lograge
@@ -1,8 +1,8 @@
1
- cence
1
+ ## Licence
2
2
 
3
3
  This software is available under an MIT-style licence.
4
4
 
5
- * Copyright 2015 Kinetic Cafe
5
+ * Copyright 2015–2016 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
@@ -11,9 +11,9 @@ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11
11
  of the Software, and to permit persons to whom the Software is furnished to do
12
12
  so, subject to the following conditions:
13
13
 
14
- * The names of its contributors may not be used to endorse or promote
15
- products derived from this software without specific prior written
16
- permission.
14
+ * The names of its contributors may not be used to endorse or promote
15
+ products derived from this software without specific prior written
16
+ permission.
17
17
 
18
18
  The above copyright notice and this permission notice shall be included in all
19
19
  copies or substantial portions of the Software.
data/Manifest.txt CHANGED
@@ -1,6 +1,6 @@
1
- .autotest
2
- History.rdoc
3
- Licence.rdoc
1
+ Contributing.md
2
+ History.md
3
+ Licence.md
4
4
  Manifest.txt
5
5
  README.rdoc
6
6
  Rakefile
@@ -9,3 +9,5 @@ lib/marlowe/formatter.rb
9
9
  lib/marlowe/middleware.rb
10
10
  lib/marlowe/rails.rb
11
11
  lib/marlowe/simple_formatter.rb
12
+ test/minitest_config.rb
13
+ test/test_marlowe.rb
data/README.rdoc CHANGED
@@ -1,27 +1,104 @@
1
- = marlowe
1
+ = Marlowe, Your Request Sleuth
2
+
3
+ code :: https://github.com/KineticCafe/marlowe/
4
+ issues :: https://github.com/KineticCafe/marlowe/issues
5
+ docs :: http://www.rubydoc.info/github/KineticCafe/marlowe/master
6
+ continuous integration :: {<img src="https://travis-ci.org/KineticCafe/marlowe.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/KineticCafe/marlowe]
2
7
 
3
8
  == Description
4
9
 
5
- {marlowe}[https://github.com/KineticCafe/marlowe] provides a correlation id
6
- header for Rails and Rack applications to correlate logs for a single request
7
- across multiple services.
10
+ {Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that
11
+ extracts or creates a request ID using a pre-defined header, permitting request
12
+ correlation across multiple services.
8
13
 
9
- == Install
14
+ When using Rails, Marlowe automatically adds itself to the middleware before
15
+ <tt>Rails::Rack::Logger</tt>.
10
16
 
11
- You can install it as a gem:
17
+ === Upgrading from Marlowe 1.x
12
18
 
13
- $ gem install marlowe
19
+ In Marlowe 1.0, the correlation header was called <tt>Correlation-Id</tt>;
20
+ since then, Rails 5 and other tools and frameworks (such as Phoenix) have
21
+ standardized on the header <tt>X-Request-Id</tt>. Marlowe 2.0 changes to this
22
+ header by default.
14
23
 
15
- or add it into a Gemfile:
24
+ To keep complete compatibility with Marlowe 1.0, the following should be used:
16
25
 
17
- gem 'marlowe'
26
+ # Rails
27
+ config.marlowe_header = 'Correlation-Id'
28
+ config.marlowe_handler = :simple
29
+ config.marlowe_return = false
30
+
31
+ # Rack
32
+ use Marlowe::Middleware, header: 'Correlation-Id', handler: :simple, return: false
18
33
 
19
34
  == Configuration
20
35
 
21
- There is no configuration as of yet. Future plans include configuring the name
22
- of the correlation id header.
36
+ Marlowe has three main configuration options: the request ID header, the
37
+ request ID handler, and the request ID return. The options may be provided to
38
+ the Rack +use+ command as a keyword option, or set in a corresponding
39
+ <tt>marlowe_<em>option</em></tt> configuration variable in Rails.
40
+
41
+ === Request ID Header
42
+
43
+ Specifies the header to be used for the request correlation ID. Defaults to
44
+ <tt>X-Request-Id</tt>.
45
+
46
+ # Rails
47
+ config.marlowe_header = 'Correlation-Id'
48
+ # OR: config.marlowe_correlation_header = 'Correlation-Id'
49
+
50
+ # Rack
51
+ use Marlowe::Middleware, header: 'Correlation-Id'
52
+ # OR: use Marlowe::Middleware, correlation_header: 'Correlation-Id'
53
+
54
+ Marlowe will convert this to an appropriate HTTP header (in the Rack +env+
55
+ parameter, the above header would be represented as
56
+ <tt>env['HTTP_CORRELATION_ID']</tt>).
57
+
58
+ === Request ID Handler
59
+
60
+ Specifies the method for sanitizing or generating the request correlation ID.
61
+ Values can be <tt>:clean</tt> (the default, which limits incoming correlation
62
+ IDs to 255 alphanumeric-or-dash characters), <tt>:simple</tt> (does not limit
63
+ incoming correlation IDs), or a proc to transform or generate a correlation ID.
64
+
65
+ In all cases, if a correlation request ID is not handled, a UUID will be
66
+ generated.
67
+
68
+ # Rails
69
+ config.marlowe_handler = :simple
70
+ config.marlowe_handler = ->(req_id) {
71
+ req_id.try(:reverse) || SecureRandom.uuid
72
+ }
73
+
74
+ # Rack
75
+ use Marlowe::Middleware, handler: :simple
76
+ use Marlowe::Middleware, handler: ->(req_id) {
77
+ req_id.try(:reverse) || SecureRandom.uuid
78
+ }
79
+
80
+ === Request ID Return
81
+
82
+ If +true+ (the default), the request correlation ID will be returned to the
83
+ client in the same header that it was provided in.
84
+
85
+ # Rails
86
+ config.marlowe_return = false
87
+
88
+ # Rack
89
+ use Marlowe::Middleware, return: false
90
+
91
+ === Using Marlowe with Rails 5
23
92
 
24
- == Accesing the Correlation ID
93
+ Rails 5 includes the <tt>ActionDispatch::RequestId</tt> middleware, reducing
94
+ the need for Marlowe. Marlowe is more configurable than the Rails 5 default, so
95
+ set +marlowe_replace_action_dispatch_request_id+ to true to have
96
+ <tt>Marlowe::Middleware</tt> will replace <tt>ActionDispatch::RequestId</tt>:
97
+
98
+ # Rails only
99
+ config.marlowe_replace_action_dispatch_request_id = true
100
+
101
+ == Accessing the Correlation ID
25
102
 
26
103
  The correlation id can be accessed throughout the application by accessing the
27
104
  {RequestStore}[https://github.com/steveklabnik/request_store] storage.
@@ -30,16 +107,15 @@ The correlation id can be accessed throughout the application by accessing the
30
107
 
31
108
  == Logging
32
109
 
33
- For a rails application, you simply need to change the log formatter to one of
34
- the provided ones. Correlated versions of both the SimpleFormatter and Formatter
35
- are included.
110
+ For a Rails application, you simply need to change the log formatter to one of
111
+ the provided ones. Correlated versions of both the SimpleFormatter and
112
+ Formatter are included.
36
113
 
37
114
  # config/environments/development.rb
38
115
  Rails.application.configure do
39
- config.log_formatter = CorrelatedSimpleFormatter.new
116
+ config.log_formatter = Marlowe::SimpleFormatter.new
40
117
  end
41
118
 
42
-
43
119
  To create your own formatter, you'll need to access the RequestStore storage.
44
120
  You can use this pattern if you've rolled your own logger/formatter:
45
121
 
@@ -52,12 +128,29 @@ You can use this pattern if you've rolled your own logger/formatter:
52
128
  end
53
129
  end
54
130
 
131
+ === Lograge
132
+
133
+ As {lograge}[https://github.com/roidrage/lograge] supplies its own formatter,
134
+ you will need to do something a little different:
135
+
136
+ # config/application.rb
137
+
138
+ class Application < Rails::Application
139
+ config.before_initialize do
140
+ ...
141
+ # use lograge for all request logs
142
+ config.lograge.enabled = true
143
+ config.lograge.custom_options = lambda do |event|
144
+ { correlation_id: RequestStore[:correlation_id] }
145
+ end
146
+ end
147
+ end
55
148
 
56
149
  == Clients
57
150
 
58
151
  Catching and creating the correlation ID is a great all on its own, but to
59
152
  really take advantage of the correlation in a service based architecture you'll
60
- need to pass the id to the next service in the change.
153
+ need to pass the request ID to the next service in the change.
61
154
 
62
155
  Here's an example of a {Hurley}[https://github.com/lostisland/hurley] client:
63
156
 
@@ -69,16 +162,16 @@ Here's an example of a {Hurley}[https://github.com/lostisland/hurley] client:
69
162
  class Hurley::CorrelatedClient < Hurley::Client
70
163
  def initialize(*args, &block)
71
164
  super
72
- header['Correlation-Id'] = ::RequestStore.store[:correlation_id]
165
+ header['X-Request-Id'] = ::RequestStore.store[:correlation_id]
73
166
  end
74
167
  end
75
168
 
76
- If you have long-lived Hurley clients, it is also possible to use the Hurley {callback
77
- machanism}[https://github.com/lostisland/hurley#client-callbacks] to add the
78
- outgoing headers:
169
+ If you have long-lived Hurley clients, it is also possible to use the Hurley
170
+ {callback machanism}[https://github.com/lostisland/hurley#client-callbacks] to
171
+ add the outgoing headers:
79
172
 
80
173
  client.before_call do |request|
81
- request.header['Correlation-Id'] = ::RequestStore.store[:correlation_id]
174
+ request.header['X-Request-Id'] = ::RequestStore.store[:correlation_id]
82
175
  end
83
176
 
84
177
  or
@@ -89,11 +182,36 @@ or
89
182
  end
90
183
 
91
184
  def call(request)
92
- request.header['Correlation-Id'] = ::RequestStore.store[:correlation_id]
185
+ request.header['X-Request-Id'] = ::RequestStore.store[:correlation_id]
93
186
  end
94
187
  end
95
188
 
96
189
  client.before_call(Correlator.new)
97
190
 
191
+ == Install
192
+
193
+ Add Marlowe to your Gemfile:
194
+
195
+ gem 'marlowe', '~> 2.0'
196
+
197
+ Or manually install:
198
+
199
+ $ gem install marlowe
200
+
201
+ == Marlowe Semantic Versioning
202
+
203
+ Marlowe uses a {Semantic Versioning}[http://semver.org/] scheme with one
204
+ significant change:
205
+
206
+ * When PATCH is zero (+0+), it will be omitted from version references.
207
+
208
+ Additionally, the major version will generally be reserved for plug-in
209
+ infrastructure changes.
98
210
 
211
+ == Community and Contributing
99
212
 
213
+ Marlowe welcomes your contributions as described in
214
+ {Contributing.md}[https://github.com/KineticCafe/marlowe/blob/master/Contributing.md].
215
+ This project, like all Kinetic Cafe {open source
216
+ projects}[https://github.com/KineticCafe], is under the Kinetic Cafe Open
217
+ Source {Code of Conduct}[https://github.com/KineticCafe/code-of-conduct].
data/Rakefile CHANGED
@@ -1,14 +1,68 @@
1
- # -*- ruby -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "rubygems"
4
4
  require "hoe"
5
+ require "rake/clean"
5
6
 
6
- Hoe.plugin :minitest
7
+ Hoe.plugin :doofus
8
+ Hoe.plugin :email unless ENV["CI"]
9
+ Hoe.plugin :gemspec2
7
10
  Hoe.plugin :git
11
+ Hoe.plugin :minitest
12
+ Hoe.plugin :rubygems
8
13
 
9
- Hoe.spec "marlowe" do
14
+ spec = Hoe.spec "marlowe" do
10
15
  developer("Trevor Oke", "toke@kineticcafe.com")
16
+ developer("Kinetic Cafe", "dev@kineticcafe.com")
17
+
18
+ self.history_file = "History.md"
19
+ self.readme_file = "README.rdoc"
20
+
11
21
  license "MIT"
22
+
23
+ require_ruby_version ">= 2.0", "< 4"
24
+
25
+ extra_deps << ["request_store", "~> 1.2"]
26
+ extra_deps << ["rack", ">= 0.9", "< 3"]
27
+
28
+ extra_dev_deps << ["appraisal", "~> 2.1"]
29
+ extra_dev_deps << ["hoe-doofus", "~> 1.0"]
30
+ extra_dev_deps << ["hoe-gemspec2", "~> 1.1"]
31
+ extra_dev_deps << ["hoe-git", "~> 1.6"]
32
+ extra_dev_deps << ["hoe-rubygems", "~> 1.0"]
33
+ extra_dev_deps << ["minitest", "~> 5.4"]
34
+ extra_dev_deps << ["minitest-autotest", "~> 1.0"]
35
+ extra_dev_deps << ["minitest-bonus-assertions", "~> 3.0"]
36
+ extra_dev_deps << ["minitest-focus", "~> 1.1"]
37
+ extra_dev_deps << ["minitest-moar", "~> 0.0"]
38
+ extra_dev_deps << ["rack-test", "~> 1.0"]
39
+ extra_dev_deps << ["rake", ">= 10.0", "< 14"]
40
+ extra_dev_deps << ["rdoc", ">= 4.2"]
41
+ extra_dev_deps << ["standard", "~> 1.0"]
42
+ extra_dev_deps << ["simplecov", "~> 0.21"]
43
+ extra_dev_deps << ["psych", "~> 3.1"]
44
+ end
45
+
46
+ ENV["RUBYOPT"] = "-W0"
47
+
48
+ module Hoe::Publish #:nodoc:
49
+ alias_method :__make_rdoc_cmd__marlowe__, :make_rdoc_cmd
50
+
51
+ def make_rdoc_cmd(*extra_args) # :nodoc:
52
+ spec.extra_rdoc_files.reject! { |f| f == "Manifest.txt" }
53
+ __make_rdoc_cmd__marlowe__(*extra_args)
54
+ end
55
+ end
56
+
57
+ if File.exist?(".simplecov-prelude.rb")
58
+ namespace :test do
59
+ task :coverage do
60
+ spec.test_prelude = 'load ".simplecov-prelude.rb"'
61
+ Rake::Task["test"].execute
62
+ end
63
+
64
+ CLOBBER << "coverage"
65
+ end
12
66
  end
13
67
 
14
- # vim: syntax=ruby
68
+ CLOBBER << "tmp"
@@ -1,13 +1,13 @@
1
- require 'request_store'
1
+ # frozen_string_literal: true
2
2
 
3
- module Marlowe
3
+ require "request_store"
4
4
 
5
+ module Marlowe
5
6
  # Marlowe::Formatter is a subclass of +ActiveSupport::Logger::Formatter+
6
7
  # that adds a correlation id string to a rails log.
7
8
  class Formatter < ActiveSupport::Logger::Formatter
8
-
9
9
  # Overrides the formatter return to add the correlation id.
10
- def call(severity, timestamp, progname, msg)
10
+ def call(_severity, _timestamp, _progname, _msg)
11
11
  "[#{RequestStore.store[:correlation_id]}] #{super}"
12
12
  end
13
13
  end
@@ -1,26 +1,97 @@
1
- require 'rack'
2
- require 'request_store'
3
- require 'securerandom'
1
+ # frozen_string_literal: true
4
2
 
5
- module Marlowe
6
- # Marlowe correlation id middleware. Including this into your
7
- # middleware stack will add a 'Correlation-Id' header as an incoming
8
- # request, and save that id in a request session variable.
3
+ require "rack"
4
+ require "request_store"
5
+ require "securerandom"
9
6
 
7
+ module Marlowe
8
+ # Marlowe correlation id middleware. Including this into your middleware
9
+ # stack will add a correlation id header as an incoming request, and save
10
+ # that id in a request session variable.
10
11
  class Middleware
11
- # Sets the the rack application to +app+
12
- def initialize(app)
12
+ # The name of the default header to look for and put the correlation id in.
13
+ CORRELATION_HEADER = "X-Request-Id" #:nodoc:
14
+
15
+ # Configure the Marlowe middleware to call +app+ with options +opts+.
16
+ #
17
+ # === Options
18
+ #
19
+ # <tt>:header</tt>:: The name of the header to inspect. Defaults to
20
+ # 'X-Request-Id'. Also available as
21
+ # <tt>:correlation_header</tt>.
22
+ # <tt>:handler</tt>:: The handler for request correlation IDs. Defaults to
23
+ # sanitizing provided request IDs or generating a UUID.
24
+ # If <tt>:simple</tt> is provided, provided request IDs
25
+ # will not be sanitized. A callable (expecting a single
26
+ # input of any possible existing request ID) may be
27
+ # provided to introduce more complex request ID
28
+ # handling.
29
+ # <tt>:return</tt>:: If +true+ (the default), the request correlation ID
30
+ # will be returned as part of the response headers.
31
+ # <tt>:action_dispatch</tt>:: If +true+, Marlowe will add code to behave
32
+ # like <tt>ActionDispatch::RequestId</tt>.
33
+ # Depends on <tt>ActionDispatch::Request</tt>.
34
+ def initialize(app, opts = {})
13
35
  @app = app
36
+ @header, @http_header = format_header_name(
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)
14
42
  end
15
43
 
16
44
  # Stores the incoming correlation id from the +env+ hash. If the correlation
17
45
  # id has not been sent, a new UUID is generated and the +env+ is modified.
18
46
  def call(env)
19
- env['HTTP_CORRELATION_ID'] ||= SecureRandom.uuid
20
- RequestStore.store[:correlation_id] = env['HTTP_CORRELATION_ID']
47
+ req_id = make_request_id(env[@http_header])
48
+ RequestStore.store[:correlation_id] = env[@http_header] = req_id
49
+
50
+ if @action_dispatch
51
+ req = ActionDispatch::Request.new(env)
52
+ req.request_id = req_id
53
+ end
54
+
55
+ @app.call(env).tap { |_status, headers, _body|
56
+ if @return
57
+ headers[@header] = if @action_dispatch
58
+ req.request_id
59
+ else
60
+ RequestStore.store[:correlation_id]
61
+ end
62
+ end
63
+ }
64
+ end
65
+
66
+ private
67
+
68
+ def format_header_name(header)
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
21
88
 
22
- @status, @headers, @response = @app.call(env)
23
- [@status, @headers, @response]
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
24
95
  end
25
96
  end
26
97
  end
data/lib/marlowe/rails.rb CHANGED
@@ -1,11 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Marlowe
2
- class Railtie < Rails::Railtie
3
- initializer 'marlowe.configure_rails_initialization' do
4
- app.middleware.insert_before Rails::Rack::Logger, Marlowe::Middleware
4
+ class Railtie < Rails::Railtie # :nodoc:
5
+ initializer "marlowe.configure_rails_initialization" do
6
+ config = app.config
7
+
8
+ opts = {
9
+ header: config.try(:marlowe_header) || config.try(:marlowe_correlation_header),
10
+ handler: config.try(:marlowe_request_id_handler),
11
+ return: config.try(:marlowe_return_request_id),
12
+ action_dispatch: config.try(:marlowe_replace_action_dispatch_request_id)
13
+ }.compact
14
+
15
+ if opts[:action_dispatch]
16
+ app.middleware.insert_before ActionDispatch::RequestId,
17
+ Marlowe::Middleware, opts
18
+ app.middleware.delete ActionDispatch::RequestId
19
+ else
20
+ app.middleware.insert_before Rails::Rack::Logger, Marlowe::Middleware,
21
+ opts
22
+ end
5
23
  end
6
24
 
7
- #:nodoc:
8
- def app
25
+ def app #:nodoc:
9
26
  Rails.application
10
27
  end
11
28
  end
@@ -1,16 +1,13 @@
1
- require 'request_store'
1
+ require "request_store"
2
2
 
3
3
  module Marlowe
4
4
  # Marlowe::SimpleFormatter is a subclass of
5
5
  # +ActiveSupport::Logger::SimpleFormatter+ that adds a correlation id
6
6
  # string to a rails log.
7
7
  class SimpleFormatter < ActiveSupport::Logger::SimpleFormatter
8
-
9
8
  # Overrides the formatter return to add the correlation id.
10
9
  def call(severity, timestamp, progname, msg)
11
10
  "[#{RequestStore.store[:correlation_id]}] #{super}"
12
11
  end
13
12
  end
14
13
  end
15
-
16
-
data/lib/marlowe.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Marlowe, a correlation id injector.
2
4
  module Marlowe
3
- VERSION = '1.0.1' #:nodoc:
5
+ VERSION = "2.1" #:nodoc:
4
6
 
5
- require 'marlowe/middleware'
6
- require 'marlowe/rails' if defined? Rails::Railtie
7
+ require "marlowe/middleware"
8
+ require "marlowe/rails" if defined? Rails::Railtie
7
9
 
8
- autoload :Formatter, 'marlowe/formatter'
9
- autoload :SimpleFormatter, 'marlowe/simple_formatter'
10
+ autoload :Formatter, "marlowe/formatter"
11
+ autoload :SimpleFormatter, "marlowe/simple_formatter"
10
12
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "minitest"
4
+
5
+ require "rack/test"
6
+ require "rack/mock"
7
+ require "minitest/autorun"
8
+ require "minitest/focus"
9
+ require "minitest/moar"
10
+
11
+ require "marlowe"
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest_config"
4
+
5
+ class TestMarlowe < Minitest::Test
6
+ include Rack::Test::Methods
7
+
8
+ attr_reader :marlowe_options
9
+
10
+ def setup
11
+ @marlowe_options = {}
12
+ end
13
+
14
+ def app
15
+ options = marlowe_options
16
+ Rack::Builder.new do
17
+ use Marlowe::Middleware, options
18
+
19
+ run lambda { |_env|
20
+ [
21
+ 200,
22
+ {"Content-Type" => "text/plain"},
23
+ [RequestStore[:correlation_id]]
24
+ ]
25
+ }
26
+ end
27
+ end
28
+
29
+ def test_default_config_no_header_value
30
+ get "/"
31
+ assert last_response.header.key?("X-Request-Id")
32
+ refute_empty last_response.header["X-Request-Id"]
33
+ assert_equal last_response.header["X-Request-Id"], last_response.body
34
+ end
35
+
36
+ def test_default_config_with_header_value
37
+ get "/", {}, {"HTTP_X_REQUEST_ID" => "testvalue"}
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
+ assert_equal "testvalue", last_response.header["X-Request-Id"]
42
+ end
43
+
44
+ def test_header_config_no_header_value
45
+ marlowe_options[:header] = "Correlation-Id"
46
+ get "/"
47
+ assert last_response.header.key?("Correlation-Id")
48
+ refute_empty last_response.header["Correlation-Id"]
49
+ assert_equal last_response.header["Correlation-Id"], last_response.body
50
+ end
51
+
52
+ def test_header_config_no_header_with_header_value
53
+ marlowe_options[:header] = "Correlation-Id"
54
+ get "/", {}, {"HTTP_CORRELATION_ID" => "testvalue"}
55
+ assert last_response.header.key?("Correlation-Id")
56
+ refute_empty last_response.header["Correlation-Id"]
57
+ assert_equal last_response.header["Correlation-Id"], last_response.body
58
+ assert_equal "testvalue", last_response.header["Correlation-Id"]
59
+ end
60
+
61
+ def test_handler_config_default_handler
62
+ get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
63
+ assert last_response.header.key?("X-Request-Id")
64
+ refute_empty last_response.header["X-Request-Id"]
65
+ assert_equal last_response.header["X-Request-Id"], last_response.body
66
+ assert_equal "testvalue", last_response.header["X-Request-Id"]
67
+ end
68
+
69
+ def test_handler_config_with_simple_handler
70
+ marlowe_options[:handler] = :simple
71
+ get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
72
+ assert last_response.header.key?("X-Request-Id")
73
+ refute_empty last_response.header["X-Request-Id"]
74
+ assert_equal last_response.header["X-Request-Id"], last_response.body
75
+ assert_equal "test+value", last_response.header["X-Request-Id"]
76
+ end
77
+
78
+ def test_handler_config_with_proc_handler
79
+ marlowe_options[:handler] = ->(item) { item && item.reverse || SecureRandom.uuid }
80
+ get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
81
+ assert last_response.header.key?("X-Request-Id")
82
+ refute_empty last_response.header["X-Request-Id"]
83
+ assert_equal last_response.header["X-Request-Id"], last_response.body
84
+ assert_equal "eulav+tset", last_response.header["X-Request-Id"]
85
+ end
86
+
87
+ def test_handler_config_with_proc_handler_returning_nil
88
+ marlowe_options[:handler] = ->(item) {}
89
+ get "/", {}, {"HTTP_X_REQUEST_ID" => "test+value"}
90
+ assert last_response.header.key?("X-Request-Id")
91
+ refute_empty last_response.header["X-Request-Id"]
92
+ assert_equal last_response.header["X-Request-Id"], last_response.body
93
+ assert_match(/\A[-\w]+\z/, last_response.header["X-Request-Id"])
94
+ end
95
+
96
+ def test_return_config_false
97
+ marlowe_options[:return] = false
98
+ get "/"
99
+ refute last_response.header.key?("X-Request-Id")
100
+ assert_equal RequestStore[:correlation_id], last_response.body
101
+ end
102
+ end
metadata CHANGED
@@ -1,73 +1,316 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marlowe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: '2.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trevor Oke
8
- autorequire:
8
+ - Kinetic Cafe
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-10-20 00:00:00.000000000 Z
12
+ date: 2021-09-09 00:00:00.000000000 Z
12
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: request_store
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rack
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0.9'
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: '3'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0.9'
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
13
48
  - !ruby/object:Gem::Dependency
14
49
  name: minitest
15
50
  requirement: !ruby/object:Gem::Requirement
16
51
  requirements:
17
52
  - - "~>"
18
53
  - !ruby/object:Gem::Version
19
- version: '5.8'
54
+ version: '5.14'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.14'
62
+ - !ruby/object:Gem::Dependency
63
+ name: appraisal
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.1'
76
+ - !ruby/object:Gem::Dependency
77
+ name: hoe-doofus
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: hoe-gemspec2
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.1'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.1'
104
+ - !ruby/object:Gem::Dependency
105
+ name: hoe-git
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.6'
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.6'
118
+ - !ruby/object:Gem::Dependency
119
+ name: hoe-rubygems
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ - !ruby/object:Gem::Dependency
133
+ name: minitest-autotest
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.0'
146
+ - !ruby/object:Gem::Dependency
147
+ name: minitest-bonus-assertions
148
+ requirement: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.0'
153
+ type: :development
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: minitest-focus
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1.1'
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.1'
174
+ - !ruby/object:Gem::Dependency
175
+ name: minitest-moar
176
+ requirement: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.0'
181
+ type: :development
182
+ prerelease: false
183
+ version_requirements: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '0.0'
188
+ - !ruby/object:Gem::Dependency
189
+ name: rack-test
190
+ requirement: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '1.0'
20
195
  type: :development
21
196
  prerelease: false
22
197
  version_requirements: !ruby/object:Gem::Requirement
23
198
  requirements:
24
199
  - - "~>"
25
200
  - !ruby/object:Gem::Version
26
- version: '5.8'
201
+ version: '1.0'
202
+ - !ruby/object:Gem::Dependency
203
+ name: rake
204
+ requirement: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '10.0'
209
+ - - "<"
210
+ - !ruby/object:Gem::Version
211
+ version: '14'
212
+ type: :development
213
+ prerelease: false
214
+ version_requirements: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '10.0'
219
+ - - "<"
220
+ - !ruby/object:Gem::Version
221
+ version: '14'
27
222
  - !ruby/object:Gem::Dependency
28
223
  name: rdoc
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '4.2'
229
+ type: :development
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '4.2'
236
+ - !ruby/object:Gem::Dependency
237
+ name: standard
29
238
  requirement: !ruby/object:Gem::Requirement
30
239
  requirements:
31
240
  - - "~>"
32
241
  - !ruby/object:Gem::Version
33
- version: '4.0'
242
+ version: '1.0'
34
243
  type: :development
35
244
  prerelease: false
36
245
  version_requirements: !ruby/object:Gem::Requirement
37
246
  requirements:
38
247
  - - "~>"
39
248
  - !ruby/object:Gem::Version
40
- version: '4.0'
249
+ version: '1.0'
250
+ - !ruby/object:Gem::Dependency
251
+ name: simplecov
252
+ requirement: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - "~>"
255
+ - !ruby/object:Gem::Version
256
+ version: '0.21'
257
+ type: :development
258
+ prerelease: false
259
+ version_requirements: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - "~>"
262
+ - !ruby/object:Gem::Version
263
+ version: '0.21'
264
+ - !ruby/object:Gem::Dependency
265
+ name: psych
266
+ requirement: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - "~>"
269
+ - !ruby/object:Gem::Version
270
+ version: '3.1'
271
+ type: :development
272
+ prerelease: false
273
+ version_requirements: !ruby/object:Gem::Requirement
274
+ requirements:
275
+ - - "~>"
276
+ - !ruby/object:Gem::Version
277
+ version: '3.1'
41
278
  - !ruby/object:Gem::Dependency
42
279
  name: hoe
43
280
  requirement: !ruby/object:Gem::Requirement
44
281
  requirements:
45
282
  - - "~>"
46
283
  - !ruby/object:Gem::Version
47
- version: '3.14'
284
+ version: '3.23'
48
285
  type: :development
49
286
  prerelease: false
50
287
  version_requirements: !ruby/object:Gem::Requirement
51
288
  requirements:
52
289
  - - "~>"
53
290
  - !ruby/object:Gem::Version
54
- version: '3.14'
55
- description: "{marlowe}[https://github.com/KineticCafe/marlowe] provides a correlation
56
- id \nheader for Rails and Rack applications to correlate logs for a single request
57
- \nacross multiple services."
291
+ version: '3.23'
292
+ description: |-
293
+ {Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that
294
+ extracts or creates a request ID using a pre-defined header, permitting request
295
+ correlation across multiple services.
296
+
297
+ When using Rails, Marlowe automatically adds itself to the middleware before
298
+ <tt>Rails::Rack::Logger</tt>.
58
299
  email:
59
300
  - toke@kineticcafe.com
301
+ - dev@kineticcafe.com
60
302
  executables: []
61
303
  extensions: []
62
304
  extra_rdoc_files:
63
- - History.rdoc
64
- - Licence.rdoc
305
+ - Contributing.md
306
+ - History.md
307
+ - Licence.md
65
308
  - Manifest.txt
66
309
  - README.rdoc
67
310
  files:
68
- - ".autotest"
69
- - History.rdoc
70
- - Licence.rdoc
311
+ - Contributing.md
312
+ - History.md
313
+ - Licence.md
71
314
  - Manifest.txt
72
315
  - README.rdoc
73
316
  - Rakefile
@@ -76,11 +319,15 @@ files:
76
319
  - lib/marlowe/middleware.rb
77
320
  - lib/marlowe/rails.rb
78
321
  - lib/marlowe/simple_formatter.rb
79
- homepage:
322
+ - test/minitest_config.rb
323
+ - test/test_marlowe.rb
324
+ homepage: https://github.com/KineticCafe/marlowe/
80
325
  licenses:
81
326
  - MIT
82
- metadata: {}
83
- post_install_message:
327
+ metadata:
328
+ source_code_uri: https://github.com/KineticCafe/marlowe/
329
+ documentation_uri: http://www.rubydoc.info/github/KineticCafe/marlowe/master
330
+ post_install_message:
84
331
  rdoc_options:
85
332
  - "--main"
86
333
  - README.rdoc
@@ -90,18 +337,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
337
  requirements:
91
338
  - - ">="
92
339
  - !ruby/object:Gem::Version
93
- version: '0'
340
+ version: '2.0'
341
+ - - "<"
342
+ - !ruby/object:Gem::Version
343
+ version: '4'
94
344
  required_rubygems_version: !ruby/object:Gem::Requirement
95
345
  requirements:
96
346
  - - ">="
97
347
  - !ruby/object:Gem::Version
98
348
  version: '0'
99
349
  requirements: []
100
- rubyforge_project:
101
- rubygems_version: 2.4.8
102
- signing_key:
350
+ rubygems_version: 3.1.6
351
+ signing_key:
103
352
  specification_version: 4
104
- summary: "{marlowe}[https://github.com/KineticCafe/marlowe] provides a correlation
105
- id header for Rails and Rack applications to correlate logs for a single request
106
- \ across multiple services."
353
+ summary: "{Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that
354
+ extracts or creates a request ID using a pre-defined header, permitting request
355
+ correlation across multiple services"
107
356
  test_files: []
data/.autotest DELETED
@@ -1,25 +0,0 @@
1
- # -*- ruby -*-
2
-
3
- require "autotest/restart"
4
-
5
- # Autotest.add_hook :initialize do |at|
6
- # at.testlib = "minitest/unit"
7
- #
8
- # at.extra_files << "../some/external/dependency.rb"
9
- #
10
- # at.libs << ":../some/external"
11
- #
12
- # at.add_exception "vendor"
13
- #
14
- # at.add_mapping(/dependency.rb/) do |f, _|
15
- # at.files_matching(/test_.*rb$/)
16
- # end
17
- #
18
- # %w(TestA TestB).each do |klass|
19
- # at.extra_class_map[klass] = "test/test_misc.rb"
20
- # end
21
- # end
22
-
23
- # Autotest.add_hook :run_command do |at|
24
- # system "rake build"
25
- # end
data/History.rdoc DELETED
@@ -1,8 +0,0 @@
1
- === 1.0.1 / 2015-10-20
2
-
3
- * Update gemspec with homepage
4
- * Update Rakefile
5
-
6
- === 1.0.0 / 2015-10-16
7
-
8
- * Initial Commit