appoptics_apm 4.2.2 → 4.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -3
  3. data/Gemfile +0 -2
  4. data/README.md +119 -95
  5. data/examples/SDK/01_basic_tracing.rb +67 -0
  6. data/ext/oboe_metal/src/VERSION +1 -1
  7. data/lib/appoptics_apm/api.rb +10 -9
  8. data/lib/appoptics_apm/api/layerinit.rb +1 -1
  9. data/lib/appoptics_apm/api/logging.rb +41 -14
  10. data/lib/appoptics_apm/api/metrics.rb +34 -0
  11. data/lib/appoptics_apm/api/profiling.rb +5 -0
  12. data/lib/appoptics_apm/api/tracing.rb +33 -148
  13. data/lib/appoptics_apm/api/util.rb +3 -9
  14. data/lib/appoptics_apm/base.rb +11 -12
  15. data/lib/appoptics_apm/frameworks/padrino/templates.rb +1 -1
  16. data/lib/appoptics_apm/frameworks/rails.rb +1 -1
  17. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +1 -1
  18. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +1 -1
  19. data/lib/appoptics_apm/inst/bunny-consumer.rb +1 -2
  20. data/lib/appoptics_apm/inst/delayed_job.rb +4 -4
  21. data/lib/appoptics_apm/inst/em-http-request.rb +1 -1
  22. data/lib/appoptics_apm/inst/rack.rb +1 -0
  23. data/lib/appoptics_apm/inst/redis.rb +1 -1
  24. data/lib/appoptics_apm/inst/resque.rb +1 -1
  25. data/lib/appoptics_apm/inst/sidekiq-worker.rb +1 -3
  26. data/lib/appoptics_apm/loading.rb +3 -4
  27. data/lib/appoptics_apm/logger.rb +1 -1
  28. data/lib/appoptics_apm/sdk.rb +317 -0
  29. data/lib/appoptics_apm/support.rb +0 -17
  30. data/lib/appoptics_apm/test.rb +1 -1
  31. data/lib/appoptics_apm/util.rb +4 -3
  32. data/lib/appoptics_apm/version.rb +1 -1
  33. data/lib/appoptics_apm/xtrace.rb +3 -3
  34. data/lib/joboe_metal.rb +2 -4
  35. data/lib/oboe_metal.rb +5 -5
  36. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +5 -4
  37. data/run_tests_docker.rb +6 -0
  38. metadata +5 -9
  39. data/examples/DNT.md +0 -35
  40. data/examples/instrumenting_metal_controller.rb +0 -8
  41. data/examples/puma_on_heroku_config.rb +0 -17
  42. data/examples/tracing_async_threads.rb +0 -124
  43. data/examples/tracing_background_jobs.rb +0 -53
  44. data/examples/tracing_forked_processes.rb +0 -99
  45. data/examples/unicorn_on_heroku_config.rb +0 -28
@@ -1 +1 @@
1
- 3.0.1
1
+ 3.0.2
@@ -2,16 +2,17 @@
2
2
  # All rights reserved.
3
3
 
4
4
  module AppOpticsAPM
5
- ##
6
- # This module implements the AppOpticsAPM tracing API.
7
- # See: https://docs.appoptics.com/kb/apm_tracing/ruby/sdk/
5
+
8
6
  module API
9
- def self.extend_with_tracing
10
- extend AppOpticsAPM::API::Logging
11
- extend AppOpticsAPM::API::Tracing
12
- extend AppOpticsAPM::API::Profiling
13
- extend AppOpticsAPM::API::LayerInit
14
- end
7
+ extend AppOpticsAPM::API::Logging
8
+ extend AppOpticsAPM::API::Metrics
9
+ extend AppOpticsAPM::API::Profiling
10
+ extend AppOpticsAPM::API::LayerInit
15
11
  extend AppOpticsAPM::API::Util
12
+
13
+ require_relative './sdk'
14
+
15
+ extend AppOpticsAPM::SDK::Tracing
16
+ extend AppOpticsAPM::API::Tracing
16
17
  end
17
18
  end
@@ -27,7 +27,7 @@ module AppOpticsAPM
27
27
  # force_trace has been deprecated and will be removed in a subsequent version.
28
28
  #
29
29
  def force_trace
30
- AppOpticsAPM.logger.warn 'AppOpticsAPM::API::LayerInit.force_trace has been deprecated and will be ' \
30
+ AppOpticsAPM.logger.warn '[appoptics_apm/api] AppOpticsAPM::API::LayerInit.force_trace has been deprecated and will be ' \
31
31
  'removed in a subsequent version.'
32
32
 
33
33
  saved_mode = AppOpticsAPM::Config[:tracing_mode]
@@ -16,7 +16,7 @@ module AppOpticsAPM
16
16
  ##
17
17
  # This modules provides the X-Trace logging facilities.
18
18
  #
19
- # These are the lower level methods, please see AppOpticsAPM::API::Tracing
19
+ # These are the lower level methods, please see AppOpticsAPM::SDK
20
20
  # for the higher level methods
21
21
  module Logging
22
22
  @@ints_or_nil = [Integer, Float, NilClass, String]
@@ -52,7 +52,7 @@ module AppOpticsAPM
52
52
  # ==== Arguments
53
53
  #
54
54
  # * +layer+ - The layer the reported event belongs to
55
- # * +exn+ - The exception to report
55
+ # * +exception+ - The exception to report
56
56
  # * +opts+ - Custom params if you want to log extra information
57
57
  #
58
58
  # ==== Example
@@ -65,19 +65,19 @@ module AppOpticsAPM
65
65
  # end
66
66
  #
67
67
  # Returns nothing.
68
- def log_exception(layer, exn, opts = {})
69
- return AppOpticsAPM::Context.toString if !AppOpticsAPM.tracing? || exn.instance_variable_get(:@oboe_logged)
68
+ def log_exception(layer, exception, opts = {})
69
+ return AppOpticsAPM::Context.toString if !AppOpticsAPM.tracing? || exception.instance_variable_get(:@exn_logged)
70
70
 
71
- unless exn
71
+ unless exception
72
72
  AppOpticsAPM.logger.debug '[appoptics_apm/debug] log_exception called with nil exception'
73
73
  return AppOpticsAPM::Context.toString
74
74
  end
75
75
 
76
- opts.merge!(:ErrorClass => exn.class.name,
77
- :ErrorMsg => exn.message,
78
- :Backtrace => exn.backtrace.join("\r\n")) if exn.backtrace
76
+ opts.merge!(:ErrorClass => exception.class.name,
77
+ :ErrorMsg => exception.message,
78
+ :Backtrace => exception.backtrace.join("\r\n")) if exception.backtrace
79
79
 
80
- exn.instance_variable_set(:@oboe_logged, true)
80
+ exception.instance_variable_set(:@exn_logged, true)
81
81
  log(layer, :error, opts)
82
82
  end
83
83
 
@@ -125,7 +125,7 @@ module AppOpticsAPM
125
125
  # end
126
126
  #++
127
127
 
128
- if AppOpticsAPM.sample?(opts.merge(:layer => layer, :xtrace => xtrace))
128
+ if AppOpticsAPM.sample?(opts.merge(:xtrace => xtrace))
129
129
  # Yes, we're sampling this request
130
130
  # Probablistic tracing of a subset of requests based off of
131
131
  # sample rate and sample source
@@ -179,13 +179,18 @@ module AppOpticsAPM
179
179
  # AppOpticsAPM::API.log_end(:layer_name, { :id => @user.id })
180
180
  #
181
181
  # Returns an xtrace metadata string if we are tracing
182
- def log_end(layer, opts = {})
182
+ def log_end(layer, opts = {}, event = nil)
183
183
  return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
184
184
 
185
- log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts)
185
+ # Deal with the transaction name
186
+ opts[:TransactionName] = determine_transaction_name(layer, opts)
187
+
188
+ event ||= AppOpticsAPM::Context.createEvent
189
+ log_event(layer, :exit, event, opts)
186
190
  ensure
187
191
  # FIXME has_incoming_context commented out, it has importance for JRuby only but breaks Ruby tests
188
192
  AppOpticsAPM::Context.clear # unless AppOpticsAPM.has_incoming_context?
193
+ AppOpticsAPM.transaction_name = nil
189
194
  end
190
195
 
191
196
  ##
@@ -301,6 +306,25 @@ module AppOpticsAPM
301
306
  AppOpticsAPM::Context.toString
302
307
  end
303
308
 
309
+ ##
310
+ # Determine the transaction name to be set on the trace
311
+ #
312
+ # A transaction name set via the opts key `:TransactionName` takes precedence
313
+ # over a currently set custom transaction name. if neither are provided it
314
+ # returns `"custom_#{span}"`
315
+ #
316
+ # === Argument:
317
+ # * +opts+ (hash) the value of :TransactionName will be set as custom transaction name
318
+ #
319
+ # === Returns:
320
+ # (string) the current transaction name
321
+ #
322
+ def determine_transaction_name(span, opts = {})
323
+ opts[:TransactionName] ||= opts.delete('TransactionName')
324
+ AppOpticsAPM::API.set_transaction_name(opts[:TransactionName])
325
+ AppOpticsAPM.transaction_name || "custom-#{span}"
326
+ end
327
+
304
328
  private
305
329
 
306
330
  ##
@@ -347,8 +371,8 @@ module AppOpticsAPM
347
371
  begin
348
372
  event.addInfo(k.to_s, value)
349
373
  rescue ArgumentError => e
350
- AppOpticsAPM.logger.debug "[AppOpticsAPM/debug] Couldn't add event KV: #{k} => #{v.class}"
351
- AppOpticsAPM.logger.debug "[AppOpticsAPM/debug] #{e.message}"
374
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Couldn't add event KV: #{k} => #{v.class}"
375
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{e.message}"
352
376
  end
353
377
  end if !opts.nil? && opts.any?
354
378
 
@@ -356,6 +380,9 @@ module AppOpticsAPM
356
380
  AppOpticsAPM::Context.toString
357
381
  end
358
382
 
383
+ # need to set the context to public, otherwise the following `extends` will be private in api.rb
384
+ public
385
+
359
386
  end
360
387
  end
361
388
  end
@@ -0,0 +1,34 @@
1
+ module AppOpticsAPM
2
+ module API
3
+ module Metrics
4
+
5
+ ##
6
+ # Internal: method to send duration for a transaction
7
+ # it checks if it can send metrics with the current transaction name
8
+ # or a default transaction name and sets the transaction name accordingly
9
+ #
10
+ # === Arguments
11
+ #
12
+ # * +span+ the name of the current span (used to construct a transaction name if none is defined)
13
+ # * +kvs+ A hash containing key/value pairs, only the value of :TransactionName will be relevant
14
+ #
15
+ # Returns the result of the block.
16
+ #
17
+
18
+ def send_metrics(span, kvs = {})
19
+ # This is a new span, we do not know the transaction name yet
20
+ AppOpticsAPM.transaction_name = nil
21
+
22
+ # if a transaction name is provided it will take precedence over transaction names defined
23
+ # later or in lower spans
24
+ start = Time.now
25
+
26
+ yield
27
+ ensure
28
+ duration =(1000 * 1000 * (Time.now - start)).round(0)
29
+ transaction_name = AppOpticsAPM::API.determine_transaction_name(span, kvs)
30
+ kvs[:TransactionName] = AppOpticsAPM::API.set_transaction_name(AppOpticsAPM::Span.createSpan(transaction_name, nil, duration))
31
+ end
32
+ end
33
+ end
34
+ end
@@ -198,6 +198,11 @@ module AppOpticsAPM
198
198
  # Merge in any extra_kvs requested
199
199
  report_kvs.merge!(extra_kvs)
200
200
  end
201
+
202
+
203
+ # need to set the context to public, otherwise the following `extends` will be private in api.rb
204
+ public
205
+
201
206
  end
202
207
  end
203
208
  end
@@ -6,77 +6,29 @@
6
6
  module AppOpticsAPM
7
7
  module API
8
8
  ##
9
- # Provides the higher-level tracing interface for the API.
9
+ # Provides the higher-level tracing interface for the API
10
10
  #
11
- # Traces are best created with a <tt>AppOpticsAPM:API.start_trace</tt> block and
12
- # <tt>AppOpticsAPM:API.trace</tt> blocks around calls to be traced.
13
- # These two methods guarantee proper nesting of tracing and handling of the tracing context.
11
+ # The tracing methods have been moved to AppOpticsAPM::SDK and AppOpticsAPM::API extends all methods from the SDK
12
+ # except for start_trace.
13
+ # AppOpticsAPM::API.start_trace is kept for backwards compatibility because it returns an array
14
+ # whereas AppOpticsAPM::SDK.start_trace will only return the result of the block.
14
15
  #
15
- # Some optional keys that can be used in the +opts+ hash:
16
- # * +:TransactionName+ - this will show up in the transactions column in the traces dashboard
17
- # * +:Controller+ - if present will be combined with +Action+ and show up as transaction in the traces dashboard
18
- # * +:Action+ - if present will be combined with +Controller+ and show up as transaction in the traces dashboard
19
- # * +:HTTP-Host+ - domain portion of URL
20
- # * +:URL+ - request URI
21
- # * +:Method+
22
- #
23
- # TODO complete the above
24
- #
25
- # Invalid keys: +:Label+, +:Layer+, +:Edge+, +:Timestamp+, +:Timestamp_u+
26
- #
27
- module Tracing
28
- # Public: Trace a given block of code. Detect any exceptions thrown by
29
- # the block and report errors.
30
- #
31
- # * +:layer+ - The layer the block of code belongs to.
32
- # * +:opts+ - A hash containing key/value pairs that will be reported along
33
- # with the first event of this layer (optional).
34
- # * +:protect_op+ - The operation being traced. Used to avoid
35
- # double tracing operations that call each other
36
- #
37
- # Example
38
- #
39
- # def computation(n)
40
- # fib(n)
41
- # raise Exception.new
42
- # end
43
- #
44
- # def computation_with_oboe(n)
45
- # trace('fib', { :number => n }, :fib) do
46
- # computation(n)
47
- # end
48
- # end
49
- #
50
- # result = computation_with_oboe(1000)
51
- #
52
- # Returns the result of the block.
53
- def trace(layer, opts = {}, protect_op = nil)
54
- return if !AppOpticsAPM.loaded || (protect_op && AppOpticsAPM.layer_op == protect_op.to_sym)
55
16
 
56
- log_entry(layer, opts, protect_op)
57
- begin
58
- yield
59
- rescue Exception => e
60
- log_exception(layer, e)
61
- raise
62
- ensure
63
- log_exit(layer, opts, protect_op)
64
- end
65
- end
17
+ module Tracing
66
18
 
67
- # Public: Trace a given block of code which can start a trace depending
68
- # on configuration and probability. Detect any exceptions thrown by the
69
- # block and report errors.
19
+ # Public: Collect metrics and start tracing a given block of code. A
20
+ # trace will be started depending on configuration and probability.
21
+ # Detect any exceptions thrown by the block and report errors.
70
22
  #
71
- # When start_trace returns control to the calling context, the oboe
72
- # context will be cleared.
23
+ # When start_trace returns control to the calling context, the trace will be
24
+ # completed and the tracing context will be cleared.
73
25
  #
74
26
  # ==== Arguments
75
27
  #
76
- # * +layer+ - name for the layer to be used as label in the trace view
28
+ # * +span+ - name for the span to be used as label in the trace view
77
29
  # * +xtrace+ - (optional) incoming X-Trace identifier to be continued
78
30
  # * +opts+ - (optional) hash containing key/value pairs that will be reported along
79
- # with the first event of this layer (optional)
31
+ # with the first event of this span
80
32
  #
81
33
  # ==== Example
82
34
  #
@@ -85,106 +37,39 @@ module AppOpticsAPM
85
37
  # end
86
38
  #
87
39
  # def handle_request_with_appoptics(request, response)
88
- # result, xtrace = start_trace('rails', request['X-Trace']) do
40
+ # start_trace('custom_span', nil, :TransactionName => 'handle_request') do
89
41
  # handle_request(request, response)
90
42
  # end
91
- # result
92
- # rescue Exception => e
93
- # xtrace = e.xtrace
94
- # ensure
95
- # response['X-trace'] = xtrace
96
43
  # end
97
44
  #
98
- # Returns a list of length two, the first element of which is the result
99
- # of the block, and the second element of which is the oboe context that
100
- # was set when the block completed execution.
101
- def start_trace(layer, xtrace = nil, opts = {})
45
+ # Returns an array with the result of the block and the last xtrace used
46
+ def start_trace(span, xtrace = nil, opts = {})
102
47
  return [yield, nil] unless AppOpticsAPM.loaded
103
48
 
104
- log_start(layer, xtrace, opts)
105
- begin
106
- result = yield
107
- rescue Exception => e
108
- log_exception(layer, e)
109
- e.instance_variable_set(:@xtrace, log_end(layer))
110
- raise
111
- end
112
- xtrace = log_end(layer)
113
-
114
- [result, xtrace]
115
- end
49
+ # if it is not an entry span!
50
+ return [trace(span, opts) { yield }, AppopticsAPM::Context.toString] if AppOpticsAPM::Context.isValid
116
51
 
117
- # Public: Trace a given block of code which can start a trace depending
118
- # on configuration and probability. Detect any exceptions thrown by the
119
- # block and report errors. Assign an X-Trace to the target.
120
- #
121
- # The motivating use case for this is HTTP streaming in rails3. We need
122
- # access to the exit event's trace id so we can set the header before any
123
- # work is done, and before any headers are sent back to the client.
124
- #
125
- # ===== Arguments
126
- # * +layer+ - The layer the block of code belongs to.
127
- # * +xtrace+ - string - The X-Trace to continue by the target
128
- # * +target+ - has to respond to #[]=, The target object in which to place the trace information
129
- # * +opts+ - A hash containing key/value pairs that will be reported along
130
- # with the first event of this layer (optional).
131
- #
132
- # ==== Example
133
- #
134
- # def handle_request(request, response)
135
- # # ... code that does something with request and response ...
136
- # end
137
- #
138
- # def handle_request_with_appoptics(request, response)
139
- # start_trace_with_target('rails', request['X-Trace'], response) do
140
- # handle_request(request, response)
141
- # end
142
- # end
143
- #
144
- # Returns the result of the block.
145
- def start_trace_with_target(layer, xtrace, target, opts = {})
146
- return yield unless AppOpticsAPM.loaded
52
+ log_start(span, xtrace, opts)
147
53
 
148
- log_start(layer, xtrace, opts)
149
- exit_evt = AppOpticsAPM::Context.createEvent
150
- begin
151
- target['X-Trace'] = AppOpticsAPM::EventUtil.metadataString(exit_evt) if AppOpticsAPM.tracing?
152
- yield
153
- rescue Exception => e
154
- log_exception(layer, e)
155
- raise
156
- ensure
157
- exit_evt.addEdge(AppOpticsAPM::Context.get)
158
- log(layer, :exit, {}, exit_evt)
159
- AppOpticsAPM::Context.clear
54
+ # send_metrics deals with the logic for setting AppOpticsAPM.transaction_name
55
+ # and ensures that metrics are sent
56
+ # log_end sets the transaction_name
57
+ result = send_metrics(span, opts) do
58
+ begin
59
+ yield
60
+ rescue Exception => e # rescue everything ok, since we are raising
61
+ AppOpticsAPM::API.log_exception(span, e)
62
+ e.instance_variable_set(:@xtrace, log_end(span))
63
+ raise
64
+ end
160
65
  end
161
- end
66
+ xtrace = AppopticsAPM::Context.toString
67
+ log_end(span, opts)
162
68
 
163
- # Public: Set a ThreadLocal custom transaction name to be used when sending a trace or metrics for the
164
- # current transaction
165
- #
166
- # In addition to setting a transaction name here there is also a configuration
167
- # AppOpticsAPM::Config['transaction_name']['prepend_domain'] which allows to have the domain prepended
168
- # to the transaction name
169
- #
170
- # ===== Arguments
171
- # * +name+ - A non-empty string with the custom transaction name
172
- #
173
- def set_transaction_name(name)
174
- if name.is_a?(String) && name.strip != ''
175
- AppOpticsAPM.transaction_name = name
176
- else
177
- AppOpticsAPM.logger.debug "[appoptics_apm/api] Could not set transaction name, provided name is empty or not a String."
178
- end
179
- AppOpticsAPM.transaction_name
69
+ [result, xtrace]
180
70
  end
181
71
 
182
72
 
183
- # this is provided for testing
184
- # returns the current transaction name
185
- def get_transaction_name
186
- AppOpticsAPM.transaction_name
187
- end
188
73
  end
189
74
  end
190
75
  end
@@ -42,16 +42,10 @@ module AppOpticsAPM
42
42
  #
43
43
  # Returns a trimmed backtrace
44
44
  def trim_backtrace(backtrace)
45
- return backtrace unless backtrace.is_a?(Array)
45
+ return backtrace unless backtrace.is_a?(Array) && backtrace.size > BACKTRACE_CUTOFF
46
46
 
47
- length = backtrace.size
48
- if length > BACKTRACE_CUTOFF
49
- # Trim backtraces by getting the first 180 and last 20 lines
50
- trimmed = backtrace[0, 180] + ['...[snip]...'] + backtrace[length - 20, 20]
51
- else
52
- trimmed = backtrace
53
- end
54
- trimmed
47
+ # Trim backtraces by getting the first 180 and last 20 lines
48
+ backtrace[0, 180] + ['...[snip]...'] + backtrace[length - 20, 20]
55
49
  end
56
50
 
57
51
  # Internal: Check if a host is blacklisted from tracing
@@ -2,25 +2,24 @@
2
2
  # All rights reserved.
3
3
 
4
4
  # Constants from liboboe
5
- OBOE_TRACE_NEVER = 0
6
- OBOE_TRACE_ALWAYS = 1
7
- OBOE_TRACE_THROUGH = 2
5
+ APPOPTICS_TRACE_NEVER = 0
6
+ APPOPTICS_TRACE_ALWAYS = 1
8
7
 
9
- OBOE_SAMPLE_RATE_SOURCE_FILE = 1
10
- OBOE_SAMPLE_RATE_SOURCE_DEFAULT = 2
11
- OBOE_SAMPLE_RATE_SOURCE_OBOE = 3
12
- OBOE_SAMPLE_RATE_SOURCE_LAST_OBOE = 4
13
- OBOE_SAMPLE_RATE_SOURCE_DEFAULT_MISCONFIGURED = 5
14
- OBOE_SAMPLE_RATE_SOURCE_OBOE_DEFAULT = 6
8
+ # OBOE_SAMPLE_RATE_SOURCE_FILE = 1
9
+ # OBOE_SAMPLE_RATE_SOURCE_DEFAULT = 2
10
+ # OBOE_SAMPLE_RATE_SOURCE_OBOE = 3
11
+ # OBOE_SAMPLE_RATE_SOURCE_LAST_OBOE = 4
12
+ # OBOE_SAMPLE_RATE_SOURCE_DEFAULT_MISCONFIGURED = 5
13
+ # OBOE_SAMPLE_RATE_SOURCE_OBOE_DEFAULT = 6
15
14
 
16
15
  # Masks for bitwise ops
17
- ZERO_MASK = 0b0000000000000000000000000000
16
+ # ZERO_MASK = 0b0000000000000000000000000000
18
17
 
19
18
  SAMPLE_RATE_MASK = 0b0000111111111111111111111111
20
19
  SAMPLE_SOURCE_MASK = 0b1111000000000000000000000000
21
20
 
22
- ZERO_SAMPLE_RATE_MASK = 0b1111000000000000000000000000
23
- ZERO_SAMPLE_SOURCE_MASK = 0b0000111111111111111111111111
21
+ # ZERO_SAMPLE_RATE_MASK = 0b1111000000000000000000000000
22
+ # ZERO_SAMPLE_SOURCE_MASK = 0b0000111111111111111111111111
24
23
 
25
24
  APPOPTICS_STR_BLANK = ''.freeze
26
25
  APPOPTICS_STR_LAYER = 'Layer'.freeze