rage-rb 1.19.2 → 1.20.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/.rspec +1 -0
- data/Appraisals +19 -0
- data/CHANGELOG.md +15 -1
- data/CODE_OF_CONDUCT.md +13 -17
- data/Gemfile +3 -0
- data/README.md +60 -63
- data/Rakefile +14 -0
- data/lib/rage/all.rb +3 -0
- data/lib/rage/cable/cable.rb +11 -7
- data/lib/rage/cable/channel.rb +6 -1
- data/lib/rage/cable/connection.rb +4 -0
- data/lib/rage/cable/router.rb +14 -9
- data/lib/rage/configuration.rb +235 -21
- data/lib/rage/controller/api.rb +49 -44
- data/lib/rage/deferred/context.rb +30 -2
- data/lib/rage/deferred/deferred.rb +18 -6
- data/lib/rage/deferred/metadata.rb +39 -0
- data/lib/rage/deferred/middleware_chain.rb +67 -0
- data/lib/rage/deferred/task.rb +45 -17
- data/lib/rage/events/events.rb +3 -3
- data/lib/rage/events/subscriber.rb +36 -25
- data/lib/rage/fiber.rb +33 -31
- data/lib/rage/fiber_scheduler.rb +6 -2
- data/lib/rage/logger/logger.rb +7 -1
- data/lib/rage/middleware/body_finalizer.rb +14 -0
- data/lib/rage/response.rb +10 -5
- data/lib/rage/rspec.rb +17 -17
- data/lib/rage/setup.rb +2 -2
- data/lib/rage/telemetry/handler.rb +131 -0
- data/lib/rage/telemetry/spans/await_fiber.rb +50 -0
- data/lib/rage/telemetry/spans/broadcast_cable_stream.rb +50 -0
- data/lib/rage/telemetry/spans/create_websocket_connection.rb +50 -0
- data/lib/rage/telemetry/spans/dispatch_fiber.rb +48 -0
- data/lib/rage/telemetry/spans/enqueue_deferred_task.rb +52 -0
- data/lib/rage/telemetry/spans/process_cable_action.rb +56 -0
- data/lib/rage/telemetry/spans/process_cable_connection.rb +56 -0
- data/lib/rage/telemetry/spans/process_controller_action.rb +56 -0
- data/lib/rage/telemetry/spans/process_deferred_task.rb +54 -0
- data/lib/rage/telemetry/spans/process_event_subscriber.rb +54 -0
- data/lib/rage/telemetry/spans/publish_event.rb +54 -0
- data/lib/rage/telemetry/spans/spawn_fiber.rb +50 -0
- data/lib/rage/telemetry/telemetry.rb +121 -0
- data/lib/rage/telemetry/tracer.rb +97 -0
- data/lib/rage/version.rb +1 -1
- data/rage.gemspec +4 -3
- metadata +38 -5
data/lib/rage/deferred/task.rb
CHANGED
|
@@ -37,29 +37,55 @@ module Rage::Deferred::Task
|
|
|
37
37
|
BACKOFF_INTERVAL = 5
|
|
38
38
|
private_constant :BACKOFF_INTERVAL
|
|
39
39
|
|
|
40
|
+
# @private
|
|
41
|
+
CONTEXT_KEY = :__rage_deferred_execution_context
|
|
42
|
+
|
|
40
43
|
def perform
|
|
41
44
|
end
|
|
42
45
|
|
|
46
|
+
# Access metadata for the current task execution.
|
|
47
|
+
# @return [Rage::Deferred::Metadata] the metadata object for the current task execution
|
|
48
|
+
# @example
|
|
49
|
+
# class MyTask
|
|
50
|
+
# include Rage::Deferred::Task
|
|
51
|
+
#
|
|
52
|
+
# def perform
|
|
53
|
+
# puts meta.retries
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
def meta
|
|
57
|
+
Rage::Deferred::Metadata
|
|
58
|
+
end
|
|
59
|
+
|
|
43
60
|
# @private
|
|
44
61
|
def __perform(context)
|
|
45
|
-
args = Rage::Deferred::Context.get_args(context)
|
|
46
|
-
kwargs = Rage::Deferred::Context.get_kwargs(context)
|
|
47
|
-
attempts = Rage::Deferred::Context.get_attempts(context)
|
|
48
|
-
|
|
49
62
|
restore_log_info(context)
|
|
50
63
|
|
|
64
|
+
attempts = Rage::Deferred::Context.get_attempts(context)
|
|
51
65
|
task_log_context = { task: self.class.name }
|
|
52
66
|
task_log_context[:attempt] = attempts + 1 if attempts
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
Fiber[CONTEXT_KEY] = context
|
|
69
|
+
|
|
70
|
+
Rage::Telemetry.tracer.span_deferred_task_process(task: self, context:) do
|
|
71
|
+
Rage::Deferred.__middleware_chain.with_perform_middleware(context, task: self) do
|
|
72
|
+
Rage.logger.with_context(task_log_context) do
|
|
73
|
+
args = Rage::Deferred::Context.get_args(context)
|
|
74
|
+
kwargs = Rage::Deferred::Context.get_kwargs(context)
|
|
75
|
+
|
|
76
|
+
perform(*args, **kwargs)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
62
79
|
end
|
|
80
|
+
|
|
81
|
+
true
|
|
82
|
+
rescue Exception => e
|
|
83
|
+
unless respond_to?(:__deferred_suppress_exception_logging?, true) && __deferred_suppress_exception_logging?
|
|
84
|
+
Rage.logger.with_context(task_log_context) do
|
|
85
|
+
Rage.logger.error("Deferred task failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
false
|
|
63
89
|
end
|
|
64
90
|
|
|
65
91
|
private def restore_log_info(context)
|
|
@@ -80,11 +106,13 @@ module Rage::Deferred::Task
|
|
|
80
106
|
|
|
81
107
|
module ClassMethods
|
|
82
108
|
def enqueue(*args, delay: nil, delay_until: nil, **kwargs)
|
|
83
|
-
Rage::Deferred.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
delay_until:
|
|
87
|
-
|
|
109
|
+
context = Rage::Deferred::Context.build(self, args, kwargs)
|
|
110
|
+
|
|
111
|
+
Rage::Telemetry.tracer.span_deferred_task_enqueue(task_class: self, context:) do
|
|
112
|
+
Rage::Deferred.__middleware_chain.with_enqueue_middleware(context, delay:, delay_until:) do
|
|
113
|
+
Rage::Deferred.__queue.enqueue(context, delay:, delay_until:)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
88
116
|
|
|
89
117
|
nil
|
|
90
118
|
end
|
data/lib/rage/events/events.rb
CHANGED
|
@@ -39,7 +39,9 @@ module Rage::Events
|
|
|
39
39
|
# Rage::Events.publish(MyEvent.new, context: { published_at: Time.now })
|
|
40
40
|
def self.publish(event, context: nil)
|
|
41
41
|
handler = __event_handlers[event.class] || __build_event_handler(event.class)
|
|
42
|
-
|
|
42
|
+
Rage::Telemetry.tracer.span_events_event_publish(event:, context:) do
|
|
43
|
+
handler.call(event, context)
|
|
44
|
+
end
|
|
43
45
|
|
|
44
46
|
nil
|
|
45
47
|
end
|
|
@@ -71,8 +73,6 @@ module Rage::Events
|
|
|
71
73
|
# @private
|
|
72
74
|
def self.__build_event_handler(event_class)
|
|
73
75
|
subscriber_calls = __get_subscribers(event_class).map do |subscriber_class|
|
|
74
|
-
subscriber_class.__register_rescue_handlers
|
|
75
|
-
|
|
76
76
|
arguments = "event"
|
|
77
77
|
|
|
78
78
|
context_type, _ = subscriber_class.instance_method(:call).parameters.find do |param_type, param_name|
|
|
@@ -79,16 +79,28 @@ module Rage::Events::Subscriber
|
|
|
79
79
|
|
|
80
80
|
# @private
|
|
81
81
|
def __call(event, context: nil)
|
|
82
|
-
Rage.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if e
|
|
88
|
-
Rage.logger.error("Subscriber failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
89
|
-
raise Rage::Deferred::TaskFailed if self.class.__is_deferred
|
|
82
|
+
Rage::Telemetry.tracer.span_events_subscriber_process(event:, context:, subscriber: self) do
|
|
83
|
+
Rage.logger.with_context(self.class.__log_context) do
|
|
84
|
+
with_exception_handler do
|
|
85
|
+
context.nil? ? call(event) : call(event, context: context.freeze)
|
|
86
|
+
end
|
|
90
87
|
end
|
|
91
88
|
end
|
|
89
|
+
rescue Exception => e
|
|
90
|
+
Rage.logger.with_context(self.class.__log_context) do
|
|
91
|
+
Rage.logger.error("Subscriber failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
92
|
+
end
|
|
93
|
+
raise e if self.class.__is_deferred
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def __deferred_suppress_exception_logging?
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def with_exception_handler
|
|
103
|
+
yield
|
|
92
104
|
end
|
|
93
105
|
|
|
94
106
|
module ClassMethods
|
|
@@ -136,6 +148,14 @@ module Rage::Events::Subscriber
|
|
|
136
148
|
end
|
|
137
149
|
|
|
138
150
|
@__rescue_handlers.unshift([klasses, with])
|
|
151
|
+
|
|
152
|
+
rebuild_exception_handler!
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Check if the subscriber is executed in the background.
|
|
156
|
+
# @return [Boolean] `true` if the subscriber is deferred, `false` otherwise
|
|
157
|
+
def deferred?
|
|
158
|
+
@__is_deferred
|
|
139
159
|
end
|
|
140
160
|
|
|
141
161
|
# @private
|
|
@@ -144,29 +164,20 @@ module Rage::Events::Subscriber
|
|
|
144
164
|
klass.subscribe_to(*@__event_classes, deferred: @__is_deferred) if @__event_classes
|
|
145
165
|
end
|
|
146
166
|
|
|
147
|
-
|
|
148
|
-
def __register_rescue_handlers
|
|
149
|
-
return if method_defined?(:__run_rescue_handlers, false) || @__rescue_handlers.nil?
|
|
150
|
-
|
|
151
|
-
matcher_calls = @__rescue_handlers.map do |klasses, handler|
|
|
152
|
-
handler_call = instance_method(handler).arity == 0 ? handler : "#{handler}(exception)"
|
|
167
|
+
private
|
|
153
168
|
|
|
169
|
+
def rebuild_exception_handler!
|
|
170
|
+
rescue_calls = @__rescue_handlers.map do |klasses, handler|
|
|
154
171
|
<<~RUBY
|
|
155
|
-
|
|
156
|
-
#{
|
|
157
|
-
nil
|
|
172
|
+
rescue #{klasses.join(", ")} => e
|
|
173
|
+
method(:#{handler}).arity == 0 ? #{handler} : #{handler}(e)
|
|
158
174
|
RUBY
|
|
159
175
|
end
|
|
160
176
|
|
|
161
177
|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
#{
|
|
165
|
-
else
|
|
166
|
-
exception
|
|
167
|
-
end
|
|
168
|
-
rescue Exception => e
|
|
169
|
-
e
|
|
178
|
+
private def with_exception_handler
|
|
179
|
+
yield
|
|
180
|
+
#{rescue_calls.join("\n")}
|
|
170
181
|
end
|
|
171
182
|
RUBY
|
|
172
183
|
end
|
data/lib/rage/fiber.rb
CHANGED
|
@@ -158,43 +158,45 @@ class Fiber
|
|
|
158
158
|
f, fibers = Fiber.current, Array(fibers)
|
|
159
159
|
await_channel = f.__await_channel(true)
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
Rage::Telemetry.tracer.span_core_fiber_await(fibers:) do
|
|
162
|
+
# check which fibers are alive (i.e. have yielded) and which have errored out
|
|
163
|
+
i, err, num_wait_for = 0, nil, 0
|
|
164
|
+
while i < fibers.length
|
|
165
|
+
if fibers[i].alive?
|
|
166
|
+
num_wait_for += 1
|
|
167
|
+
else
|
|
168
|
+
err = fibers[i].__get_err
|
|
169
|
+
break if err
|
|
170
|
+
end
|
|
171
|
+
i += 1
|
|
169
172
|
end
|
|
170
|
-
i += 1
|
|
171
|
-
end
|
|
172
173
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
# raise if one of the fibers has errored out or return the result if none have yielded
|
|
175
|
+
if err
|
|
176
|
+
raise err
|
|
177
|
+
elsif num_wait_for == 0
|
|
178
|
+
return fibers.map!(&:__get_result)
|
|
179
|
+
end
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
# wait on async fibers; resume right away if one of the fibers errors out
|
|
182
|
+
Iodine.subscribe(await_channel) do |_, err|
|
|
183
|
+
if err == AWAIT_ERROR_MESSAGE
|
|
184
|
+
f.resume
|
|
185
|
+
else
|
|
186
|
+
num_wait_for -= 1
|
|
187
|
+
f.resume if num_wait_for == 0
|
|
188
|
+
end
|
|
187
189
|
end
|
|
188
|
-
end
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
Fiber.defer(-1)
|
|
192
|
+
Iodine.defer { Iodine.unsubscribe(await_channel) }
|
|
192
193
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
# if num_wait_for is not 0 means we exited prematurely because of an error
|
|
195
|
+
if num_wait_for > 0
|
|
196
|
+
raise fibers.find(&:__get_err).__get_err
|
|
197
|
+
else
|
|
198
|
+
fibers.map!(&:__get_result)
|
|
199
|
+
end
|
|
198
200
|
end
|
|
199
201
|
end
|
|
200
202
|
|
data/lib/rage/fiber_scheduler.rb
CHANGED
|
@@ -120,7 +120,9 @@ class Rage::FiberScheduler
|
|
|
120
120
|
# the fiber to wrap a request in
|
|
121
121
|
Fiber.new(blocking: false) do
|
|
122
122
|
Fiber.current.__set_id
|
|
123
|
-
|
|
123
|
+
Rage::Telemetry.tracer.span_core_fiber_dispatch do
|
|
124
|
+
Fiber.current.__set_result(block.call)
|
|
125
|
+
end
|
|
124
126
|
end
|
|
125
127
|
else
|
|
126
128
|
# the fiber was created in the user code
|
|
@@ -128,7 +130,9 @@ class Rage::FiberScheduler
|
|
|
128
130
|
|
|
129
131
|
Fiber.new(blocking: false) do
|
|
130
132
|
Thread.current[:rage_logger] = logger
|
|
131
|
-
|
|
133
|
+
Rage::Telemetry.tracer.span_core_fiber_spawn(parent:) do
|
|
134
|
+
Fiber.current.__set_result(block.call)
|
|
135
|
+
end
|
|
132
136
|
# send a message for `Fiber.await` to work
|
|
133
137
|
Iodine.publish(parent.__await_channel, "", Iodine::PubSub::PROCESS) if parent.alive?
|
|
134
138
|
rescue Exception => e
|
data/lib/rage/logger/logger.rb
CHANGED
|
@@ -209,7 +209,13 @@ class Rage::Logger
|
|
|
209
209
|
RUBY
|
|
210
210
|
elsif @external_logger.is_a?(External::Dynamic)
|
|
211
211
|
# a callable object is used as a logger
|
|
212
|
-
|
|
212
|
+
call_method = if @external_logger.wrapped.is_a?(Proc)
|
|
213
|
+
@external_logger.wrapped
|
|
214
|
+
else
|
|
215
|
+
@external_logger.wrapped.method(:call)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
parameters = Rage::Internal.build_arguments(call_method, {
|
|
213
219
|
severity: ":#{level_name}",
|
|
214
220
|
tags: "logger[:tags].freeze",
|
|
215
221
|
context: "logger[:context].freeze",
|
data/lib/rage/response.rb
CHANGED
|
@@ -8,21 +8,26 @@ class Rage::Response
|
|
|
8
8
|
LAST_MODIFIED_HEADER = "last-modified"
|
|
9
9
|
|
|
10
10
|
# @private
|
|
11
|
-
def initialize(
|
|
12
|
-
@
|
|
13
|
-
|
|
11
|
+
def initialize(controller)
|
|
12
|
+
@controller = controller
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the HTTP status code of the response.
|
|
16
|
+
# @return [Integer]
|
|
17
|
+
def status
|
|
18
|
+
@controller.__status
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
# Returns the content of the response as a string. This contains the contents of any calls to `render`.
|
|
17
22
|
# @return [String]
|
|
18
23
|
def body
|
|
19
|
-
@
|
|
24
|
+
@controller.__body[0] || ""
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
# Returns the headers for the response.
|
|
23
28
|
# @return [Hash]
|
|
24
29
|
def headers
|
|
25
|
-
@
|
|
30
|
+
@controller.__headers
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
# Returns ETag response header or +nil+ if it's empty.
|
data/lib/rage/rspec.rb
CHANGED
|
@@ -14,23 +14,6 @@ require_relative "#{Rage.root}/config/application"
|
|
|
14
14
|
# verify the environment
|
|
15
15
|
abort("The test suite is running in #{Rage.env} mode instead of 'test'!") unless Rage.env.test?
|
|
16
16
|
|
|
17
|
-
# mock fiber methods as RSpec tests don't run concurrently
|
|
18
|
-
class Fiber
|
|
19
|
-
def self.schedule(&block)
|
|
20
|
-
fiber = Fiber.new(blocking: true) do
|
|
21
|
-
Fiber.current.__set_id
|
|
22
|
-
Fiber.current.__set_result(block.call)
|
|
23
|
-
end
|
|
24
|
-
fiber.resume
|
|
25
|
-
|
|
26
|
-
fiber
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def self.await(fibers)
|
|
30
|
-
Array(fibers).map(&:__get_result)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
17
|
# define request helpers
|
|
35
18
|
module RageRequestHelpers
|
|
36
19
|
include Rack::Test::Methods
|
|
@@ -96,6 +79,23 @@ end
|
|
|
96
79
|
# include request helpers
|
|
97
80
|
RSpec.configure do |config|
|
|
98
81
|
config.include(RageRequestHelpers, type: :request)
|
|
82
|
+
|
|
83
|
+
# mock fiber methods as RSpec tests don't run concurrently
|
|
84
|
+
config.before do
|
|
85
|
+
allow(Fiber).to receive(:schedule) do |&block|
|
|
86
|
+
fiber = Fiber.new(blocking: true) do
|
|
87
|
+
Fiber.current.__set_id
|
|
88
|
+
Fiber.current.__set_result(block.call)
|
|
89
|
+
end
|
|
90
|
+
fiber.resume
|
|
91
|
+
|
|
92
|
+
fiber
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
allow(Fiber).to receive(:await) do |fibers|
|
|
96
|
+
Array(fibers).map(&:__get_result)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
# patch MockResponse class
|
data/lib/rage/setup.rb
CHANGED
|
@@ -14,9 +14,9 @@ require "rage/ext/setup"
|
|
|
14
14
|
# Load application classes
|
|
15
15
|
Rage.code_loader.setup
|
|
16
16
|
|
|
17
|
+
require_relative "#{Rage.root}/config/routes"
|
|
18
|
+
|
|
17
19
|
# Run after_initialize hooks
|
|
18
20
|
Rage.config.run_after_initialize!
|
|
19
21
|
|
|
20
|
-
require_relative "#{Rage.root}/config/routes"
|
|
21
|
-
|
|
22
22
|
Rage.config.internal.initialized!
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The class allows developers to define telemetry handlers that observe and react to specific span executions.
|
|
5
|
+
#
|
|
6
|
+
# Handlers are defined by subclassing `Rage::Telemetry::Handler` and using the `handle` class method
|
|
7
|
+
# to specify which spans to observe and which methods to invoke when those spans are executed.
|
|
8
|
+
#
|
|
9
|
+
# See {Rage::Telemetry::Spans} for a list of available spans and arguments passed to the handler methods.
|
|
10
|
+
#
|
|
11
|
+
# Each handler method is expected to call `yield` to pass control to the next handler in the stack or the framework's core logic.
|
|
12
|
+
# The call to `yield` returns an instance of {Rage::Telemetry::SpanResult} which contains information about the span execution.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
16
|
+
# handle "controller.action.process", with: :create_span
|
|
17
|
+
#
|
|
18
|
+
# def create_span(name:)
|
|
19
|
+
# MyObservabilitySDK.in_span(name) do
|
|
20
|
+
# yield
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
class Rage::Telemetry::Handler
|
|
26
|
+
class << self
|
|
27
|
+
# @private
|
|
28
|
+
attr_accessor :handlers_map
|
|
29
|
+
|
|
30
|
+
# Defines which spans the handler will observe and which method to invoke for those spans.
|
|
31
|
+
#
|
|
32
|
+
# @param span_ids [Array<String>] one or more span IDs to observe; supports wildcards (`*`) to match multiple spans
|
|
33
|
+
# @param with [Symbol] the method name to invoke when the specified spans are executed
|
|
34
|
+
# @param except [String, Array<String>, nil] optional list of span IDs to exclude from observation; supports wildcards (`*`) to match multiple spans
|
|
35
|
+
# @raise [ArgumentError] if any specified span ID is unknown or if no spans match a wildcard ID
|
|
36
|
+
#
|
|
37
|
+
# @example Observe a specific span
|
|
38
|
+
# class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
39
|
+
# handle "controller.action.process", with: :my_handler_method
|
|
40
|
+
#
|
|
41
|
+
# def my_handler_method
|
|
42
|
+
# # ...
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# @example Observe multiple spans with wildcards
|
|
47
|
+
# class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
48
|
+
# handle "cable.*", with: :my_handler_method
|
|
49
|
+
#
|
|
50
|
+
# def my_handler_method
|
|
51
|
+
# # ...
|
|
52
|
+
# end
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# @example Observe all spans except specific ones
|
|
56
|
+
# class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
57
|
+
# handle "*", except: "core.fiber.dispatch", with: :my_handler_method
|
|
58
|
+
#
|
|
59
|
+
# def my_handler_method
|
|
60
|
+
# # ...
|
|
61
|
+
# end
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
def handle(*span_ids, with:, except: nil)
|
|
65
|
+
resolved_span_ids = resolve_span_ids(span_ids)
|
|
66
|
+
|
|
67
|
+
if except
|
|
68
|
+
resolved_span_ids -= resolve_span_ids(Array(except))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if @handlers_map.nil?
|
|
72
|
+
@handlers_map = {}
|
|
73
|
+
elsif @handlers_map.frozen?
|
|
74
|
+
@handlers_map = @handlers_map.transform_values(&:dup)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
resolved_span_ids.each do |span_id|
|
|
78
|
+
@handlers_map[span_id] ||= Set.new
|
|
79
|
+
@handlers_map[span_id] << with
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @private
|
|
84
|
+
def inherited(klass)
|
|
85
|
+
klass.handlers_map = @handlers_map.freeze
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def resolve_span_ids(span_ids)
|
|
91
|
+
all_span_ids = Rage::Telemetry.__registry.keys
|
|
92
|
+
return all_span_ids if span_ids.include?("*")
|
|
93
|
+
|
|
94
|
+
exact_span_ids, wildcard_span_ids = [], []
|
|
95
|
+
|
|
96
|
+
# separate span IDs based on whether they contain wildcards
|
|
97
|
+
span_ids.each do |span_id|
|
|
98
|
+
if span_id.include?("*")
|
|
99
|
+
wildcard_span_ids << span_id
|
|
100
|
+
else
|
|
101
|
+
exact_span_ids << span_id
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# validate exact span IDs
|
|
106
|
+
resolved_span_ids = []
|
|
107
|
+
|
|
108
|
+
exact_span_ids.each do |span_id|
|
|
109
|
+
unless all_span_ids.include?(span_id)
|
|
110
|
+
raise ArgumentError, "Unknown span ID '#{span_id}'"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
resolved_span_ids << span_id
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# validate and resolve wildcard span IDs
|
|
117
|
+
wildcard_span_ids.each do |span_id|
|
|
118
|
+
matcher = Regexp.new(span_id.gsub("*", "\\w+").gsub(".", "\\."))
|
|
119
|
+
matched_span_ids = all_span_ids.select { |id| id.match?(matcher) }
|
|
120
|
+
|
|
121
|
+
unless matched_span_ids.any?
|
|
122
|
+
raise ArgumentError, "No spans match the wildcard ID '#{span_id}'"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
resolved_span_ids += matched_span_ids
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
resolved_span_ids
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The **core.fiber.await** span wraps the processing of the {Fiber.await} call.
|
|
5
|
+
#
|
|
6
|
+
# This span is started when a fiber begins awaiting other fibers, and ends when all awaited fibers have completed.
|
|
7
|
+
# See {handle handle} for the list of arguments passed to handler methods.
|
|
8
|
+
#
|
|
9
|
+
# @see Rage::Telemetry::Handler Rage::Telemetry::Handler
|
|
10
|
+
#
|
|
11
|
+
class Rage::Telemetry::Spans::AwaitFiber
|
|
12
|
+
class << self
|
|
13
|
+
# @private
|
|
14
|
+
def id
|
|
15
|
+
"core.fiber.await"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @private
|
|
19
|
+
def span_parameters
|
|
20
|
+
%w[fibers:]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @private
|
|
24
|
+
def handler_arguments
|
|
25
|
+
{
|
|
26
|
+
name: '"Fiber.await"',
|
|
27
|
+
fibers: "Array(fibers)"
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @!parse [ruby]
|
|
32
|
+
# # @param id ["core.fiber.await"] ID of the span
|
|
33
|
+
# # @param name ["Fiber.await"] human-readable name of the operation
|
|
34
|
+
# # @param fibers [Array<Fiber>] the fibers the current fiber is awaiting
|
|
35
|
+
# # @yieldreturn [Rage::Telemetry::SpanResult]
|
|
36
|
+
# #
|
|
37
|
+
# # @example
|
|
38
|
+
# # class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
39
|
+
# # handle "core.fiber.await", with: :my_handler
|
|
40
|
+
# #
|
|
41
|
+
# # def my_handler(id:, name:, fibers:)
|
|
42
|
+
# # yield
|
|
43
|
+
# # end
|
|
44
|
+
# # end
|
|
45
|
+
# # @note Rage automatically detects which parameters your handler method accepts and only passes those parameters.
|
|
46
|
+
# # You can omit any of the parameters described here.
|
|
47
|
+
# def handle(id:, name:, fibers:)
|
|
48
|
+
# end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# The **cable.stream.broadcast** span wraps the process of broadcasting a message to a `Rage::Cable` stream.
|
|
5
|
+
#
|
|
6
|
+
# This span is started when {Rage::Cable.broadcast Rage::Cable.broadcast} is called, and ends when the broadcast operation is complete.
|
|
7
|
+
# See {handle handle} for the list of arguments passed to handler methods.
|
|
8
|
+
#
|
|
9
|
+
# @see Rage::Telemetry::Handler Rage::Telemetry::Handler
|
|
10
|
+
#
|
|
11
|
+
class Rage::Telemetry::Spans::BroadcastCableStream
|
|
12
|
+
class << self
|
|
13
|
+
# @private
|
|
14
|
+
def id
|
|
15
|
+
"cable.stream.broadcast"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @private
|
|
19
|
+
def span_parameters
|
|
20
|
+
%w[stream:]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @private
|
|
24
|
+
def handler_arguments
|
|
25
|
+
{
|
|
26
|
+
name: '"Rage::Cable.broadcast"',
|
|
27
|
+
stream: "stream"
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @!parse [ruby]
|
|
32
|
+
# # @param id ["cable.stream.broadcast"] ID of the span
|
|
33
|
+
# # @param name ["Rage::Cable.broadcast"] human-readable name of the operation
|
|
34
|
+
# # @param stream [String] the name of the stream to which the message is being broadcasted
|
|
35
|
+
# # @yieldreturn [Rage::Telemetry::SpanResult]
|
|
36
|
+
# #
|
|
37
|
+
# # @example
|
|
38
|
+
# # class MyTelemetryHandler < Rage::Telemetry::Handler
|
|
39
|
+
# # handle "cable.stream.broadcast", with: :my_handler
|
|
40
|
+
# #
|
|
41
|
+
# # def my_handler(id:, name:, stream:)
|
|
42
|
+
# # yield
|
|
43
|
+
# # end
|
|
44
|
+
# # end
|
|
45
|
+
# # @note Rage automatically detects which parameters your handler method accepts and only passes those parameters.
|
|
46
|
+
# # You can omit any of the parameters described here.
|
|
47
|
+
# def handle(id:, name:, stream:)
|
|
48
|
+
# end
|
|
49
|
+
end
|
|
50
|
+
end
|