timber 1.1.14 → 2.0.0
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 +4 -4
- data/.gitignore +4 -2
- data/.travis.yml +47 -0
- data/Gemfile +1 -28
- data/README.md +83 -298
- data/bin/timber +13 -0
- data/gemfiles/rails-3.0.gemfile +5 -0
- data/gemfiles/rails-3.1.gemfile +5 -0
- data/gemfiles/rails-3.2.gemfile +5 -0
- data/gemfiles/rails-4.0.gemfile +9 -0
- data/gemfiles/rails-4.1.gemfile +9 -0
- data/gemfiles/rails-4.2.gemfile +9 -0
- data/gemfiles/rails-5.0.gemfile +9 -0
- data/gemfiles/rails-edge.gemfile +7 -0
- data/lib/timber.rb +7 -7
- data/lib/timber/cli.rb +72 -0
- data/lib/timber/cli/api.rb +104 -0
- data/lib/timber/cli/application.rb +28 -0
- data/lib/timber/cli/install.rb +186 -0
- data/lib/timber/cli/io_helper.rb +58 -0
- data/lib/timber/cli/messages.rb +170 -0
- data/lib/timber/config.rb +47 -6
- data/lib/timber/contexts/http.rb +2 -2
- data/lib/timber/current_context.rb +1 -1
- data/lib/timber/event.rb +8 -0
- data/lib/timber/events.rb +2 -0
- data/lib/timber/events/controller_call.rb +12 -3
- data/lib/timber/events/exception.rb +4 -3
- data/lib/timber/events/http_client_request.rb +61 -0
- data/lib/timber/events/http_client_response.rb +47 -0
- data/lib/timber/events/http_server_request.rb +15 -23
- data/lib/timber/events/http_server_response.rb +9 -9
- data/lib/timber/events/sql_query.rb +2 -2
- data/lib/timber/events/template_render.rb +2 -2
- data/lib/timber/frameworks/rails.rb +31 -6
- data/lib/timber/integrations.rb +22 -0
- data/lib/timber/integrations/action_controller/log_subscriber.rb +25 -0
- data/lib/timber/integrations/action_controller/log_subscriber/timber_log_subscriber.rb +40 -0
- data/lib/timber/integrations/action_dispatch/debug_exceptions.rb +51 -0
- data/lib/timber/integrations/action_view/log_subscriber.rb +25 -0
- data/lib/timber/integrations/action_view/log_subscriber/timber_log_subscriber.rb +73 -0
- data/lib/timber/integrations/active_record/log_subscriber.rb +25 -0
- data/lib/timber/integrations/active_record/log_subscriber/timber_log_subscriber.rb +39 -0
- data/lib/timber/integrations/active_support/tagged_logging.rb +71 -0
- data/lib/timber/integrations/rack.rb +16 -0
- data/lib/timber/integrations/rack/exception_event.rb +28 -0
- data/lib/timber/integrations/rack/http_context.rb +25 -0
- data/lib/timber/integrations/rack/http_events.rb +46 -0
- data/lib/timber/integrations/rack/user_context.rb +59 -0
- data/lib/timber/integrations/rails/rack_logger.rb +49 -0
- data/lib/timber/integrator.rb +24 -0
- data/lib/timber/log_devices/http.rb +14 -21
- data/lib/timber/log_entry.rb +1 -1
- data/lib/timber/logger.rb +38 -12
- data/lib/timber/overrides.rb +9 -0
- data/lib/timber/overrides/lograge.rb +14 -0
- data/lib/timber/overrides/rails_server.rb +10 -0
- data/lib/timber/util.rb +2 -0
- data/lib/timber/util/active_support_log_subscriber.rb +13 -9
- data/lib/timber/util/http_event.rb +54 -0
- data/lib/timber/util/request.rb +44 -0
- data/lib/timber/version.rb +1 -1
- data/spec/README.md +5 -9
- data/spec/spec_helper.rb +1 -4
- data/spec/support/action_controller.rb +7 -3
- data/spec/support/active_record.rb +23 -19
- data/spec/support/rails.rb +56 -32
- data/spec/support/timber.rb +2 -3
- data/spec/support/webmock.rb +1 -0
- data/spec/timber/integrations/action_controller/log_subscriber_spec.rb +55 -0
- data/spec/timber/integrations/action_dispatch/debug_exceptions_spec.rb +53 -0
- data/spec/timber/integrations/action_view/log_subscriber_spec.rb +115 -0
- data/spec/timber/integrations/active_record/log_subscriber_spec.rb +46 -0
- data/spec/timber/integrations/rack/http_context_spec.rb +60 -0
- data/spec/timber/integrations/rails/rack_logger_spec.rb +58 -0
- data/spec/timber/logger_spec.rb +45 -9
- data/timber.gemspec +29 -3
- metadata +143 -46
- data/Appraisals +0 -41
- data/circle.yml +0 -33
- data/lib/timber/overrides/logger_add.rb +0 -38
- data/lib/timber/probe.rb +0 -23
- data/lib/timber/probes.rb +0 -23
- data/lib/timber/probes/action_controller_log_subscriber.rb +0 -20
- data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +0 -64
- data/lib/timber/probes/action_controller_user_context.rb +0 -52
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +0 -80
- data/lib/timber/probes/action_view_log_subscriber.rb +0 -20
- data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +0 -69
- data/lib/timber/probes/active_record_log_subscriber.rb +0 -20
- data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +0 -31
- data/lib/timber/probes/active_support_tagged_logging.rb +0 -63
- data/lib/timber/probes/rails_rack_logger.rb +0 -77
- data/lib/timber/rack_middlewares.rb +0 -12
- data/lib/timber/rack_middlewares/http_context.rb +0 -30
- data/spec/support/action_view.rb +0 -4
- data/spec/support/coveralls.rb +0 -2
- data/spec/support/simplecov.rb +0 -9
- data/spec/timber/overrides/logger_add_spec.rb +0 -26
- data/spec/timber/probes/action_controller_log_subscriber_spec.rb +0 -65
- data/spec/timber/probes/action_controller_user_context_spec.rb +0 -53
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +0 -48
- data/spec/timber/probes/action_view_log_subscriber_spec.rb +0 -107
- data/spec/timber/probes/active_record_log_subscriber_spec.rb +0 -47
- data/spec/timber/probes/rails_rack_logger_spec.rb +0 -46
- data/spec/timber/rack_middlewares/http_context_spec.rb +0 -47
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Events
|
|
3
|
+
# The HTTP client response event tracks responses for *outgoing* HTTP *requests*.
|
|
4
|
+
# This gives you structured insight into communication with external services.
|
|
5
|
+
#
|
|
6
|
+
# @note This event should be installed automatically through integrations,
|
|
7
|
+
# such as the {Integrations::NetHTTP} integration.
|
|
8
|
+
class HTTPClientResponse < Timber::Event
|
|
9
|
+
attr_reader :body, :headers, :request_id, :service_name, :status, :time_ms
|
|
10
|
+
|
|
11
|
+
def initialize(attributes)
|
|
12
|
+
@headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
|
|
13
|
+
@request_id = attributes[:request_id]
|
|
14
|
+
@service_name = attributes[:service_name]
|
|
15
|
+
@status = attributes[:status] || raise(ArgumentError.new(":status is required"))
|
|
16
|
+
@time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
|
|
17
|
+
@time_ms = @time_ms.round(6)
|
|
18
|
+
|
|
19
|
+
@body = Util::HTTPEvent.normalize_body(@headers["content-type"], attributes[:body])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_hash
|
|
23
|
+
{body: body, headers: headers, request_id: request_id, service_name: service_name,
|
|
24
|
+
status: status, time_ms: time_ms}
|
|
25
|
+
end
|
|
26
|
+
alias to_h to_hash
|
|
27
|
+
|
|
28
|
+
def as_json(_options = {})
|
|
29
|
+
{:server_side_app => {:http_client_response => to_hash}}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def message
|
|
33
|
+
message = "Outgoing HTTP response"
|
|
34
|
+
|
|
35
|
+
if service_name
|
|
36
|
+
message << " from #{service_name}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
message << " #{status_description} in #{time_ms}ms"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def status_description
|
|
43
|
+
Rack::Utils::HTTP_STATUS_CODES[status]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
module Timber
|
|
2
2
|
module Events
|
|
3
|
-
# The HTTP request event tracks incoming HTTP requests.
|
|
3
|
+
# The HTTP server request event tracks incoming HTTP requests to your HTTP server.
|
|
4
|
+
# Such as unicorn, webrick, puma, etc.
|
|
4
5
|
#
|
|
5
|
-
# @note This event should be installed automatically through
|
|
6
|
-
# such as the {
|
|
6
|
+
# @note This event should be installed automatically through integrations,
|
|
7
|
+
# such as the {Integrations::ActionController::LogSubscriber} integration.
|
|
7
8
|
class HTTPServerRequest < Timber::Event
|
|
8
|
-
attr_reader :host, :method, :path, :port, :query_string, :
|
|
9
|
-
:
|
|
9
|
+
attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id,
|
|
10
|
+
:scheme
|
|
10
11
|
|
|
11
12
|
def initialize(attributes)
|
|
13
|
+
@headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
|
|
12
14
|
@host = attributes[:host] || raise(ArgumentError.new(":host is required"))
|
|
13
|
-
@method = attributes[:method] || raise(ArgumentError.new(":method is required"))
|
|
15
|
+
@method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required"))
|
|
14
16
|
@path = attributes[:path] || raise(ArgumentError.new(":path is required"))
|
|
15
17
|
@port = attributes[:port]
|
|
16
|
-
@query_string = attributes[:query_string]
|
|
17
|
-
@content_type = attributes[:content_type]
|
|
18
|
-
@remote_addr = attributes[:remote_addr]
|
|
19
|
-
@referrer = attributes[:referrer]
|
|
20
|
-
@request_id = attributes[:request_id]
|
|
18
|
+
@query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string])
|
|
21
19
|
@scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required"))
|
|
22
|
-
@
|
|
20
|
+
@request_id = attributes[:request_id]
|
|
21
|
+
|
|
22
|
+
@body = Util::HTTPEvent.normalize_body(@headers["content-type"], attributes[:body])
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def to_hash
|
|
26
|
-
{host: host, method: method, path: path, port: port,
|
|
27
|
-
|
|
28
|
-
request_id: request_id, scheme: scheme, user_agent: user_agent}}
|
|
26
|
+
{body: body, headers: headers, host: host, method: method, path: path, port: port,
|
|
27
|
+
query_string: query_string, request_id: request_id, scheme: scheme}
|
|
29
28
|
end
|
|
30
29
|
alias to_h to_hash
|
|
31
30
|
|
|
@@ -34,14 +33,7 @@ module Timber
|
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
def message
|
|
37
|
-
'Started %s "%s"
|
|
38
|
-
method,
|
|
39
|
-
path,
|
|
40
|
-
remote_addr]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def status_description
|
|
44
|
-
Rack::Utils::HTTP_STATUS_CODES[status]
|
|
36
|
+
'Started %s "%s"' % [method, path]
|
|
45
37
|
end
|
|
46
38
|
end
|
|
47
39
|
end
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
module Timber
|
|
2
2
|
module Events
|
|
3
|
-
# The HTTP response event tracks outgoing HTTP
|
|
3
|
+
# The HTTP server response event tracks outgoing HTTP responses that you send
|
|
4
|
+
# to clients.
|
|
4
5
|
#
|
|
5
|
-
# @note This event should be installed automatically through
|
|
6
|
-
# such as the {
|
|
6
|
+
# @note This event should be installed automatically through integrations,
|
|
7
|
+
# such as the {Integrations::ActionController::LogSubscriber} integration.
|
|
7
8
|
class HTTPServerResponse < Timber::Event
|
|
8
|
-
attr_reader :
|
|
9
|
+
attr_reader :headers, :request_id, :status, :time_ms
|
|
9
10
|
|
|
10
11
|
def initialize(attributes)
|
|
12
|
+
@headers = Util::HTTPEvent.normalize_headers(attributes[:headers])
|
|
13
|
+
@request_id = attributes[:request_id]
|
|
11
14
|
@status = attributes[:status] || raise(ArgumentError.new(":status is required"))
|
|
12
15
|
@time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required"))
|
|
13
16
|
@time_ms = @time_ms.round(6)
|
|
14
|
-
@additions = attributes[:additions]
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def to_hash
|
|
18
|
-
{status: status, time_ms: time_ms}
|
|
20
|
+
{headers: headers, request_id: request_id, status: status, time_ms: time_ms}
|
|
19
21
|
end
|
|
20
22
|
alias to_h to_hash
|
|
21
23
|
|
|
@@ -24,9 +26,7 @@ module Timber
|
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def message
|
|
27
|
-
|
|
28
|
-
message << " (#{additions.join(" | ".freeze)})" unless additions.empty?
|
|
29
|
-
message
|
|
29
|
+
"Completed #{status} #{status_description} in #{time_ms}ms"
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def status_description
|
|
@@ -2,8 +2,8 @@ module Timber
|
|
|
2
2
|
module Events
|
|
3
3
|
# The SQL query event tracks sql queries to your database.
|
|
4
4
|
#
|
|
5
|
-
# @note This event should be installed automatically through
|
|
6
|
-
# such as the {
|
|
5
|
+
# @note This event should be installed automatically through integrations,
|
|
6
|
+
# such as the {Integrations::ActiveRecord::LogSubscriber} integration.
|
|
7
7
|
class SQLQuery < Timber::Event
|
|
8
8
|
attr_reader :sql, :time_ms, :message
|
|
9
9
|
|
|
@@ -2,8 +2,8 @@ module Timber
|
|
|
2
2
|
module Events
|
|
3
3
|
# The template render event track template renderings and their performance.
|
|
4
4
|
#
|
|
5
|
-
# @note This event should be installed automatically through
|
|
6
|
-
# such as the {
|
|
5
|
+
# @note This event should be installed automatically through integrations,
|
|
6
|
+
# such as the {Integrations::ActionView::LogSubscriber} integration.
|
|
7
7
|
class TemplateRender < Timber::Event
|
|
8
8
|
attr_reader :message, :name, :time_ms
|
|
9
9
|
|
|
@@ -4,19 +4,44 @@ module Timber
|
|
|
4
4
|
# Installs Timber into your Rails app automatically.
|
|
5
5
|
class Railtie < ::Rails::Railtie
|
|
6
6
|
config.timber = Config.instance
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
|
|
8
|
+
initializer(:timber_logger, before: :initialize_logger) do
|
|
9
|
+
Rails.configure_middlewares(config.app_middleware)
|
|
10
|
+
Integrations.integrate!
|
|
11
|
+
|
|
12
|
+
# We set a default logger because Rails tries to write to a file by default.
|
|
13
|
+
# This causes errors on paltforms with a readon only file system (Heroku)
|
|
14
|
+
# Moreover, the Timber logger gets configured properly later in an initiailizer.
|
|
15
|
+
# This is a hold over until we reach that file in the initialization process.
|
|
16
|
+
logger = if defined?(::ActiveSupport::Logger)
|
|
17
|
+
::ActiveSupport::Logger.new(STDOUT)
|
|
18
|
+
else
|
|
19
|
+
::Logger.new(STDOUT)
|
|
20
|
+
end
|
|
21
|
+
::Rails.logger = Config.instance.logger = logger
|
|
10
22
|
end
|
|
11
23
|
end
|
|
12
24
|
|
|
13
|
-
def self.
|
|
25
|
+
def self.set_logger(logger)
|
|
26
|
+
if defined?(::ActiveSupport::TaggedLogging) && !logger.is_a?(::ActiveSupport::TaggedLogging)
|
|
27
|
+
logger = ::ActiveSupport::TaggedLogging.new(logger)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Config.instance.logger = logger
|
|
31
|
+
::ActionController::Base.logger = logger
|
|
32
|
+
::ActionView::Base.logger = logger if ::ActionView::Base.respond_to?(:logger=)
|
|
33
|
+
::ActiveRecord::Base.logger = logger
|
|
34
|
+
::Rails.logger = logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.configure_middlewares(middleware)
|
|
14
38
|
var_name = :"@_timber_middlewares_inserted"
|
|
15
39
|
return true if middleware.instance_variable_defined?(var_name) && middleware.instance_variable_get(var_name) == true
|
|
40
|
+
|
|
16
41
|
# Rails uses a proxy :/, so we need to do this instance variable hack
|
|
17
42
|
middleware.instance_variable_set(var_name, true)
|
|
18
|
-
|
|
19
|
-
middleware.
|
|
43
|
+
Integrations::Rack.middlewares.each do |middleware_class|
|
|
44
|
+
middleware.use middleware_class
|
|
20
45
|
end
|
|
21
46
|
end
|
|
22
47
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "timber/integrations/action_controller/log_subscriber"
|
|
2
|
+
require "timber/integrations/action_dispatch/debug_exceptions"
|
|
3
|
+
require "timber/integrations/action_view/log_subscriber"
|
|
4
|
+
require "timber/integrations/active_record/log_subscriber"
|
|
5
|
+
require "timber/integrations/active_support/tagged_logging"
|
|
6
|
+
require "timber/integrations/rack"
|
|
7
|
+
require "timber/integrations/rails/rack_logger"
|
|
8
|
+
|
|
9
|
+
module Timber
|
|
10
|
+
# Namespace for all integrations.
|
|
11
|
+
# @private
|
|
12
|
+
module Integrations
|
|
13
|
+
def self.integrate!
|
|
14
|
+
ActionController::LogSubscriber.integrate!
|
|
15
|
+
ActionDispatch::DebugExceptions.integrate!
|
|
16
|
+
ActionView::LogSubscriber.integrate!
|
|
17
|
+
ActiveRecord::LogSubscriber.integrate!
|
|
18
|
+
ActiveSupport::TaggedLogging.integrate!
|
|
19
|
+
Rails::RackLogger.integrate!
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Integrations
|
|
3
|
+
module ActionController
|
|
4
|
+
# Responsible for removing the default ActionController::LogSubscriber and installing
|
|
5
|
+
# the TimberLogSubscriber
|
|
6
|
+
#
|
|
7
|
+
# @private
|
|
8
|
+
class LogSubscriber < Integrator
|
|
9
|
+
def initialize
|
|
10
|
+
require "action_controller/log_subscriber"
|
|
11
|
+
require "timber/integrations/action_controller/log_subscriber/timber_log_subscriber"
|
|
12
|
+
rescue LoadError => e
|
|
13
|
+
raise RequirementNotMetError.new(e.message)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def integrate!
|
|
17
|
+
return true if Util::ActiveSupportLogSubscriber.subscribed?(:action_controller, TimberLogSubscriber)
|
|
18
|
+
|
|
19
|
+
Util::ActiveSupportLogSubscriber.unsubscribe!(:action_controller, ::ActionController::LogSubscriber)
|
|
20
|
+
TimberLogSubscriber.attach_to(:action_controller)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Integrations
|
|
3
|
+
module ActionController
|
|
4
|
+
class LogSubscriber < Integrator
|
|
5
|
+
# The log subscriber that replaces the default `ActionController::LogSubscriber`.
|
|
6
|
+
# The intent of this subscriber is to, as transparently as possible, properly
|
|
7
|
+
# track events that are being logged here. This LogSubscriber will never change
|
|
8
|
+
# default behavior / log messages.
|
|
9
|
+
#
|
|
10
|
+
# @private
|
|
11
|
+
class TimberLogSubscriber < ::ActionController::LogSubscriber
|
|
12
|
+
def start_processing(event)
|
|
13
|
+
info do
|
|
14
|
+
payload = event.payload
|
|
15
|
+
params = payload[:params].except(*INTERNAL_PARAMS)
|
|
16
|
+
format = extract_format(payload)
|
|
17
|
+
format = format.to_s.upcase if format.is_a?(Symbol)
|
|
18
|
+
|
|
19
|
+
Events::ControllerCall.new(
|
|
20
|
+
controller: payload[:controller],
|
|
21
|
+
action: payload[:action],
|
|
22
|
+
format: format,
|
|
23
|
+
params: params
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def extract_format(payload)
|
|
30
|
+
if payload.key?(:format)
|
|
31
|
+
payload[:format] # rails > 4.X
|
|
32
|
+
elsif payload.key?(:formats)
|
|
33
|
+
payload[:formats].first # rails 3.X
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Integrations
|
|
3
|
+
module ActionDispatch
|
|
4
|
+
# Reponsible for disabled logging in the ActionDispatch::DebugExceptions
|
|
5
|
+
# Rack middleware. We cannot simply remove the middleware because it is
|
|
6
|
+
# coupled with displaying an exception debug screen if debug exceptions is enabled.
|
|
7
|
+
#
|
|
8
|
+
# @private
|
|
9
|
+
class DebugExceptions < Integrator
|
|
10
|
+
|
|
11
|
+
# Patch for disabling logging
|
|
12
|
+
#
|
|
13
|
+
# @private
|
|
14
|
+
module InstanceMethods
|
|
15
|
+
def self.included(klass)
|
|
16
|
+
klass.class_eval do
|
|
17
|
+
private
|
|
18
|
+
def logger(*args)
|
|
19
|
+
nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
begin
|
|
27
|
+
# Rails >= 3.1
|
|
28
|
+
require "action_dispatch/middleware/debug_exceptions"
|
|
29
|
+
rescue LoadError
|
|
30
|
+
# Rails < 3.1
|
|
31
|
+
require "action_dispatch/middleware/show_exceptions"
|
|
32
|
+
end
|
|
33
|
+
rescue LoadError => e
|
|
34
|
+
raise RequirementNotMetError.new(e.message)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def integrate!
|
|
38
|
+
if defined?(::ActionDispatch::DebugExceptions) && !::ActionDispatch::DebugExceptions.include?(InstanceMethods)
|
|
39
|
+
::ActionDispatch::DebugExceptions.send(:include, InstanceMethods)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if defined?(::ActionDispatch::ShowExceptions) && !::ActionDispatch::ShowExceptions.include?(InstanceMethods)
|
|
43
|
+
::ActionDispatch::ShowExceptions.send(:include, InstanceMethods)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Integrations
|
|
3
|
+
module ActionView
|
|
4
|
+
# Reponsible for uninstalling the default `ActionView::LogSubscriber` and installing
|
|
5
|
+
# the TimberLogSubscriber.
|
|
6
|
+
#
|
|
7
|
+
# @private
|
|
8
|
+
class LogSubscriber < Integrator
|
|
9
|
+
def initialize
|
|
10
|
+
require "action_view/log_subscriber"
|
|
11
|
+
require "timber/integrations/action_view/log_subscriber/timber_log_subscriber"
|
|
12
|
+
rescue LoadError => e
|
|
13
|
+
raise RequirementNotMetError.new(e.message)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def integrate!
|
|
17
|
+
return true if Util::ActiveSupportLogSubscriber.subscribed?(:action_view, TimberLogSubscriber)
|
|
18
|
+
|
|
19
|
+
Util::ActiveSupportLogSubscriber.unsubscribe!(:action_view, ::ActionView::LogSubscriber)
|
|
20
|
+
TimberLogSubscriber.attach_to(:action_view)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Timber
|
|
2
|
+
module Integrations
|
|
3
|
+
module ActionView
|
|
4
|
+
class LogSubscriber < Integrator
|
|
5
|
+
|
|
6
|
+
# The log subscriber that replaces the default `ActionView::LogSubscriber`.
|
|
7
|
+
# The intent of this subscriber is to, as transparently as possible, properly
|
|
8
|
+
# track events that are being logged here.
|
|
9
|
+
#
|
|
10
|
+
# @private
|
|
11
|
+
class TimberLogSubscriber < ::ActionView::LogSubscriber
|
|
12
|
+
def render_template(event)
|
|
13
|
+
info do
|
|
14
|
+
full_name = from_rails_root(event.payload[:identifier])
|
|
15
|
+
message = " Rendered #{full_name}"
|
|
16
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
|
17
|
+
message << " (#{event.duration.round(1)}ms)"
|
|
18
|
+
|
|
19
|
+
Events::TemplateRender.new(
|
|
20
|
+
name: full_name,
|
|
21
|
+
time_ms: event.duration,
|
|
22
|
+
message: message
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_partial(event)
|
|
28
|
+
info do
|
|
29
|
+
full_name = from_rails_root(event.payload[:identifier])
|
|
30
|
+
message = " Rendered #{full_name}"
|
|
31
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
|
32
|
+
message << " (#{event.duration.round(1)}ms)"
|
|
33
|
+
message << " #{cache_message(event.payload)}" if event.payload.key?(:cache_hit)
|
|
34
|
+
|
|
35
|
+
Events::TemplateRender.new(
|
|
36
|
+
name: full_name,
|
|
37
|
+
time_ms: event.duration,
|
|
38
|
+
message: message
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def render_collection(event)
|
|
44
|
+
if respond_to?(:render_count, true)
|
|
45
|
+
info do
|
|
46
|
+
identifier = event.payload[:identifier] || "templates"
|
|
47
|
+
full_name = from_rails_root(identifier)
|
|
48
|
+
message = " Rendered collection of #{full_name}" \
|
|
49
|
+
" #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
|
|
50
|
+
|
|
51
|
+
Events::TemplateRender.new(
|
|
52
|
+
name: full_name,
|
|
53
|
+
time_ms: event.duration,
|
|
54
|
+
message: message
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
# Older versions of rails delegate this method to #render_template
|
|
59
|
+
render_template(event)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
def log_rendering_start(payload)
|
|
65
|
+
# Consolidates 2 template rendering events into 1. We don't need 2 events for
|
|
66
|
+
# rendering a template. If you disagree, please feel free to open a PR and we
|
|
67
|
+
# can make this an option.
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|