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)


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

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

#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

#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) ⇒ Object



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

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

.normalize_time(time) ⇒ Object

TODO: Move this into native



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

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



152
153
154
155
# File 'lib/skylight/core/trace.rb', line 152

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

#broken?Boolean

Returns:

  • (Boolean)


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

def broken?
  !!@broken
end

#configObject



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

def config
  @instrumenter.config
end

#done(span, meta = nil) ⇒ Object



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

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 && 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



142
143
144
# File 'lib/skylight/core/trace.rb', line 142

def inspect
  to_s
end

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



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

def instrument(cat, title=nil, desc=nil, meta=nil)
  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



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

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

#maybe_broken(e) ⇒ Object



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

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

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



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/skylight/core/trace.rb', line 71

def record(cat, title=nil, desc=nil)
  return if 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



146
147
148
149
150
# File 'lib/skylight/core/trace.rb', line 146

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



113
114
115
116
# File 'lib/skylight/core/trace.rb', line 113

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

#submitObject



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/skylight/core/trace.rb', line 164

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



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

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