julewire-core 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.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +73 -0
  5. data/docs/advanced-configuration.md +66 -0
  6. data/docs/attribute-keys.md +74 -0
  7. data/docs/configuration.md +327 -0
  8. data/docs/context-and-propagation.md +353 -0
  9. data/docs/contracts.md +211 -0
  10. data/docs/development.md +49 -0
  11. data/docs/extensions-and-api.md +567 -0
  12. data/docs/health-schema.md +104 -0
  13. data/docs/instrumentation-cheatsheet.md +29 -0
  14. data/docs/internals.md +135 -0
  15. data/docs/outputs-and-lifecycle.md +206 -0
  16. data/docs/quickstart.md +133 -0
  17. data/docs/record-sources.md +17 -0
  18. data/docs/records-and-data-policy.md +230 -0
  19. data/docs/security-and-wire.md +45 -0
  20. data/docs/tail.md +91 -0
  21. data/exe/julewire +6 -0
  22. data/julewire-core.gemspec +41 -0
  23. data/lib/julewire/core/cli/doctor.rb +143 -0
  24. data/lib/julewire/core/cli/line_helpers.rb +77 -0
  25. data/lib/julewire/core/cli/log_formats/console_text.rb +25 -0
  26. data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +46 -0
  27. data/lib/julewire/core/cli/log_formats/core_json_encoder.rb +21 -0
  28. data/lib/julewire/core/cli/log_formats/record_decoder.rb +39 -0
  29. data/lib/julewire/core/cli/log_formats.rb +123 -0
  30. data/lib/julewire/core/cli/tail.rb +153 -0
  31. data/lib/julewire/core/cli/transcode.rb +105 -0
  32. data/lib/julewire/core/cli.rb +73 -0
  33. data/lib/julewire/core/configuration.rb +99 -0
  34. data/lib/julewire/core/context_store.rb +384 -0
  35. data/lib/julewire/core/destinations/chaos_output.rb +91 -0
  36. data/lib/julewire/core/destinations/collection.rb +177 -0
  37. data/lib/julewire/core/destinations/definition.rb +125 -0
  38. data/lib/julewire/core/destinations/destination.rb +268 -0
  39. data/lib/julewire/core/destinations/registry.rb +81 -0
  40. data/lib/julewire/core/destinations/sink.rb +35 -0
  41. data/lib/julewire/core/destinations/synchronized_output.rb +57 -0
  42. data/lib/julewire/core/destinations/tail_sampling.rb +321 -0
  43. data/lib/julewire/core/destinations/write_step.rb +119 -0
  44. data/lib/julewire/core/destinations.rb +33 -0
  45. data/lib/julewire/core/diagnostics/callback_notifier.rb +63 -0
  46. data/lib/julewire/core/diagnostics/doctor.rb +114 -0
  47. data/lib/julewire/core/diagnostics/failure_snapshot.rb +39 -0
  48. data/lib/julewire/core/diagnostics/health.rb +144 -0
  49. data/lib/julewire/core/diagnostics/integration_health_store.rb +64 -0
  50. data/lib/julewire/core/diagnostics/internal_records.rb +61 -0
  51. data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +112 -0
  52. data/lib/julewire/core/diagnostics/meta_observer.rb +161 -0
  53. data/lib/julewire/core/diagnostics/process_integration_health.rb +26 -0
  54. data/lib/julewire/core/diagnostics/tail/renderer.rb +36 -0
  55. data/lib/julewire/core/diagnostics/tail.rb +168 -0
  56. data/lib/julewire/core/diagnostics.rb +8 -0
  57. data/lib/julewire/core/error.rb +7 -0
  58. data/lib/julewire/core/execution/boundary.rb +106 -0
  59. data/lib/julewire/core/execution/handle.rb +77 -0
  60. data/lib/julewire/core/execution/lineage.rb +192 -0
  61. data/lib/julewire/core/execution/measurement_handle.rb +28 -0
  62. data/lib/julewire/core/execution/no_current_error.rb +9 -0
  63. data/lib/julewire/core/execution/scope.rb +246 -0
  64. data/lib/julewire/core/execution/scope_fields.rb +76 -0
  65. data/lib/julewire/core/execution/scope_identity.rb +71 -0
  66. data/lib/julewire/core/execution/scope_snapshot.rb +92 -0
  67. data/lib/julewire/core/execution/summary_state.rb +206 -0
  68. data/lib/julewire/core/execution/view.rb +56 -0
  69. data/lib/julewire/core/facade_methods.rb +181 -0
  70. data/lib/julewire/core/fields/attribute_keys.rb +54 -0
  71. data/lib/julewire/core/fields/attributes_proxy.rb +11 -0
  72. data/lib/julewire/core/fields/bags.rb +123 -0
  73. data/lib/julewire/core/fields/carry_proxy.rb +22 -0
  74. data/lib/julewire/core/fields/context_proxy.rb +11 -0
  75. data/lib/julewire/core/fields/field_set.rb +78 -0
  76. data/lib/julewire/core/fields/field_stack.rb +269 -0
  77. data/lib/julewire/core/fields/internal/deletion.rb +68 -0
  78. data/lib/julewire/core/fields/internal.rb +87 -0
  79. data/lib/julewire/core/fields/lookup.rb +35 -0
  80. data/lib/julewire/core/fields/section_proxy.rb +88 -0
  81. data/lib/julewire/core/fields/stack_set.rb +69 -0
  82. data/lib/julewire/core/fields/static_labels.rb +43 -0
  83. data/lib/julewire/core/fields/summary_proxy.rb +62 -0
  84. data/lib/julewire/core/integration/configurable.rb +52 -0
  85. data/lib/julewire/core/integration/destination_health.rb +43 -0
  86. data/lib/julewire/core/integration/event_subscriber.rb +62 -0
  87. data/lib/julewire/core/integration/facade.rb +131 -0
  88. data/lib/julewire/core/integration/fork_hooks.rb +79 -0
  89. data/lib/julewire/core/integration/health.rb +41 -0
  90. data/lib/julewire/core/integration/ivar_state.rb +38 -0
  91. data/lib/julewire/core/integration/lifecycle.rb +22 -0
  92. data/lib/julewire/core/integration/scoped.rb +34 -0
  93. data/lib/julewire/core/integration/settings.rb +92 -0
  94. data/lib/julewire/core/integration/subscriber_install.rb +39 -0
  95. data/lib/julewire/core/integration/subscription.rb +29 -0
  96. data/lib/julewire/core/integration/values.rb +192 -0
  97. data/lib/julewire/core/lifecycle_error.rb +7 -0
  98. data/lib/julewire/core/local_storage.rb +91 -0
  99. data/lib/julewire/core/processing/level_threshold.rb +53 -0
  100. data/lib/julewire/core/processing/match.rb +74 -0
  101. data/lib/julewire/core/processing/pipeline.rb +360 -0
  102. data/lib/julewire/core/processing/processor_chain.rb +69 -0
  103. data/lib/julewire/core/processing/processor_registry.rb +115 -0
  104. data/lib/julewire/core/processing/processor_wrapper.rb +44 -0
  105. data/lib/julewire/core/processing/record_field_transform.rb +124 -0
  106. data/lib/julewire/core/processing/sampling.rb +109 -0
  107. data/lib/julewire/core/processing.rb +41 -0
  108. data/lib/julewire/core/propagation/carrier.rb +93 -0
  109. data/lib/julewire/core/propagation.rb +50 -0
  110. data/lib/julewire/core/records/console_formatter.rb +24 -0
  111. data/lib/julewire/core/records/deconstruct.rb +19 -0
  112. data/lib/julewire/core/records/display_message.rb +166 -0
  113. data/lib/julewire/core/records/draft.rb +576 -0
  114. data/lib/julewire/core/records/formatter.rb +14 -0
  115. data/lib/julewire/core/records/lazy_emit_input.rb +99 -0
  116. data/lib/julewire/core/records/metadata.rb +23 -0
  117. data/lib/julewire/core/records/public_projection.rb +51 -0
  118. data/lib/julewire/core/records/raw_input.rb +41 -0
  119. data/lib/julewire/core/records/record.rb +175 -0
  120. data/lib/julewire/core/records/severity.rb +44 -0
  121. data/lib/julewire/core/runtime.rb +515 -0
  122. data/lib/julewire/core/runtime_locator.rb +20 -0
  123. data/lib/julewire/core/runtime_registry.rb +48 -0
  124. data/lib/julewire/core/runtime_state.rb +39 -0
  125. data/lib/julewire/core/scheduling/deadline.rb +24 -0
  126. data/lib/julewire/core/scheduling/deadline_scheduler.rb +207 -0
  127. data/lib/julewire/core/scheduling/shared_scheduler.rb +48 -0
  128. data/lib/julewire/core/sentinel.rb +18 -0
  129. data/lib/julewire/core/serialization/backtrace_limiter.rb +50 -0
  130. data/lib/julewire/core/serialization/bounded_transform.rb +55 -0
  131. data/lib/julewire/core/serialization/bounded_traversal.rb +274 -0
  132. data/lib/julewire/core/serialization/deep_compact_empty.rb +67 -0
  133. data/lib/julewire/core/serialization/deep_freeze.rb +63 -0
  134. data/lib/julewire/core/serialization/encoding_sanitizer.rb +40 -0
  135. data/lib/julewire/core/serialization/exception_shape.rb +88 -0
  136. data/lib/julewire/core/serialization/json_encoder.rb +69 -0
  137. data/lib/julewire/core/serialization/serializer.rb +233 -0
  138. data/lib/julewire/core/serialization/serializer_pool.rb +21 -0
  139. data/lib/julewire/core/serialization/text_encoder.rb +147 -0
  140. data/lib/julewire/core/serialization/value_copy.rb +209 -0
  141. data/lib/julewire/core/serialization/value_traversal.rb +150 -0
  142. data/lib/julewire/core/testing/chaos/catalog.rb +72 -0
  143. data/lib/julewire/core/testing/chaos/core_runtime.rb +120 -0
  144. data/lib/julewire/core/testing/chaos/destination.rb +55 -0
  145. data/lib/julewire/core/testing/chaos/emitter.rb +20 -0
  146. data/lib/julewire/core/testing/chaos/raising_output.rb +42 -0
  147. data/lib/julewire/core/testing/chaos.rb +80 -0
  148. data/lib/julewire/core/testing/contracts/component.rb +162 -0
  149. data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +59 -0
  150. data/lib/julewire/core/testing/contracts/integration.rb +166 -0
  151. data/lib/julewire/core/testing/contracts/integration_fields.rb +36 -0
  152. data/lib/julewire/core/testing/contracts/record_draft.rb +37 -0
  153. data/lib/julewire/core/testing/contracts/runtime.rb +178 -0
  154. data/lib/julewire/core/testing/contracts/wire.rb +60 -0
  155. data/lib/julewire/core/testing/contracts.rb +24 -0
  156. data/lib/julewire/core/testing/coverage.rb +58 -0
  157. data/lib/julewire/core/testing/test_reports.rb +78 -0
  158. data/lib/julewire/core/testing.rb +122 -0
  159. data/lib/julewire/core/validation.rb +69 -0
  160. data/lib/julewire/core/version.rb +7 -0
  161. data/lib/julewire/core.rb +80 -0
  162. data/lib/julewire/error.rb +5 -0
  163. data/lib/julewire-core.rb +3 -0
  164. metadata +237 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Julewire
4
+ module Core
5
+ module Records
6
+ class PublicProjection
7
+ include Enumerable
8
+
9
+ INTERNAL_KEYS = Fields::Bags.hidden_output_sections
10
+ INTERNAL_EXECUTION_KEYS = %i[
11
+ ancestors
12
+ ancestors_truncated
13
+ depth
14
+ parent
15
+ root
16
+ ].freeze
17
+
18
+ class << self
19
+ def public_execution(value)
20
+ return value unless INTERNAL_EXECUTION_KEYS.any? { value.key?(it) }
21
+
22
+ value.except(*INTERNAL_EXECUTION_KEYS)
23
+ end
24
+ end
25
+
26
+ def initialize(record)
27
+ Record.validate_normalized!(record)
28
+ @record = record
29
+ end
30
+
31
+ def each
32
+ return enum_for(:each) unless block_given?
33
+
34
+ @record.each do |key, value|
35
+ next if INTERNAL_KEYS.include?(key)
36
+
37
+ yield key, output_value(key, value)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def output_value(key, value)
44
+ return self.class.public_execution(value) if key == :execution && value.is_a?(Hash)
45
+
46
+ value
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Julewire
4
+ module Core
5
+ module Records
6
+ module RawInput
7
+ # Reads user-supplied emit hashes before draft normalization.
8
+ SEVERITY_KEYS = [:severity, "severity"].freeze
9
+ SEVERITY_KEY = SEVERITY_KEYS.fetch(0)
10
+ SEVERITY_STRING_KEY = SEVERITY_KEYS.fetch(1)
11
+ private_constant :SEVERITY_KEYS, :SEVERITY_KEY, :SEVERITY_STRING_KEY
12
+
13
+ class << self
14
+ def explicit_severity?(input)
15
+ hash_input?(input) && (input.key?(SEVERITY_KEY) || input.key?(SEVERITY_STRING_KEY))
16
+ end
17
+
18
+ def severity_key?(key)
19
+ SEVERITY_KEYS.include?(key)
20
+ end
21
+
22
+ def without_severity_keys(input)
23
+ input.except(*SEVERITY_KEYS)
24
+ end
25
+
26
+ def value(input, key, default: nil)
27
+ return default unless hash_input?(input)
28
+ return input[key] if input.key?(key)
29
+ return input[key.to_s] if input.key?(key.to_s)
30
+
31
+ default
32
+ end
33
+
34
+ def hash_input?(input)
35
+ input.is_a?(Hash) || LazyEmitInput.input?(input)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module Julewire
6
+ module Core
7
+ module Records
8
+ # @api extension
9
+ class Record
10
+ include Enumerable
11
+ include Deconstruct
12
+
13
+ KINDS = {
14
+ "point" => :point,
15
+ "summary" => :summary
16
+ }.freeze
17
+ HASH_SECTIONS = Fields::Bags.record_hash_sections
18
+ REQUIRED_KEYS = Fields::Bags.required_record_keys
19
+
20
+ class << self
21
+ def from_normalized_hash(record, lineage: nil)
22
+ if record.is_a?(Hash)
23
+ lineage ||= Execution::Lineage.from_execution_hash(record[:execution])
24
+ record = record.merge(execution: Execution::Lineage.clean_lazy_relationship_hash(record[:execution]))
25
+ end
26
+ validate_normalized_hash!(record)
27
+ new(snapshot_hash(record), lineage: lineage)
28
+ end
29
+
30
+ def from_owned_hash(record, lineage: nil, trust_frozen: false)
31
+ if record.is_a?(Hash)
32
+ lineage ||= Execution::Lineage.from_execution_hash(record.fetch(:execution))
33
+ execution = Execution::Lineage.clean_lazy_relationship_hash(record.fetch(:execution))
34
+ record = record.frozen? ? record.merge(execution: execution) : replace_execution(record, execution)
35
+ end
36
+ validate_normalized_hash!(record)
37
+ new(Serialization::DeepFreeze.call(record, trust_frozen: trust_frozen), lineage: lineage)
38
+ end
39
+
40
+ def validate_normalized!(record)
41
+ return record if record.is_a?(self)
42
+
43
+ raise TypeError, "expected Julewire::Record"
44
+ end
45
+
46
+ def validate_normalized_hash!(record)
47
+ validate_hash!(record)
48
+ record
49
+ end
50
+
51
+ private
52
+
53
+ def replace_execution(record, execution)
54
+ record[:execution] = execution
55
+ record
56
+ end
57
+
58
+ def validate_hash!(record)
59
+ raise TypeError, "record must be a normalized Hash" unless record.is_a?(Hash)
60
+
61
+ validate_symbol_keys!(record)
62
+
63
+ validate_required_keys!(record)
64
+ validate_known_keys!(record)
65
+
66
+ validate_kind!(record.fetch(:kind))
67
+ validate_severity!(record.fetch(:severity))
68
+ validate_hash_sections!(record)
69
+ validate_error!(record.fetch(:error))
70
+ end
71
+
72
+ def validate_symbol_keys!(record)
73
+ record.each_key do |key|
74
+ raise TypeError, "record must not use string keys" if key.is_a?(String)
75
+ end
76
+ end
77
+
78
+ def validate_required_keys!(record)
79
+ missing = nil
80
+ REQUIRED_KEYS.each do |key|
81
+ next if record.key?(key)
82
+
83
+ (missing ||= []) << key
84
+ end
85
+ raise TypeError, "record must be complete (missing: #{missing.join(", ")})" if missing
86
+ end
87
+
88
+ def validate_known_keys!(record)
89
+ unknown = nil
90
+ record.each_key do |key|
91
+ next if REQUIRED_KEYS.include?(key)
92
+
93
+ (unknown ||= []) << key
94
+ end
95
+ return unless unknown
96
+
97
+ raise TypeError, "record has unknown top-level keys: #{unknown.join(", ")}"
98
+ end
99
+
100
+ def validate_kind!(value)
101
+ return if KINDS.value?(value)
102
+
103
+ raise TypeError, "record kind must be :point or :summary"
104
+ end
105
+
106
+ def validate_severity!(value)
107
+ return if Severity::VALUES.include?(value)
108
+
109
+ raise TypeError, "record severity must be one of: #{Severity::VALUES.join(", ")}"
110
+ end
111
+
112
+ def validate_hash_sections!(record)
113
+ HASH_SECTIONS.each do |section|
114
+ next if record.fetch(section).is_a?(Hash)
115
+
116
+ raise TypeError, "record #{section} must be a Hash"
117
+ end
118
+ end
119
+
120
+ def validate_error!(value)
121
+ return if value.nil? || value.is_a?(Hash)
122
+
123
+ raise TypeError, "record error must be nil or a Hash"
124
+ end
125
+
126
+ def snapshot_hash(record)
127
+ Serialization::ValueCopy.call(
128
+ record,
129
+ freeze_values: true
130
+ )
131
+ end
132
+ end
133
+
134
+ attr_reader :lineage
135
+
136
+ def initialize(data, lineage: nil)
137
+ @data = data
138
+ @lineage = (lineage || Execution::Lineage.from_execution_hash(fetch(:execution))).freeze
139
+ freeze
140
+ end
141
+
142
+ def [](key) = @data[key]
143
+
144
+ def fetch(...) = @data.fetch(...)
145
+
146
+ def dig(...) = @data.dig(...)
147
+
148
+ def key?(key) = @data.key?(key)
149
+
150
+ def each(&) = @data.each(&)
151
+
152
+ def to_h = Fields::FieldSet.deep_dup(@data)
153
+
154
+ # @api internal
155
+ def serializable_data = @data
156
+
157
+ REQUIRED_KEYS.each do |key|
158
+ define_method(key) { @data[key] }
159
+ end
160
+
161
+ def ==(other)
162
+ other.instance_of?(Record) && @data == other.serializable_data
163
+ end
164
+
165
+ def eql?(other)
166
+ other.instance_of?(Record) && @data.eql?(other.serializable_data)
167
+ end
168
+
169
+ def hash = @data.hash
170
+
171
+ def inspect = "#<#{self.class} #{@data}>"
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ # shareable_constant_value: literal
3
+
4
+ module Julewire
5
+ module Core
6
+ module Records
7
+ module Severity
8
+ VALUES = %i[debug info warn error fatal unknown].freeze
9
+ STRING_VALUES = VALUES.to_h { [it.name, it] }.freeze
10
+ RANKS = VALUES.each_with_index.to_h.freeze
11
+ LOGGER_INTEGER_VALUES = VALUES.each_with_index.to_h.invert.freeze
12
+
13
+ class << self
14
+ def normalize(value)
15
+ return value if RANKS.key?(value)
16
+
17
+ severity = severity_symbol(value)
18
+ return severity if RANKS.key?(severity)
19
+
20
+ raise ArgumentError, "unsupported severity: #{value.inspect}"
21
+ end
22
+
23
+ def severity_symbol(value)
24
+ case value
25
+ when Symbol
26
+ value.downcase
27
+ when String
28
+ STRING_VALUES[value.downcase]
29
+ when Integer
30
+ LOGGER_INTEGER_VALUES[value]
31
+ end
32
+ end
33
+
34
+ def rank(value)
35
+ rank = RANKS[value]
36
+ return rank unless rank.nil?
37
+
38
+ RANKS.fetch(normalize(value))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end