logtail-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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