appoptics_apm 4.1.2 → 4.2.0

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.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require
5
+
6
+ # Make sure oboe is at the bottom of your Gemfile.
7
+ # This is likely redundant but just in case.
8
+ require 'oboe'
9
+
10
+ # Tracing mode can be 'never' or 'always'
11
+ AppOpticsAPM::Config[:tracing_mode] = 'always'
12
+
13
+ #
14
+ # Update April 9, 2015 - this is done automagically now
15
+ # and doesn't have to be called manually
16
+ #
17
+ # Load library instrumentation to auto-capture stuff we know about...
18
+ # e.g. ActiveRecord, Cassandra, Dalli, Redis, Memcache, Mongo
19
+ # AppOpticsAPM::Ruby.load
20
+
21
+ # Some KVs to report to the dashboard
22
+ report_kvs = {}
23
+ report_kvs[:command_line_params] = ARGV.to_s
24
+ report_kvs[:user_id] = `whoami`
25
+
26
+ AppOpticsAPM::API.start_trace('my_background_job', nil, report_kvs) do
27
+ #
28
+ # Initialization code
29
+ #
30
+
31
+ tasks = get_all_tasks
32
+
33
+ tasks.each do |t|
34
+ # Optional: Here we embed another 'trace' to separate actual
35
+ # work for each task. In the APPOPTICS dashboard, this will show
36
+ # up as a large 'my_background_job' parent layer with many
37
+ # child 'task" layers.
38
+ AppOpticsAPM::API.trace('task', :task_id => t.id) do
39
+ t.perform
40
+ end
41
+ end
42
+ #
43
+ # cleanup code
44
+ #
45
+ end
46
+
47
+ # Note that we use 'start_trace' in the outer block and 'trace' for
48
+ # any sub-blocks of code we wish to instrument. The arguments for
49
+ # both methods vary slightly.
50
+ #
51
+ # TODO update location of the following doc
52
+ # Details in RubyDoc:
53
+ # https://www.omniref.com/ruby/gems/oboe/2.7.10.1/symbols/AppOpticsAPM::API::Tracing#tab=Methods
@@ -0,0 +1,99 @@
1
+ #
2
+ # This sample demonstrates how to instrument a main loop that
3
+ # retrieves work and calls fork to do the actual work
4
+ #
5
+
6
+ require 'math'
7
+ require 'oboe'
8
+
9
+ AppOpticsAPM::Config[:tracing_mode] = :always
10
+ AppOpticsAPM::Config[:verbose] = true
11
+
12
+ # The parent process/loop which collects data
13
+ Kernel.loop do
14
+ # For each loop, we instrument the work retrieval. These traces
15
+ # will show up as layer 'get_the_work'.
16
+ AppOpticsAPM::API.start_trace('get_the_work') do
17
+ work = get_the_work
18
+
19
+ # Loop through work and pass to `do_the_work` method
20
+ # that spawns a thread each time
21
+ work.each do |job|
22
+ fork do
23
+ # Since the context is copied from the parent process, we clear it
24
+ # and start a new trace via `AppOpticsAPM::API.start_trace`.
25
+ AppOpticsAPM::Context.clear
26
+ result = nil
27
+
28
+ AppOpticsAPM::API.start_trace('do_the_work', nil, :job_id => job.id) do
29
+ result = do_the_work(job)
30
+ end
31
+
32
+ result
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ ##
39
+ # get_the_work
40
+ #
41
+ # Method to retrieve work to do
42
+ #
43
+ def get_the_work
44
+ # We'll just return random integers as a
45
+ # fake work load
46
+ w = []
47
+ w << rand(25)
48
+ w << rand(25)
49
+ w << rand(25)
50
+ end
51
+
52
+ ##
53
+ # do_the_work
54
+ #
55
+ # The work-horse method
56
+ #
57
+ def do_the_work(job_to_do)
58
+ i = job_to_do
59
+ i * Math::PI
60
+ end
61
+
62
+ #########################################################################
63
+ # Notes
64
+ #########################################################################
65
+
66
+ # If your parent process only forks a small number of processes per loop (< 5..10),
67
+ # you may want to mark the child traces as asynchronous and have them directly
68
+ # linked to the parent tracing context.
69
+ #
70
+ # The benefit of this is that instead of having two independent traces (parent
71
+ # and child), you will have a single view of the parent trace showing the
72
+ # spawned child process and it's performance in the AppOptics dashboard.
73
+ #
74
+ # To do this:
75
+ # 1. Don't clear the context in the child process
76
+ # 2. Use `AppOpticsAPM::API.trace` instead
77
+ # 3. Pass the `Async` flag to mark this child as asynchronous
78
+ #
79
+ Kernel.loop do
80
+ AppOpticsAPM::API.start_trace('get_the_work') do
81
+
82
+ work = get_the_work
83
+
84
+ work.each do |job|
85
+ fork do
86
+ result = nil
87
+ # 1 Don't clear context
88
+ # 2 Use `AppOpticsAPM::API.trace` instead
89
+ # 3 Pass the Async flag
90
+ AppOpticsAPM::API.trace('do_the_work', {:job_id => job.id, :Async => 1 }) do
91
+ result = do_the_work(job)
92
+ end
93
+
94
+ result
95
+ end
96
+ end
97
+ end
98
+ sleep 5
99
+ end
@@ -0,0 +1,28 @@
1
+ worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
2
+ timeout 15
3
+ preload_app true
4
+
5
+ before_fork do |server, worker|
6
+ Signal.trap 'TERM' do
7
+ puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
8
+ Process.kill 'QUIT', Process.pid
9
+ end
10
+
11
+ defined?(ActiveRecord::Base) and
12
+ ActiveRecord::Base.connection.disconnect!
13
+
14
+ defined?(::AppOpticsAPM) and
15
+ ::AppOpticsAPM.disconnect!
16
+ end
17
+
18
+ after_fork do |server, worker|
19
+ Signal.trap 'TERM' do
20
+ puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
21
+ end
22
+
23
+ defined?(ActiveRecord::Base) and
24
+ ActiveRecord::Base.establish_connection
25
+
26
+ defined?(::AppOpticsAPM) and
27
+ ::AppOpticsAPM.reconnect!
28
+ end
@@ -1 +1 @@
1
- 2.0.10
1
+ 3.0.0
data/lib/appoptics_apm.rb CHANGED
@@ -60,6 +60,8 @@ begin
60
60
  require 'appoptics_apm/frameworks/sinatra'
61
61
  require 'appoptics_apm/frameworks/padrino'
62
62
  require 'appoptics_apm/frameworks/grape'
63
+ else
64
+ require 'appoptics_apm/noop/context'
63
65
  end
64
66
 
65
67
  # Load Ruby module last. If there is no framework detected,
@@ -40,7 +40,7 @@ module AppOpticsAPM
40
40
  #
41
41
  # Returns nothing.
42
42
  def log(layer, label, opts = {}, event=nil)
43
- return if !AppOpticsAPM.tracing?
43
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
44
44
 
45
45
  event ||= AppOpticsAPM::Context.createEvent
46
46
  log_event(layer, label, event, opts)
@@ -66,11 +66,11 @@ module AppOpticsAPM
66
66
  #
67
67
  # Returns nothing.
68
68
  def log_exception(layer, exn, opts = {})
69
- return if !AppOpticsAPM.tracing? || exn.instance_variable_get(:@oboe_logged)
69
+ return AppOpticsAPM::Context.toString if !AppOpticsAPM.tracing? || exn.instance_variable_get(:@oboe_logged)
70
70
 
71
71
  unless exn
72
72
  AppOpticsAPM.logger.debug '[appoptics_apm/debug] log_exception called with nil exception'
73
- return
73
+ return AppOpticsAPM::Context.toString
74
74
  end
75
75
 
76
76
  opts.merge!(:ErrorClass => exn.class.name,
@@ -180,12 +180,11 @@ module AppOpticsAPM
180
180
  #
181
181
  # Returns an xtrace metadata string if we are tracing
182
182
  def log_end(layer, opts = {})
183
- return unless AppOpticsAPM.tracing?
183
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
184
184
 
185
- log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts) if AppOpticsAPM.tracing?
186
- AppOpticsAPM::Context.toString
185
+ log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts)
187
186
  ensure
188
- # FIXME has_incoming_context commented out, it has importance for JRuby only and breaks ruby tests
187
+ # FIXME has_incoming_context commented out, it has importance for JRuby only but breaks Ruby tests
189
188
  AppOpticsAPM::Context.clear # unless AppOpticsAPM.has_incoming_context?
190
189
  end
191
190
 
@@ -206,7 +205,7 @@ module AppOpticsAPM
206
205
  #
207
206
  # Returns an xtrace metadata string if we are tracing
208
207
  def log_entry(layer, opts = {}, op = nil)
209
- return unless AppOpticsAPM.tracing?
208
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
210
209
 
211
210
  AppOpticsAPM.layer_op = op.to_sym if op
212
211
  log_event(layer, :entry, AppOpticsAPM::Context.createEvent, opts)
@@ -228,7 +227,7 @@ module AppOpticsAPM
228
227
  #
229
228
  # Returns an xtrace metadata string if we are tracing
230
229
  def log_info(layer, opts = {})
231
- return unless AppOpticsAPM.tracing?
230
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
232
231
 
233
232
  log_event(layer, :info, AppOpticsAPM::Context.createEvent, opts)
234
233
  end
@@ -251,7 +250,7 @@ module AppOpticsAPM
251
250
  #
252
251
  # Returns an xtrace metadata string if we are tracing
253
252
  def log_exit(layer, opts = {}, op = nil)
254
- return unless AppOpticsAPM.tracing?
253
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
255
254
 
256
255
  AppOpticsAPM.layer_op = nil if op
257
256
  log_event(layer, :exit, AppOpticsAPM::Context.createEvent, opts)
@@ -270,7 +269,7 @@ module AppOpticsAPM
270
269
  # * +traces+ - An array with X-Trace strings returned from the requests
271
270
  #
272
271
  def log_multi_exit(layer, traces)
273
- return unless AppOpticsAPM.tracing?
272
+ return AppOpticsAPM::Context.toString unless AppOpticsAPM.tracing?
274
273
  task_id = AppOpticsAPM::XTrace.task_id(AppOpticsAPM::Context.toString)
275
274
  event = AppOpticsAPM::Context.createEvent
276
275
  traces.each do |trace|
@@ -289,7 +288,7 @@ module AppOpticsAPM
289
288
  # * +opts+ - A hash containing key/value pairs that will be reported along with this event
290
289
  def log_init(layer = :rack, opts = {})
291
290
  context = AppOpticsAPM::Metadata.makeRandom
292
- return unless context.isValid
291
+ return AppOpticsAPM::Context.toString unless context.isValid
293
292
 
294
293
  event = context.createEvent
295
294
  event.addInfo(APPOPTICS_STR_LAYER, layer.to_s)
@@ -299,6 +298,7 @@ module AppOpticsAPM
299
298
  end
300
299
 
301
300
  AppOpticsAPM::Reporter.sendStatus(event, context)
301
+ AppOpticsAPM::Context.toString
302
302
  end
303
303
 
304
304
  private
@@ -353,6 +353,7 @@ module AppOpticsAPM
353
353
  end if !opts.nil? && opts.any?
354
354
 
355
355
  AppOpticsAPM::Reporter.sendReport(event)
356
+ AppOpticsAPM::Context.toString
356
357
  end
357
358
 
358
359
  end
@@ -51,7 +51,8 @@ module AppOpticsAPM
51
51
  #
52
52
  # Returns the result of the block.
53
53
  def trace(layer, opts = {}, protect_op = nil)
54
- return if protect_op && AppOpticsAPM.layer_op == protect_op.to_sym
54
+ return if !AppOpticsAPM.loaded || (protect_op && AppOpticsAPM.layer_op == protect_op.to_sym)
55
+
55
56
  log_entry(layer, opts, protect_op)
56
57
  begin
57
58
  yield
@@ -158,6 +159,32 @@ module AppOpticsAPM
158
159
  AppOpticsAPM::Context.clear
159
160
  end
160
161
  end
162
+
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
180
+ end
181
+
182
+
183
+ # this is provided for testing
184
+ # returns the current transaction name
185
+ def get_transaction_name
186
+ AppOpticsAPM.transaction_name
187
+ end
161
188
  end
162
189
  end
163
190
  end
@@ -42,6 +42,12 @@ module AppOpticsAPMBase
42
42
  thread_local :sample_rate
43
43
  thread_local :layer
44
44
  thread_local :layer_op
45
+
46
+ # transaction_name is used for custom transaction naming
47
+ # It needs to be globally accessible, but is only set by the request processors of the different frameworks
48
+ # and read by rack
49
+ thread_local :transaction_name
50
+
45
51
  # Semaphore used during the test suite to test
46
52
  # global config options.
47
53
  thread_local :config_lock
@@ -54,7 +60,7 @@ module AppOpticsAPMBase
54
60
  # X-Trace request header, tracing may have already been started
55
61
  # by Joboe. Such a scenario occurs when the application is being
56
62
  # hosted by a Java container (such as Tomcat or Glassfish) and
57
- # JAppOpticsAPM has already initiated tracing. In this case, we shouldn't
63
+ # AppOpticsAPM has already initiated tracing. In this case, we shouldn't
58
64
  # pickup the X-Trace context in the X-Trace header and we shouldn't
59
65
  # set the outgoing response X-Trace header or clear context.
60
66
  # Yeah I know. Yuck.
@@ -73,31 +73,32 @@ module AppOpticsAPM
73
73
  # to create an output similar to the content of the config file
74
74
  #
75
75
  def self.print_config
76
- puts "# General configurations"
76
+ AppOpticsAPM.logger.warn "# General configurations"
77
77
  non_instrumentation = @@config.keys - @@instrumentation
78
78
  non_instrumentation.each do |config|
79
- puts "AppOpticsAPM::Config[:#{config}] = #{@@config[config]}"
79
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:#{config}] = #{@@config[config]}"
80
80
  end
81
81
 
82
- puts "\n# Instrumentation specific configurations"
83
- puts "# Enabled/Disabled Instrumentation"
82
+ AppOpticsAPM.logger.warn "\n# Instrumentation specific configurations"
83
+ AppOpticsAPM.logger.warn "# Enabled/Disabled Instrumentation"
84
84
  @@instrumentation.each do |config|
85
- puts "AppOpticsAPM::Config[:#{config}][:enabled] = #{@@config[config][:enabled]}"
85
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:#{config}][:enabled] = #{@@config[config][:enabled]}"
86
86
  end
87
87
 
88
- puts "\n# Enabled/Disabled Backtrace Collection"
88
+ AppOpticsAPM.logger.warn "\n# Enabled/Disabled Backtrace Collection"
89
89
  @@instrumentation.each do |config|
90
- puts "AppOpticsAPM::Config[:#{config}][:collect_backtraces] = #{@@config[config][:collect_backtraces]}"
90
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:#{config}][:collect_backtraces] = #{@@config[config][:collect_backtraces]}"
91
+ AppOpticsAPM.logger.warn
91
92
  end
92
93
 
93
- puts "\n# Logging of outgoing HTTP query args"
94
+ AppOpticsAPM.logger.warn "\n# Logging of outgoing HTTP query args"
94
95
  @@instrumentation.each do |config|
95
- puts "AppOpticsAPM::Config[:#{config}][:log_args] = #{@@config[config][:log_args]}"
96
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:#{config}][:log_args] = #{@@config[config][:log_args]}"
96
97
  end
97
98
 
98
- puts "\n# Bunny Controller and Action"
99
- puts "AppOpticsAPM::Config[:bunnyconsumer][:controller] = #{@@config[:bunnyconsumer][:controller].inspect}"
100
- puts "AppOpticsAPM::Config[:bunnyconsumer][:action] = #{@@config[:bunnyconsumer][:action].inspect}"
99
+ AppOpticsAPM.logger.warn "\n# Bunny Controller and Action"
100
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:bunnyconsumer][:controller] = #{@@config[:bunnyconsumer][:controller].inspect}"
101
+ AppOpticsAPM.logger.warn "AppOpticsAPM::Config[:bunnyconsumer][:action] = #{@@config[:bunnyconsumer][:action].inspect}"
101
102
  nil
102
103
  end
103
104
 
@@ -112,7 +113,8 @@ module AppOpticsAPM
112
113
  @@instrumentation.each do |k|
113
114
  @@config[k] = {}
114
115
  end
115
- load(File.join(File.dirname(File.dirname(__FILE__)),
116
+ @@config[:transaction_name] = {}
117
+ load(File.join(File.dirname(File.dirname(__FILE__)),
116
118
  'rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb'))
117
119
  end
118
120
  # rubocop:enable Metrics/AbcSize
@@ -51,6 +51,8 @@ module AppOpticsAPM
51
51
  def error_response_with_appoptics(error = {})
52
52
  status, headers, body = error_response_without_appoptics(error)
53
53
 
54
+ xtrace = AppOpticsAPM::Context.toString
55
+
54
56
  if AppOpticsAPM.tracing?
55
57
  # Since Grape uses throw/catch and not Exceptions, we manually log
56
58
  # the error here.
@@ -58,18 +60,17 @@ module AppOpticsAPM
58
60
  kvs[:ErrorClass] = 'GrapeError'
59
61
  kvs[:ErrorMsg] = error[:message] ? error[:message] : "No message given."
60
62
  kvs[:Backtrace] = ::AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:grape][:collect_backtraces]
61
-
62
- ::AppOpticsAPM::API.log(nil, 'error', kvs)
63
+ ::AppOpticsAPM::API.log('rack', 'error', kvs)
63
64
 
64
65
  # Since calls to error() are handled similar to abort in Grape. We
65
66
  # manually log the rack exit here since the original code won't
66
67
  # be returned to
67
68
  xtrace = AppOpticsAPM::API.log_end('rack', :Status => status)
69
+ end
68
70
 
69
- if headers && AppOpticsAPM::XTrace.valid?(xtrace)
70
- unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
71
- headers['X-Trace'] = xtrace if headers.is_a?(Hash)
72
- end
71
+ if headers && AppOpticsAPM::XTrace.valid?(xtrace)
72
+ unless defined?(JRUBY_VERSION) && AppOpticsAPM.is_continued_trace?
73
+ headers['X-Trace'] = xtrace if headers.is_a?(Hash)
73
74
  end
74
75
  end
75
76