marlowe 1.0.1 → 2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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