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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/Appraisals +19 -0
  4. data/CHANGELOG.md +15 -1
  5. data/CODE_OF_CONDUCT.md +13 -17
  6. data/Gemfile +3 -0
  7. data/README.md +60 -63
  8. data/Rakefile +14 -0
  9. data/lib/rage/all.rb +3 -0
  10. data/lib/rage/cable/cable.rb +11 -7
  11. data/lib/rage/cable/channel.rb +6 -1
  12. data/lib/rage/cable/connection.rb +4 -0
  13. data/lib/rage/cable/router.rb +14 -9
  14. data/lib/rage/configuration.rb +235 -21
  15. data/lib/rage/controller/api.rb +49 -44
  16. data/lib/rage/deferred/context.rb +30 -2
  17. data/lib/rage/deferred/deferred.rb +18 -6
  18. data/lib/rage/deferred/metadata.rb +39 -0
  19. data/lib/rage/deferred/middleware_chain.rb +67 -0
  20. data/lib/rage/deferred/task.rb +45 -17
  21. data/lib/rage/events/events.rb +3 -3
  22. data/lib/rage/events/subscriber.rb +36 -25
  23. data/lib/rage/fiber.rb +33 -31
  24. data/lib/rage/fiber_scheduler.rb +6 -2
  25. data/lib/rage/logger/logger.rb +7 -1
  26. data/lib/rage/middleware/body_finalizer.rb +14 -0
  27. data/lib/rage/response.rb +10 -5
  28. data/lib/rage/rspec.rb +17 -17
  29. data/lib/rage/setup.rb +2 -2
  30. data/lib/rage/telemetry/handler.rb +131 -0
  31. data/lib/rage/telemetry/spans/await_fiber.rb +50 -0
  32. data/lib/rage/telemetry/spans/broadcast_cable_stream.rb +50 -0
  33. data/lib/rage/telemetry/spans/create_websocket_connection.rb +50 -0
  34. data/lib/rage/telemetry/spans/dispatch_fiber.rb +48 -0
  35. data/lib/rage/telemetry/spans/enqueue_deferred_task.rb +52 -0
  36. data/lib/rage/telemetry/spans/process_cable_action.rb +56 -0
  37. data/lib/rage/telemetry/spans/process_cable_connection.rb +56 -0
  38. data/lib/rage/telemetry/spans/process_controller_action.rb +56 -0
  39. data/lib/rage/telemetry/spans/process_deferred_task.rb +54 -0
  40. data/lib/rage/telemetry/spans/process_event_subscriber.rb +54 -0
  41. data/lib/rage/telemetry/spans/publish_event.rb +54 -0
  42. data/lib/rage/telemetry/spans/spawn_fiber.rb +50 -0
  43. data/lib/rage/telemetry/telemetry.rb +121 -0
  44. data/lib/rage/telemetry/tracer.rb +97 -0
  45. data/lib/rage/version.rb +1 -1
  46. data/rage.gemspec +4 -3
  47. metadata +38 -5
@@ -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
- Rage.logger.with_context(task_log_context) do
55
- perform(*args, **kwargs)
56
- true
57
- rescue Rage::Deferred::TaskFailed
58
- false
59
- rescue Exception => e
60
- Rage.logger.error("Deferred task failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
61
- false
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.__queue.enqueue(
84
- Rage::Deferred::Context.build(self, args, kwargs),
85
- delay:,
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
@@ -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
- handler.call(event, context)
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.logger.with_context(self.class.__log_context) do
83
- context.nil? ? call(event) : call(event, context: context.freeze)
84
- rescue Exception => _e
85
- e = self.class.__rescue_handlers ? __run_rescue_handlers(_e) : _e
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
- # @private
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
- when #{klasses.join(", ")}
156
- #{handler_call}
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 __run_rescue_handlers(exception)
163
- case exception
164
- #{matcher_calls.join("\n")}
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
- # check which fibers are alive (i.e. have yielded) and which have errored out
162
- i, err, num_wait_for = 0, nil, 0
163
- while i < fibers.length
164
- if fibers[i].alive?
165
- num_wait_for += 1
166
- else
167
- err = fibers[i].__get_err
168
- break if err
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
- # raise if one of the fibers has errored out or return the result if none have yielded
174
- if err
175
- raise err
176
- elsif num_wait_for == 0
177
- return fibers.map!(&:__get_result)
178
- end
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
- # wait on async fibers; resume right away if one of the fibers errors out
181
- Iodine.subscribe(await_channel) do |_, err|
182
- if err == AWAIT_ERROR_MESSAGE
183
- f.resume
184
- else
185
- num_wait_for -= 1
186
- f.resume if num_wait_for == 0
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
- Fiber.defer(-1)
191
- Iodine.defer { Iodine.unsubscribe(await_channel) }
191
+ Fiber.defer(-1)
192
+ Iodine.defer { Iodine.unsubscribe(await_channel) }
192
193
 
193
- # if num_wait_for is not 0 means we exited prematurely because of an error
194
- if num_wait_for > 0
195
- raise fibers.find(&:__get_err).__get_err
196
- else
197
- fibers.map!(&:__get_result)
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
 
@@ -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
- Fiber.current.__set_result(block.call)
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
- Fiber.current.__set_result(block.call)
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
@@ -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
- parameters = Rage::Internal.build_arguments(@external_logger.wrapped, {
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",
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Rage::BodyFinalizer
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ response = @app.call(env)
10
+ response[2].close
11
+
12
+ response
13
+ end
14
+ end
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(headers, body)
12
- @headers = headers
13
- @body = body
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
- @body[0] || ""
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
- @headers
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