julewire-core 1.0.1 → 1.1.1
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 +22 -0
- data/docs/configuration.md +14 -7
- data/docs/context-and-propagation.md +20 -0
- data/docs/contracts.md +23 -49
- data/docs/extensions-and-api.md +78 -82
- data/docs/health-schema.md +12 -2
- data/docs/internals.md +21 -20
- data/docs/outputs-and-lifecycle.md +10 -6
- data/docs/records-and-data-policy.md +36 -5
- data/lib/julewire/core/cli/doctor.rb +20 -16
- data/lib/julewire/core/cli/line_helpers.rb +23 -12
- data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
- data/lib/julewire/core/cli/log_formats.rb +9 -9
- data/lib/julewire/core/cli/tail.rb +19 -14
- data/lib/julewire/core/cli/transcode.rb +4 -5
- data/lib/julewire/core/cli.rb +1 -1
- data/lib/julewire/core/configuration.rb +1 -1
- data/lib/julewire/core/context_store.rb +51 -87
- data/lib/julewire/core/destinations/chaos_output.rb +2 -2
- data/lib/julewire/core/destinations/collection.rb +8 -16
- data/lib/julewire/core/destinations/definition.rb +1 -9
- data/lib/julewire/core/destinations/destination.rb +13 -63
- data/lib/julewire/core/destinations/processor_handling.rb +78 -0
- data/lib/julewire/core/destinations/registry.rb +1 -1
- data/lib/julewire/core/destinations/sink.rb +0 -1
- data/lib/julewire/core/destinations/synchronized_output.rb +14 -13
- data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
- data/lib/julewire/core/destinations/write_step.rb +5 -5
- data/lib/julewire/core/destinations.rb +0 -7
- data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
- data/lib/julewire/core/diagnostics/doctor.rb +16 -9
- data/lib/julewire/core/diagnostics/failure_snapshot.rb +6 -2
- data/lib/julewire/core/diagnostics/health.rb +41 -57
- data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
- data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
- data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
- data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
- data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
- data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
- data/lib/julewire/core/diagnostics/tail.rb +18 -22
- data/lib/julewire/core/execution/boundary.rb +2 -8
- data/lib/julewire/core/execution/handle.rb +12 -17
- data/lib/julewire/core/execution/lineage.rb +16 -56
- data/lib/julewire/core/execution/measurement_handle.rb +6 -10
- data/lib/julewire/core/execution/scope.rb +15 -20
- data/lib/julewire/core/execution/scope_fields.rb +3 -12
- data/lib/julewire/core/execution/scope_identity.rb +1 -9
- data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
- data/lib/julewire/core/execution/summary_state.rb +16 -19
- data/lib/julewire/core/execution/view.rb +8 -17
- data/lib/julewire/core/facade_methods.rb +67 -65
- data/lib/julewire/core/fields/attribute_keys.rb +0 -2
- data/lib/julewire/core/fields/bags.rb +23 -27
- data/lib/julewire/core/fields/field_set.rb +29 -19
- data/lib/julewire/core/fields/field_stack.rb +68 -134
- data/lib/julewire/core/fields/internal/deletion.rb +6 -9
- data/lib/julewire/core/fields/internal.rb +19 -38
- data/lib/julewire/core/fields/lookup.rb +9 -13
- data/lib/julewire/core/fields/section_proxy.rb +1 -9
- data/lib/julewire/core/fields/stack_set.rb +7 -12
- data/lib/julewire/core/fields/static_labels.rb +1 -1
- data/lib/julewire/core/integration/configurable.rb +4 -3
- data/lib/julewire/core/integration/destination_health.rb +17 -7
- data/lib/julewire/core/integration/event_subscriber.rb +1 -1
- data/lib/julewire/core/integration/facade.rb +12 -15
- data/lib/julewire/core/integration/fork_hooks.rb +12 -36
- data/lib/julewire/core/integration/health.rb +0 -3
- data/lib/julewire/core/integration/ivar_state.rb +0 -5
- data/lib/julewire/core/integration/protocol.rb +20 -0
- data/lib/julewire/core/integration/settings.rb +4 -4
- data/lib/julewire/core/integration/subscriber_install.rb +2 -2
- data/lib/julewire/core/integration/subscription.rb +3 -2
- data/lib/julewire/core/integration/values.rb +0 -17
- data/lib/julewire/core/local_storage.rb +3 -19
- data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
- data/lib/julewire/core/processing/level_threshold.rb +7 -14
- data/lib/julewire/core/processing/match.rb +6 -4
- data/lib/julewire/core/processing/pipeline.rb +46 -40
- data/lib/julewire/core/processing/processor_chain.rb +22 -11
- data/lib/julewire/core/processing/processor_registry.rb +3 -4
- data/lib/julewire/core/processing/record_field_transform.rb +0 -4
- data/lib/julewire/core/processing/sampling.rb +1 -3
- data/lib/julewire/core/processing.rb +2 -2
- data/lib/julewire/core/propagation/carrier.rb +14 -14
- data/lib/julewire/core/propagation.rb +15 -2
- data/lib/julewire/core/records/build_input.rb +75 -0
- data/lib/julewire/core/records/console_formatter.rb +6 -6
- data/lib/julewire/core/records/deconstruct.rb +1 -1
- data/lib/julewire/core/records/display_message.rb +2 -4
- data/lib/julewire/core/records/draft.rb +125 -262
- data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
- data/lib/julewire/core/records/metadata.rb +4 -3
- data/lib/julewire/core/records/public_projection.rb +2 -2
- data/lib/julewire/core/records/raw_input.rb +1 -1
- data/lib/julewire/core/records/record.rb +19 -37
- data/lib/julewire/core/records/severity.rb +7 -15
- data/lib/julewire/core/runtime.rb +67 -71
- data/lib/julewire/core/runtime_registry.rb +20 -15
- data/lib/julewire/core/runtime_state.rb +5 -3
- data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
- data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
- data/lib/julewire/core/serialization/bounded_transform.rb +2 -12
- data/lib/julewire/core/serialization/bounded_traversal.rb +49 -66
- data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
- data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
- data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
- data/lib/julewire/core/serialization/exception_shape.rb +7 -6
- data/lib/julewire/core/serialization/json_encoder.rb +10 -13
- data/lib/julewire/core/serialization/serializer.rb +23 -50
- data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
- data/lib/julewire/core/serialization/text_encoder.rb +5 -5
- data/lib/julewire/core/serialization/truncation_metadata.rb +63 -48
- data/lib/julewire/core/serialization/value_copy.rb +60 -227
- data/lib/julewire/core/serialization/value_traversal.rb +10 -127
- data/lib/julewire/core/testing.rb +6 -56
- data/lib/julewire/core/validation.rb +2 -3
- data/lib/julewire/core/version.rb +1 -1
- data/lib/julewire/core.rb +2 -7
- metadata +6 -19
- data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
- data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
- data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
- data/lib/julewire/core/testing/chaos/destination.rb +0 -55
- data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
- data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
- data/lib/julewire/core/testing/chaos.rb +0 -80
- data/lib/julewire/core/testing/contracts/component.rb +0 -162
- data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
- data/lib/julewire/core/testing/contracts/integration.rb +0 -166
- data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
- data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
- data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
- data/lib/julewire/core/testing/contracts/wire.rb +0 -60
- data/lib/julewire/core/testing/contracts.rb +0 -24
- data/lib/julewire/core/testing/coverage.rb +0 -58
- data/lib/julewire/core/testing/test_reports.rb +0 -78
|
@@ -12,15 +12,14 @@ module Julewire
|
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
14
|
def build( # rubocop:disable Metrics/ParameterLists -- Record construction has fixed public sections.
|
|
15
|
-
input =
|
|
15
|
+
input = nil,
|
|
16
16
|
context:,
|
|
17
17
|
scope:,
|
|
18
|
-
attributes:
|
|
19
|
-
neutral:
|
|
20
|
-
carry:
|
|
21
|
-
static_labels:
|
|
22
|
-
|
|
23
|
-
error_backtrace_lines: Core::MAX_BACKTRACE_LINES,
|
|
18
|
+
attributes: nil,
|
|
19
|
+
neutral: nil,
|
|
20
|
+
carry: nil,
|
|
21
|
+
static_labels: nil,
|
|
22
|
+
error_backtrace_lines: DEFAULT_ERROR_BACKTRACE_LINES,
|
|
24
23
|
invalid_severity_reporter: Diagnostics::InvalidSeverityReporter
|
|
25
24
|
)
|
|
26
25
|
build_with(
|
|
@@ -32,24 +31,21 @@ module Julewire
|
|
|
32
31
|
static_labels: static_labels,
|
|
33
32
|
scope: scope,
|
|
34
33
|
invalid_severity_reporter: invalid_severity_reporter,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
)
|
|
34
|
+
fields_owned: false,
|
|
35
|
+
input_owned: false,
|
|
36
|
+
error_backtrace_lines: error_backtrace_lines
|
|
39
37
|
)
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def build_pipeline_owned( # rubocop:disable Metrics/ParameterLists -- Record construction has fixed public sections.
|
|
43
|
-
input
|
|
41
|
+
input,
|
|
44
42
|
context:,
|
|
45
43
|
scope:,
|
|
46
|
-
attributes:
|
|
47
|
-
neutral:
|
|
48
|
-
carry:
|
|
49
|
-
static_labels: {},
|
|
44
|
+
attributes: nil,
|
|
45
|
+
neutral: nil,
|
|
46
|
+
carry: nil,
|
|
50
47
|
input_owned: false,
|
|
51
|
-
|
|
52
|
-
error_backtrace_lines: Core::MAX_BACKTRACE_LINES,
|
|
48
|
+
error_backtrace_lines: DEFAULT_ERROR_BACKTRACE_LINES,
|
|
53
49
|
invalid_severity_reporter: Diagnostics::InvalidSeverityReporter
|
|
54
50
|
)
|
|
55
51
|
build_with(
|
|
@@ -58,21 +54,20 @@ module Julewire
|
|
|
58
54
|
neutral: neutral,
|
|
59
55
|
attributes: attributes,
|
|
60
56
|
carry: carry,
|
|
61
|
-
static_labels:
|
|
57
|
+
static_labels: nil,
|
|
62
58
|
scope: scope,
|
|
63
59
|
invalid_severity_reporter: invalid_severity_reporter,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
error_backtrace_lines: error_backtrace_lines
|
|
68
|
-
)
|
|
60
|
+
fields_owned: true,
|
|
61
|
+
input_owned: input_owned,
|
|
62
|
+
error_backtrace_lines: error_backtrace_lines
|
|
69
63
|
)
|
|
70
64
|
end
|
|
71
65
|
|
|
72
66
|
private
|
|
73
67
|
|
|
74
68
|
def build_with(input, context:, neutral:, attributes:, carry:, static_labels:, scope:, # rubocop:disable Metrics/ParameterLists
|
|
75
|
-
invalid_severity_reporter:,
|
|
69
|
+
invalid_severity_reporter:, fields_owned:, input_owned:,
|
|
70
|
+
error_backtrace_lines:)
|
|
76
71
|
builder = Builder.new(
|
|
77
72
|
input,
|
|
78
73
|
context: context,
|
|
@@ -82,95 +77,45 @@ module Julewire
|
|
|
82
77
|
static_labels: static_labels,
|
|
83
78
|
scope: scope,
|
|
84
79
|
invalid_severity_reporter: invalid_severity_reporter,
|
|
85
|
-
|
|
80
|
+
fields_owned: fields_owned,
|
|
81
|
+
input_owned: input_owned,
|
|
82
|
+
error_backtrace_lines: error_backtrace_lines
|
|
86
83
|
)
|
|
87
|
-
new(builder.to_h, lineage: builder.lineage
|
|
84
|
+
new(builder.to_h, lineage: builder.lineage)
|
|
88
85
|
end
|
|
89
86
|
|
|
90
87
|
public
|
|
91
88
|
|
|
92
|
-
def from_normalized_hash(data, lineage: nil
|
|
89
|
+
def from_normalized_hash(data, lineage: nil)
|
|
93
90
|
Record.validate_normalized_hash!(data)
|
|
94
|
-
normalized = data
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
normalized =
|
|
98
|
-
new(
|
|
99
|
-
normalized,
|
|
100
|
-
lineage: lineage,
|
|
101
|
-
freeze_sections: freeze_sections
|
|
102
|
-
)
|
|
91
|
+
normalized = Fields::FieldSet.deep_dup_owned(data)
|
|
92
|
+
execution = normalized.fetch(:execution)
|
|
93
|
+
lineage ||= Execution::Lineage.from_execution_hash(execution)
|
|
94
|
+
normalized[:execution] = Execution::Lineage.clean_normalized_lazy_relationship_hash(execution)
|
|
95
|
+
new(normalized, lineage: lineage)
|
|
103
96
|
end
|
|
104
97
|
|
|
105
|
-
def from_record(record
|
|
98
|
+
def from_record(record)
|
|
106
99
|
Record.validate_normalized!(record)
|
|
107
|
-
from_normalized_hash(record.to_h, lineage: record.lineage
|
|
100
|
+
from_normalized_hash(record.to_h, lineage: record.lineage)
|
|
108
101
|
end
|
|
109
102
|
end
|
|
103
|
+
private_class_method :new
|
|
110
104
|
|
|
105
|
+
DEFAULT_ERROR_BACKTRACE_LINES = Core::MAX_BACKTRACE_LINES
|
|
111
106
|
LINEAGE_IDENTITY_KEYS = %i[type id depth root parent].freeze
|
|
112
|
-
private_constant :LINEAGE_IDENTITY_KEYS
|
|
113
|
-
|
|
114
|
-
BuildOptions = Data.define(:fields_owned, :input_owned, :freeze_sections, :error_backtrace_lines) do
|
|
115
|
-
class << self
|
|
116
|
-
def defensive(freeze_sections:, error_backtrace_lines:)
|
|
117
|
-
return default_defensive.fetch(freeze_sections) if default_backtrace_lines?(error_backtrace_lines)
|
|
118
|
-
|
|
119
|
-
new(false, false, freeze_sections, error_backtrace_lines)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def pipeline_owned(input_owned:, freeze_sections:, error_backtrace_lines:)
|
|
123
|
-
if default_backtrace_lines?(error_backtrace_lines)
|
|
124
|
-
return default_pipeline.fetch(input_owned).fetch(freeze_sections)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
new(true, input_owned, freeze_sections, error_backtrace_lines)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
private
|
|
107
|
+
private_constant :DEFAULT_ERROR_BACKTRACE_LINES, :LINEAGE_IDENTITY_KEYS
|
|
131
108
|
|
|
132
|
-
|
|
133
|
-
value == Core::MAX_BACKTRACE_LINES
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def default_defensive
|
|
137
|
-
# Hot-path defaults are reused by every emitted record.
|
|
138
|
-
@default_defensive ||= {
|
|
139
|
-
false => new(false, false, false, Core::MAX_BACKTRACE_LINES),
|
|
140
|
-
true => new(false, false, true, Core::MAX_BACKTRACE_LINES)
|
|
141
|
-
}.freeze
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def default_pipeline
|
|
145
|
-
@default_pipeline ||= {
|
|
146
|
-
false => {
|
|
147
|
-
false => new(true, false, false, Core::MAX_BACKTRACE_LINES),
|
|
148
|
-
true => new(true, false, true, Core::MAX_BACKTRACE_LINES)
|
|
149
|
-
}.freeze,
|
|
150
|
-
true => {
|
|
151
|
-
false => new(true, true, false, Core::MAX_BACKTRACE_LINES),
|
|
152
|
-
true => new(true, true, true, Core::MAX_BACKTRACE_LINES)
|
|
153
|
-
}.freeze
|
|
154
|
-
}.freeze
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
private_constant :BuildOptions
|
|
159
|
-
|
|
160
|
-
def initialize(data, lineage: nil, freeze_sections: true)
|
|
109
|
+
def initialize(data, lineage:)
|
|
161
110
|
@data = data
|
|
162
|
-
@
|
|
163
|
-
@lineage = lineage || Execution::Lineage.from_execution_hash(@data[:execution])
|
|
164
|
-
validate!
|
|
111
|
+
@lineage = lineage
|
|
165
112
|
end
|
|
166
113
|
|
|
167
114
|
def [](key) = @data[key]
|
|
168
115
|
|
|
169
116
|
def []=(key, value)
|
|
170
|
-
@lineage = nil if key == :execution
|
|
171
|
-
ensure_mutable_data!
|
|
172
117
|
@data[key] = value
|
|
173
|
-
@
|
|
118
|
+
@lineage = nil if key == :execution
|
|
174
119
|
end
|
|
175
120
|
|
|
176
121
|
def fetch(...) = @data.fetch(...)
|
|
@@ -186,14 +131,16 @@ module Julewire
|
|
|
186
131
|
def to_h = Fields::FieldSet.deep_dup_owned(@data)
|
|
187
132
|
|
|
188
133
|
def transform_field!(key)
|
|
189
|
-
|
|
190
|
-
|
|
134
|
+
ensure_unfinalized!
|
|
135
|
+
validate_transform_key!(key, Record::REQUIRED_KEYS, kind: :field)
|
|
136
|
+
replace_transformed_field!(key, yield(self[key]))
|
|
191
137
|
self
|
|
192
138
|
end
|
|
193
139
|
|
|
194
140
|
def transform_section!(key)
|
|
195
|
-
|
|
196
|
-
|
|
141
|
+
ensure_unfinalized!
|
|
142
|
+
validate_transform_key!(key, Record::HASH_SECTIONS, kind: :section)
|
|
143
|
+
section = self[key]
|
|
197
144
|
replacement = yield(section)
|
|
198
145
|
raise TypeError, "record #{key} must be a Hash" unless replacement.is_a?(Hash)
|
|
199
146
|
|
|
@@ -202,12 +149,12 @@ module Julewire
|
|
|
202
149
|
end
|
|
203
150
|
|
|
204
151
|
def transform_record!
|
|
152
|
+
ensure_unfinalized!
|
|
205
153
|
previous_lineage = @lineage
|
|
206
|
-
previous_identity = execution_lineage_identity(
|
|
154
|
+
previous_identity = execution_lineage_identity(fetch(:execution))
|
|
207
155
|
replacement = yield(@data)
|
|
208
156
|
@lineage = replacement_lineage_for(previous_lineage, previous_identity, replacement)
|
|
209
|
-
@data = replacement
|
|
210
|
-
@to_record = nil
|
|
157
|
+
@data = replacement.dup
|
|
211
158
|
self
|
|
212
159
|
end
|
|
213
160
|
|
|
@@ -220,123 +167,86 @@ module Julewire
|
|
|
220
167
|
self
|
|
221
168
|
end
|
|
222
169
|
|
|
223
|
-
def lineage = (@lineage ||= Execution::Lineage.from_execution_hash(
|
|
170
|
+
def lineage = (@lineage ||= Execution::Lineage.from_execution_hash(fetch(:execution)))
|
|
224
171
|
|
|
225
172
|
def to_record
|
|
226
|
-
@to_record
|
|
173
|
+
return @to_record if @to_record
|
|
174
|
+
|
|
175
|
+
record = Record.from_owned_hash(@data, lineage: @lineage)
|
|
176
|
+
@lineage = record.lineage
|
|
177
|
+
@to_record = record
|
|
178
|
+
freeze
|
|
179
|
+
record
|
|
227
180
|
end
|
|
228
181
|
|
|
229
182
|
private
|
|
230
183
|
|
|
184
|
+
def validate_transform_key!(key, allowed, kind:)
|
|
185
|
+
raise TypeError, "record transform #{kind} must be a Symbol" unless key.instance_of?(Symbol)
|
|
186
|
+
return if allowed.include?(key)
|
|
187
|
+
|
|
188
|
+
raise ArgumentError, "unknown record #{kind}: #{key}"
|
|
189
|
+
end
|
|
190
|
+
|
|
231
191
|
def replace_transformed_field!(key, value)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
192
|
+
if key == :execution
|
|
193
|
+
replace_transformed_execution!(value)
|
|
194
|
+
else
|
|
195
|
+
self[key] = value
|
|
196
|
+
end
|
|
237
197
|
end
|
|
238
198
|
|
|
239
|
-
def
|
|
240
|
-
|
|
199
|
+
def replace_transformed_execution!(value)
|
|
200
|
+
preserve_lineage = transformed_execution_lineage(value)
|
|
201
|
+
self[:execution] = value
|
|
202
|
+
@lineage = preserve_lineage
|
|
241
203
|
end
|
|
242
204
|
|
|
243
|
-
def
|
|
244
|
-
|
|
205
|
+
def ensure_unfinalized!
|
|
206
|
+
raise FrozenError, "can't transform a finalized record draft" if frozen?
|
|
207
|
+
end
|
|
245
208
|
|
|
209
|
+
def transformed_execution_lineage(value)
|
|
246
210
|
replacement_lineage_for(
|
|
247
211
|
@lineage,
|
|
248
|
-
execution_lineage_identity(
|
|
212
|
+
execution_lineage_identity(fetch(:execution)),
|
|
249
213
|
@data.merge(execution: value)
|
|
250
214
|
)
|
|
251
215
|
end
|
|
252
216
|
|
|
253
217
|
def replacement_lineage_for(lineage, previous, data)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
current = data.is_a?(Hash) ? execution_lineage_identity(data[:execution]) : nil
|
|
218
|
+
current = execution_lineage_identity(data[:execution]) if data.is_a?(Hash)
|
|
257
219
|
lineage if previous == current
|
|
258
220
|
end
|
|
259
221
|
|
|
260
222
|
def execution_lineage_identity(execution)
|
|
261
223
|
return unless execution.is_a?(Hash)
|
|
262
224
|
|
|
263
|
-
normalized = Fields::FieldSet.deep_symbolize_keys(execution)
|
|
264
225
|
LINEAGE_IDENTITY_KEYS.each_with_object({}) do |key, identity|
|
|
265
|
-
identity[key] =
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
class BuildInput
|
|
270
|
-
# Normalizes raw emit input into draft top-level fields plus payload.
|
|
271
|
-
RECORD_INPUT_KEYS = Record::REQUIRED_KEYS.freeze
|
|
272
|
-
RECORD_INPUT_KEY_SET = RECORD_INPUT_KEYS.to_h { [it, true] }.freeze
|
|
273
|
-
private_constant :RECORD_INPUT_KEYS
|
|
274
|
-
private_constant :RECORD_INPUT_KEY_SET
|
|
275
|
-
|
|
276
|
-
class << self
|
|
277
|
-
def call(input, owned:)
|
|
278
|
-
return {} if input.nil?
|
|
279
|
-
return input if owned && input.is_a?(Hash)
|
|
280
|
-
return shallow_symbolize(input) if RawInput.hash_input?(input)
|
|
281
|
-
|
|
282
|
-
{ message: input.to_s }
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
private
|
|
286
|
-
|
|
287
|
-
def shallow_symbolize(input)
|
|
288
|
-
normalized = {}
|
|
289
|
-
payload_fields = {}
|
|
290
|
-
input.each do |key, raw_value|
|
|
291
|
-
normalized_key = Fields::Internal.normalize_key(key)
|
|
292
|
-
value = raw_value.equal?(input) ? CIRCULAR_REFERENCE : raw_value
|
|
293
|
-
if RECORD_INPUT_KEY_SET.key?(normalized_key)
|
|
294
|
-
normalized[normalized_key] = value
|
|
295
|
-
else
|
|
296
|
-
payload_fields[normalized_key] = value
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
merge_unknown_payload!(normalized, payload_fields)
|
|
300
|
-
normalized
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
def merge_unknown_payload!(normalized, payload_fields)
|
|
304
|
-
return if payload_fields.empty?
|
|
305
|
-
|
|
306
|
-
normalized[:payload] = if normalized.key?(:payload)
|
|
307
|
-
merge_payload_input(normalized[:payload], payload_fields)
|
|
308
|
-
else
|
|
309
|
-
payload_fields
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def merge_payload_input(explicit_payload, unknown_payload)
|
|
314
|
-
if explicit_payload.is_a?(Hash)
|
|
315
|
-
Fields::FieldSet.merge(unknown_payload, explicit_payload)
|
|
316
|
-
else
|
|
317
|
-
Fields::FieldSet.merge(unknown_payload, Fields::FieldSet::VALUE_KEY => explicit_payload)
|
|
318
|
-
end
|
|
319
|
-
end
|
|
226
|
+
identity[key] = execution.fetch(key) if execution.key?(key)
|
|
320
227
|
end
|
|
321
228
|
end
|
|
322
|
-
private_constant :BuildInput
|
|
323
229
|
|
|
324
230
|
class Builder
|
|
325
231
|
EMPTY_HASH = {}.freeze
|
|
326
232
|
private_constant :EMPTY_HASH
|
|
327
233
|
|
|
328
|
-
def initialize(input
|
|
329
|
-
invalid_severity_reporter:,
|
|
330
|
-
|
|
331
|
-
@
|
|
234
|
+
def initialize(input, context:, neutral:, attributes:, carry:, static_labels:, scope:, # rubocop:disable Metrics/ParameterLists
|
|
235
|
+
invalid_severity_reporter:, fields_owned:, input_owned:,
|
|
236
|
+
error_backtrace_lines:)
|
|
237
|
+
@input_owned = input_owned
|
|
238
|
+
@input = if @input_owned
|
|
239
|
+
BuildInput.validate_owned(input)
|
|
240
|
+
else
|
|
241
|
+
BuildInput.normalize_public(input)
|
|
242
|
+
end
|
|
332
243
|
@context = context || {}
|
|
333
244
|
@neutral = neutral || {}
|
|
334
245
|
@attributes = attributes || {}
|
|
335
246
|
@carry = carry || {}
|
|
336
247
|
@static_labels = static_labels || {}
|
|
337
|
-
@fields_owned =
|
|
338
|
-
@
|
|
339
|
-
@error_backtrace_lines = options.error_backtrace_lines
|
|
248
|
+
@fields_owned = fields_owned
|
|
249
|
+
@error_backtrace_lines = error_backtrace_lines
|
|
340
250
|
@invalid_severity_reporter = invalid_severity_reporter
|
|
341
251
|
@scope = scope
|
|
342
252
|
end
|
|
@@ -357,7 +267,7 @@ module Julewire
|
|
|
357
267
|
def base_record(source, event)
|
|
358
268
|
{
|
|
359
269
|
timestamp: timestamp_value,
|
|
360
|
-
severity:
|
|
270
|
+
severity: severity_value,
|
|
361
271
|
kind: kind_for(value(:kind)),
|
|
362
272
|
event: event,
|
|
363
273
|
message: normalized_value(:message),
|
|
@@ -385,23 +295,22 @@ module Julewire
|
|
|
385
295
|
Serialization::ValueCopy.call(raw_value.nil? ? Time.now.utc : raw_value, freeze_values: true)
|
|
386
296
|
end
|
|
387
297
|
|
|
388
|
-
def
|
|
389
|
-
return normalize_record_severity(value(:severity)
|
|
298
|
+
def severity_value
|
|
299
|
+
return normalize_record_severity(value(:severity)) if present?(:severity)
|
|
390
300
|
|
|
391
301
|
:info
|
|
392
302
|
end
|
|
393
303
|
|
|
394
|
-
def normalize_record_severity(raw_value
|
|
395
|
-
|
|
304
|
+
def normalize_record_severity(raw_value)
|
|
305
|
+
Severity.normalize(raw_value)
|
|
396
306
|
rescue ArgumentError
|
|
397
307
|
# Below-threshold raw inputs warn before draft construction.
|
|
398
|
-
@invalid_severity_reporter.call(raw_value
|
|
308
|
+
@invalid_severity_reporter.call(raw_value)
|
|
399
309
|
:info
|
|
400
310
|
end
|
|
401
311
|
|
|
402
312
|
def kind_for(kind)
|
|
403
313
|
return :point if kind.nil?
|
|
404
|
-
return kind if Record::KINDS.value?(kind)
|
|
405
314
|
|
|
406
315
|
Record::KINDS.fetch(kind.to_s) do
|
|
407
316
|
raise ArgumentError, "unsupported record kind: #{kind.inspect}"
|
|
@@ -415,13 +324,11 @@ module Julewire
|
|
|
415
324
|
end
|
|
416
325
|
|
|
417
326
|
def base_execution_hash
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
normalized_hash(scope_execution_hash)
|
|
327
|
+
normalized_hash(scope_execution_hash, owned: true)
|
|
421
328
|
end
|
|
422
329
|
|
|
423
330
|
def input_execution_hash
|
|
424
|
-
|
|
331
|
+
hash_value(:execution)
|
|
425
332
|
end
|
|
426
333
|
|
|
427
334
|
def context_hash
|
|
@@ -441,73 +348,34 @@ module Julewire
|
|
|
441
348
|
end
|
|
442
349
|
|
|
443
350
|
def labels_hash
|
|
444
|
-
section_hash(
|
|
351
|
+
section_hash(@static_labels.merge(scope_labels_hash || EMPTY_HASH), :labels)
|
|
445
352
|
end
|
|
446
353
|
|
|
447
354
|
def merge_section(base, key)
|
|
448
|
-
return merge_owned_section(base, key) if @input_owned
|
|
449
|
-
|
|
450
|
-
value = hash_value(key)
|
|
451
|
-
value = Execution::Lineage.clean_execution_hash(value) if key == :execution
|
|
452
|
-
return normalized_hash(value) if base.empty?
|
|
453
|
-
|
|
454
|
-
base = Fields::FieldSet.deep_dup(base)
|
|
455
|
-
merged = if key == :attributes
|
|
456
|
-
Fields::Internal.deep_merge!(base, value)
|
|
457
|
-
else
|
|
458
|
-
Fields::FieldSet.merge!(base, value)
|
|
459
|
-
end
|
|
460
|
-
normalized_hash(merged)
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
def merge_owned_section(base, key)
|
|
464
355
|
value = hash_value(key)
|
|
465
356
|
value = clean_owned_execution_hash(value) if key == :execution
|
|
466
|
-
return normalized_hash(value) if base.empty?
|
|
467
357
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
normalized_hash(merged)
|
|
358
|
+
target = normalized_hash(base, owned: @fields_owned)
|
|
359
|
+
if key == :attributes
|
|
360
|
+
Fields::Internal.deep_merge_owned!(target, value)
|
|
361
|
+
else
|
|
362
|
+
Fields::Internal.merge_owned!(target, value)
|
|
363
|
+
end
|
|
475
364
|
end
|
|
476
365
|
|
|
477
366
|
def clean_owned_execution_hash(value)
|
|
478
|
-
return Execution::Lineage.clean_execution_hash(value) if @freeze_sections
|
|
479
|
-
|
|
480
367
|
Execution::Lineage.clean_owned_execution_hash(value)
|
|
481
368
|
end
|
|
482
369
|
|
|
483
|
-
def section_hash(base, key)
|
|
484
|
-
return base if owned_frozen_section?(base) && !present?(key)
|
|
485
|
-
return normalized_hash(base) unless present?(key)
|
|
486
|
-
|
|
487
|
-
merge_section(base, key)
|
|
488
|
-
end
|
|
489
|
-
|
|
490
|
-
def owned_frozen_section?(base)
|
|
491
|
-
@fields_owned && @freeze_sections && base.is_a?(Hash) && base.frozen?
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
def owned_frozen_scope_execution?
|
|
495
|
-
@scope && @fields_owned && @freeze_sections
|
|
496
|
-
end
|
|
497
|
-
|
|
498
|
-
def labels_base
|
|
499
|
-
return scope_labels_hash if @static_labels.empty?
|
|
500
|
-
|
|
501
|
-
Fields::FieldSet.merge!(Fields::FieldSet.deep_dup(@static_labels), scope_labels_hash)
|
|
502
|
-
end
|
|
370
|
+
def section_hash(base, key) = merge_section(base, key)
|
|
503
371
|
|
|
504
372
|
def hash_value(key)
|
|
505
|
-
return
|
|
373
|
+
return {} unless present?(key)
|
|
506
374
|
|
|
507
375
|
raw_value = value(key)
|
|
508
|
-
return
|
|
376
|
+
return normalized_input_hash(raw_value) if raw_value.is_a?(Hash)
|
|
509
377
|
|
|
510
|
-
|
|
378
|
+
normalized_input_hash(Fields::FieldSet::VALUE_KEY => raw_value)
|
|
511
379
|
end
|
|
512
380
|
|
|
513
381
|
def value(key)
|
|
@@ -520,11 +388,8 @@ module Julewire
|
|
|
520
388
|
|
|
521
389
|
def immutable_scalar_value(value)
|
|
522
390
|
return value unless value.is_a?(String)
|
|
523
|
-
return value if value.frozen?
|
|
524
|
-
return value.dup unless @freeze_sections
|
|
525
|
-
return value.freeze if @input_owned
|
|
526
391
|
|
|
527
|
-
value.dup
|
|
392
|
+
value.dup
|
|
528
393
|
end
|
|
529
394
|
|
|
530
395
|
def present?(key)
|
|
@@ -536,44 +401,42 @@ module Julewire
|
|
|
536
401
|
when nil
|
|
537
402
|
nil
|
|
538
403
|
when Exception
|
|
539
|
-
|
|
404
|
+
Serialization::ExceptionShape.call(error, max_backtrace_lines: @error_backtrace_lines)
|
|
540
405
|
when Hash
|
|
541
|
-
|
|
406
|
+
normalize_error_hash(error)
|
|
542
407
|
else
|
|
543
|
-
|
|
408
|
+
{ message: immutable_scalar_value(error.to_s) }
|
|
544
409
|
end
|
|
545
410
|
end
|
|
546
411
|
|
|
547
412
|
def normalize_error_hash(error)
|
|
548
413
|
Serialization::BacktraceLimiter.call(
|
|
549
|
-
|
|
414
|
+
mutable_input_hash(error),
|
|
550
415
|
max_backtrace_lines: @error_backtrace_lines
|
|
551
416
|
)
|
|
552
417
|
end
|
|
553
418
|
|
|
554
|
-
def
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if @input_owned
|
|
558
|
-
return Fields::Internal.frozen_deep_symbolize_owned_keys(value) if @freeze_sections
|
|
559
|
-
return value if value.is_a?(Hash)
|
|
419
|
+
def normalized_input_hash(value)
|
|
420
|
+
normalized_hash(value, owned: @input_owned)
|
|
421
|
+
end
|
|
560
422
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
return Fields::Internal.frozen_deep_symbolize_keys(value) if @freeze_sections
|
|
423
|
+
def normalized_hash(value, owned:)
|
|
424
|
+
return Fields::FieldSet.deep_dup_owned(value) if owned
|
|
564
425
|
|
|
565
426
|
Fields::FieldSet.deep_symbolize_keys(value)
|
|
566
427
|
end
|
|
567
428
|
|
|
568
|
-
def
|
|
569
|
-
@
|
|
429
|
+
def mutable_input_hash(value)
|
|
430
|
+
if @input_owned
|
|
431
|
+
Fields::FieldSet.deep_dup_owned(value)
|
|
432
|
+
else
|
|
433
|
+
Fields::FieldSet.deep_symbolize_keys(value)
|
|
434
|
+
end
|
|
570
435
|
end
|
|
571
436
|
|
|
572
437
|
def scope_execution_hash = @scope ? @scope.frozen_execution_hash : EMPTY_HASH
|
|
573
438
|
|
|
574
|
-
def
|
|
575
|
-
|
|
576
|
-
def scope_labels_hash = @scope ? @scope.frozen_labels_hash : EMPTY_HASH
|
|
439
|
+
def scope_labels_hash = @scope&.frozen_labels_hash
|
|
577
440
|
end
|
|
578
441
|
private_constant :Builder
|
|
579
442
|
end
|
|
@@ -24,7 +24,7 @@ module Julewire
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def each
|
|
27
|
-
return enum_for
|
|
27
|
+
return enum_for unless block_given?
|
|
28
28
|
|
|
29
29
|
input_hash.each do |key, value|
|
|
30
30
|
yield key, value unless severity_key?(key)
|
|
@@ -32,12 +32,6 @@ module Julewire
|
|
|
32
32
|
yield :severity, @severity
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def to_h
|
|
36
|
-
each_with_object({}) do |(key, value), hash|
|
|
37
|
-
hash[key] = value
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
35
|
private
|
|
42
36
|
|
|
43
37
|
def input_hash
|
|
@@ -66,9 +60,7 @@ module Julewire
|
|
|
66
60
|
SeverityInput.new(severity, input)
|
|
67
61
|
end
|
|
68
62
|
|
|
69
|
-
def input?(value)
|
|
70
|
-
value.is_a?(SeverityInput)
|
|
71
|
-
end
|
|
63
|
+
def input?(value) = value.instance_of?(SeverityInput)
|
|
72
64
|
|
|
73
65
|
private
|
|
74
66
|
|
|
@@ -88,8 +80,6 @@ module Julewire
|
|
|
88
80
|
end
|
|
89
81
|
|
|
90
82
|
def without_severity(input)
|
|
91
|
-
return input unless input.is_a?(Hash)
|
|
92
|
-
|
|
93
83
|
RawInput.without_severity_keys(input)
|
|
94
84
|
end
|
|
95
85
|
end
|
|
@@ -6,15 +6,16 @@ module Julewire
|
|
|
6
6
|
module Metadata
|
|
7
7
|
class << self
|
|
8
8
|
def call(record)
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
labels = record[:labels]
|
|
11
10
|
{
|
|
12
11
|
event: record[:event],
|
|
13
|
-
labels:
|
|
12
|
+
labels: labels.is_a?(Hash) ? Fields::FieldSet.deep_dup(labels) : {},
|
|
14
13
|
logger: record[:logger],
|
|
15
14
|
severity: record[:severity],
|
|
16
15
|
source: record[:source]
|
|
17
16
|
}.compact
|
|
17
|
+
rescue StandardError
|
|
18
|
+
{}
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
end
|