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
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 +VALUE+ scalar, 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 ⇒ Object
Returns the value of attribute resource.
-
#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.
-
#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, or +name+ if it's missing. +nil+ can be used as a placeholder, when the resource value is not yet known at +#initialize+ time.
- +span_type+: the type of the span (such as +http+, +db+ and 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
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 |
# File 'lib/ddtrace/span.rb', line 75 def initialize(tracer, name, = {}) @tracer = tracer @name = name @service = .fetch(:service, nil) @resource = .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.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def context @context end |
#duration ⇒ Object
405 406 407 408 409 410 411 |
# File 'lib/ddtrace/span.rb', line 405 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.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def end_time @end_time end |
#name ⇒ Object
Returns the value of attribute name.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def name @name end |
#parent ⇒ Object
Returns the value of attribute parent.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def parent @parent end |
#parent_id ⇒ Object
Returns the value of attribute parent_id.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def parent_id @parent_id end |
#resource ⇒ Object
Returns the value of attribute resource.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def resource @resource end |
#resource_container ⇒ Object (readonly)
Returns the value of attribute resource_container.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def resource_container @resource_container end |
#sampled ⇒ Object
Returns the value of attribute sampled.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def sampled @sampled end |
#service ⇒ Object
Returns the value of attribute service.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def service @service end |
#span_id ⇒ Object
Returns the value of attribute span_id.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def span_id @span_id end |
#span_type ⇒ Object
Returns the value of attribute span_type.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def span_type @span_type end |
#start_time ⇒ Object
Returns the value of attribute start_time.
61 62 63 |
# File 'lib/ddtrace/span.rb', line 61 def start_time @start_time end |
#status ⇒ Object
Returns the value of attribute status.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def status @status end |
#trace_id ⇒ Object
Returns the value of attribute trace_id.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def trace_id @trace_id end |
#tracer ⇒ Object
Returns the value of attribute tracer.
56 57 58 |
# File 'lib/ddtrace/span.rb', line 56 def tracer @tracer end |
Instance Method Details
#allocations ⇒ Object
276 277 278 |
# File 'lib/ddtrace/span.rb', line 276 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.
171 172 173 |
# File 'lib/ddtrace/span.rb', line 171 def clear_metric(key) @metrics.delete(key) end |
#clear_tag(key) ⇒ Object
This method removes a tag for the given key.
148 149 150 |
# File 'lib/ddtrace/span.rb', line 148 def clear_tag(key) @meta.delete(key) end |
#finish(finish_time = nil) ⇒ Object
Mark the span finished at the current time and submit it.
213 214 215 216 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 |
# File 'lib/ddtrace/span.rb', line 213 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.
401 402 403 |
# File 'lib/ddtrace/span.rb', line 401 def finished? !@end_time.nil? end |
#get_metric(key) ⇒ Object
Return the metric with the given key, nil if it doesn't exist.
176 177 178 |
# File 'lib/ddtrace/span.rb', line 176 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.
153 154 155 |
# File 'lib/ddtrace/span.rb', line 153 def get_tag(key) @meta[key] || @metrics[key] end |
#pretty_print(q) ⇒ Object
Return a human readable version of the span
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 391 392 393 |
# File 'lib/ddtrace/span.rb', line 363 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 |
#set_error(e) ⇒ Object
Mark the span with the given error.
181 182 183 184 185 186 187 188 |
# File 'lib/ddtrace/span.rb', line 181 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.
159 160 161 162 163 164 165 166 167 168 |
# File 'lib/ddtrace/span.rb', line 159 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=
256 257 258 |
# File 'lib/ddtrace/span.rb', line 256 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)
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/ddtrace/span.rb', line 116 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.set_tags({ "http.method" => "GET", "user.id" => "234" })
143 144 145 |
# File 'lib/ddtrace/span.rb', line 143 def () .each { |k, v| set_tag(k, v) } end |
#start(start_time = nil) ⇒ Object
Mark the span started at the current time.
191 192 193 194 195 196 197 198 199 200 |
# File 'lib/ddtrace/span.rb', line 191 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
396 397 398 |
# File 'lib/ddtrace/span.rb', line 396 def started? !@start_time.nil? end |
#to_hash ⇒ Object
Return the hash representation of the current span.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/ddtrace/span.rb', line 281 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.
358 359 360 |
# File 'lib/ddtrace/span.rb', line 358 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.
312 313 314 315 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 |
# File 'lib/ddtrace/span.rb', line 312 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.
251 252 253 |
# File 'lib/ddtrace/span.rb', line 251 def to_s "Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})" end |