Class: Datadog::Tracing::SpanOperation
- Inherits:
-
Object
- Object
- Datadog::Tracing::SpanOperation
- Includes:
- Metadata
- Defined in:
- lib/datadog/tracing/span_operation.rb
Overview
Represents the act of taking a span measurement. It gives a Span a context which can be used to build a Span. When completed, it yields the Span.
Defined Under Namespace
Classes: AlreadyStartedError, Events
Instance Attribute Summary collapse
-
#end_time ⇒ Object
Span attributes NOTE: In the future, we should drop the me.
-
#id ⇒ Object
readonly
Span attributes NOTE: In the future, we should drop the me.
-
#links ⇒ Object
Returns the value of attribute links.
-
#logger ⇒ Object
readonly
Span attributes NOTE: In the future, we should drop the me.
-
#name ⇒ String
Operation name.
-
#parent_id ⇒ Object
readonly
Span attributes NOTE: In the future, we should drop the me.
-
#resource ⇒ String
Span resource.
-
#service ⇒ String
Service name.
-
#span_events ⇒ Object
Returns the value of attribute span_events.
-
#start_time ⇒ Object
Span attributes NOTE: In the future, we should drop the me.
-
#status ⇒ Object
Returns the value of attribute status.
-
#trace_id ⇒ Object
readonly
Span attributes NOTE: In the future, we should drop the me.
-
#type ⇒ String
Span type.
Instance Method Summary collapse
- #duration ⇒ Object
- #finish(end_time = nil) ⇒ Object
- #finished? ⇒ Boolean
- #get_collector_or_initialize ⇒ Object
-
#initialize(name, logger: Datadog.logger, events: nil, on_error: nil, parent_id: 0, resource: name, service: nil, start_time: nil, tags: nil, trace_id: nil, type: nil, links: nil, span_events: nil, id: nil) ⇒ SpanOperation
constructor
A new instance of SpanOperation.
- #measure ⇒ Object
-
#pretty_print(q) ⇒ Object
Return a human readable version of the span.
-
#record_exception(exception, attributes: {}) ⇒ void
Record an exception during the execution of this span.
- #root? ⇒ Boolean
- #set_error(e) ⇒ Object
- #start(start_time = nil) ⇒ Object
-
#started? ⇒ Boolean
Return whether the duration is started or not.
-
#stop(stop_time = nil, exception: nil) ⇒ Object
Mark the span stopped at the current time.
-
#stopped? ⇒ Boolean
Return whether the duration is stopped or not.
-
#to_hash ⇒ Object
Return the hash representation of the current span.
-
#to_s ⇒ Object
Return a string representation of the span.
Methods included from Metadata
Constructor Details
#initialize(name, logger: Datadog.logger, events: nil, on_error: nil, parent_id: 0, resource: name, service: nil, start_time: nil, tags: nil, trace_id: nil, type: nil, links: nil, span_events: nil, id: nil) ⇒ SpanOperation
Returns a new instance of SpanOperation.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/datadog/tracing/span_operation.rb', line 44 def initialize( name, logger: Datadog.logger, events: nil, on_error: nil, parent_id: 0, resource: name, service: nil, start_time: nil, tags: nil, trace_id: nil, type: nil, links: nil, span_events: nil, id: nil ) @logger = logger # Ensure dynamically created strings are UTF-8 encoded. # # All strings created in Ruby land are UTF-8. The only sources of non-UTF-8 string are: # * Strings explicitly encoded as non-UTF-8. # * Some natively created string, although most natively created strings are UTF-8. self.name = name self.service = service self.type = type self.resource = resource @id = id.nil? ? Tracing::Utils.next_id : id @parent_id = parent_id || 0 @trace_id = trace_id || Tracing::Utils::TraceId.next_id @status = 0 # stores array of span links @links = links || [] # stores array of span events @span_events = span_events || [] # start_time and end_time track wall clock. In Ruby, wall clock # has less accuracy than monotonic clock, so if possible we look to only use wall clock # to measure duration when a time is supplied by the user, or if monotonic clock # is unsupported. @start_time = nil @end_time = nil # duration_start and duration_end track monotonic clock, and may remain nil in cases where it # is known that we have to use wall clock to measure duration. @duration_start = nil @duration_end = nil # Set tags if provided. () if # Some other SpanOperation-specific behavior @events = events || Events.new(logger: logger) @span = nil if on_error.nil? # Nothing, default error handler is already set up. elsif on_error.is_a?(Proc) # Subscribe :on_error event @events.on_error.wrap_default(&on_error) else logger.warn("on_error argument to SpanOperation ignored because is not a Proc: #{on_error}") end # Start the span with start time, if given. start(start_time) if start_time end |
Instance Attribute Details
#end_time ⇒ Object
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def end_time @end_time end |
#id ⇒ Object (readonly)
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def id @id end |
#links ⇒ Object
Returns the value of attribute links.
42 43 44 |
# File 'lib/datadog/tracing/span_operation.rb', line 42 def links @links end |
#logger ⇒ Object (readonly)
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def logger @logger end |
#name ⇒ String
Operation name.
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def name @name end |
#parent_id ⇒ Object (readonly)
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def parent_id @parent_id end |
#resource ⇒ String
Span resource.
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def resource @resource end |
#service ⇒ String
Service name.
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def service @service end |
#span_events ⇒ Object
Returns the value of attribute span_events.
42 43 44 |
# File 'lib/datadog/tracing/span_operation.rb', line 42 def span_events @span_events end |
#start_time ⇒ Object
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def start_time @start_time end |
#status ⇒ Object
Returns the value of attribute status.
42 43 44 |
# File 'lib/datadog/tracing/span_operation.rb', line 42 def status @status end |
#trace_id ⇒ Object (readonly)
Span attributes NOTE: In the future, we should drop the me
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def trace_id @trace_id end |
#type ⇒ String
Span type.
30 31 32 |
# File 'lib/datadog/tracing/span_operation.rb', line 30 def type @type end |
Instance Method Details
#duration ⇒ Object
287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/datadog/tracing/span_operation.rb', line 287 def duration # Steep: https://github.com/soutaro/steep/issues/477 # @type ivar @duration_end: Time # @type ivar @duration_start: Time return @duration_end - @duration_start if @duration_start && @duration_end # Steep: https://github.com/soutaro/steep/issues/477 # @type ivar @end_time: Time # @type ivar @start_time: Time @end_time - @start_time if @start_time && @end_time end |
#finish(end_time = nil) ⇒ Object
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/datadog/tracing/span_operation.rb', line 266 def finish(end_time = nil) # Returned memoized span if already finished return span if finished? # Stop timing stop(end_time) # Build span # Memoize for performance reasons @span = build_span # Trigger after_finish event events.after_finish.publish(span, self) span end |
#finished? ⇒ Boolean
283 284 285 |
# File 'lib/datadog/tracing/span_operation.rb', line 283 def finished? !span.nil? end |
#get_collector_or_initialize ⇒ Object
144 145 146 |
# File 'lib/datadog/tracing/span_operation.rb', line 144 def get_collector_or_initialize @collector ||= yield end |
#measure ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/datadog/tracing/span_operation.rb', line 148 def measure raise ArgumentError, 'Must provide block to measure!' unless block_given? # TODO: Should we just invoke the block and skip tracing instead? raise AlreadyStartedError if started? return_value = nil begin # If span fails to start, don't prevent the operation from # running, to minimize impact on normal application function. begin start rescue => e logger.debug { "Failed to start span: #{e}" } ensure # We should yield to the provided block when possible, as this # block is application code that we don't want to hinder. # * We don't yield during a fatal error, as the application is likely trying to # end its execution (either due to a system error or graceful shutdown). # @type var e: Exception? # Steep: https://github.com/soutaro/steep/issues/919 return_value = yield(self) unless e && !e.is_a?(StandardError) # steep:ignore FallbackAny end # rubocop:disable Lint/RescueException # Here we really want to catch *any* exception, not only StandardError, # as we really have no clue of what is in the block, # and it is user code which should be executed no matter what. # It's not a problem since we re-raise it afterwards so for example a # SignalException::Interrupt would still bubble up. rescue Exception => e # Stop the span first, so timing is a more accurate. # If the span failed to start, timing may be inaccurate, # but this is not really a serious concern. stop(exception: e) # Trigger the on_error event events.on_error.publish(self, e) # We must finish the span to trigger callbacks, # and build the final span. finish raise e # Use an ensure block here to make sure the span closes. # NOTE: It's not sufficient to use "else": when a function # uses "return", it will skip "else". ensure # Finish the span # NOTE: If an error was raised, this "finish" might be redundant. finish unless finished? end # rubocop:enable Lint/RescueException return_value end |
#pretty_print(q) ⇒ Object
Return a human readable version of the span
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/datadog/tracing/span_operation.rb', line 358 def pretty_print(q) start_time = (self.start_time.to_f * 1e9).to_i end_time = (self.end_time.to_f * 1e9).to_i q.group 0 do q.breakable q.text "Name: #{@name}\n" q.text "Span ID: #{@id}\n" q.text "Parent ID: #{@parent_id}\n" q.text "Trace ID: #{@trace_id}\n" q.text "Type: #{@type}\n" q.text "Service: #{@service}\n" q.text "Resource: #{@resource}\n" q.text "Error: #{@status}\n" q.text "Start: #{start_time}\n" q.text "End: #{end_time}\n" q.text "Duration: #{duration.to_f if stopped?}\n" q.group(2, 'Tags: [', "]\n") do q.breakable q.seplist .each do |key, value| q.text "#{key} => #{value}" end end q.group(2, 'Metrics: [', "]\n") do q.breakable q.seplist metrics.each do |key, value| q.text "#{key} => #{value}" end end q.group(2, 'Metastruct: [', ']') do .pretty_print(q) end end end |
#record_exception(exception, attributes: {}) ⇒ void
This method returns an undefined value.
Record an exception during the execution of this span. Multiple exceptions can be recorded on a span.
314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/datadog/tracing/span_operation.rb', line 314 def record_exception(exception, attributes: {}) exc = Core::Error.build_from(exception) event_attributes = { 'exception.type' => exc.type, 'exception.message' => exc., 'exception.stacktrace' => exc.backtrace, } # Steep: Caused by wrong declaration, should be the same parameters as `merge` # https://github.com/ruby/rbs/blob/3d0fb3a7fdde60af7120e875fe3bd7237b5b6a88/core/hash.rbs#L1468 @span_events << SpanEvent.new('exception', attributes: event_attributes.merge!(attributes)) # steep:ignore ArgumentTypeMismatch end |
#root? ⇒ Boolean
252 253 254 |
# File 'lib/datadog/tracing/span_operation.rb', line 252 def root? parent_id == 0 end |
#set_error(e) ⇒ Object
299 300 301 302 |
# File 'lib/datadog/tracing/span_operation.rb', line 299 def set_error(e) @status = Metadata::Ext::Errors::STATUS (e) end |
#start(start_time = nil) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/datadog/tracing/span_operation.rb', line 204 def start(start_time = nil) # Span can only be started once return self if started? # Trigger before_start event events.before_start.publish(self) # Start the span @start_time = start_time || Core::Utils::Time.now.utc @duration_start = start_time.nil? ? duration_marker : nil self end |
#started? ⇒ Boolean
Return whether the duration is started or not
243 244 245 |
# File 'lib/datadog/tracing/span_operation.rb', line 243 def started? !@start_time.nil? end |
#stop(stop_time = nil, exception: nil) ⇒ Object
Mark the span stopped at the current time
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/datadog/tracing/span_operation.rb', line 219 def stop(stop_time = nil, exception: nil) # A span should not be stopped twice. Note that this is not thread-safe, # stop is called from multiple threads, a given span might be stopped # several times. Again, one should not do this, so this test is more a # fallback to avoid very bad things and protect you in most common cases. return if stopped? now = Core::Utils::Time.now.utc # Provide a default start_time if unset. # Using `now` here causes duration to be 0; this is expected # behavior when start_time is unknown. start(stop_time || now) unless started? @end_time = stop_time || now @duration_end = stop_time.nil? ? duration_marker : nil # Trigger after_stop event events.after_stop.publish(self, exception) self end |
#stopped? ⇒ Boolean
Return whether the duration is stopped or not.
248 249 250 |
# File 'lib/datadog/tracing/span_operation.rb', line 248 def stopped? !@end_time.nil? end |
#to_hash ⇒ Object
Return the hash representation of the current span.
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/datadog/tracing/span_operation.rb', line 334 def to_hash h = { error: @status, id: @id, meta: , metrics: metrics, metastruct: , name: @name, parent_id: @parent_id, resource: @resource, service: @service, trace_id: @trace_id, type: @type } if stopped? h[:start] = start_time_nano h[:duration] = duration_nano end h end |
#to_s ⇒ Object
Return a string representation of the span.
329 330 331 |
# File 'lib/datadog/tracing/span_operation.rb', line 329 def to_s "SpanOperation(name:#{@name},sid:#{@id},tid:#{@trace_id},pid:#{@parent_id})" end |