Class: Datadog::Span
- Inherits:
-
Object
- Object
- Datadog::Span
- Includes:
- Analytics::Span, ForcedTracing::Span
- Defined in:
- lib/ddtrace/span.rb
Overview
Represents a logical unit of work in the system. Each trace consists of one or more spans. Each span consists of a start time and a duration. For example, a span can describe the time spent on a distributed call on a separate machine, or the time spent in a small component within a larger operation. Spans can be nested within each other, and in those instances will have a parent-child relationship.
rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: ResourceContainer
Constant Summary collapse
- RUBY_MAX_ID =
The max value for a Span identifier. Span and trace identifiers should be strictly positive and strictly inferior to this limit.
Limited to 2<<62-1 positive integers, as Ruby is able to represent such numbers “inline”, inside a
VALUEscalar, thus not requiring memory allocation.The range of IDs also has to consider portability across different languages and platforms.
(1 << 62) - 1
- EXTERNAL_MAX_ID =
While we only generate 63-bit integers due to limitations in other languages, we support parsing 64-bit integers for distributed tracing since an upstream system may generate one
1 << 64
- NUMERIC_TAG_SIZE_RANGE =
This limit is for numeric tags because uint64 could end up rounded.
(-1 << 53..1 << 53).freeze
- ENSURE_AGENT_TAGS =
Some associated values should always be sent as Tags, never as Metrics, regardless if their value is numeric or not. The Datadog agent will look for these values only as Tags, not Metrics.
{ Ext::DistributedTracing::ORIGIN_KEY => true, Ext::Environment::TAG_VERSION => true, Ext::HTTP::STATUS_CODE => true, Ext::NET::TAG_HOSTNAME => true }.freeze
Instance Attribute Summary collapse
-
#context ⇒ Object
Returns the value of attribute context.
- #duration ⇒ Object
-
#end_time ⇒ Object
Returns the value of attribute end_time.
-
#name ⇒ Object
Returns the value of attribute name.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#parent_id ⇒ Object
Returns the value of attribute parent_id.
-
#resource_container ⇒ Object
readonly
Returns the value of attribute resource_container.
-
#sampled ⇒ Object
Returns the value of attribute sampled.
-
#service ⇒ Object
Returns the value of attribute service.
-
#span_id ⇒ Object
Returns the value of attribute span_id.
-
#span_type ⇒ Object
Returns the value of attribute span_type.
-
#start_time ⇒ Object
Returns the value of attribute start_time.
-
#status ⇒ Object
Returns the value of attribute status.
-
#trace_id ⇒ Object
Returns the value of attribute trace_id.
-
#tracer ⇒ Object
Returns the value of attribute tracer.
Instance Method Summary collapse
- #allocations ⇒ Object
-
#clear_metric(key) ⇒ Object
This method removes a metric for the given key.
-
#clear_tag(key) ⇒ Object
This method removes a tag for the given key.
-
#finish(finish_time = nil) ⇒ Object
Mark the span finished at the current time and submit it.
-
#finished? ⇒ Boolean
Return whether the duration is finished or not.
-
#get_metric(key) ⇒ Object
Return the metric with the given key, nil if it doesn’t exist.
-
#get_tag(key) ⇒ Object
Return the tag with the given key, nil if it doesn’t exist.
-
#initialize(tracer, name, options = {}) ⇒ Span
constructor
Create a new span linked to the given tracer.
-
#pretty_print(q) ⇒ Object
Return a human readable version of the span.
- #resource ⇒ Object
- #resource=(resource) ⇒ Object
-
#set_error(e) ⇒ Object
Mark the span with the given error.
-
#set_metric(key, value) ⇒ Object
This method sets a tag with a floating point value for the given key.
-
#set_parent(parent) ⇒ Object
DEPRECATED: remove this function in the next release, replaced by “parent=“.
-
#set_tag(key, value = nil) ⇒ Object
Set the given key / value tag pair on the span.
-
#set_tags(tags) ⇒ Object
Sets tags from given hash, for each key in hash it sets the tag with that key and associated value from the hash.
-
#start(start_time = nil) ⇒ Object
Mark the span started at the current time.
-
#started? ⇒ Boolean
Return whether the duration is started or not.
-
#to_hash ⇒ Object
Return the hash representation of the current span.
-
#to_json(*args) ⇒ Object
JSON serializer interface.
-
#to_msgpack(packer = nil) ⇒ Object
MessagePack serializer interface.
-
#to_s ⇒ Object
Return a string representation of the span.
Constructor Details
#initialize(tracer, name, options = {}) ⇒ Span
Create a new span linked to the given tracer. Call the Tracer method start_span() and then finish() once the tracer operation is over.
-
service: the service name for this span -
resource: the resource this span refers, ornameif it’s missing -
span_type: the type of the span (such ashttp,dband so on) -
parent_id: the identifier of the parent span -
trace_id: the identifier of the root span for this trace -
context: the context of the span
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 113 114 |
# File 'lib/ddtrace/span.rb', line 79 def initialize(tracer, name, = {}) @tracer = tracer @name = name @service = .fetch(:service, nil) @resource_container = ResourceContainer.new(.fetch(:resource, name)) @span_type = .fetch(:span_type, nil) @span_id = Datadog::Utils.next_id @parent_id = .fetch(:parent_id, 0) @trace_id = .fetch(:trace_id, Datadog::Utils.next_id) @context = .fetch(:context, nil) @meta = {} @metrics = {} @status = 0 @parent = nil @sampled = true @allocation_count_start = now_allocations @allocation_count_finish = @allocation_count_start # 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 end |
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def context @context end |
#duration ⇒ Object
409 410 411 412 413 414 415 |
# File 'lib/ddtrace/span.rb', line 409 def duration if @duration_end.nil? || @duration_start.nil? @end_time - @start_time else @duration_end - @duration_start end end |
#end_time ⇒ Object
Returns the value of attribute end_time.
66 67 68 |
# File 'lib/ddtrace/span.rb', line 66 def end_time @end_time end |
#name ⇒ Object
Returns the value of attribute name.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def name @name end |
#parent ⇒ Object
Returns the value of attribute parent.
66 67 68 |
# File 'lib/ddtrace/span.rb', line 66 def parent @parent end |
#parent_id ⇒ Object
Returns the value of attribute parent_id.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def parent_id @parent_id end |
#resource_container ⇒ Object (readonly)
Returns the value of attribute resource_container.
66 67 68 |
# File 'lib/ddtrace/span.rb', line 66 def resource_container @resource_container end |
#sampled ⇒ Object
Returns the value of attribute sampled.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def sampled @sampled end |
#service ⇒ Object
Returns the value of attribute service.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def service @service end |
#span_id ⇒ Object
Returns the value of attribute span_id.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def span_id @span_id end |
#span_type ⇒ Object
Returns the value of attribute span_type.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def span_type @span_type end |
#start_time ⇒ Object
Returns the value of attribute start_time.
66 67 68 |
# File 'lib/ddtrace/span.rb', line 66 def start_time @start_time end |
#status ⇒ Object
Returns the value of attribute status.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def status @status end |
#trace_id ⇒ Object
Returns the value of attribute trace_id.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def trace_id @trace_id end |
#tracer ⇒ Object
Returns the value of attribute tracer.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def tracer @tracer end |
Instance Method Details
#allocations ⇒ Object
280 281 282 |
# File 'lib/ddtrace/span.rb', line 280 def allocations @allocation_count_finish - @allocation_count_start end |
#clear_metric(key) ⇒ Object
This method removes a metric for the given key. It acts like #remove_tag.
175 176 177 |
# File 'lib/ddtrace/span.rb', line 175 def clear_metric(key) @metrics.delete(key) end |
#clear_tag(key) ⇒ Object
This method removes a tag for the given key.
152 153 154 |
# File 'lib/ddtrace/span.rb', line 152 def clear_tag(key) @meta.delete(key) end |
#finish(finish_time = nil) ⇒ Object
Mark the span finished at the current time and submit it.
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/ddtrace/span.rb', line 217 def finish(finish_time = nil) # A span should not be finished twice. Note that this is not thread-safe, # finish is called from multiple threads, a given span might be finished # 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 finished? @allocation_count_finish = now_allocations now = 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(finish_time || now) unless started? @end_time = finish_time || now @duration_end = finish_time.nil? ? duration_marker : nil # Finish does not really do anything if the span is not bound to a tracer and a context. return self if @tracer.nil? || @context.nil? # spans without a service would be dropped, so here we provide a default. # This should really never happen with integrations in contrib, as a default # service is always set. It's only for custom instrumentation. @service ||= (@tracer && @tracer.default_service) begin @context.close_span(self) @tracer.record(self) rescue StandardError => e Datadog.logger.debug("error recording finished trace: #{e}") Datadog.health_metrics.error_span_finish(1, tags: ["error:#{e.class.name}"]) end self end |
#finished? ⇒ Boolean
Return whether the duration is finished or not.
405 406 407 |
# File 'lib/ddtrace/span.rb', line 405 def finished? !@end_time.nil? end |
#get_metric(key) ⇒ Object
Return the metric with the given key, nil if it doesn’t exist.
180 181 182 |
# File 'lib/ddtrace/span.rb', line 180 def get_metric(key) @metrics[key] || @meta[key] end |
#get_tag(key) ⇒ Object
Return the tag with the given key, nil if it doesn’t exist.
157 158 159 |
# File 'lib/ddtrace/span.rb', line 157 def get_tag(key) @meta[key] || @metrics[key] end |
#pretty_print(q) ⇒ Object
Return a human readable version of the span
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/ddtrace/span.rb', line 367 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: #{@span_id}\n" q.text "Parent ID: #{@parent_id}\n" q.text "Trace ID: #{@trace_id}\n" q.text "Type: #{@span_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 finished?}\n" q.text "Allocations: #{allocations}\n" q.group(2, 'Tags: [', "]\n") do q.breakable q.seplist @meta.each do |key, value| q.text "#{key} => #{value}" end end q.group(2, 'Metrics: [', ']') do q.breakable q.seplist @metrics.each do |key, value| q.text "#{key} => #{value}" end end end end |
#resource ⇒ Object
417 418 419 |
# File 'lib/ddtrace/span.rb', line 417 def resource @resource_container.latest end |
#resource=(resource) ⇒ Object
421 422 423 |
# File 'lib/ddtrace/span.rb', line 421 def resource=(resource) @resource_container.latest = resource end |
#set_error(e) ⇒ Object
Mark the span with the given error.
185 186 187 188 189 190 191 192 |
# File 'lib/ddtrace/span.rb', line 185 def set_error(e) e = Error.build_from(e) @status = Ext::Errors::STATUS set_tag(Ext::Errors::TYPE, e.type) unless e.type.empty? set_tag(Ext::Errors::MSG, e.) unless e..empty? set_tag(Ext::Errors::STACK, e.backtrace) unless e.backtrace.empty? end |
#set_metric(key, value) ⇒ Object
This method sets a tag with a floating point value for the given key. It acts like ‘set_tag()` and it simply add a tag without further processing.
163 164 165 166 167 168 169 170 171 172 |
# File 'lib/ddtrace/span.rb', line 163 def set_metric(key, value) # Keys must be unique between tags and metrics @meta.delete(key) # enforce that the value is a floating point number value = Float(value) @metrics[key] = value rescue StandardError => e Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e}") end |
#set_parent(parent) ⇒ Object
DEPRECATED: remove this function in the next release, replaced by “parent=“
260 261 262 |
# File 'lib/ddtrace/span.rb', line 260 def set_parent(parent) self.parent = parent end |
#set_tag(key, value = nil) ⇒ Object
Set the given key / value tag pair on the span. Keys and values must be strings. A valid example is:
span.set_tag('http.method', request.method)
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/ddtrace/span.rb', line 120 def set_tag(key, value = nil) # Keys must be unique between tags and metrics @metrics.delete(key) # DEV: This is necessary because the agent looks at `meta[key]`, not `metrics[key]`. value = value.to_s if ENSURE_AGENT_TAGS[key] # NOTE: Adding numeric tags as metrics is stop-gap support # for numeric typed tags. Eventually they will become # tags again. # Any numeric that is not an integer greater than max size is logged as a metric. # Everything else gets logged as a tag. if value.is_a?(Numeric) && !(value.is_a?(Integer) && !NUMERIC_TAG_SIZE_RANGE.cover?(value)) set_metric(key, value) else @meta[key] = value.to_s end rescue StandardError => e Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}") end |
#set_tags(tags) ⇒ Object
Sets tags from given hash, for each key in hash it sets the tag with that key and associated value from the hash. It is shortcut for set_tag. Keys and values of the hash must be strings. Note that nested hashes are not supported. A valid example is:
span.({ "http.method" => "GET", "user.id" => "234" })
147 148 149 |
# File 'lib/ddtrace/span.rb', line 147 def () .each { |k, v| set_tag(k, v) } end |
#start(start_time = nil) ⇒ Object
Mark the span started at the current time.
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/ddtrace/span.rb', line 195 def start(start_time = nil) # A span should not be started twice. However, this is existing # behavior and so we maintain it for backward compatibility for those # who are using async manual instrumentation that may rely on this @start_time = start_time || Utils::Time.now.utc @duration_start = start_time.nil? ? duration_marker : nil self end |
#started? ⇒ Boolean
Return whether the duration is started or not
400 401 402 |
# File 'lib/ddtrace/span.rb', line 400 def started? !@start_time.nil? end |
#to_hash ⇒ Object
Return the hash representation of the current span.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/ddtrace/span.rb', line 285 def to_hash h = { span_id: @span_id, parent_id: @parent_id, trace_id: @trace_id, name: @name, service: @service, resource: resource, type: @span_type, meta: @meta, metrics: @metrics, allocations: allocations, error: @status } if finished? h[:start] = start_time_nano h[:duration] = duration_nano end h end |
#to_json(*args) ⇒ Object
JSON serializer interface. Used by older version of the transport.
362 363 364 |
# File 'lib/ddtrace/span.rb', line 362 def to_json(*args) to_hash.to_json(*args) end |
#to_msgpack(packer = nil) ⇒ Object
MessagePack serializer interface. Making this object respond to #to_msgpack allows it to be automatically serialized by MessagePack.
This is more efficient than doing MessagePack.pack(span.to_hash) as we don’t have to create an intermediate Hash.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/ddtrace/span.rb', line 316 def to_msgpack(packer = nil) # As of 1.3.3, JRuby implementation doesn't pass an existing packer packer ||= MessagePack::Packer.new if finished? packer.write_map_header(13) # Set header with how many elements in the map packer.write('start') packer.write(start_time_nano) packer.write('duration') packer.write(duration_nano) else packer.write_map_header(11) # Set header with how many elements in the map end # DEV: We use strings as keys here, instead of symbols, as # DEV: MessagePack will ultimately convert them to strings. # DEV: By providing strings directly, we skip this indirection operation. packer.write('span_id') packer.write(@span_id) packer.write('parent_id') packer.write(@parent_id) packer.write('trace_id') packer.write(@trace_id) packer.write('name') packer.write(@name) packer.write('service') packer.write(@service) packer.write('resource') packer.write(resource) packer.write('type') packer.write(@span_type) packer.write('meta') packer.write(@meta) packer.write('metrics') packer.write(@metrics) packer.write('allocations') packer.write(allocations) packer.write('error') packer.write(@status) packer end |
#to_s ⇒ Object
Return a string representation of the span.
255 256 257 |
# File 'lib/ddtrace/span.rb', line 255 def to_s "Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})" end |