logtail-rails 0.2.13 → 0.2.15
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/README.md +2 -0
- data/lib/logtail-rails/action_view/log_subscriber/logtail_log_subscriber.rb +6 -3
- data/lib/logtail-rails/config.rb +19 -0
- data/lib/logtail-rails/error_event.rb +28 -8
- data/lib/logtail-rails/event_log_subscriber.rb +20 -7
- data/lib/logtail-rails/logger.rb +5 -1
- data/lib/logtail-rails/railtie.rb +20 -0
- data/lib/logtail-rails/version.rb +1 -1
- data/lib/logtail-rails.rb +6 -0
- data/logtail-rails.gemspec +2 -2
- metadata +11 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 69e0f9b99939d4b4ab4c3b5b185f09fcc824120618371f8881e10b671b49a218
|
|
4
|
+
data.tar.gz: 47c66f5323fbab6dd1b1fe2e559cce6097c13c540cd5ce7b5c2b8cb857a65b4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3378d7cf4c494bc061759ca956239472c8ad9d1f4066d47cba0e0ef7c975ea50e0be2ecc91ec9a778a9d00d78e3f79e8347092505b5a4b8a03de03c902e7fa67
|
|
7
|
+
data.tar.gz: 6b873bad4830cfe29f52958d5b9493e0c3b0c7ddb634eabe3f6719860722e947548f710ac9b99aecd0e997105a3a1350933df4e85436889b9c0e3db08296a5f5
|
data/README.md
CHANGED
|
@@ -14,6 +14,8 @@ Experience SQL-compatible structured log management based on ClickHouse. [Learn
|
|
|
14
14
|
|
|
15
15
|
[Getting started ⇗](https://betterstack.com/docs/logs/ruby-and-rails/#logging-from-rails)
|
|
16
16
|
|
|
17
|
+
To turn the integration off in an environment, e.g. development or test, set `config.logtail.enabled = false` in `config/environments/<environment>.rb`. Rails then logs to `log/<environment>.log` as usual, even if `config/application.rb` creates the Better Stack logger.
|
|
18
|
+
|
|
17
19
|
## Need help?
|
|
18
20
|
Please let us know at [hello@betterstack.com](mailto:hello@betterstack.com). We're happy to help!
|
|
19
21
|
|
|
@@ -76,9 +76,12 @@ module Logtail
|
|
|
76
76
|
super
|
|
77
77
|
|
|
78
78
|
if ::Rails::VERSION::MAJOR > 7 || ::Rails::VERSION::MAJOR == 7 && ::Rails::VERSION::MINOR >= 1
|
|
79
|
-
# Clean extra listeners subscribed in parent's attach_to method
|
|
80
|
-
::
|
|
81
|
-
|
|
79
|
+
# Clean extra listeners subscribed in parent's attach_to method. Since Rails 7.2 they count as
|
|
80
|
+
# silenced while ActionView::Base.logger is nil, as it is at boot, and listeners_for skips those.
|
|
81
|
+
# all_listeners_for returns the notifier's cached array, so don't concat onto it.
|
|
82
|
+
notifier = ::ActiveSupport::Notifications.notifier
|
|
83
|
+
%w(render_template.action_view render_layout.action_view)
|
|
84
|
+
.flat_map { |name| notifier.respond_to?(:all_listeners_for) ? notifier.all_listeners_for(name) : notifier.listeners_for(name) }
|
|
82
85
|
.filter { |listener| listener.delegate.class == ::ActionView::LogSubscriber::Start }
|
|
83
86
|
.each { |listener| ActiveSupport::Notifications.unsubscribe(listener) }
|
|
84
87
|
end
|
data/lib/logtail-rails/config.rb
CHANGED
|
@@ -10,3 +10,22 @@ Logtail::Config.instance.define_singleton_method(:logrageify!) do
|
|
|
10
10
|
integrations.active_record.silence = true
|
|
11
11
|
integrations.rack.http_events.collapse_into_single_event = true
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
module Logtail
|
|
15
|
+
class Config
|
|
16
|
+
# Turns the Rails integration on or off, it's on by default. Set it in `config/application.rb`
|
|
17
|
+
# or `config/environments/*.rb`, see {Logtail::Frameworks::Rails::Railtie} for what it turns off.
|
|
18
|
+
#
|
|
19
|
+
# @example Keep the standard Rails logging in development
|
|
20
|
+
# # config/environments/development.rb
|
|
21
|
+
# config.logtail.enabled = false
|
|
22
|
+
def enabled=(value)
|
|
23
|
+
@enabled = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Accessor method for {#enabled=}
|
|
27
|
+
def enabled?
|
|
28
|
+
@enabled != false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -18,19 +18,15 @@ module Logtail
|
|
|
18
18
|
# We determine this when the app loads to avoid the overhead on a per request basis.
|
|
19
19
|
EXCEPTION_WRAPPER_TAKES_CLEANER = defined?(::ActionDispatch::ExceptionWrapper) &&
|
|
20
20
|
!::ActionDispatch::ExceptionWrapper.instance_methods.include?(:env)
|
|
21
|
+
# config.action_dispatch.log_rescued_responses (Rails 7.0+), as ActionDispatch::DebugExceptions
|
|
22
|
+
# reads it. This gem silences its logging and logs the exception here instead.
|
|
23
|
+
LOG_RESCUED_RESPONSES_KEY = "action_dispatch.log_rescued_responses".freeze
|
|
21
24
|
|
|
22
25
|
def call(env)
|
|
23
26
|
begin
|
|
24
27
|
status, headers, body = @app.call(env)
|
|
25
28
|
rescue Exception => exception
|
|
26
|
-
|
|
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
|
|
29
|
+
log_exception(env, exception)
|
|
34
30
|
|
|
35
31
|
raise exception
|
|
36
32
|
end
|
|
@@ -38,6 +34,30 @@ module Logtail
|
|
|
38
34
|
|
|
39
35
|
private
|
|
40
36
|
|
|
37
|
+
# Never raises, so that the exception of the app is the one that propagates.
|
|
38
|
+
def log_exception(env, exception)
|
|
39
|
+
return if !log_exception?(env, exception)
|
|
40
|
+
|
|
41
|
+
Config.instance.logger.fatal do
|
|
42
|
+
backtrace = extract_backtrace(env, exception)
|
|
43
|
+
Events::Error.new(
|
|
44
|
+
name: exception.class.name,
|
|
45
|
+
error_message: exception.message,
|
|
46
|
+
backtrace: backtrace
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
Config.instance.debug { "#{self.class.name} could not log #{exception.class}: #{e.inspect}" }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Like DebugExceptions, skip rescued responses such as ActiveRecord::RecordNotFound when
|
|
54
|
+
# log_rescued_responses is false. Older Rails versions don't set it and log every exception.
|
|
55
|
+
def log_exception?(env, exception)
|
|
56
|
+
return true if !env.key?(LOG_RESCUED_RESPONSES_KEY) || env[LOG_RESCUED_RESPONSES_KEY]
|
|
57
|
+
|
|
58
|
+
!::ActionDispatch::ExceptionWrapper.rescue_responses.key?(exception.class.name)
|
|
59
|
+
end
|
|
60
|
+
|
|
41
61
|
# Rails provides a backtrace cleaner, so we use it here.
|
|
42
62
|
def extract_backtrace(env, exception)
|
|
43
63
|
if defined?(::ActionDispatch::ExceptionWrapper)
|
|
@@ -6,7 +6,7 @@ module Logtail
|
|
|
6
6
|
# them with all their data to Rails.logger, which sends them to Better Stack.
|
|
7
7
|
class EventLogSubscriber
|
|
8
8
|
# Rails logger instance
|
|
9
|
-
|
|
9
|
+
attr_accessor :logger
|
|
10
10
|
|
|
11
11
|
# Log level to use for logging events
|
|
12
12
|
mattr_accessor :log_level, default: :info
|
|
@@ -14,6 +14,17 @@ module Logtail
|
|
|
14
14
|
# Allows to disable the subscriber
|
|
15
15
|
mattr_accessor :enabled, default: true
|
|
16
16
|
|
|
17
|
+
# Subscribes to Rails.event once. Later calls only hand the events to the new logger, so that
|
|
18
|
+
# calling Logtail::Logger.create_default_logger again doesn't log every event twice.
|
|
19
|
+
def self.subscribe(logger)
|
|
20
|
+
if @subscriber
|
|
21
|
+
@subscriber.logger = logger
|
|
22
|
+
else
|
|
23
|
+
@subscriber = new(logger)
|
|
24
|
+
::Rails.event.subscribe(@subscriber)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
17
28
|
# Initialize the subscriber with a logger instance
|
|
18
29
|
def initialize(logger)
|
|
19
30
|
@logger = logger
|
|
@@ -43,14 +54,16 @@ module Logtail
|
|
|
43
54
|
|
|
44
55
|
private
|
|
45
56
|
|
|
46
|
-
# Rails 8.
|
|
47
|
-
#
|
|
48
|
-
#
|
|
57
|
+
# Rails 8.1+ reports its own instrumentation (controller calls, SQL queries, template
|
|
58
|
+
# renders, ...) to Rails.event as well, through the ActiveSupport::StructuredEventSubscriber
|
|
59
|
+
# it attaches for each of its components. The Rails log subscribers, and ours replacing
|
|
60
|
+
# them, already log those, so only the application's own events are forwarded.
|
|
49
61
|
def framework_event?(event)
|
|
50
|
-
return false unless defined?(::ActiveSupport::
|
|
62
|
+
return false unless defined?(::ActiveSupport::StructuredEventSubscriber)
|
|
51
63
|
|
|
52
|
-
::ActiveSupport::
|
|
53
|
-
|
|
64
|
+
::ActiveSupport::StructuredEventSubscriber.descendants.any? do |bridge|
|
|
65
|
+
# The namespace attach_to recorded is a private reader on ActiveSupport::Subscriber
|
|
66
|
+
event[:name].start_with?("#{bridge.send(:namespace)}.")
|
|
54
67
|
end
|
|
55
68
|
end
|
|
56
69
|
|
data/lib/logtail-rails/logger.rb
CHANGED
|
@@ -49,6 +49,10 @@ module Logtail
|
|
|
49
49
|
def self.create_logger(*io_devices_and_loggers)
|
|
50
50
|
logger = Logtail::Logger.new(*io_devices_and_loggers)
|
|
51
51
|
|
|
52
|
+
# Rails applies config.log_level to Rails.logger while booting, but not to a logger added with broadcast_to later
|
|
53
|
+
log_level = Rails.application.config.log_level if ENV['LOG_LEVEL'].blank? && Rails.application
|
|
54
|
+
logger.level = ::ActiveSupport::Logger.const_get(log_level.to_s.upcase) if log_level
|
|
55
|
+
|
|
52
56
|
tagged_logging_supported = Rails::VERSION::MAJOR >= 7 || Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1
|
|
53
57
|
logger = ::ActiveSupport::TaggedLogging.new(logger) if tagged_logging_supported
|
|
54
58
|
|
|
@@ -79,7 +83,7 @@ module Logtail
|
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
# For Rails 8.1 and above, subscribe to the event system
|
|
82
|
-
|
|
86
|
+
Logtail::Integrations::Rails::EventLogSubscriber.subscribe(logger) if Rails.respond_to?(:event)
|
|
83
87
|
|
|
84
88
|
logger
|
|
85
89
|
end
|
|
@@ -13,6 +13,26 @@ module Logtail
|
|
|
13
13
|
Logtail::Config.instance.logger = Proc.new { ::Rails.logger }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# `config.logtail.enabled = false` turns the integration off. The environment files are loaded
|
|
17
|
+
# by now, and Rails hasn't set up Rails.logger from config.logger yet.
|
|
18
|
+
initializer(:logtail_enabled, after: :load_environment_config, before: :initialize_logger) do |app|
|
|
19
|
+
next if app.config.logtail.enabled?
|
|
20
|
+
|
|
21
|
+
# Before the :logtail initializer below picks the middlewares
|
|
22
|
+
Integrations::Rails.enabled = false
|
|
23
|
+
|
|
24
|
+
# The setup docs create the logger in config/application.rb, i.e. in every environment. Log to the
|
|
25
|
+
# default log file instead, with a Logtail::Logger so that `logger.info("message", key: value)` works.
|
|
26
|
+
if app.config.logger.is_a?(Logtail::Logger)
|
|
27
|
+
log_file = app.paths["log"].first
|
|
28
|
+
FileUtils.mkdir_p(File.dirname(log_file))
|
|
29
|
+
# Opened like Rails opens it: Logger only writes its "# Logfile created" header into files it creates
|
|
30
|
+
file = File.open(log_file, "a")
|
|
31
|
+
file.binmode
|
|
32
|
+
app.config.logger = Logtail::Logger.create_logger(file)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
16
36
|
# Must be loaded after initializers so that we respect any Logtail configuration set
|
|
17
37
|
initializer(:logtail, before: :build_middleware_stack, after: :load_config_initializers) do
|
|
18
38
|
Integrations::Rails.integrate!
|
data/lib/logtail-rails.rb
CHANGED
|
@@ -39,6 +39,12 @@ module Logtail
|
|
|
39
39
|
def self.integrate!
|
|
40
40
|
return false if !enabled?
|
|
41
41
|
|
|
42
|
+
# Log the status Rails responds with when the app raises, the way ShowExceptions
|
|
43
|
+
# determines it: from config.action_dispatch.rescue_responses, 500 by default.
|
|
44
|
+
Logtail::Integrations::Rack::HTTPEvents.status_for_exception = lambda do |exception|
|
|
45
|
+
::ActionDispatch::ExceptionWrapper.new(nil, exception).status_code
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
ActionController.integrate!
|
|
43
49
|
ActionDispatch.integrate!
|
|
44
50
|
ActionView.integrate!
|
data/logtail-rails.gemspec
CHANGED
|
@@ -27,8 +27,8 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
|
|
30
|
-
spec.add_runtime_dependency "logtail", "~> 0.1", ">= 0.1.
|
|
31
|
-
spec.add_runtime_dependency "logtail-rack", "~> 0.
|
|
30
|
+
spec.add_runtime_dependency "logtail", "~> 0.1", ">= 0.1.21"
|
|
31
|
+
spec.add_runtime_dependency "logtail-rack", "~> 0.2", ">= 0.2.9"
|
|
32
32
|
|
|
33
33
|
spec.add_runtime_dependency 'activerecord', '>= 5.0.0'
|
|
34
34
|
spec.add_runtime_dependency 'railties', '>= 5.0.0'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logtail-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Better Stack
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
version: '0.1'
|
|
19
19
|
- - ">="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.1.
|
|
21
|
+
version: 0.1.21
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -28,21 +28,27 @@ dependencies:
|
|
|
28
28
|
version: '0.1'
|
|
29
29
|
- - ">="
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.1.
|
|
31
|
+
version: 0.1.21
|
|
32
32
|
- !ruby/object:Gem::Dependency
|
|
33
33
|
name: logtail-rack
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
35
35
|
requirements:
|
|
36
36
|
- - "~>"
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: '0.
|
|
38
|
+
version: '0.2'
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 0.2.9
|
|
39
42
|
type: :runtime
|
|
40
43
|
prerelease: false
|
|
41
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
45
|
requirements:
|
|
43
46
|
- - "~>"
|
|
44
47
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '0.
|
|
48
|
+
version: '0.2'
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 0.2.9
|
|
46
52
|
- !ruby/object:Gem::Dependency
|
|
47
53
|
name: activerecord
|
|
48
54
|
requirement: !ruby/object:Gem::Requirement
|