julewire-core 1.0.1 → 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 +18 -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
|
@@ -17,8 +17,6 @@ module Julewire
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def after_fork!
|
|
20
|
-
@mutex = Mutex.new
|
|
21
|
-
@lifecycle_mutex = Mutex.new
|
|
22
20
|
@output.after_fork! if @output.respond_to?(:after_fork!)
|
|
23
21
|
@lifecycle = lifecycle_methods
|
|
24
22
|
self
|
|
@@ -41,7 +39,7 @@ module Julewire
|
|
|
41
39
|
lifecycle = @lifecycle[:flush]
|
|
42
40
|
return true unless lifecycle
|
|
43
41
|
|
|
44
|
-
call_lifecycle(:flush, lifecycle, timeout: timeout)
|
|
42
|
+
lifecycle_succeeded?(call_lifecycle(:flush, lifecycle, timeout: timeout))
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
|
|
@@ -52,12 +50,13 @@ module Julewire
|
|
|
52
50
|
@mutex.synchronize do
|
|
53
51
|
return true if output_closed?
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
if @close_output && (close_lifecycle = @lifecycle[:close])
|
|
54
|
+
return lifecycle_succeeded?(call_lifecycle(:close, close_lifecycle, timeout: timeout))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return true unless (flush_lifecycle = @lifecycle[:flush])
|
|
58
|
+
|
|
59
|
+
lifecycle_succeeded?(call_lifecycle(:flush, flush_lifecycle, timeout: timeout))
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
62
|
end
|
|
@@ -65,9 +64,11 @@ module Julewire
|
|
|
65
64
|
private
|
|
66
65
|
|
|
67
66
|
def output_closed?
|
|
68
|
-
@output.respond_to?(:closed?)
|
|
67
|
+
@output.respond_to?(:closed?) && @output.closed?
|
|
69
68
|
end
|
|
70
69
|
|
|
70
|
+
def lifecycle_succeeded?(result) = !result.equal?(false)
|
|
71
|
+
|
|
71
72
|
def call_lifecycle(name, lifecycle, timeout:)
|
|
72
73
|
return @output.public_send(name, timeout: timeout) if lifecycle.fetch(:timeout)
|
|
73
74
|
|
|
@@ -78,9 +79,9 @@ module Julewire
|
|
|
78
79
|
%i[flush close].each_with_object({}) do |name, methods|
|
|
79
80
|
next unless @output.respond_to?(name)
|
|
80
81
|
|
|
81
|
-
method = @output.
|
|
82
|
-
methods[name] = { timeout: accepts_timeout_keyword?(method) }
|
|
83
|
-
end
|
|
82
|
+
method = @output.public_method(name)
|
|
83
|
+
methods[name] = { timeout: accepts_timeout_keyword?(method) }
|
|
84
|
+
end
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
def accepts_timeout_keyword?(method)
|
|
@@ -29,7 +29,8 @@ module Julewire
|
|
|
29
29
|
slow_ms
|
|
30
30
|
].freeze
|
|
31
31
|
private_constant :COUNTER_KEYS, :ERROR_RANK, :OPTION_KEYS
|
|
32
|
-
|
|
32
|
+
Sampling = Processing::Sampling
|
|
33
|
+
private_constant :Sampling
|
|
33
34
|
TailOptions = Data.define(:decider, :max_executions, :max_records_per_execution, :name, :on_drop, :on_failure,
|
|
34
35
|
:sample_rate, :slow_ms)
|
|
35
36
|
private_constant :TailOptions
|
|
@@ -42,7 +43,7 @@ module Julewire
|
|
|
42
43
|
@destination = Registry.validate!(destination)
|
|
43
44
|
@name = Destinations.normalize_name(options.name)
|
|
44
45
|
@sample_rate = options.sample_rate
|
|
45
|
-
|
|
46
|
+
Sampling.threshold_for(options.sample_rate)
|
|
46
47
|
@slow_ms = validate_slow_ms(options.slow_ms)
|
|
47
48
|
@max_executions = Validation.validate_integer_limit!(
|
|
48
49
|
options.max_executions,
|
|
@@ -65,7 +66,7 @@ module Julewire
|
|
|
65
66
|
|
|
66
67
|
def emit(record)
|
|
67
68
|
result = @mutex.synchronize { accept_record(record) }
|
|
68
|
-
result.losses.
|
|
69
|
+
result.losses.each { notify_drop(it) }
|
|
69
70
|
result.records.each { emit_target(it) }
|
|
70
71
|
nil
|
|
71
72
|
rescue StandardError => e
|
|
@@ -160,7 +161,7 @@ module Julewire
|
|
|
160
161
|
@order << key
|
|
161
162
|
Buffer.new([])
|
|
162
163
|
end)
|
|
163
|
-
if buffer.records.length
|
|
164
|
+
if buffer.records.length == @max_records_per_execution
|
|
164
165
|
dropped = buffer.records.shift
|
|
165
166
|
losses << record_loss(:overflow_dropped, dropped)
|
|
166
167
|
end
|
|
@@ -174,8 +175,7 @@ module Julewire
|
|
|
174
175
|
return [] if @buffers.length < @max_executions
|
|
175
176
|
|
|
176
177
|
oldest = @order.shift
|
|
177
|
-
|
|
178
|
-
Array(buffer&.records).map { record_loss(:overflow_dropped, it) }
|
|
178
|
+
@buffers.delete(oldest).records.map { record_loss(:overflow_dropped, it) }
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
def finish_execution(key, summary)
|
|
@@ -205,7 +205,7 @@ module Julewire
|
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
def keep_execution?(record, key)
|
|
208
|
-
return
|
|
208
|
+
return @decider.call(record, key: key) if @decider
|
|
209
209
|
|
|
210
210
|
default_keep_execution?(record, key)
|
|
211
211
|
rescue StandardError => e
|
|
@@ -214,7 +214,7 @@ module Julewire
|
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
def default_keep_execution?(record, key)
|
|
217
|
-
error_record?(record) || slow_record?(record) ||
|
|
217
|
+
error_record?(record) || slow_record?(record) || Sampling.keep?(rate: @sample_rate, key: key)
|
|
218
218
|
end
|
|
219
219
|
|
|
220
220
|
def error_record?(record)
|
|
@@ -235,7 +235,7 @@ module Julewire
|
|
|
235
235
|
def summary_record?(record) = record[:kind] == :summary
|
|
236
236
|
|
|
237
237
|
def execution_key(record)
|
|
238
|
-
reference = record.respond_to?(:lineage)
|
|
238
|
+
reference = record.lineage.root_reference if record.respond_to?(:lineage)
|
|
239
239
|
reference = record[:execution] unless reference.is_a?(Hash)
|
|
240
240
|
id = field_value(reference, :id)
|
|
241
241
|
return unless id
|
|
@@ -256,7 +256,7 @@ module Julewire
|
|
|
256
256
|
end
|
|
257
257
|
|
|
258
258
|
def lifecycle(method_name, timeout:)
|
|
259
|
-
|
|
259
|
+
!@destination.public_send(method_name, timeout: timeout).eql?(false)
|
|
260
260
|
rescue StandardError => e
|
|
261
261
|
record_failure(e, nil, phase: :tail_sampling_lifecycle, action: method_name)
|
|
262
262
|
false
|
|
@@ -270,8 +270,6 @@ module Julewire
|
|
|
270
270
|
|
|
271
271
|
def record_loss(reason, record)
|
|
272
272
|
@health.record_loss(reason: reason, record_metadata: Records::Metadata.call(record))
|
|
273
|
-
rescue StandardError => e
|
|
274
|
-
record_failure(e, record, phase: :tail_sampling_drop)
|
|
275
273
|
end
|
|
276
274
|
|
|
277
275
|
def notify_drop(loss)
|
|
@@ -281,29 +279,22 @@ module Julewire
|
|
|
281
279
|
end
|
|
282
280
|
|
|
283
281
|
def record_failure(error, record, phase: :tail_sampling, **metadata)
|
|
284
|
-
failure_metadata =
|
|
285
|
-
|
|
286
|
-
else
|
|
287
|
-
@mutex.synchronize { record_failure_state(error, record, phase: phase, **metadata) }
|
|
288
|
-
end
|
|
289
|
-
@on_failure&.call(error, **failure_metadata)
|
|
282
|
+
failure_metadata = record_failure_state(error, record, phase: phase, **metadata)
|
|
283
|
+
@on_failure.call(error, **failure_metadata)
|
|
290
284
|
rescue StandardError
|
|
291
285
|
nil
|
|
292
286
|
end
|
|
293
287
|
|
|
294
288
|
def record_failure_state(error, record, phase:, **metadata)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
record_metadata: record ? Records::Metadata.call(record) : nil
|
|
301
|
-
)
|
|
302
|
-
metadata.merge(destination: @name, phase: phase)
|
|
289
|
+
metadata = metadata.merge(destination: @name, phase: phase)
|
|
290
|
+
record_metadata = record && Records::Metadata.call(record)
|
|
291
|
+
metadata[:record_metadata] = record_metadata if record_metadata
|
|
292
|
+
@health.record_failure(error, **metadata)
|
|
293
|
+
metadata
|
|
303
294
|
end
|
|
304
295
|
|
|
305
296
|
def health_status
|
|
306
|
-
return
|
|
297
|
+
return if @health.last_failure || @health.last_loss&.fetch(:reason) == :overflow_dropped
|
|
307
298
|
|
|
308
299
|
:ok
|
|
309
300
|
end
|
|
@@ -28,16 +28,16 @@ module Julewire
|
|
|
28
28
|
def call(record)
|
|
29
29
|
increment(:received)
|
|
30
30
|
payload = format_record(record)
|
|
31
|
-
return
|
|
31
|
+
return false unless payload
|
|
32
32
|
|
|
33
33
|
increment(:formatted)
|
|
34
34
|
encoded = encode_payload(payload, record)
|
|
35
|
-
return
|
|
36
|
-
return
|
|
37
|
-
return
|
|
35
|
+
return false unless encoded
|
|
36
|
+
return false unless within_limit?(encoded, record)
|
|
37
|
+
return false unless write(encoded, record)
|
|
38
38
|
|
|
39
39
|
increment(:output_accepted)
|
|
40
|
-
|
|
40
|
+
true
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
private
|
|
@@ -13,13 +13,6 @@ module Julewire
|
|
|
13
13
|
nil
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# Private testing seam for `Julewire::Testing.unregister_destination`.
|
|
17
|
-
def unregister(kind)
|
|
18
|
-
@factories.delete(normalize_name(kind))
|
|
19
|
-
nil
|
|
20
|
-
end
|
|
21
|
-
private :unregister
|
|
22
|
-
|
|
23
16
|
def factory_for(kind)
|
|
24
17
|
@factories[normalize_name(kind)]
|
|
25
18
|
end
|
|
@@ -11,7 +11,9 @@ module Julewire
|
|
|
11
11
|
|
|
12
12
|
private_constant :NestedCallback
|
|
13
13
|
|
|
14
|
-
Failure = Data.define(:at, :class_name, :metadata)
|
|
14
|
+
Failure = Data.define(:at, :class_name, :metadata)
|
|
15
|
+
|
|
16
|
+
class Failure
|
|
15
17
|
def to_h
|
|
16
18
|
{
|
|
17
19
|
action: metadata[:action],
|
|
@@ -42,7 +44,7 @@ module Julewire
|
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def failure?(result)
|
|
45
|
-
result.
|
|
47
|
+
result.instance_of?(Failure)
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
def nested_callback_result(metadata)
|
|
@@ -52,7 +52,7 @@ module Julewire
|
|
|
52
52
|
|
|
53
53
|
def destination_info(destinations)
|
|
54
54
|
destinations.transform_values do |destination|
|
|
55
|
-
|
|
55
|
+
destination_component_info(destination)
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -62,12 +62,18 @@ module Julewire
|
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def destination_component_info(component)
|
|
66
|
+
info = component_info(component)
|
|
67
|
+
last_loss = component[:last_loss]
|
|
68
|
+
info[:last_loss] = last_loss if last_loss
|
|
69
|
+
info
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def component_info(component)
|
|
66
73
|
{
|
|
67
|
-
counts: component
|
|
74
|
+
counts: component.fetch(:counts),
|
|
68
75
|
last_failure: component[:last_failure],
|
|
69
|
-
|
|
70
|
-
status: component[:status]
|
|
76
|
+
status: component.fetch(:status)
|
|
71
77
|
}.compact
|
|
72
78
|
end
|
|
73
79
|
|
|
@@ -82,7 +88,7 @@ module Julewire
|
|
|
82
88
|
|
|
83
89
|
def pipeline_warnings(pipeline, items)
|
|
84
90
|
items << warning(:no_destinations, "pipeline has no destinations") unless pipeline.fetch(:configured)
|
|
85
|
-
if pipeline.fetch(:configured) && pipeline.fetch(:status)
|
|
91
|
+
if pipeline.fetch(:configured) && !pipeline.fetch(:status).equal?(:ok)
|
|
86
92
|
items << warning(:pipeline_degraded, "pipeline is #{pipeline.fetch(:status)}")
|
|
87
93
|
end
|
|
88
94
|
component_warnings(
|
|
@@ -99,14 +105,15 @@ module Julewire
|
|
|
99
105
|
|
|
100
106
|
def component_warnings(components, items, code:, label:)
|
|
101
107
|
components.each do |name, component|
|
|
102
|
-
|
|
108
|
+
status = component.fetch(:status)
|
|
109
|
+
next if status == :ok
|
|
103
110
|
|
|
104
|
-
items << warning(code, "#{label} #{name} is #{
|
|
111
|
+
items << warning(code, "#{label} #{name} is #{status}")
|
|
105
112
|
end
|
|
106
113
|
end
|
|
107
114
|
|
|
108
115
|
def warning(code, message)
|
|
109
|
-
{ code: code, message: message }
|
|
116
|
+
{ code: code, message: message }
|
|
110
117
|
end
|
|
111
118
|
end
|
|
112
119
|
end
|
|
@@ -16,8 +16,10 @@ module Julewire
|
|
|
16
16
|
integration: metadata[:integration],
|
|
17
17
|
output_class: metadata[:output_class],
|
|
18
18
|
phase: metadata[:phase],
|
|
19
|
+
processor: metadata[:processor],
|
|
19
20
|
reason: metadata[:reason],
|
|
20
21
|
record: record_metadata(metadata[:record_metadata]),
|
|
22
|
+
result_class: metadata[:result_class],
|
|
21
23
|
status: metadata[:status]
|
|
22
24
|
}.compact.freeze
|
|
23
25
|
end
|
|
@@ -27,12 +29,14 @@ module Julewire
|
|
|
27
29
|
def record_metadata(value)
|
|
28
30
|
return unless value.is_a?(Hash)
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
labels = value[:labels]
|
|
33
|
+
metadata = {
|
|
31
34
|
event: value[:event],
|
|
32
|
-
labels: value[:labels].is_a?(Hash) ? Fields::FieldSet.deep_dup(value[:labels]) : nil,
|
|
33
35
|
severity: value[:severity],
|
|
34
36
|
source: value[:source]
|
|
35
37
|
}.compact
|
|
38
|
+
metadata[:labels] = Fields::FieldSet.deep_dup(labels) if labels.is_a?(Hash)
|
|
39
|
+
metadata
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
42
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_fixnum"
|
|
4
|
+
require "concurrent/atomic/atomic_reference"
|
|
5
|
+
|
|
3
6
|
module Julewire
|
|
4
7
|
module Core
|
|
5
8
|
module Diagnostics
|
|
@@ -15,106 +18,91 @@ module Julewire
|
|
|
15
18
|
@callback_metadata = callback_metadata
|
|
16
19
|
@failure_counter = failure_counter
|
|
17
20
|
@track_failures = track_failures
|
|
18
|
-
|
|
19
|
-
counter_keys = counter_keys.to_a
|
|
21
|
+
counter_keys = counter_keys.map { it }
|
|
20
22
|
counter_keys = counter_keys.union([:failures]) if @track_failures
|
|
21
|
-
@counts = counter_keys.to_h { [it,
|
|
22
|
-
@current_degradation =
|
|
23
|
-
@last_callback_failure =
|
|
24
|
-
@last_failure =
|
|
25
|
-
@last_loss =
|
|
23
|
+
@counts = counter_keys.to_h { [it, Concurrent::AtomicFixnum.new] }
|
|
24
|
+
@current_degradation = Concurrent::AtomicReference.new
|
|
25
|
+
@last_callback_failure = Concurrent::AtomicReference.new
|
|
26
|
+
@last_failure = Concurrent::AtomicReference.new
|
|
27
|
+
@last_loss = Concurrent::AtomicReference.new
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def increment(key, by: 1)
|
|
29
|
-
@
|
|
31
|
+
@counts.fetch(key).increment(by)
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def counts
|
|
33
|
-
@
|
|
35
|
+
@counts.to_h { |key, counter| [key, counter.value] }.freeze
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def degradation_marker
|
|
37
|
-
@
|
|
39
|
+
@current_degradation.get
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def degraded?(status_from: :current)
|
|
41
|
-
|
|
43
|
+
degraded_from?(status_from)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def last_callback_failure
|
|
45
|
-
@
|
|
47
|
+
@last_callback_failure.get
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
def last_failure
|
|
49
|
-
@
|
|
51
|
+
@last_failure.get
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def last_loss
|
|
53
|
-
@
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def clear_degradation
|
|
57
|
-
@mutex.synchronize { @current_degradation = nil }
|
|
55
|
+
@last_loss.get
|
|
58
56
|
end
|
|
59
57
|
|
|
60
58
|
def clear_degradation_if_unchanged(marker)
|
|
61
|
-
@
|
|
59
|
+
@current_degradation.compare_and_set(marker, nil)
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
def clear_failures!
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@last_loss = nil
|
|
70
|
-
end
|
|
63
|
+
@current_degradation.set(nil)
|
|
64
|
+
@last_callback_failure.set(nil)
|
|
65
|
+
@last_failure.set(nil)
|
|
66
|
+
@last_loss.set(nil)
|
|
71
67
|
self
|
|
72
68
|
end
|
|
73
69
|
|
|
74
70
|
def record_failure(error, callback: nil, counter: @failure_counter, degrade: true, **metadata)
|
|
75
71
|
failure = FailureSnapshot.build(error, **metadata)
|
|
76
|
-
@
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@current_degradation = failure if degrade
|
|
81
|
-
end
|
|
72
|
+
increment(:failures) if @track_failures
|
|
73
|
+
increment(counter) if counter && !counter.equal?(:failures) && @counts.key?(counter)
|
|
74
|
+
@last_failure.set(failure)
|
|
75
|
+
@current_degradation.set(failure) if degrade
|
|
82
76
|
notify_failure_callback(callback, error, metadata)
|
|
83
77
|
failure
|
|
84
78
|
end
|
|
85
79
|
|
|
86
80
|
def record_callback_failure(callback_failure)
|
|
87
|
-
@
|
|
88
|
-
|
|
89
|
-
increment_unlocked(@callback_failure_counter) if @callback_failure_counter
|
|
90
|
-
end
|
|
81
|
+
@last_callback_failure.set(callback_failure.to_h)
|
|
82
|
+
increment(@callback_failure_counter) if @callback_failure_counter
|
|
91
83
|
end
|
|
92
84
|
|
|
93
85
|
def record_loss(reason:, counter: reason, degrade: true, **metadata)
|
|
94
86
|
loss = { reason: reason }.merge(metadata).compact.freeze
|
|
95
|
-
@
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@current_degradation = loss if degrade
|
|
99
|
-
end
|
|
87
|
+
increment(counter) if counter && @counts.key?(counter)
|
|
88
|
+
@last_loss.set(loss)
|
|
89
|
+
@current_degradation.set(loss) if degrade
|
|
100
90
|
loss
|
|
101
91
|
end
|
|
102
92
|
|
|
103
93
|
def record_success
|
|
104
|
-
@
|
|
94
|
+
@current_degradation.set(nil)
|
|
105
95
|
self
|
|
106
96
|
end
|
|
107
97
|
|
|
108
98
|
def snapshot(status: nil, status_from: :current, include_loss: false, **fields)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
result.merge(fields).compact.freeze
|
|
117
|
-
end
|
|
99
|
+
result = {
|
|
100
|
+
counts: counts,
|
|
101
|
+
last_failure: last_failure,
|
|
102
|
+
status: status || (degraded_from?(status_from) ? :degraded : :ok)
|
|
103
|
+
}
|
|
104
|
+
result[:last_loss] = last_loss if include_loss
|
|
105
|
+
result.merge(fields).compact.freeze
|
|
118
106
|
end
|
|
119
107
|
|
|
120
108
|
private
|
|
@@ -124,16 +112,12 @@ module Julewire
|
|
|
124
112
|
record_callback_failure(callback_result) if CallbackNotifier.failure?(callback_result)
|
|
125
113
|
end
|
|
126
114
|
|
|
127
|
-
def
|
|
128
|
-
@counts[key] = @counts.fetch(key) + by
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def degraded_unlocked?(status_from)
|
|
115
|
+
def degraded_from?(status_from)
|
|
132
116
|
case status_from
|
|
133
117
|
when :current
|
|
134
|
-
|
|
118
|
+
!!degradation_marker
|
|
135
119
|
when :failure_or_loss
|
|
136
|
-
!!(
|
|
120
|
+
!!(last_failure || last_loss)
|
|
137
121
|
else
|
|
138
122
|
raise ArgumentError, "unknown health status source: #{status_from.inspect}"
|
|
139
123
|
end
|
|
@@ -1,60 +1,47 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/map"
|
|
4
|
+
|
|
3
5
|
module Julewire
|
|
4
6
|
module Core
|
|
5
7
|
module Diagnostics
|
|
6
8
|
class IntegrationHealthStore
|
|
7
9
|
def initialize
|
|
8
|
-
@
|
|
9
|
-
@entries = {}
|
|
10
|
+
@entries = Concurrent::Map.new
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def record_failure(integration, error, **metadata)
|
|
13
14
|
name = normalize_name(integration)
|
|
14
15
|
metadata = { phase: :integration, integration: name }.merge(metadata)
|
|
15
|
-
|
|
16
|
-
entry_for(name).record_failure(error, **metadata)
|
|
17
|
-
end
|
|
18
|
-
nil
|
|
19
|
-
rescue StandardError
|
|
16
|
+
entry_for(name).record_failure(error, **metadata)
|
|
20
17
|
nil
|
|
21
18
|
end
|
|
22
19
|
|
|
23
20
|
def record_success(integration)
|
|
24
21
|
name = normalize_name(integration)
|
|
25
|
-
|
|
26
|
-
entry_for(name).record_success
|
|
27
|
-
end
|
|
28
|
-
nil
|
|
29
|
-
rescue StandardError
|
|
22
|
+
entry_for(name).record_success
|
|
30
23
|
nil
|
|
31
24
|
end
|
|
32
25
|
|
|
33
26
|
def health
|
|
34
|
-
@
|
|
35
|
-
|
|
36
|
-
end
|
|
27
|
+
@entries.each_pair.with_object({}) do |(name, entry), snapshot|
|
|
28
|
+
snapshot[name] = entry.snapshot
|
|
29
|
+
end.freeze
|
|
37
30
|
end
|
|
38
31
|
|
|
39
32
|
def reset!
|
|
40
|
-
@
|
|
41
|
-
nil
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def after_fork!
|
|
45
|
-
@mutex = Mutex.new
|
|
46
|
-
@entries = {}
|
|
33
|
+
@entries.clear
|
|
47
34
|
nil
|
|
48
35
|
end
|
|
49
36
|
|
|
50
37
|
private
|
|
51
38
|
|
|
52
39
|
def entry_for(name)
|
|
53
|
-
@entries
|
|
40
|
+
@entries.compute_if_absent(name) { Health.new(counter_keys: []) }
|
|
54
41
|
end
|
|
55
42
|
|
|
56
43
|
def normalize_name(value)
|
|
57
|
-
Core.normalize_name(value
|
|
44
|
+
Core.normalize_name(value)
|
|
58
45
|
rescue StandardError
|
|
59
46
|
:unknown
|
|
60
47
|
end
|
|
@@ -5,11 +5,10 @@ module Julewire
|
|
|
5
5
|
module Diagnostics
|
|
6
6
|
module InternalRecords
|
|
7
7
|
class << self
|
|
8
|
-
def emit_error(error
|
|
9
|
-
|
|
8
|
+
def emit_error(error)
|
|
9
|
+
Records::Draft.build(
|
|
10
10
|
{
|
|
11
11
|
severity: :error,
|
|
12
|
-
kind: :point,
|
|
13
12
|
event: "julewire.emit_error",
|
|
14
13
|
source: "julewire",
|
|
15
14
|
message: "Julewire emit failed",
|
|
@@ -17,17 +16,15 @@ module Julewire
|
|
|
17
16
|
error: failure_details(error)
|
|
18
17
|
}
|
|
19
18
|
},
|
|
20
|
-
context:
|
|
21
|
-
scope: nil
|
|
22
|
-
error_backtrace_lines: error_backtrace_lines
|
|
19
|
+
context: nil,
|
|
20
|
+
scope: nil
|
|
23
21
|
)
|
|
24
22
|
end
|
|
25
23
|
|
|
26
|
-
def processor_error(processor_name:, error:, record_metadata
|
|
27
|
-
|
|
24
|
+
def processor_error(processor_name:, error:, record_metadata:)
|
|
25
|
+
Records::Draft.build(
|
|
28
26
|
{
|
|
29
27
|
severity: :error,
|
|
30
|
-
kind: :point,
|
|
31
28
|
event: "julewire.processor_error",
|
|
32
29
|
source: "julewire",
|
|
33
30
|
message: "Julewire processor failed",
|
|
@@ -38,9 +35,8 @@ module Julewire
|
|
|
38
35
|
record: record_metadata
|
|
39
36
|
}
|
|
40
37
|
},
|
|
41
|
-
context:
|
|
42
|
-
scope: nil
|
|
43
|
-
error_backtrace_lines: error_backtrace_lines
|
|
38
|
+
context: nil,
|
|
39
|
+
scope: nil
|
|
44
40
|
)
|
|
45
41
|
end
|
|
46
42
|
|