julewire-rails 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/docs/capture-and-filtering.md +4 -3
- data/docs/configuration.md +3 -2
- data/julewire-rails.gemspec +3 -3
- data/lib/julewire/rails/configuration.rb +4 -4
- data/lib/julewire/rails/context_body_proxy.rb +8 -3
- data/lib/julewire/rails/debug_exception_log_silencer.rb +9 -18
- data/lib/julewire/rails/doctor_app.rb +21 -21
- data/lib/julewire/rails/lifecycle_hooks.rb +0 -11
- data/lib/julewire/rails/log_subscriber_silencer.rb +4 -17
- data/lib/julewire/rails/logger.rb +29 -24
- data/lib/julewire/rails/logger_outputs.rb +0 -3
- data/lib/julewire/rails/output_requirement.rb +2 -2
- data/lib/julewire/rails/parameter_filter_plan.rb +13 -37
- data/lib/julewire/rails/parameter_filter_processor.rb +13 -45
- data/lib/julewire/rails/parameter_filters.rb +21 -0
- data/lib/julewire/rails/railtie.rb +42 -28
- data/lib/julewire/rails/request_attributes.rb +37 -16
- data/lib/julewire/rails/request_completion.rb +21 -20
- data/lib/julewire/rails/request_context.rb +29 -7
- data/lib/julewire/rails/request_error_ownership.rb +11 -19
- data/lib/julewire/rails/request_fields.rb +2 -2
- data/lib/julewire/rails/request_lifecycle.rb +7 -13
- data/lib/julewire/rails/request_middleware.rb +14 -13
- data/lib/julewire/rails/request_summary_timeout_scheduler.rb +0 -9
- data/lib/julewire/rails/structured_event_record.rb +11 -39
- data/lib/julewire/rails/subscribers/controller_response.rb +9 -14
- data/lib/julewire/rails/subscribers/error.rb +11 -15
- data/lib/julewire/rails/subscribers/event.rb +6 -8
- data/lib/julewire/rails/subscribers/rendered_exception.rb +12 -16
- data/lib/julewire/rails/version.rb +1 -1
- metadata +9 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/parameter_filter"
|
|
4
|
+
require "julewire/rails/parameter_filters"
|
|
4
5
|
|
|
5
6
|
module Julewire
|
|
6
7
|
module Rails
|
|
@@ -8,70 +9,47 @@ module Julewire
|
|
|
8
9
|
EMPTY_CONTAINER_MARKER = Core.sentinel(:empty_container)
|
|
9
10
|
private_constant :EMPTY_CONTAINER_MARKER
|
|
10
11
|
|
|
11
|
-
def initialize(filters =
|
|
12
|
+
def initialize(filters = ParameterFilters.configured)
|
|
12
13
|
@filter = build_filter(filters)
|
|
13
14
|
# Rails exposes filter_param for scalar fields; use it to avoid whole-record
|
|
14
|
-
# copies when the filter list has no Proc semantics to preserve.
|
|
15
|
-
|
|
16
|
-
# required record container.
|
|
17
|
-
@filter_param_fast_path = filter_param_safe?(filters) && @filter.respond_to?(:filter_param)
|
|
15
|
+
# copies when the filter list has no Proc semantics to preserve.
|
|
16
|
+
@filter_param_fast_path = filter_param_safe?(filters)
|
|
18
17
|
@field_plan = ParameterFilterPlan.build(filters) if @filter_param_fast_path
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def call(draft)
|
|
22
21
|
validate_draft!(draft)
|
|
23
|
-
return if @filter.nil?
|
|
24
|
-
|
|
25
22
|
@filter_param_fast_path ? filter_draft_fields!(draft) : filter_whole_record!(draft)
|
|
23
|
+
draft
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
private
|
|
29
27
|
|
|
30
28
|
def validate_draft!(draft)
|
|
31
|
-
return
|
|
29
|
+
return if draft.instance_of?(RecordDraft)
|
|
32
30
|
|
|
33
31
|
raise TypeError, "expected Julewire::RecordDraft"
|
|
34
32
|
end
|
|
35
33
|
|
|
36
34
|
def build_filter(filters)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
filters = Array(filters)
|
|
40
|
-
return if filters.empty?
|
|
41
|
-
|
|
42
|
-
::ActiveSupport::ParameterFilter.new(::ActiveSupport::ParameterFilter.precompile_filters(filters))
|
|
35
|
+
ParameterFilters.build(filters)
|
|
43
36
|
end
|
|
44
37
|
|
|
45
38
|
def filter_param_safe?(filters)
|
|
46
39
|
return false if filters.respond_to?(:filter) && !filters.is_a?(Array)
|
|
47
40
|
|
|
48
41
|
filters = Array(filters)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
filters.none?(Regexp) || filters.any? { regexp_matches_record_container?(it) }
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def regexp_matches_record_container?(filter)
|
|
55
|
-
filter.is_a?(Regexp) && Core::Processing::RecordFieldTransform.container_keys.any? { filter.match?(it.name) }
|
|
42
|
+
filters.none?(Proc)
|
|
56
43
|
end
|
|
57
44
|
|
|
58
45
|
def filter_draft_fields!(draft)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
value = draft[key]
|
|
46
|
+
draft.each_key do |key|
|
|
47
|
+
value = draft.fetch(key)
|
|
63
48
|
next if skip_empty_container?(key, value)
|
|
64
49
|
|
|
65
50
|
filtered = filter_record_param(key, value)
|
|
66
|
-
draft.transform_field!(key) { filtered }
|
|
51
|
+
draft.transform_field!(key) { filtered }
|
|
67
52
|
end
|
|
68
|
-
draft
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def each_filter_field_key(draft, &)
|
|
72
|
-
return @field_plan.filtered_field_keys.each(&) if @field_plan&.filtered_field_keys
|
|
73
|
-
|
|
74
|
-
draft.each_key(&)
|
|
75
53
|
end
|
|
76
54
|
|
|
77
55
|
def filter_whole_record!(draft)
|
|
@@ -79,12 +57,10 @@ module Julewire
|
|
|
79
57
|
end
|
|
80
58
|
|
|
81
59
|
def filter_record_param(key, value)
|
|
82
|
-
if @field_plan
|
|
83
|
-
return @field_plan.filter_value(value)
|
|
84
|
-
end
|
|
60
|
+
return @field_plan.filter_value(value) if @field_plan && record_container_key?(key)
|
|
85
61
|
|
|
86
62
|
filtered = @filter.filter_param(key, value)
|
|
87
|
-
return filtered unless record_container_key?(key) && !filtered.
|
|
63
|
+
return filtered unless record_container_key?(key) && !filtered.instance_of?(Hash)
|
|
88
64
|
return value unless value.is_a?(Hash)
|
|
89
65
|
|
|
90
66
|
@filter.filter(value)
|
|
@@ -104,14 +80,6 @@ module Julewire
|
|
|
104
80
|
def record_container_key?(key)
|
|
105
81
|
Core::Processing::RecordFieldTransform.container_key?(key)
|
|
106
82
|
end
|
|
107
|
-
|
|
108
|
-
def rails_filter_parameters
|
|
109
|
-
app = ::Rails.application if defined?(::Rails) && ::Rails.respond_to?(:application)
|
|
110
|
-
config = app.config if app.respond_to?(:config)
|
|
111
|
-
config.filter_parameters if config.respond_to?(:filter_parameters)
|
|
112
|
-
rescue StandardError
|
|
113
|
-
[]
|
|
114
|
-
end
|
|
115
83
|
end
|
|
116
84
|
end
|
|
117
85
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module Rails
|
|
5
|
+
module ParameterFilters
|
|
6
|
+
class << self
|
|
7
|
+
def configured
|
|
8
|
+
Array(::Rails.application.config.filter_parameters)
|
|
9
|
+
rescue StandardError
|
|
10
|
+
[]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def build(filters)
|
|
14
|
+
return filters if filters.respond_to?(:filter) && !filters.is_a?(Array)
|
|
15
|
+
|
|
16
|
+
ActiveSupport::ParameterFilter.new(Array(filters))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -9,41 +9,53 @@ module Julewire
|
|
|
9
9
|
config.julewire_rails = Configuration.new
|
|
10
10
|
|
|
11
11
|
initializer "julewire_rails.logger", before: :initialize_logger do |app|
|
|
12
|
-
|
|
13
|
-
settings.validate!
|
|
14
|
-
next unless settings.logger?
|
|
15
|
-
|
|
16
|
-
logger = Logger.new(name: settings.logger_name, source: settings.source)
|
|
17
|
-
logger.level = ::Logger::Severity.const_get(app.config.log_level.to_s.upcase)
|
|
18
|
-
logger.formatter = app.config.log_formatter if app.config.respond_to?(:log_formatter)
|
|
19
|
-
app.config.logger = ::ActiveSupport::TaggedLogging.new(logger)
|
|
20
|
-
LoggerOutputs.install!
|
|
12
|
+
Railtie.initialize_logger!(app)
|
|
21
13
|
end
|
|
22
14
|
|
|
23
15
|
initializer "julewire_rails.request_middleware", before: :build_middleware_stack do |app|
|
|
24
|
-
|
|
25
|
-
settings.validate!
|
|
26
|
-
next unless settings.request_middleware?
|
|
27
|
-
|
|
28
|
-
self.class.install_request_middleware(app, settings, app.config.log_tags)
|
|
16
|
+
Railtie.initialize_request_middleware!(app)
|
|
29
17
|
end
|
|
30
18
|
|
|
31
19
|
initializer "julewire_rails.exception_logging", before: :build_middleware_stack do |app|
|
|
32
|
-
|
|
33
|
-
settings.validate!
|
|
34
|
-
self.class.configure_exception_logging(app, settings)
|
|
20
|
+
Railtie.initialize_exception_logging!(app)
|
|
35
21
|
end
|
|
36
22
|
|
|
37
23
|
config.after_initialize do |app|
|
|
38
|
-
|
|
39
|
-
settings.validate!
|
|
40
|
-
OutputRequirement.check!(settings)
|
|
41
|
-
LifecycleHooks.install!(settings)
|
|
42
|
-
Railtie.install_subscribers(settings)
|
|
43
|
-
DebugExceptionLogSilencer.install!(settings)
|
|
24
|
+
Railtie.finish_initialization!(app)
|
|
44
25
|
end
|
|
45
26
|
|
|
46
27
|
class << self
|
|
28
|
+
def initialize_logger!(app)
|
|
29
|
+
settings = validated_settings(app)
|
|
30
|
+
return unless settings.logger?
|
|
31
|
+
|
|
32
|
+
logger = Logger.new(name: settings.logger_name, source: settings.source)
|
|
33
|
+
logger.level = app.config.log_level
|
|
34
|
+
logger.formatter = app.config.log_formatter
|
|
35
|
+
app.config.logger = ::ActiveSupport::TaggedLogging.new(logger)
|
|
36
|
+
LoggerOutputs.install!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize_request_middleware!(app)
|
|
40
|
+
settings = validated_settings(app)
|
|
41
|
+
return unless settings.request_middleware?
|
|
42
|
+
|
|
43
|
+
install_request_middleware(app, settings, app.config.log_tags)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize_exception_logging!(app)
|
|
47
|
+
settings = validated_settings(app)
|
|
48
|
+
configure_exception_logging(app, settings)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def finish_initialization!(app)
|
|
52
|
+
settings = validated_settings(app)
|
|
53
|
+
OutputRequirement.check!(settings)
|
|
54
|
+
LifecycleHooks.install!(settings)
|
|
55
|
+
install_subscribers(settings)
|
|
56
|
+
DebugExceptionLogSilencer.install!(settings)
|
|
57
|
+
end
|
|
58
|
+
|
|
47
59
|
def install_subscribers(settings)
|
|
48
60
|
Subscribers::ControllerResponse.install!(settings)
|
|
49
61
|
settings.error_reports? ? Subscribers::Error.install!(settings) : Subscribers::Error.reset!
|
|
@@ -70,13 +82,15 @@ module Julewire
|
|
|
70
82
|
end
|
|
71
83
|
|
|
72
84
|
def log_rescued_responses_value(settings)
|
|
73
|
-
|
|
74
|
-
return false if settings.logger? && settings.request_summary?
|
|
85
|
+
return settings.log_rescued_responses unless settings.log_rescued_responses == :auto
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
false if settings.logger? && settings.request_summary?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
78
91
|
|
|
79
|
-
|
|
92
|
+
def validated_settings(app)
|
|
93
|
+
app.config.julewire_rails.tap(&:validate!)
|
|
80
94
|
end
|
|
81
95
|
end
|
|
82
96
|
end
|
|
@@ -9,7 +9,7 @@ module Julewire
|
|
|
9
9
|
values = Core::Integration::Values::Shape
|
|
10
10
|
{}.tap do |fields|
|
|
11
11
|
values.append_field(fields, :request_id, request.id)
|
|
12
|
-
values.append_field(fields, :http_method, request.
|
|
12
|
+
values.append_field(fields, :http_method, request.request_method)
|
|
13
13
|
values.append_field(fields, :path, request.path)
|
|
14
14
|
values.append_field(fields, :remote_ip, request.remote_ip)
|
|
15
15
|
end
|
|
@@ -26,16 +26,8 @@ module Julewire
|
|
|
26
26
|
}
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
request,
|
|
32
|
-
rendered_error.fetch(:error),
|
|
33
|
-
status: status,
|
|
34
|
-
wrapper: nil
|
|
35
|
-
).tap do |fields|
|
|
36
|
-
fields[:attributes][:rails][:rescue_response] = rendered_error[:rescue_response]
|
|
37
|
-
fields[:attributes][:rails][:rescue_template] = rendered_error[:rescue_template]
|
|
38
|
-
end
|
|
29
|
+
def rendered_error_details(rendered_error)
|
|
30
|
+
apply_rendered_error_metadata(error_details(rendered_error.fetch(:error), wrapper: nil), rendered_error)
|
|
39
31
|
end
|
|
40
32
|
|
|
41
33
|
def error_summary(request, error, status:, wrapper:)
|
|
@@ -45,10 +37,23 @@ module Julewire
|
|
|
45
37
|
}
|
|
46
38
|
end
|
|
47
39
|
|
|
40
|
+
def error_details(error, wrapper:)
|
|
41
|
+
{
|
|
42
|
+
attributes: { rails: rails_exception_attributes(error, wrapper: wrapper) },
|
|
43
|
+
neutral: {}
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
48
47
|
def request_id(request)
|
|
49
48
|
request_fields(request).id
|
|
50
49
|
end
|
|
51
50
|
|
|
51
|
+
# Rack headers and parsed bodies are user input. Normalize them before
|
|
52
|
+
# passing adapter-owned records to the core integration boundary.
|
|
53
|
+
def normalize_user_fields(fields)
|
|
54
|
+
Core::Fields::FieldSet.deep_symbolize_keys(fields)
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
private
|
|
53
58
|
|
|
54
59
|
def request_neutral_attributes(request)
|
|
@@ -58,7 +63,7 @@ module Julewire
|
|
|
58
63
|
values.append_field(
|
|
59
64
|
fields,
|
|
60
65
|
Core::Fields::AttributeKeys::HTTP_REQUEST_METHOD,
|
|
61
|
-
request.
|
|
66
|
+
request.request_method
|
|
62
67
|
)
|
|
63
68
|
values.append_field(fields, Core::Fields::AttributeKeys::URL_FULL, request.filtered_url)
|
|
64
69
|
values.append_field(fields, Core::Fields::AttributeKeys::URL_PATH, request.path)
|
|
@@ -82,20 +87,36 @@ module Julewire
|
|
|
82
87
|
def rails_error_attributes(request, error, status:, wrapper:)
|
|
83
88
|
rails_request_attributes(request).tap do |rails|
|
|
84
89
|
values = Core::Integration::Values::Shape
|
|
85
|
-
values.append_field(rails, :error_class, error.class.name)
|
|
86
90
|
values.append_field(rails, :status, status)
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
rails_exception_attributes(error, wrapper: wrapper).each do |key, value|
|
|
92
|
+
values.append_field(rails, key, value)
|
|
93
|
+
end
|
|
89
94
|
end
|
|
90
95
|
end
|
|
91
96
|
|
|
97
|
+
def rails_exception_attributes(error, wrapper:)
|
|
98
|
+
values = Core::Integration::Values::Shape
|
|
99
|
+
{}.tap do |fields|
|
|
100
|
+
values.append_field(fields, :error_class, error.class.name)
|
|
101
|
+
values.append_field(fields, :rescue_response, rescue_response?(wrapper))
|
|
102
|
+
values.append_field(fields, :rescue_template, rescue_template(wrapper))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def apply_rendered_error_metadata(fields, rendered_error)
|
|
107
|
+
rails = fields.fetch(:attributes).fetch(:rails)
|
|
108
|
+
rails[:rescue_response] = rendered_error[:rescue_response]
|
|
109
|
+
rails[:rescue_template] = rendered_error[:rescue_template]
|
|
110
|
+
fields
|
|
111
|
+
end
|
|
112
|
+
|
|
92
113
|
def rails_request_attributes(request)
|
|
93
114
|
request = request_fields(request)
|
|
94
115
|
values = Core::Integration::Values::Shape
|
|
95
116
|
{}.tap do |fields|
|
|
96
117
|
values.append_field(fields, :filtered_url, request.filtered_url)
|
|
97
118
|
values.append_field(fields, :filtered_path, request.filtered_path)
|
|
98
|
-
values.append_field(fields, :request_method, request.
|
|
119
|
+
values.append_field(fields, :request_method, request.request_method)
|
|
99
120
|
values.append_field(fields, :path, request.path)
|
|
100
121
|
values.append_field(fields, :user_agent, request.user_agent)
|
|
101
122
|
end
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
|
|
3
5
|
module Julewire
|
|
4
6
|
module Rails
|
|
5
7
|
class RequestCompletion
|
|
6
8
|
class << self
|
|
7
9
|
def finish_instrumentation(instrumenter_handle)
|
|
8
|
-
instrumenter_handle
|
|
10
|
+
instrumenter_handle.finish
|
|
9
11
|
rescue StandardError
|
|
10
12
|
nil
|
|
11
13
|
ensure
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
flush_log_subscribers
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def flush_log_subscribers
|
|
20
|
+
::ActiveSupport::LogSubscriber.flush_all!
|
|
21
|
+
rescue StandardError
|
|
22
|
+
nil
|
|
17
23
|
end
|
|
18
24
|
end
|
|
19
25
|
|
|
@@ -50,21 +56,17 @@ module Julewire
|
|
|
50
56
|
response_finished = @env["rack.response_finished"]
|
|
51
57
|
return unless response_finished.respond_to?(:<<)
|
|
52
58
|
|
|
53
|
-
response_finished <<
|
|
59
|
+
response_finished << lambda do |_rack_env, _status, _headers, error|
|
|
54
60
|
finish_once.call(error)
|
|
55
61
|
end
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
def completion_callback
|
|
59
|
-
|
|
60
|
-
finished = false
|
|
65
|
+
finished = Concurrent::AtomicBoolean.new
|
|
61
66
|
lambda do |error|
|
|
62
|
-
|
|
63
|
-
return if finished
|
|
67
|
+
return unless finished.make_true
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
-
yield if block_given?
|
|
69
|
+
yield
|
|
68
70
|
@execution_handle.with_context { finish_completion(error) }
|
|
69
71
|
end
|
|
70
72
|
end
|
|
@@ -90,12 +92,12 @@ module Julewire
|
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
def completion_timeout_context
|
|
93
|
-
return
|
|
95
|
+
return unless @configuration.request_context?
|
|
94
96
|
|
|
95
97
|
{
|
|
96
98
|
request_id: request_id,
|
|
97
99
|
path: @request.path
|
|
98
|
-
}
|
|
100
|
+
}
|
|
99
101
|
end
|
|
100
102
|
|
|
101
103
|
def emit_completion_timeout_warning(timeout, context)
|
|
@@ -103,17 +105,16 @@ module Julewire
|
|
|
103
105
|
event: "request.completion_timeout",
|
|
104
106
|
logger: @configuration.logger_name,
|
|
105
107
|
source: @configuration.source,
|
|
106
|
-
attributes: { rails: { completion_timeout_ms: (timeout * 1000).round } }
|
|
108
|
+
attributes: { rails: { completion_timeout_ms: (timeout * 1000).round } },
|
|
109
|
+
context: context
|
|
107
110
|
}
|
|
108
|
-
record[:context] = context unless context.empty?
|
|
109
111
|
Julewire.warn(record)
|
|
110
112
|
rescue StandardError
|
|
111
113
|
nil
|
|
112
114
|
end
|
|
113
115
|
|
|
114
116
|
def request_id
|
|
115
|
-
|
|
116
|
-
value || @request.get_header("action_dispatch.request_id") || @request.get_header("HTTP_X_REQUEST_ID")
|
|
117
|
+
RequestAttributes.request_id(@request)
|
|
117
118
|
end
|
|
118
119
|
end
|
|
119
120
|
end
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
module Julewire
|
|
4
4
|
module Rails
|
|
5
5
|
class RequestContext
|
|
6
|
-
def initialize(
|
|
6
|
+
def initialize(
|
|
7
|
+
configuration:,
|
|
8
|
+
request:,
|
|
9
|
+
active_support_context: Julewire::Core::UNSET,
|
|
10
|
+
event_reporter: Julewire::Core::UNSET
|
|
11
|
+
)
|
|
7
12
|
@configuration = configuration
|
|
8
13
|
@request = request
|
|
9
14
|
@active_support_context = default_provider(active_support_context) { ::ActiveSupport::ExecutionContext }
|
|
@@ -23,14 +28,14 @@ module Julewire
|
|
|
23
28
|
private
|
|
24
29
|
|
|
25
30
|
def default_provider(value)
|
|
26
|
-
value.equal?(Core::UNSET) ? yield : value
|
|
31
|
+
value.equal?(Julewire::Core::UNSET) ? yield : value
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def with_request_context(&)
|
|
30
35
|
return yield unless @configuration.request_context?
|
|
31
36
|
|
|
32
37
|
fields = RequestAttributes.context_fields(@request)
|
|
33
|
-
Core::Integration::Facade.with_context(fields) do
|
|
38
|
+
Julewire::Core::Integration::Facade.with_context(fields) do
|
|
34
39
|
with_active_support_execution_context(fields) do
|
|
35
40
|
with_rails_event_context(fields, &)
|
|
36
41
|
end
|
|
@@ -41,10 +46,14 @@ module Julewire
|
|
|
41
46
|
return yield unless @configuration.carry_request_headers
|
|
42
47
|
raise ArgumentError, "carry_request_headers must be an explicit header list" if all_carry_headers?
|
|
43
48
|
|
|
44
|
-
headers = Julewire::Rack::Capture::Headers.request(
|
|
49
|
+
headers = Julewire::Rack::Capture::Headers.request(
|
|
50
|
+
@request,
|
|
51
|
+
selector: @configuration.carry_request_headers
|
|
52
|
+
)
|
|
45
53
|
return yield if headers.empty?
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
headers = RequestAttributes.normalize_user_fields(headers)
|
|
56
|
+
Julewire::Core::Integration::Facade.with_carry(http: { request_headers: headers }, &)
|
|
48
57
|
end
|
|
49
58
|
|
|
50
59
|
def all_carry_headers?
|
|
@@ -67,7 +76,7 @@ module Julewire
|
|
|
67
76
|
@event_reporter.set_context(fields)
|
|
68
77
|
context_set = true
|
|
69
78
|
rescue StandardError
|
|
70
|
-
|
|
79
|
+
nil
|
|
71
80
|
end
|
|
72
81
|
yield
|
|
73
82
|
ensure
|
|
@@ -81,8 +90,21 @@ module Julewire
|
|
|
81
90
|
end
|
|
82
91
|
|
|
83
92
|
def restore_rails_event_context(previous)
|
|
93
|
+
return unless clear_rails_event_context
|
|
94
|
+
return if previous.nil? || previous.empty?
|
|
95
|
+
|
|
96
|
+
apply_rails_event_context(previous)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def clear_rails_event_context
|
|
84
100
|
@event_reporter.clear_context
|
|
85
|
-
|
|
101
|
+
true
|
|
102
|
+
rescue StandardError
|
|
103
|
+
false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def apply_rails_event_context(previous)
|
|
107
|
+
@event_reporter.set_context(previous)
|
|
86
108
|
rescue StandardError
|
|
87
109
|
nil
|
|
88
110
|
end
|
|
@@ -11,25 +11,19 @@ module Julewire
|
|
|
11
11
|
class << self
|
|
12
12
|
def clear
|
|
13
13
|
::ActiveSupport::IsolatedExecutionState.delete(KEY)
|
|
14
|
-
rescue StandardError
|
|
15
|
-
nil
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def mark(error)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
errors = error_map
|
|
22
|
-
each_exception(error) { errors[it] = true }
|
|
23
|
-
rescue StandardError
|
|
24
|
-
nil
|
|
17
|
+
each_exception(error) { error_map[it] = true }
|
|
25
18
|
end
|
|
26
19
|
|
|
27
20
|
def consume?(error)
|
|
28
21
|
errors = current_error_map
|
|
29
22
|
return false unless errors
|
|
30
23
|
|
|
31
|
-
each_exception(error)
|
|
32
|
-
|
|
24
|
+
each_exception(error) do |exception|
|
|
25
|
+
return true if errors.delete(exception)
|
|
26
|
+
end
|
|
33
27
|
false
|
|
34
28
|
end
|
|
35
29
|
|
|
@@ -40,22 +34,20 @@ module Julewire
|
|
|
40
34
|
end
|
|
41
35
|
|
|
42
36
|
def set_error_map
|
|
43
|
-
ObjectSpace::
|
|
37
|
+
ObjectSpace::WeakMap.new.tap { ::ActiveSupport::IsolatedExecutionState[KEY] = it }
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
def current_error_map
|
|
47
41
|
::ActiveSupport::IsolatedExecutionState[KEY]
|
|
48
42
|
end
|
|
49
43
|
|
|
50
|
-
def each_exception(error)
|
|
51
|
-
return
|
|
44
|
+
def each_exception(error, seen = {}.compare_by_identity, &)
|
|
45
|
+
return unless error
|
|
46
|
+
return if seen.key?(error)
|
|
52
47
|
|
|
53
|
-
seen =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
seen[error] = true
|
|
57
|
-
error = error.cause
|
|
58
|
-
end
|
|
48
|
+
seen[error] = nil
|
|
49
|
+
yield error
|
|
50
|
+
each_exception(error.cause, seen, &)
|
|
59
51
|
end
|
|
60
52
|
end
|
|
61
53
|
end
|
|
@@ -16,7 +16,7 @@ module Julewire
|
|
|
16
16
|
nil
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def request_method
|
|
20
20
|
@request.request_method
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -40,7 +40,7 @@ module Julewire
|
|
|
40
40
|
|
|
41
41
|
def remote_ip
|
|
42
42
|
env = @request.env if @request.respond_to?(:env)
|
|
43
|
-
return env
|
|
43
|
+
return env.fetch(REMOTE_IP_ENV_KEY) if env&.key?(REMOTE_IP_ENV_KEY)
|
|
44
44
|
|
|
45
45
|
value = @request.remote_ip
|
|
46
46
|
env[REMOTE_IP_ENV_KEY] = value if env
|
|
@@ -3,23 +3,16 @@
|
|
|
3
3
|
module Julewire
|
|
4
4
|
module Rails
|
|
5
5
|
class RequestLifecycle
|
|
6
|
-
attr_reader :execution_handle
|
|
7
|
-
|
|
8
6
|
def initialize(configuration:, env:, request:, taggers:)
|
|
9
7
|
@configuration = configuration
|
|
10
8
|
@env = env
|
|
11
9
|
@request = request
|
|
12
10
|
@taggers = taggers
|
|
13
|
-
@completion_attached = false
|
|
14
|
-
@execution_handle = nil
|
|
15
|
-
@instrumenter_handle = nil
|
|
16
|
-
@tag_count = 0
|
|
17
11
|
end
|
|
18
12
|
|
|
19
13
|
def start
|
|
20
14
|
@tag_count = push_tags
|
|
21
15
|
@instrumenter_handle = start_request_instrumentation
|
|
22
|
-
self
|
|
23
16
|
end
|
|
24
17
|
|
|
25
18
|
def start_execution!(neutral:)
|
|
@@ -60,7 +53,7 @@ module Julewire
|
|
|
60
53
|
private
|
|
61
54
|
|
|
62
55
|
def push_tags
|
|
63
|
-
return
|
|
56
|
+
return unless ::Rails.logger.respond_to?(:push_tags)
|
|
64
57
|
|
|
65
58
|
::Rails.logger.push_tags(*compute_tags).size
|
|
66
59
|
end
|
|
@@ -88,12 +81,13 @@ module Julewire
|
|
|
88
81
|
end
|
|
89
82
|
|
|
90
83
|
def finish_request_thread_logging
|
|
91
|
-
return unless @tag_count
|
|
84
|
+
return unless @tag_count&.positive?
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
begin
|
|
87
|
+
::Rails.logger.pop_tags(@tag_count)
|
|
88
|
+
rescue StandardError
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
97
91
|
end
|
|
98
92
|
|
|
99
93
|
def finish_unattached_request
|