Module: WideEvent
- Defined in:
- lib/wide_event.rb,
lib/wide_event/railtie.rb,
lib/wide_event/version.rb,
lib/wide_event/registry.rb,
lib/wide_event/middleware.rb,
lib/wide_event/subscribers.rb,
lib/wide_event/test_helper.rb,
lib/wide_event/sinks/memory.rb,
lib/wide_event/configuration.rb,
lib/wide_event/sinks/log_line.rb,
lib/wide_event/sinks/otel_span.rb,
lib/wide_event/job_instrumentation.rb,
lib/wide_event/span_counter_processor.rb
Overview
Wide-event accumulator: one flat attribute hash per unit of work (HTTP
request or job execution), flushed to the configured sink by
WideEvent::Middleware / WideEvent::JobInstrumentation. Safe no-op when no
unit of work is open.
Defined Under Namespace
Modules: JobInstrumentation, Sinks, Subscribers, TestHelper
Classes: Configuration, Middleware, Railtie, Registry, SpanCounterProcessor
Constant Summary
collapse
- KEY =
:wide_event_attributes
- PROCESS_START =
Process.clock_gettime(Process::CLOCK_MONOTONIC)
- VERSION =
"0.1.1"
Class Method Summary
collapse
Class Method Details
.active? ⇒ Boolean
52
53
54
|
# File 'lib/wide_event.rb', line 52
def active?
!store.nil?
end
|
.attributes ⇒ Object
102
103
104
|
# File 'lib/wide_event.rb', line 102
def attributes
store&.dup
end
|
.config ⇒ Object
26
27
28
|
# File 'lib/wide_event.rb', line 26
def config
@config ||= Configuration.new
end
|
30
31
32
33
|
# File 'lib/wide_event.rb', line 30
def configure
yield config
config
end
|
.count(dep, duration_ms) ⇒ Object
65
66
67
68
69
70
71
72
73
74
75
|
# File 'lib/wide_event.rb', line 65
def count(dep, duration_ms)
s = store
return if s.nil?
registry_check([ "stats.#{dep}_count", "stats.#{dep}_duration_ms" ])
s["stats.#{dep}_count"] = s.fetch("stats.#{dep}_count", 0) + 1
s["stats.#{dep}_duration_ms"] = (s.fetch("stats.#{dep}_duration_ms", 0.0) + duration_ms).round(2)
nil
rescue StandardError => e
handle_error(e, "count")
nil
end
|
.enabled? ⇒ Boolean
39
40
41
|
# File 'lib/wide_event.rb', line 39
def enabled?
config.enabled
end
|
.error!(slug:, exception: nil, expected: false) ⇒ Object
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/wide_event.rb', line 90
def error!(slug:, exception: nil, expected: false)
attrs = { "error" => true, "exception.slug" => slug, "exception.expected" => expected }
if exception
attrs["exception.type"] = exception.class.name
attrs["exception.message"] = exception.message.to_s[0, 500]
end
set(attrs)
rescue StandardError => e
handle_error(e, "error!")
nil
end
|
.flush(attrs) ⇒ Object
Hands a finished wide event to the configured sink. Never raises into
the caller.
120
121
122
123
124
125
126
127
|
# File 'lib/wide_event.rb', line 120
def flush(attrs)
return if attrs.nil? || attrs.empty?
config.resolved_sink&.flush(attrs)
nil
rescue StandardError => e
handle_error(e, "flush")
nil
end
|
.flush_hash(hash, span) ⇒ Object
Sanitize and write a wide-event hash onto an OTel span. span may be
nil (OTel inactive). Never raises into the caller.
145
146
147
148
149
150
151
|
# File 'lib/wide_event.rb', line 145
def flush_hash(hash, span)
return if hash.nil? || hash.empty? || span.nil?
span.add_attributes(sanitize(hash))
rescue StandardError => e
handle_error(e, "flush")
nil
end
|
.handle_error(exception, message) ⇒ Object
Telemetry must never raise into app code: every public entry point
funnels its rescues here, and a broken handler is itself swallowed.
168
169
170
171
172
173
|
# File 'lib/wide_event.rb', line 168
def handle_error(exception, message)
config.error_handler&.call(exception, message)
nil
rescue StandardError
nil
end
|
.install! ⇒ Object
Wires the pieces that need the host's OpenTelemetry SDK configured:
notification subscribers and, for the OTel sink, the span counter on
the global tracer provider. Called by the railtie after the app's
initializers; call it manually from a non-Rails setup.
133
134
135
136
137
138
139
140
141
|
# File 'lib/wide_event.rb', line 133
def install!
Subscribers.subscribe!
if config.sink == :otel && defined?(OpenTelemetry) &&
OpenTelemetry.respond_to?(:tracer_provider) &&
OpenTelemetry.tracer_provider.respond_to?(:add_span_processor)
OpenTelemetry.tracer_provider.add_span_processor(SpanCounterProcessor.new)
end
nil
end
|
.peek ⇒ Object
Read-only view of the live store for hot-path handlers that would
otherwise dup per event (e.g. every cache read). Callers must not
mutate: writes go through set/count/error!.
109
110
111
|
# File 'lib/wide_event.rb', line 109
def peek
store
end
|
.phase(name) ⇒ Object
77
78
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/wide_event.rb', line 77
def phase(name)
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
ensure
elapsed_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0
begin
set("#{name}.duration_ms" => elapsed_ms.round(2))
rescue StandardError => e
handle_error(e, "phase")
nil
end
end
|
.reset_configuration! ⇒ Object
35
36
37
|
# File 'lib/wide_event.rb', line 35
def reset_configuration!
@config = Configuration.new
end
|
.sanitize(attrs) ⇒ Object
153
154
155
156
157
158
159
160
161
162
163
164
|
# File 'lib/wide_event.rb', line 153
def sanitize(attrs)
attrs.each_with_object({}) do |(k, v), out|
key = k.to_s
case v
when String, Integer, Float, true, false then out[key] = v
when Symbol then out[key] = v.to_s
when Time, DateTime, ActiveSupport::TimeWithZone then out[key] = v.to_time.utc.iso8601(3)
when nil then nil else out[key] = v.to_s[0, 300]
end
end
end
|
.set(attrs) ⇒ Object
56
57
58
59
60
61
62
63
|
# File 'lib/wide_event.rb', line 56
def set(attrs)
registry_check(attrs.keys)
store&.merge!(attrs)
nil
rescue StandardError => e
handle_error(e, "set")
nil
end
|
.uptime_attributes ⇒ Object
113
114
115
116
|
# File 'lib/wide_event.rb', line 113
def uptime_attributes
uptime = Process.clock_gettime(Process::CLOCK_MONOTONIC) - PROCESS_START
{ "uptime_sec" => uptime.round, "uptime_sec_log10" => Math.log10([ uptime, 1 ].max).round(3) }
end
|
.with ⇒ Object
43
44
45
46
47
48
49
50
|
# File 'lib/wide_event.rb', line 43
def with
previous = store
current = {}
ActiveSupport::IsolatedExecutionState[KEY] = current
yield current
ensure
ActiveSupport::IsolatedExecutionState[KEY] = previous
end
|