julewire-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/CHANGELOG.md +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +94 -0
- data/docs/advanced-configuration.md +17 -0
- data/docs/capture-and-filtering.md +102 -0
- data/docs/configuration.md +83 -0
- data/docs/development.md +21 -0
- data/docs/events-and-errors.md +46 -0
- data/docs/lifecycle.md +24 -0
- data/docs/request-logging.md +49 -0
- data/julewire-rails.gemspec +44 -0
- data/lib/generators/julewire/install_generator.rb +15 -0
- data/lib/generators/julewire/templates/julewire.rb +16 -0
- data/lib/julewire/rails/configuration.rb +74 -0
- data/lib/julewire/rails/context_body_proxy.rb +54 -0
- data/lib/julewire/rails/debug_exception_log_silencer.rb +53 -0
- data/lib/julewire/rails/doctor_app.rb +233 -0
- data/lib/julewire/rails/exception_severity.rb +27 -0
- data/lib/julewire/rails/lifecycle_hooks.rb +76 -0
- data/lib/julewire/rails/log_subscriber_silencer.rb +52 -0
- data/lib/julewire/rails/logger.rb +185 -0
- data/lib/julewire/rails/logger_outputs.rb +36 -0
- data/lib/julewire/rails/output_requirement.rb +38 -0
- data/lib/julewire/rails/parameter_filter_plan.rb +100 -0
- data/lib/julewire/rails/parameter_filter_processor.rb +117 -0
- data/lib/julewire/rails/railtie.rb +84 -0
- data/lib/julewire/rails/request_attributes.rb +126 -0
- data/lib/julewire/rails/request_completion.rb +120 -0
- data/lib/julewire/rails/request_context.rb +91 -0
- data/lib/julewire/rails/request_error_ownership.rb +63 -0
- data/lib/julewire/rails/request_fields.rb +61 -0
- data/lib/julewire/rails/request_lifecycle.rb +109 -0
- data/lib/julewire/rails/request_middleware.rb +130 -0
- data/lib/julewire/rails/request_summary_timeout_scheduler.rb +38 -0
- data/lib/julewire/rails/structured_event_record.rb +128 -0
- data/lib/julewire/rails/subscribers/controller_response.rb +118 -0
- data/lib/julewire/rails/subscribers/error.rb +86 -0
- data/lib/julewire/rails/subscribers/event.rb +118 -0
- data/lib/julewire/rails/subscribers/rendered_exception.rb +141 -0
- data/lib/julewire/rails/suppression.rb +29 -0
- data/lib/julewire/rails/version.rb +7 -0
- data/lib/julewire/rails.rb +37 -0
- data/lib/julewire-rails.rb +3 -0
- metadata +201 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/notifications"
|
|
4
|
+
|
|
5
|
+
module Julewire
|
|
6
|
+
module Rails
|
|
7
|
+
module Subscribers
|
|
8
|
+
class ControllerResponse
|
|
9
|
+
EVENT_NAME = "process_action.action_controller"
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
include Core::Integration::SubscriberInstall
|
|
13
|
+
|
|
14
|
+
def install!(configuration)
|
|
15
|
+
return reset! unless configuration.controller_capture?
|
|
16
|
+
|
|
17
|
+
install_subscriber(configuration, enabled: true) do |subscriber|
|
|
18
|
+
subscription = ::ActiveSupport::Notifications.subscribe(EVENT_NAME) do |*arguments|
|
|
19
|
+
subscriber.process_action(::ActiveSupport::Notifications::Event.new(*arguments))
|
|
20
|
+
end
|
|
21
|
+
-> { ::ActiveSupport::Notifications.unsubscribe(subscription) }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize(configuration = Configuration.new)
|
|
27
|
+
@configuration = configuration
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_writer :configuration
|
|
31
|
+
|
|
32
|
+
def process_action(event)
|
|
33
|
+
return unless Julewire.current_execution?
|
|
34
|
+
return if Suppression.active?
|
|
35
|
+
|
|
36
|
+
IntegrationHealth.with_failure_health(action: :process_action, component: :controller_response_subscriber) do
|
|
37
|
+
fields = capture_attributes(event.payload)
|
|
38
|
+
Core::Integration::Facade.add_summary_attributes(fields[:attributes])
|
|
39
|
+
Core::Integration::Facade.add_summary_neutral(fields[:neutral])
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def capture_attributes(payload)
|
|
46
|
+
rails_fields = capture_fields(payload)
|
|
47
|
+
neutral = {}
|
|
48
|
+
response_body_bytes = rails_fields[:response_body_bytes]
|
|
49
|
+
if response_body_bytes
|
|
50
|
+
neutral = {
|
|
51
|
+
Core::Fields::AttributeKeys::HTTP_RESPONSE_BODY_SIZE => response_body_bytes
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
{ attributes: { rails: rails_fields }, neutral: neutral }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def capture_fields(payload)
|
|
58
|
+
fields = {}
|
|
59
|
+
merge_capture_fields(fields, request_headers_fields(payload))
|
|
60
|
+
merge_capture_fields(
|
|
61
|
+
fields,
|
|
62
|
+
body_fields_for(:request, payload[:request], Julewire::Rack::Capture::RequestBody)
|
|
63
|
+
)
|
|
64
|
+
merge_capture_fields(fields, response_headers_fields(payload))
|
|
65
|
+
merge_capture_fields(
|
|
66
|
+
fields,
|
|
67
|
+
body_fields_for(:response, payload[:response], Julewire::Rack::Capture::BufferedResponseBody)
|
|
68
|
+
)
|
|
69
|
+
fields
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def request_headers_fields(payload)
|
|
73
|
+
return unless @configuration.request_capture.headers?
|
|
74
|
+
|
|
75
|
+
capture_headers(:request_headers) do
|
|
76
|
+
Julewire::Rack::Capture::Headers.request(
|
|
77
|
+
payload[:request],
|
|
78
|
+
selector: @configuration.request_capture.headers
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def response_headers_fields(payload)
|
|
84
|
+
response = payload[:response]
|
|
85
|
+
return unless @configuration.response_capture.headers? && response
|
|
86
|
+
|
|
87
|
+
capture_headers(:response_headers) do
|
|
88
|
+
Julewire::Rack::Capture::Headers.response(
|
|
89
|
+
response.headers,
|
|
90
|
+
selector: @configuration.response_capture.headers
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def body_fields_for(type, target, capture)
|
|
96
|
+
capture_configuration = @configuration.public_send("#{type}_capture")
|
|
97
|
+
return unless capture_configuration.body?
|
|
98
|
+
|
|
99
|
+
capture.call(
|
|
100
|
+
target,
|
|
101
|
+
content_types: capture_configuration.body_content_types,
|
|
102
|
+
limit: capture_configuration.body_bytes,
|
|
103
|
+
mode: capture_configuration.body_mode
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def capture_headers(key)
|
|
108
|
+
headers = yield
|
|
109
|
+
headers.empty? ? nil : { key => headers }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def merge_capture_fields(fields, captured)
|
|
113
|
+
fields.merge!(captured) if captured && !captured.empty?
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module Rails
|
|
5
|
+
module Subscribers
|
|
6
|
+
class Error
|
|
7
|
+
class << self
|
|
8
|
+
include Core::Integration::SubscriberInstall
|
|
9
|
+
|
|
10
|
+
def install!(configuration)
|
|
11
|
+
return reset! unless configuration.error_reports?
|
|
12
|
+
return unless defined?(::Rails) && ::Rails.respond_to?(:error)
|
|
13
|
+
return unless ::Rails.error.respond_to?(:subscribe)
|
|
14
|
+
|
|
15
|
+
reporter = ::Rails.error
|
|
16
|
+
install_subscriber(configuration, enabled: true) do |subscriber|
|
|
17
|
+
Julewire::RailsSupport::EventReporter.subscribe(reporter, subscriber)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(configuration = Configuration.new)
|
|
23
|
+
@configuration = configuration
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_writer :configuration
|
|
27
|
+
|
|
28
|
+
def report(error, handled:, severity:, context:, source:)
|
|
29
|
+
return unless @configuration.error_reports?
|
|
30
|
+
return if Suppression.active?
|
|
31
|
+
return if request_owned_dispatch_error?(error, handled, source)
|
|
32
|
+
|
|
33
|
+
Core::Integration::Facade.emit(
|
|
34
|
+
severity: julewire_severity(severity),
|
|
35
|
+
event: "rails.error",
|
|
36
|
+
logger: "Rails.error",
|
|
37
|
+
source: @configuration.source,
|
|
38
|
+
context: hash_or_empty(context),
|
|
39
|
+
attributes: { rails: {
|
|
40
|
+
handled: handled,
|
|
41
|
+
source: source
|
|
42
|
+
} },
|
|
43
|
+
error: error
|
|
44
|
+
)
|
|
45
|
+
IntegrationHealth.record_success(action: :report, component: :error_subscriber)
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
IntegrationHealth.record_failure(
|
|
48
|
+
e,
|
|
49
|
+
action: :report,
|
|
50
|
+
component: :error_subscriber
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def request_owned_dispatch_error?(error, handled, source)
|
|
57
|
+
return false unless handled == false
|
|
58
|
+
return false unless source == "application.action_dispatch"
|
|
59
|
+
|
|
60
|
+
RequestErrorOwnership.consume?(error)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def julewire_severity(severity)
|
|
64
|
+
severity.to_sym == :warning ? :warn : severity
|
|
65
|
+
rescue StandardError
|
|
66
|
+
severity
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def hash_or_empty(value)
|
|
70
|
+
return {} unless value.is_a?(Hash)
|
|
71
|
+
|
|
72
|
+
values = Core::Integration::Values::Shape
|
|
73
|
+
normalize_context(values.hash_or_empty(value))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def normalize_context(context)
|
|
77
|
+
controller = context[:controller]
|
|
78
|
+
return context unless context.key?(:controller)
|
|
79
|
+
return context if controller.nil? || controller.is_a?(String)
|
|
80
|
+
|
|
81
|
+
context.merge(controller: controller.class.name || controller.to_s)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module Rails
|
|
5
|
+
module Subscribers
|
|
6
|
+
class Event
|
|
7
|
+
include Core::Integration::EventSubscriber
|
|
8
|
+
|
|
9
|
+
STRUCTURED_EVENT_FILES = %w[
|
|
10
|
+
action_controller/structured_event_subscriber
|
|
11
|
+
action_dispatch/structured_event_subscriber
|
|
12
|
+
action_view/structured_event_subscriber
|
|
13
|
+
active_record/structured_event_subscriber
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
REQUEST_STARTED = "action_controller.request_started"
|
|
17
|
+
REQUEST_COMPLETED = "action_controller.request_completed"
|
|
18
|
+
REQUEST_CONTEXT_KEYS = %i[controller action format].freeze
|
|
19
|
+
|
|
20
|
+
event_subscriber integration_health: IntegrationHealth, configuration_class: Configuration
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
include Core::Integration::SubscriberInstall
|
|
24
|
+
|
|
25
|
+
def install!(configuration)
|
|
26
|
+
return reset! unless configuration.structured_events?
|
|
27
|
+
|
|
28
|
+
require_structured_event_subscribers
|
|
29
|
+
reporter = Julewire::RailsSupport::EventReporter.default
|
|
30
|
+
return unless Julewire::RailsSupport::EventReporter.subscribable?(reporter)
|
|
31
|
+
|
|
32
|
+
install_subscriber(configuration, enabled: true) do |subscriber|
|
|
33
|
+
Julewire::RailsSupport::EventReporter.subscribe(reporter, subscriber) do |event|
|
|
34
|
+
subscriber.accept?(event)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def require_structured_event_subscribers
|
|
42
|
+
STRUCTURED_EVENT_FILES.each { Core::Integration::Lifecycle.require_optional(it) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def accept?(event)
|
|
47
|
+
return false unless @configuration.structured_events?
|
|
48
|
+
return false if Suppression.active?
|
|
49
|
+
|
|
50
|
+
name = event[:name].to_s
|
|
51
|
+
return false if excluded_event?(name)
|
|
52
|
+
|
|
53
|
+
included_event?(name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def emit_event(event)
|
|
59
|
+
name = event[:name].to_s
|
|
60
|
+
payload = event_record.payload_hash(event[:payload])
|
|
61
|
+
|
|
62
|
+
if name == REQUEST_STARTED && current_execution?
|
|
63
|
+
enrich_request_start(payload)
|
|
64
|
+
elsif name == REQUEST_COMPLETED && current_execution?
|
|
65
|
+
enrich_request_completion(payload)
|
|
66
|
+
else
|
|
67
|
+
Core::Integration::Facade.emit(event_record.call(event, name: name, payload: payload))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def after_configuration_change
|
|
72
|
+
@event_record = nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def current_execution?
|
|
76
|
+
Julewire.current_execution?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def included_event?(name)
|
|
80
|
+
names = @configuration.structured_event_names
|
|
81
|
+
prefixes = @configuration.structured_event_prefixes
|
|
82
|
+
return true if prefixes.nil?
|
|
83
|
+
return true if Array(names).any? { name == it.to_s }
|
|
84
|
+
|
|
85
|
+
Array(prefixes).any? { name.start_with?(it.to_s) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def excluded_event?(name)
|
|
89
|
+
Array(@configuration.structured_event_exclude_names).any? { name == it.to_s } ||
|
|
90
|
+
Array(@configuration.structured_event_exclude_prefixes).any? { name.start_with?(it.to_s) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def enrich_request_start(payload)
|
|
94
|
+
fields = payload.slice(*REQUEST_CONTEXT_KEYS, :params).compact
|
|
95
|
+
add_summary_attributes(rails: fields)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def enrich_request_completion(payload)
|
|
99
|
+
add_summary_attributes(rails: request_completion_fields(payload))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def add_summary_attributes(fields)
|
|
103
|
+
Core::Integration::Facade.add_summary_attributes(fields)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def request_completion_fields(payload)
|
|
107
|
+
return payload unless payload.key?(:duration_ms)
|
|
108
|
+
|
|
109
|
+
payload.merge(action_runtime_ms: payload[:duration_ms]).except(:duration_ms)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def event_record
|
|
113
|
+
@event_record ||= StructuredEventRecord.new(@configuration)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_dispatch/middleware/debug_exceptions"
|
|
4
|
+
|
|
5
|
+
module Julewire
|
|
6
|
+
module Rails
|
|
7
|
+
module Subscribers
|
|
8
|
+
class RenderedException
|
|
9
|
+
class << self
|
|
10
|
+
include Core::Integration::SubscriberInstall
|
|
11
|
+
|
|
12
|
+
def install!(configuration)
|
|
13
|
+
return reset! unless configuration.request_summary? || configuration.rendered_exceptions?
|
|
14
|
+
return unless defined?(::ActionDispatch::DebugExceptions)
|
|
15
|
+
return unless ::ActionDispatch::DebugExceptions.respond_to?(:register_interceptor)
|
|
16
|
+
|
|
17
|
+
install_subscriber(configuration, enabled: true) do |subscriber|
|
|
18
|
+
::ActionDispatch::DebugExceptions.register_interceptor(subscriber)
|
|
19
|
+
-> { unregister_interceptor(subscriber) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def unregister_interceptor(subscriber)
|
|
26
|
+
return unless ::ActionDispatch::DebugExceptions.respond_to?(:interceptors)
|
|
27
|
+
|
|
28
|
+
::ActionDispatch::DebugExceptions.interceptors.delete(subscriber)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(configuration = Configuration.new)
|
|
33
|
+
@configuration = configuration
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_writer :configuration
|
|
37
|
+
|
|
38
|
+
def call(request, exception)
|
|
39
|
+
return if Suppression.active?
|
|
40
|
+
|
|
41
|
+
wrapper = exception_wrapper(request, exception)
|
|
42
|
+
return unless showable_response?(request, wrapper)
|
|
43
|
+
|
|
44
|
+
capture_request_error(request, exception, wrapper)
|
|
45
|
+
unless @configuration.rendered_exceptions?
|
|
46
|
+
IntegrationHealth.record_success(action: :call, component: :rendered_exception_subscriber)
|
|
47
|
+
return
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Core::Integration::Facade.emit(
|
|
51
|
+
severity: severity_for(request, wrapper),
|
|
52
|
+
event: "action_dispatch.rendered_exception",
|
|
53
|
+
logger: "ActionDispatch::DebugExceptions",
|
|
54
|
+
source: @configuration.source,
|
|
55
|
+
attributes: attributes_for(request, wrapper),
|
|
56
|
+
neutral: neutral_for(request, wrapper),
|
|
57
|
+
error: exception
|
|
58
|
+
)
|
|
59
|
+
IntegrationHealth.record_success(action: :call, component: :rendered_exception_subscriber)
|
|
60
|
+
rescue StandardError => e
|
|
61
|
+
IntegrationHealth.record_failure(
|
|
62
|
+
e,
|
|
63
|
+
action: :call,
|
|
64
|
+
component: :rendered_exception_subscriber
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def exception_wrapper(request, exception)
|
|
71
|
+
cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
|
72
|
+
::ActionDispatch::ExceptionWrapper.new(cleaner, exception)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def showable_response?(request, wrapper)
|
|
76
|
+
wrapper.show?(request)
|
|
77
|
+
rescue StandardError
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def capture_request_error(request, exception, wrapper)
|
|
82
|
+
return unless @configuration.request_summary?
|
|
83
|
+
|
|
84
|
+
request.set_header(
|
|
85
|
+
RequestMiddleware::RENDERED_EXCEPTION_ENV_KEY,
|
|
86
|
+
{
|
|
87
|
+
error: exception,
|
|
88
|
+
severity: severity_for(request, wrapper),
|
|
89
|
+
status: status_code(wrapper),
|
|
90
|
+
rescue_response: rescue_response?(wrapper),
|
|
91
|
+
rescue_template: rescue_template(wrapper)
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
RequestErrorOwnership.mark(exception)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def severity_for(request, _wrapper)
|
|
98
|
+
ExceptionSeverity.for_request(request)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def attributes_for(request, wrapper)
|
|
102
|
+
{
|
|
103
|
+
rails: {
|
|
104
|
+
rescue_response: rescue_response?(wrapper),
|
|
105
|
+
request_method: request.request_method,
|
|
106
|
+
path: request.path,
|
|
107
|
+
status: status_code(wrapper),
|
|
108
|
+
rescue_template: rescue_template(wrapper)
|
|
109
|
+
}.compact
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def neutral_for(request, wrapper)
|
|
114
|
+
Core::Fields::AttributeKeys.fields(
|
|
115
|
+
Core::Fields::AttributeKeys::HTTP_REQUEST_METHOD => request.request_method,
|
|
116
|
+
Core::Fields::AttributeKeys::URL_PATH => request.path,
|
|
117
|
+
Core::Fields::AttributeKeys::HTTP_RESPONSE_STATUS_CODE => status_code(wrapper)
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def status_code(wrapper)
|
|
122
|
+
wrapper.status_code
|
|
123
|
+
rescue StandardError
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def rescue_response?(wrapper)
|
|
128
|
+
wrapper.rescue_response?
|
|
129
|
+
rescue StandardError
|
|
130
|
+
false
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def rescue_template(wrapper)
|
|
134
|
+
wrapper.rescue_template
|
|
135
|
+
rescue StandardError
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/isolated_execution_state"
|
|
4
|
+
|
|
5
|
+
module Julewire
|
|
6
|
+
module Rails
|
|
7
|
+
module Suppression
|
|
8
|
+
KEY = :julewire_rails_suppressed
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def active?
|
|
12
|
+
!!::ActiveSupport::IsolatedExecutionState[KEY]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def suppress
|
|
16
|
+
previous = ::ActiveSupport::IsolatedExecutionState[KEY]
|
|
17
|
+
::ActiveSupport::IsolatedExecutionState[KEY] = true
|
|
18
|
+
yield
|
|
19
|
+
ensure
|
|
20
|
+
if previous
|
|
21
|
+
::ActiveSupport::IsolatedExecutionState[KEY] = previous
|
|
22
|
+
else
|
|
23
|
+
::ActiveSupport::IsolatedExecutionState.delete(KEY)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "julewire/core"
|
|
5
|
+
require "julewire/rack"
|
|
6
|
+
require "julewire/rails_support"
|
|
7
|
+
require "rails"
|
|
8
|
+
|
|
9
|
+
module Julewire
|
|
10
|
+
module Rails
|
|
11
|
+
class Error < Julewire::Error; end
|
|
12
|
+
IntegrationHealth = Core::Integration::Health.scoped(:rails)
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def config
|
|
16
|
+
application = ::Rails.application
|
|
17
|
+
raise Error, "Rails.application is not available" unless application
|
|
18
|
+
|
|
19
|
+
application.config.julewire_rails
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configure
|
|
23
|
+
raise ArgumentError, "Julewire::Rails.configure requires a block" unless block_given?
|
|
24
|
+
|
|
25
|
+
yield config
|
|
26
|
+
config
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
loader = Zeitwerk::Loader.for_gem_extension(self)
|
|
32
|
+
loader.setup
|
|
33
|
+
Core::Processing.register(:rails_parameter_filter) do |*args, **options|
|
|
34
|
+
Rails::ParameterFilterProcessor.new(*args, **options)
|
|
35
|
+
end
|
|
36
|
+
Julewire::Rails::Railtie
|
|
37
|
+
end
|