logtail-rails 0.1.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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +144 -0
  3. data/.gitignore +23 -0
  4. data/.rspec +3 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.md +13 -0
  7. data/README.md +10 -0
  8. data/Rakefile +6 -0
  9. data/gemfiles/rails-3.0.gemfile +8 -0
  10. data/gemfiles/rails-3.1.gemfile +8 -0
  11. data/gemfiles/rails-3.2.gemfile +8 -0
  12. data/gemfiles/rails-4.0.gemfile +12 -0
  13. data/gemfiles/rails-4.1.gemfile +12 -0
  14. data/gemfiles/rails-4.2.gemfile +12 -0
  15. data/gemfiles/rails-5.0.gemfile +12 -0
  16. data/gemfiles/rails-5.1.gemfile +12 -0
  17. data/gemfiles/rails-5.2.gemfile +12 -0
  18. data/gemfiles/rails-6.0.gemfile +12 -0
  19. data/gemfiles/rails-6.1.gemfile +12 -0
  20. data/gemfiles/rails-edge.gemfile +10 -0
  21. data/lib/logtail-rails.rb +64 -0
  22. data/lib/logtail-rails/action_controller.rb +17 -0
  23. data/lib/logtail-rails/action_controller/log_subscriber.rb +25 -0
  24. data/lib/logtail-rails/action_controller/log_subscriber/logtail_log_subscriber.rb +46 -0
  25. data/lib/logtail-rails/action_dispatch.rb +20 -0
  26. data/lib/logtail-rails/action_dispatch/debug_exceptions.rb +51 -0
  27. data/lib/logtail-rails/action_view.rb +17 -0
  28. data/lib/logtail-rails/action_view/log_subscriber.rb +25 -0
  29. data/lib/logtail-rails/action_view/log_subscriber/logtail_log_subscriber.rb +84 -0
  30. data/lib/logtail-rails/active_record.rb +17 -0
  31. data/lib/logtail-rails/active_record/log_subscriber.rb +20 -0
  32. data/lib/logtail-rails/active_record/log_subscriber/logtail_log_subscriber.rb +52 -0
  33. data/lib/logtail-rails/active_support_log_subscriber.rb +39 -0
  34. data/lib/logtail-rails/config.rb +12 -0
  35. data/lib/logtail-rails/config/action_controller.rb +29 -0
  36. data/lib/logtail-rails/config/action_view.rb +29 -0
  37. data/lib/logtail-rails/config/active_record.rb +29 -0
  38. data/lib/logtail-rails/error_event.rb +62 -0
  39. data/lib/logtail-rails/logger.rb +20 -0
  40. data/lib/logtail-rails/overrides.rb +12 -0
  41. data/lib/logtail-rails/overrides/active_support_3_tagged_logging.rb +111 -0
  42. data/lib/logtail-rails/overrides/active_support_buffered_logger.rb +22 -0
  43. data/lib/logtail-rails/overrides/active_support_tagged_logging.rb +66 -0
  44. data/lib/logtail-rails/overrides/lograge.rb +18 -0
  45. data/lib/logtail-rails/overrides/rails_stdout_logging.rb +20 -0
  46. data/lib/logtail-rails/rack_logger.rb +59 -0
  47. data/lib/logtail-rails/railtie.rb +27 -0
  48. data/lib/logtail-rails/session_context.rb +37 -0
  49. data/lib/logtail-rails/version.rb +7 -0
  50. data/logtail-rails.gemspec +60 -0
  51. metadata +263 -0
@@ -0,0 +1,17 @@
1
+ require "logtail-rails/action_controller/log_subscriber"
2
+
3
+ module Logtail
4
+ module Integrations
5
+ # Module for holding *all* ActionController integrations. See {Integration} for
6
+ # configuration details for all integrations.
7
+ module ActionController
8
+ extend Integration
9
+
10
+ def self.integrate!
11
+ return false if !enabled?
12
+
13
+ LogSubscriber.integrate!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Logtail
2
+ module Integrations
3
+ module ActionController
4
+ # Responsible for removing the default ActionController::LogSubscriber and installing
5
+ # the LogtailLogSubscriber
6
+ #
7
+ # @private
8
+ class LogSubscriber < Integrator
9
+ def initialize
10
+ require "action_controller/log_subscriber"
11
+ require "logtail-rails/action_controller/log_subscriber/logtail_log_subscriber"
12
+ rescue LoadError => e
13
+ raise RequirementNotMetError.new(e.message)
14
+ end
15
+
16
+ def integrate!
17
+ return true if Logtail::Integrations::Rails::ActiveSupportLogSubscriber.subscribed?(:action_controller, LogtailLogSubscriber)
18
+
19
+ Logtail::Integrations::Rails::ActiveSupportLogSubscriber.unsubscribe!(:action_controller, ::ActionController::LogSubscriber)
20
+ LogtailLogSubscriber.attach_to(:action_controller)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ module Logtail
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 LogtailLogSubscriber < ::ActionController::LogSubscriber
12
+ def start_processing(event)
13
+ return true if silence?
14
+
15
+ info do
16
+ payload = event.payload
17
+ params = payload[:params].except(*INTERNAL_PARAMS)
18
+ format = extract_format(payload)
19
+ format = format.to_s.upcase if format.is_a?(Symbol)
20
+
21
+ Events::ControllerCall.new(
22
+ controller: payload[:controller],
23
+ action: payload[:action],
24
+ format: format,
25
+ params: params
26
+ )
27
+ end
28
+ end
29
+
30
+ private
31
+ def extract_format(payload)
32
+ if payload.key?(:format)
33
+ payload[:format] # rails > 4.X
34
+ elsif payload.key?(:formats)
35
+ payload[:formats].first # rails 3.X
36
+ end
37
+ end
38
+
39
+ def silence?
40
+ ActionController.silence?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ require "logtail-rails/action_dispatch/debug_exceptions"
2
+
3
+ module Logtail
4
+ module Integrations
5
+ # Module for holding *all* ActionDispatch integrations. This
6
+ # module simply disables the exception tracking middleware so that our middleware
7
+ # works as expected.
8
+ module ActionDispatch
9
+ def self.enabled?
10
+ Rails::ErrorEvent.enabled?
11
+ end
12
+
13
+ def self.integrate!
14
+ return false if !enabled?
15
+
16
+ DebugExceptions.integrate!
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ module Logtail
2
+ module Integrations
3
+ module ActionDispatch
4
+ # Responsible 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,17 @@
1
+ require "logtail-rails/action_view/log_subscriber"
2
+
3
+ module Logtail
4
+ module Integrations
5
+ # Module for holding *all* ActionView integrations. See {Integration} for
6
+ # configuration details for all integrations.
7
+ module ActionView
8
+ extend Integration
9
+
10
+ def self.integrate!
11
+ return false if !enabled?
12
+
13
+ LogSubscriber.integrate!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Logtail
2
+ module Integrations
3
+ module ActionView
4
+ # Responsible for uninstalling the default `ActionView::LogSubscriber` and installing
5
+ # the LogtailLogSubscriber.
6
+ #
7
+ # @private
8
+ class LogSubscriber < Integrator
9
+ def initialize
10
+ require "action_view/log_subscriber"
11
+ require "logtail-rails/action_view/log_subscriber/logtail_log_subscriber"
12
+ rescue LoadError => e
13
+ raise RequirementNotMetError.new(e.message)
14
+ end
15
+
16
+ def integrate!
17
+ return true if Logtail::Integrations::Rails::ActiveSupportLogSubscriber.subscribed?(:action_view, LogtailLogSubscriber)
18
+
19
+ Logtail::Integrations::Rails::ActiveSupportLogSubscriber.unsubscribe!(:action_view, ::ActionView::LogSubscriber)
20
+ LogtailLogSubscriber.attach_to(:action_view)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,84 @@
1
+ module Logtail
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 LogtailLogSubscriber < ::ActionView::LogSubscriber
12
+ def render_template(event)
13
+ return true if silence?
14
+
15
+ info do
16
+ full_name = from_rails_root(event.payload[:identifier])
17
+ message = " Rendered #{full_name}"
18
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
19
+ message << " (#{event.duration.round(1)}ms)"
20
+
21
+ Events::TemplateRender.new(
22
+ name: full_name,
23
+ duration_ms: event.duration,
24
+ message: message
25
+ )
26
+ end
27
+ end
28
+
29
+ def render_partial(event)
30
+ return true if silence?
31
+
32
+ info do
33
+ full_name = from_rails_root(event.payload[:identifier])
34
+ message = " Rendered #{full_name}"
35
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
36
+ message << " (#{event.duration.round(1)}ms)"
37
+ message << " #{cache_message(event.payload)}" if event.payload.key?(:cache_hit)
38
+
39
+ Events::TemplateRender.new(
40
+ name: full_name,
41
+ duration_ms: event.duration,
42
+ message: message
43
+ )
44
+ end
45
+ end
46
+
47
+ def render_collection(event)
48
+ return true if silence?
49
+
50
+ if respond_to?(:render_count, true)
51
+ info do
52
+ identifier = event.payload[:identifier] || "templates"
53
+ full_name = from_rails_root(identifier)
54
+ message = " Rendered collection of #{full_name}" \
55
+ " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
56
+
57
+ Events::TemplateRender.new(
58
+ name: full_name,
59
+ duration_ms: event.duration,
60
+ message: message
61
+ )
62
+ end
63
+ else
64
+ # Older versions of rails delegate this method to #render_template
65
+ render_template(event)
66
+ end
67
+ end
68
+
69
+ private
70
+ def log_rendering_start(*args)
71
+ # Consolidates 2 template rendering events into 1. We don't need 2 events for
72
+ # rendering a template. If you disagree, please feel free to open a PR and we
73
+ # can make this an option.
74
+ # *args is to be future proof for Rails 6.1, which adds another method argument
75
+ end
76
+
77
+ def silence?
78
+ ActionView.silence?
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,17 @@
1
+ require "logtail-rails/active_record/log_subscriber"
2
+
3
+ module Logtail
4
+ module Integrations
5
+ # Module for holding *all* ActiveRecord integrations. See {Integration} for
6
+ # configuration details for all integrations.
7
+ module ActiveRecord
8
+ extend Integration
9
+
10
+ def self.integrate!
11
+ return false if !enabled?
12
+
13
+ LogSubscriber.integrate!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require "logtail-rails/active_record/log_subscriber/logtail_log_subscriber"
2
+
3
+ module Logtail
4
+ module Integrations
5
+ module ActiveRecord
6
+ # Responsible for uninstalling the default `ActiveRecord::LogSubscriber` and replacing it
7
+ # with the `LogtailLogSubscriber`.
8
+ #
9
+ # @private
10
+ class LogSubscriber < Integrator
11
+ def integrate!
12
+ return true if Logtail::Integrations::Rails::ActiveSupportLogSubscriber.subscribed?(:active_record, LogtailLogSubscriber)
13
+
14
+ Logtail::Integrations::Rails::ActiveSupportLogSubscriber.unsubscribe!(:active_record, ::ActiveRecord::LogSubscriber)
15
+ LogtailLogSubscriber.attach_to(:active_record)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ # We require all of ActiveRecord because the #logger method in ::ActiveRecord::LogSubscriber
2
+ # uses ActiveRecord::Base. We can't require active_record/base directly because ActiveRecord
3
+ # does not require files properly and we receive unintialized constant errors.
4
+ require "active_record"
5
+ require "active_record/log_subscriber"
6
+
7
+ module Logtail
8
+ module Integrations
9
+ module ActiveRecord
10
+ class LogSubscriber < Integrator
11
+ # The log subscriber that replaces the default `ActiveRecord::LogSubscriber`.
12
+ # The intent of this subscriber is to, as transparently as possible, properly
13
+ # track events that are being logged here. This LogSubscriber will never change
14
+ # default behavior / log messages.
15
+ #
16
+ # @private
17
+ class LogtailLogSubscriber < ::ActiveRecord::LogSubscriber
18
+ def sql(event)
19
+ return true if silence?
20
+
21
+ r = super(event)
22
+
23
+ if @message
24
+ payload = event.payload
25
+
26
+ sql_event = Events::SQLQuery.new(
27
+ sql: payload[:sql],
28
+ duration_ms: event.duration,
29
+ message: @message,
30
+ )
31
+
32
+ logger.debug sql_event
33
+
34
+ @message = nil
35
+ end
36
+
37
+ r
38
+ end
39
+
40
+ private
41
+ def debug(message)
42
+ @message = message
43
+ end
44
+
45
+ def silence?
46
+ ActiveRecord.silence?
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,39 @@
1
+ module Logtail
2
+ module Integrations
3
+ module Rails
4
+ # @private
5
+ module ActiveSupportLogSubscriber
6
+ extend self
7
+
8
+ def find(component, type)
9
+ ::ActiveSupport::LogSubscriber.log_subscribers.find do |subscriber|
10
+ subscriber.class == type
11
+ end
12
+ end
13
+
14
+ def subscribed?(component, type)
15
+ !find(component, type).nil?
16
+ end
17
+
18
+ # I don't know why this has to be so complicated, but it is. This code was taken from
19
+ # lograge :/
20
+ def unsubscribe!(component, type)
21
+ subscriber = find(component, type)
22
+
23
+ if !subscriber
24
+ raise "We could not find a log subscriber for #{component.inspect} of type #{type.inspect}"
25
+ end
26
+
27
+ events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
28
+ events.each do |event|
29
+ ::ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
30
+ if listener.instance_variable_get('@delegate') == subscriber
31
+ ::ActiveSupport::Notifications.unsubscribe listener
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ require 'logtail/config'
2
+ require 'logtail-rack/config'
3
+ require 'logtail-rails/config/action_view'
4
+ require 'logtail-rails/config/active_record'
5
+ require 'logtail-rails/config/action_controller'
6
+
7
+ Logtail::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