Class: Skylight::Core::Trace

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

Constant Summary collapse

GC_CAT =
"noise.gc".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, #log_env_prefix, #raise_on_error?, #t, #trace, #trace?, #warn

Constructor Details

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

Returns a new instance of Trace.

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/skylight/core/trace.rb', line 29

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

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

  @notifications = []

  @spans = []

  # 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")
end

Instance Attribute Details

#compound_response_error_statusObject

Returns the value of attribute compound_response_error_status.



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

def compound_response_error_status
  @compound_response_error_status
end

#endpointObject

Returns the value of attribute endpoint.



9
10
11
# File 'lib/skylight/core/trace.rb', line 9

def endpoint
  @endpoint
end

#instrumenterObject (readonly)

Returns the value of attribute instrumenter.



9
10
11
# File 'lib/skylight/core/trace.rb', line 9

def instrumenter
  @instrumenter
end

#metaObject (readonly)

Returns the value of attribute meta.



9
10
11
# File 'lib/skylight/core/trace.rb', line 9

def meta
  @meta
end

#notificationsObject (readonly)

Returns the value of attribute notifications.



9
10
11
# File 'lib/skylight/core/trace.rb', line 9

def notifications
  @notifications
end

#segmentObject

Returns the value of attribute segment.



9
10
11
# File 'lib/skylight/core/trace.rb', line 9

def segment
  @segment
end

#uuidObject

Returns the value of attribute uuid.



10
11
12
# File 'lib/skylight/core/trace.rb', line 10

def uuid
  @uuid
end

Class Method Details

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



12
13
14
15
16
17
18
19
20
# File 'lib/skylight/core/trace.rb', line 12

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



23
24
25
26
27
# File 'lib/skylight/core/trace.rb', line 23

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



170
171
172
173
# File 'lib/skylight/core/trace.rb', line 170

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

#broken?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/skylight/core/trace.rb', line 80

def broken?
  !!@broken
end

#configObject



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

def config
  @instrumenter.config
end

#done(span, meta = nil) ⇒ Object



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

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] ||= (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, Util::Clock.nanos - gc_time)
rescue => 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

#inspectObject



160
161
162
# File 'lib/skylight/core/trace.rb', line 160

def inspect
  to_s
end

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



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/skylight/core/trace.rb', line 107

def instrument(cat, title = nil, desc = nil, meta = nil)
  return if muted?
  return if broken?
  t { "instrument: #{cat}, #{title}" }

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

  original_desc = desc
  now           = Util::Clock.nanos
  desc          = @instrumenter.limited_description(desc)

  if desc == Instrumenter::TOO_MANY_UNIQUES
    error "[E0002] The number of unique span descriptions allowed per-request has been exceeded " \
              "for endpoint: #{endpoint}."
    debug "original desc=%s", original_desc
    debug "cat=%s, title=%s, desc=%s", cat, title, desc
  end

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

#log_contextObject



49
50
51
# File 'lib/skylight/core/trace.rb', line 49

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

#maybe_broken(err) ⇒ Object



84
85
86
87
# File 'lib/skylight/core/trace.rb', line 84

def maybe_broken(err)
  error "failed to instrument span; msg=%s; endpoint=%s", err.message, endpoint
  broken!
end

#muted?Boolean

Returns:

  • (Boolean)


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

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

#record(cat, title = nil, desc = nil) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/skylight/core/trace.rb', line 89

def record(cat, title = nil, desc = nil)
  return if muted? || broken?

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

  desc = @instrumenter.limited_description(desc)

  time = Util::Clock.nanos - gc_time

  stop(start(time, cat, title, desc, nil), time)

  nil
rescue => e
  maybe_broken(e)
  nil
end

#releaseObject



164
165
166
167
168
# File 'lib/skylight/core/trace.rb', line 164

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

#span_correlation_header(span) ⇒ Object



132
133
134
135
# File 'lib/skylight/core/trace.rb', line 132

def span_correlation_header(span)
  return unless span
  native_span_get_correlation_header(span)
end

#submitObject



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/skylight/core/trace.rb', line 182

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

#tracedObject



175
176
177
178
179
180
# File 'lib/skylight/core/trace.rb', line 175

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