Module: Smith::Trace
- Defined in:
- lib/smith/trace.rb,
lib/smith/trace/logger.rb,
lib/smith/trace/memory.rb,
lib/smith/trace/open_telemetry.rb
Defined Under Namespace
Classes: Logger, Memory, OpenTelemetry
Constant Summary
collapse
- SENSITIVITY_CONTENT_KEYS =
%i[args result].freeze
Class Method Summary
collapse
Class Method Details
.apply_content_policy(data, sensitivity) ⇒ Object
53
54
55
56
57
58
59
60
61
62
|
# File 'lib/smith/trace.rb', line 53
def self.apply_content_policy(data, sensitivity)
case Smith.config.trace_content
when true
apply_sensitivity(data, sensitivity)
when :redacted
apply_sensitivity(redact_sensitive_keys(data), sensitivity)
else
data.except(*SENSITIVITY_CONTENT_KEYS)
end
end
|
.apply_sensitivity(data, sensitivity) ⇒ Object
64
65
66
67
68
69
70
71
72
73
|
# File 'lib/smith/trace.rb', line 64
def self.apply_sensitivity(data, sensitivity)
case sensitivity
when :high
data.except(*SENSITIVITY_CONTENT_KEYS)
when :medium
redact_sensitive_keys(data)
else
data
end
end
|
.attributed(data) ⇒ Object
Ambient attribution keys are identifiers, not content: they merge in
under the caller's own keys (the caller wins on conflict) and then pass
through the same content policy and field allowlist as everything else,
so a host's configured trace_fields contract keeps holding.
26
27
28
29
30
31
32
33
|
# File 'lib/smith/trace.rb', line 26
def self.attributed(data)
return data unless Smith.config.trace_attribution
fields = Attribution.current_fields
return data if fields.empty?
fields.merge(data)
end
|
.filter_fields(type, data) ⇒ Object
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/smith/trace.rb', line 96
def self.filter_fields(type, data)
configured_fields = Smith.config.trace_fields
return data unless configured_fields.is_a?(Hash)
allowed = configured_fields[type]
return data unless allowed.respond_to?(:include?)
data.each_with_object({}) do |(key, value), filtered|
filtered[key] = value if allowed.include?(key)
end
end
|
.record(type:, data:, sensitivity: :low) ⇒ Object
11
12
13
14
15
16
17
18
19
20
|
# File 'lib/smith/trace.rb', line 11
def self.record(type:, data:, sensitivity: :low)
adapter = resolve_adapter
return unless adapter
filtered = apply_content_policy(attributed(data), sensitivity)
filtered = filter_fields(type, filtered)
adapter.record(type: type, data: filtered)
rescue StandardError => e
Smith.config.logger&.error("Smith::Trace adapter error: #{e.message}")
end
|
.redact_sensitive_keys(data) ⇒ Object
75
76
77
78
79
80
81
82
83
|
# File 'lib/smith/trace.rb', line 75
def self.redact_sensitive_keys(data)
data.each_with_object({}) do |(key, value), filtered|
filtered[key] = if SENSITIVITY_CONTENT_KEYS.include?(key)
redact_value(value)
else
value
end
end
end
|
.redact_value(value) ⇒ Object
85
86
87
88
89
90
91
92
93
94
|
# File 'lib/smith/trace.rb', line 85
def self.redact_value(value)
case value
when String
"[REDACTED]"
when Hash
value.transform_values { |v| v.is_a?(String) ? "[REDACTED]" : v }
else
value
end
end
|
.reset! ⇒ Object
49
50
51
|
# File 'lib/smith/trace.rb', line 49
def self.reset!
ADAPTER_MUTEX.synchronize { @adapter_instances = nil }
end
|
.resolve_adapter ⇒ Object
Class-configured adapters memoize one instance under a mutex so
concurrent first records (every fan-out branch emits) share a single
adapter instead of racing separate instances and losing entries.
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/smith/trace.rb', line 38
def self.resolve_adapter
configured = Smith.config.trace_adapter
return nil unless configured
return configured unless configured.is_a?(Class)
ADAPTER_MUTEX.synchronize do
@adapter_instances ||= {}
@adapter_instances[configured] ||= configured.new
end
end
|