Class: Skylight::Trace

Inherits:
Object show all
Includes:
Util::Logging
Defined in:
lib/skylight/trace.rb

Constant Summary collapse

GC_CAT =
"noise.gc".freeze
SYNTHETIC =
"<synthetic>".freeze
META_KEYS =
%i[mute_children database].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Logging

#config_for_logging, #debug, #error, #fmt, #info, #log, #raise_on_error?, #t, #trace, #trace?, #warn

Constructor Details

#initialize(instrumenter, cat, title, desc, meta, component: nil) ⇒ Trace

Returns a new instance of Trace.

Raises:

  • (ArgumentError)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/skylight/trace.rb', line 32

def initialize(instrumenter, cat, title, desc, meta, component: nil)
  raise ArgumentError, "instrumenter is required" unless instrumenter

  @instrumenter = instrumenter
  @submitted = false
  @broken = false

  @notifications = []

  @spans = []

  preprocess_meta(meta) if meta

  # create the root node
  @root = start(native_get_started_at, cat, title, desc, meta, normalize: false)

  # Also store meta for later access
  @meta = meta

  @gc = config.gc.track unless ENV.key?("SKYLIGHT_DISABLE_GC_TRACKING")

  self.component = component if component
  @too_many_spans = false
  native_use_pruning if use_pruning?
end

Instance Attribute Details

#componentObject

Returns the value of attribute component.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def component
  @component
end

#compound_response_error_statusObject

Returns the value of attribute compound_response_error_status.



90
91
92
# File 'lib/skylight/trace.rb', line 90

def compound_response_error_status
  @compound_response_error_status
end

#endpointObject

Returns the value of attribute endpoint.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def endpoint
  @endpoint
end

#instrumenterObject (readonly)

Returns the value of attribute instrumenter.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def instrumenter
  @instrumenter
end

#metaObject (readonly)

Returns the value of attribute meta.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def meta
  @meta
end

#notificationsObject (readonly)

Returns the value of attribute notifications.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def notifications
  @notifications
end

#segmentObject

Returns the value of attribute segment.



13
14
15
# File 'lib/skylight/trace.rb', line 13

def segment
  @segment
end

Class Method Details

.new(instrumenter, endpoint, start, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil) ⇒ Object



15
16
17
18
19
20
21
22
23
# File 'lib/skylight/trace.rb', line 15

def self.new(instrumenter, endpoint, start, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
  uuid = SecureRandom.uuid
  inst = native_new(normalize_time(start), uuid, endpoint, meta)
  inst.uuid = uuid
  inst.send(:initialize, instrumenter, cat, title, desc, meta, component: component)
  inst.endpoint = endpoint
  inst.segment = segment
  inst
end

.normalize_time(time) ⇒ Object

TODO: Move this into native



26
27
28
29
30
# File 'lib/skylight/trace.rb', line 26

def self.normalize_time(time)
  # At least one customer has extensions that cause integer division to produce rationals.
  # Since the native code expects an integer, we force it again.
  (time.to_i / 100_000).to_i
end

Instance Method Details

#broken!Object



174
175
176
177
# File 'lib/skylight/trace.rb', line 174

def broken!
  debug "trace is broken"
  @broken = true
end

#broken?Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/skylight/trace.rb', line 108

def broken?
  !!@broken
end

#configObject



92
93
94
# File 'lib/skylight/trace.rb', line 92

def config
  @instrumenter.config
end

#done(span, meta = nil) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/skylight/trace.rb', line 140

def done(span, meta = nil)
  # `span` will be `nil` if we failed to start instrumenting, such as in
  # the case of too many spans in a request.
  return unless span
  return if broken?

  if meta&.[](:defer)
    deferred_spans[span] ||= (Skylight::Util::Clock.nanos - gc_time)
    return
  end

  if meta && (meta[:exception_object] || meta[:exception])
    native_span_set_exception(span, meta[:exception_object], meta[:exception])
  end

  stop(span, Skylight::Util::Clock.nanos - gc_time)
rescue StandardError => e
  error "failed to close span; msg=%s; endpoint=%s", e.message, endpoint
  log_trace "Original Backtrace:\n#{e.backtrace.join("\n")}"
  broken!
  nil
end

#endpoint_assignment_muted?Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/skylight/trace.rb', line 104

def endpoint_assignment_muted?
  tracing_muted? || @instrumenter.endpoint_assignment_muted?
end

#inspectObject



163
164
165
# File 'lib/skylight/trace.rb', line 163

def inspect
  to_s
end

#instrument(cat, title = nil, desc = nil, meta = nil) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/skylight/trace.rb', line 121

def instrument(cat, title = nil, desc = nil, meta = nil)
  return if tracing_muted?
  return if broken?

  t { "instrument: #{cat}, #{title}" }

  title.freeze if title.is_a?(String)
  desc.freeze if desc.is_a?(String)

  now = Skylight::Util::Clock.nanos

  preprocess_meta(meta) if meta

  start(now - gc_time, cat, title, desc, meta)
rescue StandardError => e
  maybe_broken(e)
  nil
end

#log_contextObject



75
76
77
# File 'lib/skylight/trace.rb', line 75

def log_context
  @log_context ||= { trace: uuid }
end

#maybe_broken(err) ⇒ Object



112
113
114
115
116
117
118
119
# File 'lib/skylight/trace.rb', line 112

def maybe_broken(err)
  if err.is_a?(Skylight::MaximumTraceSpansError) && config.get(:report_max_spans_exceeded)
    too_many_spans!
  else
    error "failed to instrument span; msg=%s; endpoint=%s", err.message, endpoint
    broken!
  end
end

#mutedObject



96
97
98
# File 'lib/skylight/trace.rb', line 96

def muted
  @child_instrumentation_muted_by || @instrumenter.muted
end

#releaseObject



167
168
169
170
171
172
# File 'lib/skylight/trace.rb', line 167

def release
  t { "release; is_current=#{@instrumenter.current_trace == self}" }
  return unless @instrumenter.current_trace == self

  @instrumenter.current_trace = nil
end

#submitObject



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/skylight/trace.rb', line 195

def submit
  t { "submitting trace" }

  # This must always be called to clean up properly
  release

  if broken?
    t { "broken, not submitting" }
    return
  end

  if @submitted
    t { "already submitted" }
    return
  end

  @submitted = true

  traced

  @instrumenter.process(self)
rescue Exception => e
  error e.message
  t { e.backtrace.join("\n") }
end

#too_many_spans!Object



67
68
69
# File 'lib/skylight/trace.rb', line 67

def too_many_spans!
  @too_many_spans = true
end

#too_many_spans?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/skylight/trace.rb', line 71

def too_many_spans?
  !!@too_many_spans
end

#tracedObject



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/skylight/trace.rb', line 179

def traced
  if too_many_spans?
    error(
      "[E%04d] The request exceeded the maximum number of spans allowed. It will still " \
        "be tracked but with reduced information. endpoint=%s",
      Skylight::MaximumTraceSpansError.code,
      endpoint
    )
  end

  gc = gc_time
  now = Skylight::Util::Clock.nanos
  track_gc(gc, now)
  stop(@root, now)
end

#tracing_muted?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/skylight/trace.rb', line 100

def tracing_muted?
  !!@child_instrumentation_muted_by || @instrumenter.tracing_muted?
end

#uuidObject



58
59
60
# File 'lib/skylight/trace.rb', line 58

def uuid
  native_get_uuid
end

#uuid=(value) ⇒ Object



62
63
64
65
# File 'lib/skylight/trace.rb', line 62

def uuid=(value)
  # We can't change the UUID so just check to make sure we weren't trying to change
  raise "unable to change uuid" unless value == uuid
end