Class: Datadog::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/ddtrace/context.rb

Overview

Context is used to keep track of a hierarchy of spans for the current execution flow. During each logical execution, the same Context is used to represent a single logical trace, even if the trace is built asynchronously.

A single code execution may use multiple Context if part of the execution must not be related to the current tracing. As example, a delayed job may compose a standalone trace instead of being related to the same trace that generates the job itself. On the other hand, if it’s part of the same Context, it will be related to the original trace.

This data structure is thread-safe. rubocop:disable Metrics/ClassLength

Constant Summary collapse

DEFAULT_MAX_LENGTH =

100k spans is about a 100Mb footprint

100_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Context

Initialize a new thread-safe Context.



24
25
26
27
28
29
30
# File 'lib/ddtrace/context.rb', line 24

def initialize(options = {})
  @mutex = Mutex.new
  # max_length is the amount of spans above which, for a given trace,
  # the context will simply drop and ignore spans, avoiding high memory usage.
  @max_length = options.fetch(:max_length, DEFAULT_MAX_LENGTH)
  reset(options)
end

Instance Attribute Details

#max_lengthObject (readonly)

Returns the value of attribute max_length.



21
22
23
# File 'lib/ddtrace/context.rb', line 21

def max_length
  @max_length
end

Instance Method Details

#add_span(span) ⇒ Object

Add a span to the context trace list, keeping it as the last active span.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/ddtrace/context.rb', line 85

def add_span(span)
  @mutex.synchronize do
    # If hitting the hard limit, just drop spans. This is really a rare case
    # as it means despite the soft limit, the hard limit is reached, so the trace
    # by default has 10000 spans, all of which belong to unfinished parts of a
    # larger trace. This is a catch-all to reduce global memory usage.
    if @max_length > 0 && @trace.length >= @max_length
      Datadog::Tracer.log.debug("context full, ignoring span #{span.name}")
      # Detach the span from any context, it's being dropped and ignored.
      span.context = nil
      return
    end
    set_current_span(span)
    @current_root_span = span if @trace.empty?
    @trace << span
    span.context = self
  end
end

#close_span(span) ⇒ Object

Mark a span as a finished, increasing the internal counter to prevent cycles inside _trace list.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ddtrace/context.rb', line 106

def close_span(span)
  @mutex.synchronize do
    @finished_spans += 1
    # Current span is only meaningful for linear tree-like traces,
    # in other cases, this is just broken and one should rely
    # on per-instrumentation code to retrieve handle parent/child relations.
    set_current_span(span.parent)
    return if span.tracer.nil?
    return unless Datadog::Tracer.debug_logging
    if span.parent.nil? && !check_finished_spans
      opened_spans = @trace.length - @finished_spans
      Datadog::Tracer.log.debug("root span #{span.name} closed but has #{opened_spans} unfinished spans:")
      @trace.each do |s|
        Datadog::Tracer.log.debug("unfinished span: #{s}") unless s.finished?
      end
    end
  end
end

#current_root_spanObject



78
79
80
81
82
# File 'lib/ddtrace/context.rb', line 78

def current_root_span
  @mutex.synchronize do
    return @current_root_span
  end
end

#current_spanObject

Return the last active span that corresponds to the last inserted item in the trace list. This cannot be considered as the current active span in asynchronous environments, because some spans can be closed earlier while child spans still need to finish their traced execution.



72
73
74
75
76
# File 'lib/ddtrace/context.rb', line 72

def current_span
  @mutex.synchronize do
    return @current_span
  end
end

#finished?Boolean

Returns if the trace for the current Context is finished or not. A Context is considered finished if all spans in this context are finished.

Returns:

  • (Boolean)


127
128
129
130
131
# File 'lib/ddtrace/context.rb', line 127

def finished?
  @mutex.synchronize do
    return check_finished_spans
  end
end

#getObject

Returns both the trace list generated in the current context and if the context is sampled or not. It returns nil, nil if the “Context“ is not finished. If a trace is returned, the Context will be reset so that it can be re-used immediately.

This operation is thread-safe.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ddtrace/context.rb', line 147

def get
  @mutex.synchronize do
    trace = @trace
    sampled = @sampled

    attach_sampling_priority if sampled && @sampling_priority
    attach_origin if @origin

    # still return sampled attribute, even if context is not finished
    return nil, sampled unless check_finished_spans()

    reset
    [trace, sampled]
  end
end

#originObject



56
57
58
59
60
# File 'lib/ddtrace/context.rb', line 56

def origin
  @mutex.synchronize do
    @origin
  end
end

#origin=(origin) ⇒ Object



62
63
64
65
66
# File 'lib/ddtrace/context.rb', line 62

def origin=(origin)
  @mutex.synchronize do
    @origin = origin
  end
end

#sampled?Boolean

Returns true if the context is sampled, that is, if it should be kept and sent to the trace agent.

Returns:

  • (Boolean)


135
136
137
138
139
# File 'lib/ddtrace/context.rb', line 135

def sampled?
  @mutex.synchronize do
    return @sampled
  end
end

#sampling_priorityObject



44
45
46
47
48
# File 'lib/ddtrace/context.rb', line 44

def sampling_priority
  @mutex.synchronize do
    @sampling_priority
  end
end

#sampling_priority=(priority) ⇒ Object



50
51
52
53
54
# File 'lib/ddtrace/context.rb', line 50

def sampling_priority=(priority)
  @mutex.synchronize do
    @sampling_priority = priority
  end
end

#span_idObject



38
39
40
41
42
# File 'lib/ddtrace/context.rb', line 38

def span_id
  @mutex.synchronize do
    @parent_span_id
  end
end

#to_sObject

Return a string representation of the context.



164
165
166
167
168
169
# File 'lib/ddtrace/context.rb', line 164

def to_s
  @mutex.synchronize do
    # rubocop:disable Metrics/LineLength
    "Context(trace.length:#{@trace.length},sampled:#{@sampled},finished_spans:#{@finished_spans},current_span:#{@current_span})"
  end
end

#trace_idObject



32
33
34
35
36
# File 'lib/ddtrace/context.rb', line 32

def trace_id
  @mutex.synchronize do
    @parent_trace_id
  end
end