timber 1.1.14 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -2
  3. data/.travis.yml +47 -0
  4. data/Gemfile +1 -28
  5. data/README.md +83 -298
  6. data/bin/timber +13 -0
  7. data/gemfiles/rails-3.0.gemfile +5 -0
  8. data/gemfiles/rails-3.1.gemfile +5 -0
  9. data/gemfiles/rails-3.2.gemfile +5 -0
  10. data/gemfiles/rails-4.0.gemfile +9 -0
  11. data/gemfiles/rails-4.1.gemfile +9 -0
  12. data/gemfiles/rails-4.2.gemfile +9 -0
  13. data/gemfiles/rails-5.0.gemfile +9 -0
  14. data/gemfiles/rails-edge.gemfile +7 -0
  15. data/lib/timber.rb +7 -7
  16. data/lib/timber/cli.rb +72 -0
  17. data/lib/timber/cli/api.rb +104 -0
  18. data/lib/timber/cli/application.rb +28 -0
  19. data/lib/timber/cli/install.rb +186 -0
  20. data/lib/timber/cli/io_helper.rb +58 -0
  21. data/lib/timber/cli/messages.rb +170 -0
  22. data/lib/timber/config.rb +47 -6
  23. data/lib/timber/contexts/http.rb +2 -2
  24. data/lib/timber/current_context.rb +1 -1
  25. data/lib/timber/event.rb +8 -0
  26. data/lib/timber/events.rb +2 -0
  27. data/lib/timber/events/controller_call.rb +12 -3
  28. data/lib/timber/events/exception.rb +4 -3
  29. data/lib/timber/events/http_client_request.rb +61 -0
  30. data/lib/timber/events/http_client_response.rb +47 -0
  31. data/lib/timber/events/http_server_request.rb +15 -23
  32. data/lib/timber/events/http_server_response.rb +9 -9
  33. data/lib/timber/events/sql_query.rb +2 -2
  34. data/lib/timber/events/template_render.rb +2 -2
  35. data/lib/timber/frameworks/rails.rb +31 -6
  36. data/lib/timber/integrations.rb +22 -0
  37. data/lib/timber/integrations/action_controller/log_subscriber.rb +25 -0
  38. data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +40 -0
  39. data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +51 -0
  40. data/lib/timber/integrations/action_view/log_subscriber.rb +25 -0
  41. data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +73 -0
  42. data/lib/timber/integrations/active_record/log_subscriber.rb +25 -0
  43. data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +39 -0
  44. data/lib/timber/integrations/active_support/tagged_logging.rb +71 -0
  45. data/lib/timber/integrations/rack.rb +16 -0
  46. data/lib/timber/integrations/rack/exception_event.rb +28 -0
  47. data/lib/timber/integrations/rack/http_context.rb +25 -0
  48. data/lib/timber/integrations/rack/http_events.rb +46 -0
  49. data/lib/timber/integrations/rack/user_context.rb +59 -0
  50. data/lib/timber/integrations/rails/rack_logger.rb +49 -0
  51. data/lib/timber/integrator.rb +24 -0
  52. data/lib/timber/log_devices/http.rb +14 -21
  53. data/lib/timber/log_entry.rb +1 -1
  54. data/lib/timber/logger.rb +38 -12
  55. data/lib/timber/overrides.rb +9 -0
  56. data/lib/timber/overrides/lograge.rb +14 -0
  57. data/lib/timber/overrides/rails_server.rb +10 -0
  58. data/lib/timber/util.rb +2 -0
  59. data/lib/timber/util/active_support_log_subscriber.rb +13 -9
  60. data/lib/timber/util/http_event.rb +54 -0
  61. data/lib/timber/util/request.rb +44 -0
  62. data/lib/timber/version.rb +1 -1
  63. data/spec/README.md +5 -9
  64. data/spec/spec_helper.rb +1 -4
  65. data/spec/support/action_controller.rb +7 -3
  66. data/spec/support/active_record.rb +23 -19
  67. data/spec/support/rails.rb +56 -32
  68. data/spec/support/timber.rb +2 -3
  69. data/spec/support/webmock.rb +1 -0
  70. data/spec/timber/integrations/action_controller/log_subscriber_spec.rb +55 -0
  71. data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +53 -0
  72. data/spec/timber/integrations/action_view/log_subscriber_spec.rb +115 -0
  73. data/spec/timber/integrations/active_record/log_subscriber_spec.rb +46 -0
  74. data/spec/timber/integrations/rack/http_context_spec.rb +60 -0
  75. data/spec/timber/integrations/rails/rack_logger_spec.rb +58 -0
  76. data/spec/timber/logger_spec.rb +45 -9
  77. data/timber.gemspec +29 -3
  78. metadata +143 -46
  79. data/Appraisals +0 -41
  80. data/circle.yml +0 -33
  81. data/lib/timber/overrides/logger_add.rb +0 -38
  82. data/lib/timber/probe.rb +0 -23
  83. data/lib/timber/probes.rb +0 -23
  84. data/lib/timber/probes/action_controller_log_subscriber.rb +0 -20
  85. data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +0 -64
  86. data/lib/timber/probes/action_controller_user_context.rb +0 -52
  87. data/lib/timber/probes/action_dispatch_debug_exceptions.rb +0 -80
  88. data/lib/timber/probes/action_view_log_subscriber.rb +0 -20
  89. data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +0 -69
  90. data/lib/timber/probes/active_record_log_subscriber.rb +0 -20
  91. data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +0 -31
  92. data/lib/timber/probes/active_support_tagged_logging.rb +0 -63
  93. data/lib/timber/probes/rails_rack_logger.rb +0 -77
  94. data/lib/timber/rack_middlewares.rb +0 -12
  95. data/lib/timber/rack_middlewares/http_context.rb +0 -30
  96. data/spec/support/action_view.rb +0 -4
  97. data/spec/support/coveralls.rb +0 -2
  98. data/spec/support/simplecov.rb +0 -9
  99. data/spec/timber/overrides/logger_add_spec.rb +0 -26
  100. data/spec/timber/probes/action_controller_log_subscriber_spec.rb +0 -65
  101. data/spec/timber/probes/action_controller_user_context_spec.rb +0 -53
  102. data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +0 -48
  103. data/spec/timber/probes/action_view_log_subscriber_spec.rb +0 -107
  104. data/spec/timber/probes/active_record_log_subscriber_spec.rb +0 -47
  105. data/spec/timber/probes/rails_rack_logger_spec.rb +0 -46
  106. data/spec/timber/rack_middlewares/http_context_spec.rb +0 -47
@@ -3,7 +3,7 @@ module Timber
3
3
  # `Logger` and the log device that you set it up with.
4
4
  class LogEntry #:nodoc:
5
5
  DT_PRECISION = 6.freeze
6
- SCHEMA = "https://raw.githubusercontent.com/timberio/log-event-json-schema/1.2.6/schema.json".freeze
6
+ SCHEMA = "https://raw.githubusercontent.com/timberio/log-event-json-schema/1.2.20/schema.json".freeze
7
7
 
8
8
  attr_reader :context_snapshot, :event, :level, :message, :progname, :tags, :time, :time_ms
9
9
 
@@ -16,7 +16,7 @@ module Timber
16
16
  # @example Using a Hash
17
17
  # # The :message key is required, the other additional key is your event type and data
18
18
  # # :type is the namespace used in timber for the :data
19
- # logger.info message: "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
19
+ # logger.info "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
20
20
  #
21
21
  # @example Using a Struct (a simple, more structured way, to define events)
22
22
  # PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
@@ -69,7 +69,7 @@ module Timber
69
69
  "INFO" => :info,
70
70
  "WARN" => :warn,
71
71
  "ERROR" => :error,
72
- "FATAL" => :datal,
72
+ "FATAL" => :fatal,
73
73
  "UNKNOWN" => :unknown
74
74
  }
75
75
 
@@ -81,9 +81,12 @@ module Timber
81
81
  tags = extract_active_support_tagged_logging_tags
82
82
  time_ms = nil
83
83
  if msg.is_a?(Hash)
84
- tags << msg.delete(:tag) if msg.key?(:tag)
85
- tags += msg.delete(:tags) if msg.key?(:tags)
86
- tags.uniq!
84
+ if msg.key?(:tag) || msg.key?(:tags)
85
+ tags = tags.clone
86
+ tags << msg.delete(:tag) if msg.key?(:tag)
87
+ tags += msg.delete(:tags) if msg.key?(:tags)
88
+ tags.uniq!
89
+ end
87
90
  time_ms = msg.delete(:time_ms) if msg.key?(:time_ms)
88
91
 
89
92
  msg = msg[:message] if msg.length == 1
@@ -106,6 +109,16 @@ module Timber
106
109
  end
107
110
  end
108
111
 
112
+ # For use in development and test environments where you do not want metadata
113
+ # included in the log lines.
114
+ class SimpleFormatter < Formatter
115
+
116
+ # This method is invoked when a log event occurs
117
+ def call(severity, timestamp, progname, msg)
118
+ "#{String === msg ? msg : msg.inspect}\n"
119
+ end
120
+ end
121
+
109
122
  # Structures your log messages into Timber's hybrid format, which makes
110
123
  # it easy to read while also appending the appropriate metadata.
111
124
  #
@@ -153,6 +166,11 @@ module Timber
153
166
  end
154
167
  end
155
168
 
169
+ class << self
170
+ def new_from_config
171
+ end
172
+ end
173
+
156
174
  # Creates a new Timber::Logger instances. Accepts the same arguments as `::Logger.new`.
157
175
  # The only difference is that it default the formatter to {HybridFormatter}. Using
158
176
  # a different formatter is easy. For example, if you prefer your logs in JSON.
@@ -175,26 +193,34 @@ module Timber
175
193
  end
176
194
 
177
195
  self.level = environment_level
196
+
197
+ @initialized = true
178
198
  end
179
199
 
180
200
  def formatter=(value)
181
- if @dev.is_a?(Timber::LogDevices::HTTP)
201
+ if @initialized && @logdev && @logdev.dev.is_a?(Timber::LogDevices::HTTP) && !value.is_a?(PassThroughFormatter)
182
202
  raise ArgumentError.new("The formatter cannot be changed when using the " +
183
203
  "Timber::LogDevices::HTTP log device. The PassThroughFormatter must be used for proper " +
184
204
  "delivery.")
185
205
  end
186
206
 
187
- if !value.is_a?(Timber::Logger::Formatter)
188
- # silently discard this value since rails calls this during initialization :/
189
- nil
190
- else
191
- super
192
- end
207
+ super
193
208
  end
194
209
 
195
210
  # Backwards compatibility with older ActiveSupport::Logger versions
196
211
  Logger::Severity.constants.each do |severity|
197
212
  class_eval(<<-EOT, __FILE__, __LINE__ + 1)
213
+ def #{severity.downcase}(*args, &block)
214
+ progname = args.first
215
+ options = args.last
216
+
217
+ if args.length == 2 and options.is_a?(Hash) && options != {}
218
+ progname = options.merge(message: progname)
219
+ end
220
+
221
+ add(#{severity}, nil, progname, &block)
222
+ end
223
+
198
224
  def #{severity.downcase}? # def debug?
199
225
  Logger::#{severity} >= level # DEBUG >= level
200
226
  end # end
@@ -0,0 +1,9 @@
1
+ require "timber/overrides/lograge"
2
+ require "timber/overrides/rails_server"
3
+ require "timber/overrides/rails_stdout_logging"
4
+
5
+ module Timber
6
+ # @private
7
+ module Overrides
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # Timber and lograge are not compatible installed together. Using lograge
2
+ # with the Timber.io *service* is perfectly fine, but not with the Timber *gem*.
3
+ begin
4
+ require "lograge"
5
+
6
+ module Lograge
7
+ module_function
8
+
9
+ def setup(app)
10
+ return true
11
+ end
12
+ end
13
+ rescue Exception
14
+ end
@@ -0,0 +1,10 @@
1
+ begin
2
+ require "rails/commands/server"
3
+
4
+ class ::Rails::Server < ::Rack::Server
5
+ private
6
+ def log_to_stdout
7
+ end
8
+ end
9
+ rescue Exception
10
+ end
@@ -1,6 +1,8 @@
1
1
  require "timber/util/active_support_log_subscriber"
2
2
  require "timber/util/hash"
3
+ require "timber/util/http_event"
3
4
  require "timber/util/object"
5
+ require "timber/util/request"
4
6
  require "timber/util/struct"
5
7
 
6
8
  module Timber
@@ -5,7 +5,7 @@ module Timber
5
5
  extend self
6
6
 
7
7
  def find(component, type)
8
- ActiveSupport::LogSubscriber.log_subscribers.find do |subscriber|
8
+ ::ActiveSupport::LogSubscriber.log_subscribers.find do |subscriber|
9
9
  subscriber.class == type
10
10
  end
11
11
  end
@@ -14,16 +14,20 @@ module Timber
14
14
  !find(component, type).nil?
15
15
  end
16
16
 
17
- def unsubscribe(component, type)
17
+ # I don't know why this has to be so complicated, but it is. This code was taken from
18
+ # lograge :/
19
+ def unsubscribe!(component, type)
18
20
  subscriber = find(component, type)
19
21
 
20
- if subscriber
21
- events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
22
- events.each do |event|
23
- ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
24
- if listener.instance_variable_get('@delegate') == subscriber
25
- ActiveSupport::Notifications.unsubscribe listener
26
- end
22
+ if !subscriber
23
+ raise "We could not find a log subscriber for #{component.inspect} of type #{type.inspect}"
24
+ end
25
+
26
+ events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
27
+ events.each do |event|
28
+ ::ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
29
+ if listener.instance_variable_get('@delegate') == subscriber
30
+ ::ActiveSupport::Notifications.unsubscribe listener
27
31
  end
28
32
  end
29
33
  end
@@ -0,0 +1,54 @@
1
+ module Timber
2
+ module Util
3
+ module HTTPEvent
4
+ BODY_LIMIT = 5_000.freeze
5
+ QUERY_STRING_LIMIT = 5_000.freeze
6
+
7
+ extend self
8
+
9
+ def full_path(path, query_string)
10
+ if query_string
11
+ "#{path}?#{query_string}"
12
+ else
13
+ path
14
+ end
15
+ end
16
+
17
+ def normalize_body(content_type, body)
18
+ if Config.instance.capture_http_body_content_types.include?(content_type)
19
+ if body.respond_to?(:body)
20
+ body = body.body.to_s
21
+ end
22
+
23
+ body[0..(BODY_LIMIT - 1)]
24
+ else
25
+ # Drop the body if it is not a format we want to capture.
26
+ # This gives users more control to avoid loggin files, etc.
27
+ nil
28
+ end
29
+ end
30
+
31
+ def normalize_headers(headers)
32
+ if headers.is_a?(::Hash)
33
+ headers.each_with_object({}) do |(k, v), h|
34
+ h[k.to_s.downcase] = v
35
+ end
36
+ else
37
+ headers
38
+ end
39
+ end
40
+
41
+ def normalize_method(method)
42
+ method.is_a?(::String) ? method.upcase : method
43
+ end
44
+
45
+ def normalize_query_string(query_string)
46
+ if !query_string.nil?
47
+ query_string = query_string.to_s
48
+ end
49
+
50
+ query_string[0..(QUERY_STRING_LIMIT - 1)]
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,44 @@
1
+ begin
2
+ require "rack"
3
+ rescue LoadError
4
+ end
5
+
6
+ if defined?(::Rack::Request)
7
+ module Timber
8
+ module Util
9
+ class Request < ::Rack::Request
10
+ def body_content
11
+ content = body.read
12
+ body.rewind
13
+ content
14
+ end
15
+
16
+ def headers
17
+ @headers ||= ::Hash[*@env.select {|k,v| k.start_with? 'HTTP_'}
18
+ .collect {|k,v| [k.sub(/^HTTP_/, ''), v]}
19
+ .collect {|k,v| [k.split('_').collect(&:capitalize).join('-'), v]}
20
+ .sort
21
+ .flatten]
22
+ end
23
+
24
+ def ip
25
+ @ip ||= if @env["action_dispatch.remote_ip"]
26
+ @env["action_dispatch.remote_ip"].to_s || super
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def referer
33
+ # Rails 3.X returns "/" for some reason
34
+ @referer ||= super == "/" ? nil : super
35
+ end
36
+
37
+ def request_id
38
+ @request_id ||= @env["action_dispatch.request_id"] || @env["X-Request-ID"] ||
39
+ @env["X-Request-Id"]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Timber
2
- VERSION = "1.1.14"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -1,23 +1,19 @@
1
1
  # Testing
2
2
 
3
- Testing Timber uses [`appraisal`](https://github.com/thoughtbot/appraisal). This allows us to
4
- test across multiple versions and combinations of libraries.
5
-
6
3
  To get started:
7
4
 
8
5
  ```shell
9
6
  bundle install
10
- bundle exec appraisal install
11
7
  ```
12
8
 
13
- To see all appraisal commands:
9
+ Be sure to install specific Gemfile dependencies:
14
10
 
15
- ```shell
16
- appraisal --help
11
+ ```
12
+ BUNDLE_GEMFILE=gemfiles/rails-4.2.gemfile bundle install
17
13
  ```
18
14
 
19
- You can run tests with:
15
+ You can run tests like this:
20
16
 
21
17
  ```shell
22
- appraisal rails-3.2.X rspec
18
+ BUNDLE_GEMFILE=gemfiles/rails-4.2.gemfile bundle exec rspec
23
19
  ```
@@ -3,23 +3,20 @@ require 'rubygems'
3
3
  require 'bundler/setup'
4
4
 
5
5
  # Testing
6
- require 'pry'
7
6
  require 'rspec'
8
7
  require 'rspec/its'
9
8
  require 'rspec/mocks'
10
9
 
11
10
  # Support files, order is relevant
12
- require File.join(File.dirname(__FILE__), 'support', 'coveralls')
13
11
  require File.join(File.dirname(__FILE__), 'support', 'socket_hostname')
14
- require File.join(File.dirname(__FILE__), 'support', 'simplecov')
15
12
  require File.join(File.dirname(__FILE__), 'support', 'timecop')
16
13
  require File.join(File.dirname(__FILE__), 'support', 'webmock')
17
14
  require File.join(File.dirname(__FILE__), 'support', 'timber')
18
15
 
16
+ # Load framework files after we've setup everything
19
17
  if !ENV["RAILS_23"]
20
18
  require File.join(File.dirname(__FILE__), 'support', 'rails')
21
19
  require File.join(File.dirname(__FILE__), 'support', 'action_controller')
22
- require File.join(File.dirname(__FILE__), 'support', 'action_view')
23
20
  require File.join(File.dirname(__FILE__), 'support', 'active_record')
24
21
  end
25
22
 
@@ -1,4 +1,8 @@
1
- require "action_controller"
1
+ begin
2
+ require "action_controller"
3
+ rescue LoadError
4
+ end
2
5
 
3
- ActionController::Base.prepend_view_path("#{File.dirname(__FILE__)}/rails/templates")
4
- ActionController::Base.logger = Rails.logger
6
+ if defined?(::ActionController::Base)
7
+ ::ActionController::Base.prepend_view_path("#{File.dirname(__FILE__)}/rails/templates")
8
+ end
@@ -1,28 +1,32 @@
1
- require 'active_record'
1
+ begin
2
+ require 'active_record'
3
+ rescue LoadError
4
+ end
2
5
 
3
- ActiveRecord::Base.logger = Rails.logger
4
- ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
5
- ActiveRecord::Schema.define do
6
- self.verbose = false
6
+ if defined?(::ActiveRecord::Base)
7
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
8
+ ActiveRecord::Schema.define do
9
+ self.verbose = false
7
10
 
8
- create_table :organizations, :force => true do |t|
9
- t.string :name
11
+ create_table :organizations, :force => true do |t|
12
+ t.string :name
10
13
 
11
- t.timestamps :null => false
12
- end
14
+ t.timestamps :null => false
15
+ end
13
16
 
14
- create_table :users, :force => true do |t|
15
- t.integer :age
16
- t.string :email
17
- t.string :first_name
17
+ create_table :users, :force => true do |t|
18
+ t.integer :age
19
+ t.string :email
20
+ t.string :first_name
18
21
 
19
- t.timestamps :null => false
20
- end
22
+ t.timestamps :null => false
23
+ end
21
24
 
22
- end
25
+ end
23
26
 
24
- class Organization < ::ActiveRecord::Base
25
- end
27
+ class Organization < ::ActiveRecord::Base
28
+ end
26
29
 
27
- class User < ::ActiveRecord::Base
30
+ class User < ::ActiveRecord::Base
31
+ end
28
32
  end
@@ -1,37 +1,61 @@
1
- require "rails"
2
-
3
- # Defualt the rails logger to nothing, each test shoould be
4
- # responsible for setting up the logger
5
- logger = ::Logger.new(STDOUT) # change to STDOUT to see rails logs
6
- Rails.logger = logger
7
-
8
- class RailsApp < Rails::Application
9
- if ::Rails.version =~ /^3\./
10
- config.secret_token = '1e05af2b349457936a41427e63450937'
11
- else
12
- config.secret_key_base = '1e05af2b349457936a41427e63450937'
13
- end
14
- config.active_support.deprecation = :stderr
15
- config.eager_load = false
1
+ begin
2
+ require "rails"
3
+ rescue LoadError
16
4
  end
17
5
 
18
- RailsApp.initialize!
19
-
20
- module Support
21
- module Rails
22
- def dispatch_rails_request(path, additional_env_options = {})
23
- application = ::Rails.application
24
- env = application.respond_to?(:env_config) ? application.env_config.clone : application.env_defaults.clone
25
- env["rack.request.cookie_hash"] = {}.with_indifferent_access
26
- env["REMOTE_ADDR"] = "123.456.789.10"
27
- env["X-Request-Id"] = "unique-request-id-1234"
28
- env["action_dispatch.request_id"] = env["X-Request-Id"]
29
- env = env.merge(additional_env_options)
30
- ::Rack::MockRequest.new(application).get(path, env)
6
+ if defined?(::Rails)
7
+ # Defualt the rails logger to nothing, each test shoould be
8
+ # responsible for setting up the logger
9
+ logger = Timber::Logger.new(nil) # change to STDOUT to see rails logs
10
+ Rails.logger = logger
11
+
12
+ class RailsApp < Rails::Application
13
+ if ::Rails.version =~ /^3\./
14
+ config.secret_token = '1e05af2b349457936a41427e63450937'
15
+ else
16
+ config.secret_key_base = '1e05af2b349457936a41427e63450937'
31
17
  end
18
+
19
+ # This ensures our tests fail, otherwise exceptions get swallowed by ActionDispatch::DebugExceptions
20
+ config.action_dispatch.show_exceptions = false
21
+ config.active_support.deprecation = :stderr
22
+ config.eager_load = false
32
23
  end
33
- end
34
24
 
35
- RSpec.configure do |config|
36
- config.include Support::Rails
37
- end
25
+ RailsApp.initialize!
26
+
27
+ module Support
28
+ module Rails
29
+ def dispatch_rails_request(path, additional_env_options = {})
30
+ application = ::Rails.application
31
+ env = application.respond_to?(:env_config) ? application.env_config.clone : application.env_defaults.clone
32
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
33
+ env["REMOTE_ADDR"] = "123.456.789.10"
34
+ env["HTTP_X_REQUEST_ID"] = "unique-request-id-1234"
35
+ env["action_dispatch.request_id"] = env["HTTP_X_REQUEST_ID"]
36
+ env = env.merge(additional_env_options)
37
+ ::Rack::MockRequest.new(application).get(path, env)
38
+ end
39
+
40
+ def with_rails_logger(logger)
41
+ old_logger = ::Rails.logger
42
+
43
+ # We have to set these again because rails set's these after initialization.
44
+ # Since we've already booted the rails app we need to update them all as we
45
+ # change the logger.
46
+ #
47
+ # You can see here that they use simple class attribute, hence the reason we need
48
+ # to update all of them: https://github.com/rails/rails/blob/700ec897f97c60016ad748236bf3a49ef15a20de/actionview/lib/action_view/base.rb#L157
49
+ Timber::Frameworks::Rails.set_logger(logger)
50
+
51
+ yield
52
+
53
+ ::Rails.logger = old_logger
54
+ end
55
+ end
56
+ end
57
+
58
+ RSpec.configure do |config|
59
+ config.include Support::Rails
60
+ end
61
+ end