appoptics_apm 4.1.2 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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