timber-rails 1.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +56 -0
- data/Gemfile +8 -0
- data/LICENSE.md +15 -0
- data/README.md +12 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gemfiles/rails-3.0.gemfile +8 -0
- data/gemfiles/rails-3.1.gemfile +8 -0
- data/gemfiles/rails-3.2.gemfile +8 -0
- data/gemfiles/rails-4.0.gemfile +12 -0
- data/gemfiles/rails-4.1.gemfile +12 -0
- data/gemfiles/rails-4.2.gemfile +12 -0
- data/gemfiles/rails-5.0.gemfile +12 -0
- data/gemfiles/rails-5.1.gemfile +12 -0
- data/gemfiles/rails-5.2.gemfile +12 -0
- data/gemfiles/rails-edge.gemfile +11 -0
- data/lib/timber-rails/action_controller/log_subscriber/timber_log_subscriber.rb +46 -0
- data/lib/timber-rails/action_controller/log_subscriber.rb +25 -0
- data/lib/timber-rails/action_controller.rb +17 -0
- data/lib/timber-rails/action_dispatch/debug_exceptions.rb +51 -0
- data/lib/timber-rails/action_dispatch.rb +20 -0
- data/lib/timber-rails/action_view/log_subscriber/timber_log_subscriber.rb +83 -0
- data/lib/timber-rails/action_view/log_subscriber.rb +25 -0
- data/lib/timber-rails/action_view.rb +17 -0
- data/lib/timber-rails/active_record/log_subscriber/timber_log_subscriber.rb +52 -0
- data/lib/timber-rails/active_record/log_subscriber.rb +20 -0
- data/lib/timber-rails/active_record.rb +17 -0
- data/lib/timber-rails/config/action_controller.rb +29 -0
- data/lib/timber-rails/config/action_view.rb +29 -0
- data/lib/timber-rails/config/active_record.rb +29 -0
- data/lib/timber-rails/config.rb +12 -0
- data/lib/timber-rails/error_event.rb +61 -0
- data/lib/timber-rails/logger.rb +15 -0
- data/lib/timber-rails/overrides/active_support_3_tagged_logging.rb +111 -0
- data/lib/timber-rails/overrides/active_support_buffered_logger.rb +22 -0
- data/lib/timber-rails/overrides/active_support_tagged_logging.rb +66 -0
- data/lib/timber-rails/overrides/lograge.rb +18 -0
- data/lib/timber-rails/overrides/rails_stdout_logging.rb +21 -0
- data/lib/timber-rails/overrides.rb +12 -0
- data/lib/timber-rails/rack_logger.rb +58 -0
- data/lib/timber-rails/railtie.rb +27 -0
- data/lib/timber-rails/session_context.rb +40 -0
- data/lib/timber-rails/version.rb +7 -0
- data/lib/timber-rails.rb +63 -0
- data/timber-rails.gemspec +71 -0
- metadata +240 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require "timber-rails/action_controller"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
class Config
|
5
|
+
# Convenience module for accessing the various `Timber::Integrations::*` classes
|
6
|
+
# through the {Timber::Config} object. Timber couples configuration with the class
|
7
|
+
# responsible for implementing it. This provides for a tighter design, but also
|
8
|
+
# requires the user to understand and access the various classes. This module aims
|
9
|
+
# to provide a simple ruby-like configuration interface for internal Timber classes.
|
10
|
+
#
|
11
|
+
# For example:
|
12
|
+
#
|
13
|
+
# config = Timber::Config.instance
|
14
|
+
# config.integrations.active_record.silence = true
|
15
|
+
module Integrations
|
16
|
+
extend self
|
17
|
+
|
18
|
+
# Convenience method for accessing the {Timber::Integrations::ActionController} class
|
19
|
+
# specific configuration.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# config = Timber::Config.instance
|
23
|
+
# config.integrations.action_controller.silence = true
|
24
|
+
def action_controller
|
25
|
+
Timber::Integrations::ActionController
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "timber-rails/action_view"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
class Config
|
5
|
+
# Convenience module for accessing the various `Timber::Integrations::*` classes
|
6
|
+
# through the {Timber::Config} object. Timber couples configuration with the class
|
7
|
+
# responsible for implementing it. This provides for a tighter design, but also
|
8
|
+
# requires the user to understand and access the various classes. This module aims
|
9
|
+
# to provide a simple ruby-like configuration interface for internal Timber classes.
|
10
|
+
#
|
11
|
+
# For example:
|
12
|
+
#
|
13
|
+
# config = Timber::Config.instance
|
14
|
+
# config.integrations.active_record.silence = true
|
15
|
+
module Integrations
|
16
|
+
extend self
|
17
|
+
|
18
|
+
# Convenience method for accessing the {Timber::Integrations::ActionView} class
|
19
|
+
# specific configuration.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# config = Timber::Config.instance
|
23
|
+
# config.integrations.action_view.silence = true
|
24
|
+
def action_view
|
25
|
+
Timber::Integrations::ActionView
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "timber-rails/active_record"
|
2
|
+
|
3
|
+
module Timber
|
4
|
+
class Config
|
5
|
+
# Convenience module for accessing the various `Timber::Integrations::*` classes
|
6
|
+
# through the {Timber::Config} object. Timber couples configuration with the class
|
7
|
+
# responsible for implementing it. This provides for a tighter design, but also
|
8
|
+
# requires the user to understand and access the various classes. This module aims
|
9
|
+
# to provide a simple ruby-like configuration interface for internal Timber classes.
|
10
|
+
#
|
11
|
+
# For example:
|
12
|
+
#
|
13
|
+
# config = Timber::Config.instance
|
14
|
+
# config.integrations.active_record.silence = true
|
15
|
+
module Integrations
|
16
|
+
extend self
|
17
|
+
|
18
|
+
# Convenience method for accessing the {Timber::Integrations::ActiveRecord} class
|
19
|
+
# specific configuration.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# config = Timber::Config.instance
|
23
|
+
# config.integrations.active_record.silence = true
|
24
|
+
def active_record
|
25
|
+
Timber::Integrations::ActiveRecord
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'timber/config'
|
2
|
+
require 'timber-rack/config'
|
3
|
+
require 'timber-rails/config/action_view'
|
4
|
+
require 'timber-rails/config/active_record'
|
5
|
+
require 'timber-rails/config/action_controller'
|
6
|
+
|
7
|
+
Timber::Config.instance.define_singleton_method(:logrageify!) do
|
8
|
+
integrations.action_controller.silence = true
|
9
|
+
integrations.action_view.silence = true
|
10
|
+
integrations.active_record.silence = true
|
11
|
+
integrations.rack.http_events.collapse_into_single_event = true
|
12
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
begin
|
2
|
+
# Rails 3.2 requires you to require all of Rails before requiring
|
3
|
+
# the exception wrapper.
|
4
|
+
require "action_dispatch/middleware/exception_wrapper"
|
5
|
+
rescue Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
require "timber/events/error"
|
9
|
+
require "timber-rack/middleware"
|
10
|
+
require "timber/util"
|
11
|
+
|
12
|
+
module Timber
|
13
|
+
module Integrations
|
14
|
+
module Rails
|
15
|
+
# A Rack middleware that is reponsible for capturing exception and error events
|
16
|
+
# {Timber::Events::Error}.
|
17
|
+
class ErrorEvent < Timber::Integrations::Rack::Middleware
|
18
|
+
# We determine this when the app loads to avoid the overhead on a per request basis.
|
19
|
+
EXCEPTION_WRAPPER_TAKES_CLEANER = defined?(::ActionDispatch::ExceptionWrapper) &&
|
20
|
+
!::ActionDispatch::ExceptionWrapper.instance_methods.include?(:env)
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
begin
|
24
|
+
status, headers, body = @app.call(env)
|
25
|
+
rescue Exception => exception
|
26
|
+
Config.instance.logger.fatal do
|
27
|
+
backtrace = extract_backtrace(env, exception)
|
28
|
+
Events::Error.new(
|
29
|
+
name: exception.class.name,
|
30
|
+
error_message: exception.message,
|
31
|
+
backtrace: backtrace
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
raise exception
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
# Rails provides a backtrace cleaner, so we use it here.
|
41
|
+
def extract_backtrace(env, exception)
|
42
|
+
if defined?(::ActionDispatch::ExceptionWrapper)
|
43
|
+
wrapper = if EXCEPTION_WRAPPER_TAKES_CLEANER
|
44
|
+
request = Util::Request.new(env)
|
45
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
46
|
+
::ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception)
|
47
|
+
else
|
48
|
+
::ActionDispatch::ExceptionWrapper.new(env, exception)
|
49
|
+
end
|
50
|
+
|
51
|
+
trace = wrapper.application_trace
|
52
|
+
trace = wrapper.framework_trace if trace.empty?
|
53
|
+
trace
|
54
|
+
else
|
55
|
+
exception.backtrace
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Timber
|
2
|
+
# The Timber Logger behaves exactly like the standard Ruby `::Logger`, except that it supports a
|
3
|
+
# transparent API for logging structured data and events.
|
4
|
+
#
|
5
|
+
# @example Basic logging
|
6
|
+
# logger.info "Payment rejected for customer #{customer_id}"
|
7
|
+
#
|
8
|
+
# @example Logging an event
|
9
|
+
# logger.info "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
|
10
|
+
class Logger < ::Logger
|
11
|
+
include ::ActiveSupport::LoggerThreadSafeLevel if defined?(::ActiveSupport::LoggerThreadSafeLevel)
|
12
|
+
include ::LoggerSilence if defined?(::LoggerSilence)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Please note, this patch is merely an upgrade, backporting improved tagged logging code
|
2
|
+
# from newer versions of Rails:
|
3
|
+
# https://github.com/rails/rails/blob/5-1-stable/activesupport/lib/active_support/tagged_logging.rb
|
4
|
+
# The behavior of tagged logging will not change in any way.
|
5
|
+
#
|
6
|
+
# This patch is specifically for Rails 3. The legacy approach to wrapping the logger in
|
7
|
+
# ActiveSupport::TaggedLogging is rather poor, hence the reason it was changed entirely
|
8
|
+
# for Rails 4 and 5. The problem is that ActiveSupport::TaggedLogging is a wrapping
|
9
|
+
# class that entirely redefines the public API for the logger. As a result, any deviations
|
10
|
+
# from this API in the logger are not exposed (such as accepting event data as a second argument).
|
11
|
+
# This is assuming, so we're fixing it here.
|
12
|
+
|
13
|
+
begin
|
14
|
+
require "active_support/tagged_logging"
|
15
|
+
|
16
|
+
# Instead of patching the class we're pulling the code from Rails master. This brings in
|
17
|
+
# a number of improvements while also addressing the issue above.
|
18
|
+
if ActiveSupport::TaggedLogging.instance_of?(Class)
|
19
|
+
ActiveSupport.send(:remove_const, :TaggedLogging)
|
20
|
+
|
21
|
+
require "active_support/core_ext/module/delegation"
|
22
|
+
require "active_support/core_ext/object/blank"
|
23
|
+
require "logger"
|
24
|
+
|
25
|
+
module ActiveSupport
|
26
|
+
# Wraps any standard Logger object to provide tagging capabilities.
|
27
|
+
#
|
28
|
+
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
29
|
+
# logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
|
30
|
+
# logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
|
31
|
+
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
|
32
|
+
#
|
33
|
+
# This is used by the default Rails.logger as configured by Railties to make
|
34
|
+
# it easy to stamp log lines with subdomains, request ids, and anything else
|
35
|
+
# to aid debugging of multi-user production applications.
|
36
|
+
module TaggedLogging
|
37
|
+
module Formatter # :nodoc:
|
38
|
+
# This method is invoked when a log event occurs.
|
39
|
+
def call(severity, timestamp, progname, msg)
|
40
|
+
super(severity, timestamp, progname, "#{tags_text}#{msg}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def tagged(*tags)
|
44
|
+
new_tags = push_tags(*tags)
|
45
|
+
yield self
|
46
|
+
ensure
|
47
|
+
pop_tags(new_tags.size)
|
48
|
+
end
|
49
|
+
|
50
|
+
def push_tags(*tags)
|
51
|
+
tags.flatten.reject(&:blank?).tap do |new_tags|
|
52
|
+
current_tags.concat new_tags
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def pop_tags(size = 1)
|
57
|
+
current_tags.pop size
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear_tags!
|
61
|
+
current_tags.clear
|
62
|
+
end
|
63
|
+
|
64
|
+
def current_tags
|
65
|
+
# We use our object ID here to avoid conflicting with other instances
|
66
|
+
thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
|
67
|
+
Thread.current[thread_key] ||= []
|
68
|
+
end
|
69
|
+
|
70
|
+
def tags_text
|
71
|
+
tags = current_tags
|
72
|
+
if tags.any?
|
73
|
+
tags.collect { |tag| "[#{tag}] " }.join
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Simple formatter which only displays the message.
|
79
|
+
class SimpleFormatter < ::Logger::Formatter
|
80
|
+
# This method is invoked when a log event occurs
|
81
|
+
def call(severity, timestamp, progname, msg)
|
82
|
+
"#{String === msg ? msg : msg.inspect}\n"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.new(logger)
|
87
|
+
if logger.respond_to?(:formatter=) && logger.respond_to?(:formatter)
|
88
|
+
# Ensure we set a default formatter so we aren't extending nil!
|
89
|
+
logger.formatter ||= SimpleFormatter.new
|
90
|
+
logger.formatter.extend Formatter
|
91
|
+
end
|
92
|
+
|
93
|
+
logger.extend(self)
|
94
|
+
end
|
95
|
+
|
96
|
+
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
|
97
|
+
|
98
|
+
def tagged(*tags)
|
99
|
+
formatter.tagged(*tags) { yield self }
|
100
|
+
end
|
101
|
+
|
102
|
+
def flush
|
103
|
+
clear_tags!
|
104
|
+
super if defined?(super)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
rescue Exception
|
111
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This adds a #formatter and #formatter= method to the legacy ActiveSupport::BufferedLogger
|
2
|
+
# class. This bug was never resolved due to it being phased out past Rails >= 4.
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "active_support/buffered_logger"
|
6
|
+
|
7
|
+
class ActiveSupport::BufferedLogger
|
8
|
+
def formatter
|
9
|
+
if @log.respond_to?(:formatter)
|
10
|
+
@log.formatter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def formatter=(value)
|
15
|
+
if @log.respond_to?(:formatter=)
|
16
|
+
@log.formatter = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
rescue Exception
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# This is an override instead of an integration because without this Timber would not
|
2
|
+
# work properly if ActiveSupport::TaggedLogging is used.
|
3
|
+
#
|
4
|
+
# This is called after 'active_support_3_tagged_logging' where the constant is loaded and
|
5
|
+
# replaced. I want to make sure we don't attempt to load it again undoing the patches
|
6
|
+
# applied there.
|
7
|
+
if defined?(ActiveSupport::TaggedLogging)
|
8
|
+
module Timber
|
9
|
+
module Overrides
|
10
|
+
# @private
|
11
|
+
module ActiveSupportTaggedLogging
|
12
|
+
# @private
|
13
|
+
module FormatterMethods
|
14
|
+
def self.included(mod)
|
15
|
+
mod.module_eval do
|
16
|
+
alias_method :_timber_original_push_tags, :push_tags
|
17
|
+
alias_method :_timber_original_pop_tags, :pop_tags
|
18
|
+
|
19
|
+
def call(severity, timestamp, progname, msg)
|
20
|
+
if is_a?(Timber::Logger::Formatter)
|
21
|
+
# Don't convert the message into a string
|
22
|
+
super(severity, timestamp, progname, msg)
|
23
|
+
else
|
24
|
+
super(severity, timestamp, progname, "#{tags_text}#{msg}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @private
|
32
|
+
module LoggerMethods
|
33
|
+
def self.included(klass)
|
34
|
+
klass.class_eval do
|
35
|
+
def add(severity, message = nil, progname = nil, &block)
|
36
|
+
if message.nil?
|
37
|
+
if block_given?
|
38
|
+
message = block.call
|
39
|
+
else
|
40
|
+
message = progname
|
41
|
+
progname = nil #No instance variable for this like Logger
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if @logger.is_a?(Timber::Logger)
|
45
|
+
@logger.add(severity, message, progname)
|
46
|
+
else
|
47
|
+
@logger.add(severity, "#{tags_text}#{message}", progname)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if defined?(::ActiveSupport::TaggedLogging::Formatter)
|
55
|
+
if !::ActiveSupport::TaggedLogging::Formatter.include?(FormatterMethods)
|
56
|
+
::ActiveSupport::TaggedLogging::Formatter.send(:include, FormatterMethods)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
if !::ActiveSupport::TaggedLogging.include?(LoggerMethods)
|
60
|
+
::ActiveSupport::TaggedLogging.send(:include, LoggerMethods)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
+
#
|
4
|
+
# Timber does ship with a {Timber::Config#logrageify!} option that configures
|
5
|
+
# Timber to behave similarly to Lograge (silencing various logs). Check out
|
6
|
+
# the aforementioned method or the README for info.
|
7
|
+
begin
|
8
|
+
require "lograge"
|
9
|
+
|
10
|
+
module Lograge
|
11
|
+
module_function
|
12
|
+
|
13
|
+
def setup(app)
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
rescue Exception
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# See https://github.com/heroku/rails_stdout_logging
|
2
|
+
# I have no idea why this library was created, but most Heroku / Rails apps use it.
|
3
|
+
# This library completely obliterates any logger configuration you set.
|
4
|
+
# So this patch fixes that.
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "rails_stdout_logging"
|
8
|
+
|
9
|
+
module RailsStdoutLogging
|
10
|
+
class Rails2 < Rails
|
11
|
+
def self.set_logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Rails3 < Rails
|
16
|
+
def self.set_logger(config)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue Exception
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# The order is relevant
|
2
|
+
require "timber-rails/overrides/active_support_3_tagged_logging"
|
3
|
+
require "timber-rails/overrides/active_support_tagged_logging"
|
4
|
+
require "timber-rails/overrides/active_support_buffered_logger"
|
5
|
+
require "timber-rails/overrides/lograge"
|
6
|
+
require "timber-rails/overrides/rails_stdout_logging"
|
7
|
+
|
8
|
+
module Timber
|
9
|
+
# @private
|
10
|
+
module Overrides
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Timber
|
2
|
+
module Integrations
|
3
|
+
module Rails
|
4
|
+
# Disables the default rail's rack logging. Note, we cannot simply uninstall this rack
|
5
|
+
# middleware because rails couples this with ActiveSupport instrumentation. As such,
|
6
|
+
# we simply disable the logger and let our Rack middleware handle the logging.
|
7
|
+
#
|
8
|
+
# See: https://github.com/rails/rails/blob/80e66cc4d90bf8c15d1a5f6e3152e90147f00772/railties/lib/rails/rack/logger.rb#L34
|
9
|
+
#
|
10
|
+
# @private
|
11
|
+
class RackLogger < Integrator
|
12
|
+
|
13
|
+
# @private
|
14
|
+
module InstanceMethods
|
15
|
+
LOGGER = ::Logger.new(nil)
|
16
|
+
|
17
|
+
def self.included(klass)
|
18
|
+
klass.class_eval do
|
19
|
+
private
|
20
|
+
if ::Rails::VERSION::MAJOR == 3
|
21
|
+
# Rails 3.2 calls Rails.logger directly in call_app, so we
|
22
|
+
# will just replace it with a version that doesn't
|
23
|
+
def call_app(_, env)
|
24
|
+
# Put some space between requests in development logs.
|
25
|
+
if ::Rails.env.development?
|
26
|
+
::Rails.logger.info ''
|
27
|
+
::Rails.logger.info ''
|
28
|
+
end
|
29
|
+
@app.call(env)
|
30
|
+
ensure
|
31
|
+
ActiveSupport::LogSubscriber.flush_all!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Rails > 3.2 uses a logger method. Muting logs is accomplished by
|
36
|
+
# passing a dummy logger instance with a nil log device.
|
37
|
+
def logger
|
38
|
+
LOGGER
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
require "rails/rack/logger"
|
46
|
+
rescue LoadError => e
|
47
|
+
raise RequirementNotMetError.new(e.message)
|
48
|
+
end
|
49
|
+
|
50
|
+
def integrate!
|
51
|
+
return true if ::Rails::Rack::Logger.include?(InstanceMethods)
|
52
|
+
|
53
|
+
::Rails::Rack::Logger.send(:include, InstanceMethods)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Timber
|
2
|
+
module Frameworks
|
3
|
+
# Module for Rails specific code, such as the Railtie and any methods that assist
|
4
|
+
# with Rails setup.
|
5
|
+
module Rails
|
6
|
+
# Installs Timber into your Rails app automatically.
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
config.timber = Config.instance
|
9
|
+
|
10
|
+
config.before_initialize do
|
11
|
+
Timber::Config.instance.logger = Proc.new { ::Rails.logger }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Must be loaded after initializers so that we respect any Timber configuration set
|
15
|
+
initializer(:timber, before: :build_middleware_stack, after: :load_config_initializers) do
|
16
|
+
Integrations::Rails.integrate!
|
17
|
+
|
18
|
+
# Install the Rack middlewares so that we capture structured data instead of
|
19
|
+
# raw text logs.
|
20
|
+
Integrations::Rails.middlewares.collect do |middleware_class|
|
21
|
+
config.app_middleware.use middleware_class
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "timber/contexts/session"
|
2
|
+
require "timber-rack/middleware"
|
3
|
+
|
4
|
+
module Timber
|
5
|
+
module Integrations
|
6
|
+
module Rails
|
7
|
+
# A Rack middleware that is responsible for adding the Session context
|
8
|
+
# {Timber::Contexts::Session}.
|
9
|
+
class SessionContext < Timber::Integrations::Rack::Middleware
|
10
|
+
def call(env)
|
11
|
+
id = get_session_id(env)
|
12
|
+
if id
|
13
|
+
context = Contexts::Session.new(id: id)
|
14
|
+
CurrentContext.with(context) do
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def get_session_id(env)
|
24
|
+
session_key = ::Rails.application.config.session_options[:key]
|
25
|
+
request = ::ActionDispatch::Request.new(env)
|
26
|
+
extract_from_cookie(request, session_key)
|
27
|
+
rescue Exception => e
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_from_cookie(request, session_key)
|
32
|
+
data = request
|
33
|
+
.cookie_jar
|
34
|
+
.signed_or_encrypted[session_key] || {}
|
35
|
+
data["session_id"]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/timber-rails.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "timber-rails/overrides"
|
2
|
+
|
3
|
+
require "timber"
|
4
|
+
require "rails"
|
5
|
+
require "timber-rails/config"
|
6
|
+
require "timber-rails/railtie"
|
7
|
+
|
8
|
+
require "timber-rack/http_context"
|
9
|
+
require "timber-rack/http_events"
|
10
|
+
require "timber-rack/user_context"
|
11
|
+
require "timber-rails/session_context"
|
12
|
+
require "timber-rails/rack_logger"
|
13
|
+
require "timber-rails/error_event"
|
14
|
+
|
15
|
+
require "timber-rails/action_controller"
|
16
|
+
require "timber-rails/action_dispatch"
|
17
|
+
require "timber-rails/action_view"
|
18
|
+
require "timber-rails/active_record"
|
19
|
+
|
20
|
+
require "timber-rails/logger"
|
21
|
+
|
22
|
+
module Timber
|
23
|
+
module Integrations
|
24
|
+
# Module for holding *all* Rails integrations. This module does *not*
|
25
|
+
# extend {Integration} because it's dependent on {Rack::HTTPEvents}. This
|
26
|
+
# module simply disables the default HTTP request logging.
|
27
|
+
module Rails
|
28
|
+
def self.enabled?
|
29
|
+
Timber::Integrations::Rack::HTTPEvents.enabled?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.integrate!
|
33
|
+
return false if !enabled?
|
34
|
+
|
35
|
+
ActionController.integrate!
|
36
|
+
ActionDispatch.integrate!
|
37
|
+
ActionView.integrate!
|
38
|
+
ActiveRecord.integrate!
|
39
|
+
RackLogger.integrate!
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.enabled=(value)
|
43
|
+
Timber::Integrations::Rails::ErrorEvent.enabled = value
|
44
|
+
Timber::Integrations::Rack::HTTPContext.enabled = value
|
45
|
+
Timber::Integrations::Rack::HTTPEvents.enabled = value
|
46
|
+
Timber::Integrations::Rack::UserContext.enabled = value
|
47
|
+
SessionContext.enabled = value
|
48
|
+
|
49
|
+
ActionController.enabled = value
|
50
|
+
ActionView.enabled = value
|
51
|
+
ActiveRecord.enabled = value
|
52
|
+
end
|
53
|
+
|
54
|
+
# All enabled middlewares. The order is relevant. Middlewares that set
|
55
|
+
# context are added first so that context is included in subsequent log lines.
|
56
|
+
def self.middlewares
|
57
|
+
@middlewares ||= [Timber::Integrations::Rack::HTTPContext, SessionContext, Timber::Integrations::Rack::UserContext,
|
58
|
+
Timber::Integrations::Rack::HTTPEvents, Timber::Integrations::Rails::ErrorEvent].select(&:enabled?)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|