scout_apm 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +4 -0
- data/lib/scout_apm.rb +48 -23
- data/lib/scout_apm/agent.rb +93 -130
- data/lib/scout_apm/agent/reporting.rb +34 -63
- data/lib/scout_apm/app_server_load.rb +29 -0
- data/lib/scout_apm/background_worker.rb +6 -6
- data/lib/scout_apm/capacity.rb +48 -48
- data/lib/scout_apm/config.rb +5 -5
- data/lib/scout_apm/context.rb +3 -3
- data/lib/scout_apm/environment.rb +64 -100
- data/lib/scout_apm/framework_integrations/rails_2.rb +32 -0
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +33 -0
- data/lib/scout_apm/framework_integrations/ruby.rb +26 -0
- data/lib/scout_apm/framework_integrations/sinatra.rb +27 -0
- data/lib/scout_apm/instruments/active_record_instruments.rb +1 -1
- data/lib/scout_apm/instruments/mongoid_instruments.rb +1 -1
- data/lib/scout_apm/instruments/moped_instruments.rb +3 -3
- data/lib/scout_apm/instruments/net_http.rb +2 -2
- data/lib/scout_apm/instruments/process/process_cpu.rb +41 -20
- data/lib/scout_apm/instruments/process/process_memory.rb +45 -30
- data/lib/scout_apm/instruments/rails/action_controller_instruments.rb +20 -18
- data/lib/scout_apm/instruments/rails3_or_4/action_controller_instruments.rb +17 -14
- data/lib/scout_apm/layaway.rb +12 -12
- data/lib/scout_apm/layaway_file.rb +1 -1
- data/lib/scout_apm/metric_meta.rb +9 -9
- data/lib/scout_apm/metric_stats.rb +8 -8
- data/lib/scout_apm/reporter.rb +83 -0
- data/lib/scout_apm/serializers/app_server_load_serializer.rb +15 -0
- data/lib/scout_apm/serializers/directive_serializer.rb +15 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +14 -0
- data/lib/scout_apm/server_integrations/null.rb +30 -0
- data/lib/scout_apm/server_integrations/passenger.rb +35 -0
- data/lib/scout_apm/server_integrations/puma.rb +30 -0
- data/lib/scout_apm/server_integrations/rainbows.rb +36 -0
- data/lib/scout_apm/server_integrations/thin.rb +41 -0
- data/lib/scout_apm/server_integrations/unicorn.rb +35 -0
- data/lib/scout_apm/server_integrations/webrick.rb +25 -0
- data/lib/scout_apm/slow_transaction.rb +3 -3
- data/lib/scout_apm/stack_item.rb +19 -17
- data/lib/scout_apm/store.rb +35 -35
- data/lib/scout_apm/tracer.rb +121 -110
- data/lib/scout_apm/utils/sql_sanitizer.rb +2 -2
- data/lib/scout_apm/version.rb +2 -1
- data/test/unit/environment_test.rb +5 -5
- metadata +18 -2
@@ -0,0 +1,25 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module ServerIntegrations
|
3
|
+
class Webrick
|
4
|
+
attr_reader :logger
|
5
|
+
|
6
|
+
def initialize(logger)
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
:webrick
|
12
|
+
end
|
13
|
+
|
14
|
+
def forking?; false; end
|
15
|
+
|
16
|
+
def present?
|
17
|
+
defined?(::WEBrick) && defined?(::WEBrick::VERSION)
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: What does it mean to install on a non-forking env?
|
21
|
+
def install
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,7 +3,7 @@ class ScoutApm::SlowTransaction
|
|
3
3
|
BACKTRACE_LIMIT = 5 # Max length of callers to display
|
4
4
|
MAX_SIZE = 100 # Limits the size of the metric hash to prevent a metric explosion.
|
5
5
|
attr_reader :metric_name, :total_call_time, :metrics, :meta, :uri, :context, :time
|
6
|
-
|
6
|
+
|
7
7
|
# Given a call stack, generates a filtered backtrace that:
|
8
8
|
# * Limits to the app/models, app/controllers, or app/views directories
|
9
9
|
# * Limits to 5 total callers
|
@@ -18,7 +18,7 @@ class ScoutApm::SlowTransaction
|
|
18
18
|
end
|
19
19
|
stack
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def initialize(uri,metric_name,total_call_time,metrics,context,time)
|
23
23
|
@uri = uri
|
24
24
|
@metric_name = metric_name
|
@@ -33,4 +33,4 @@ class ScoutApm::SlowTransaction
|
|
33
33
|
@metrics = nil
|
34
34
|
self
|
35
35
|
end
|
36
|
-
end
|
36
|
+
end
|
data/lib/scout_apm/stack_item.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(metric_name)
|
6
|
-
@metric_name = metric_name
|
7
|
-
@start_time = Time.now
|
8
|
-
@children_time = 0
|
9
|
-
end
|
10
|
-
|
11
|
-
def ==(o)
|
12
|
-
self.eql?(o)
|
13
|
-
end
|
1
|
+
module ScoutApm
|
2
|
+
class StackItem
|
3
|
+
attr_accessor :children_time
|
4
|
+
attr_reader :metric_name, :start_time
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
def initialize(metric_name)
|
7
|
+
@metric_name = metric_name
|
8
|
+
@start_time = Time.now
|
9
|
+
@children_time = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(o)
|
13
|
+
self.eql?(o)
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(o)
|
17
|
+
self.class == o.class && metric_name.eql?(o.metric_name)
|
18
|
+
end
|
19
|
+
end # class StackItem
|
20
|
+
end
|
data/lib/scout_apm/store.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# The store encapsolutes the logic that (1) saves instrumented data by Metric name to memory and (2) maintains a stack (just an Array)
|
2
|
-
# of instrumented methods that are being called. It's accessed via +ScoutApm::Agent.instance.store+.
|
2
|
+
# of instrumented methods that are being called. It's accessed via +ScoutApm::Agent.instance.store+.
|
3
3
|
class ScoutApm::Store
|
4
|
-
|
5
|
-
# Limits the size of the metric hash to prevent a metric explosion.
|
4
|
+
|
5
|
+
# Limits the size of the metric hash to prevent a metric explosion.
|
6
6
|
MAX_SIZE = 1000
|
7
7
|
|
8
|
-
# Limit the number of slow transactions that we store metrics with to prevent writing too much data to the layaway file if there are are many processes and many slow slow_transactions.
|
8
|
+
# Limit the number of slow transactions that we store metrics with to prevent writing too much data to the layaway file if there are are many processes and many slow slow_transactions.
|
9
9
|
MAX_SLOW_TRANSACTIONS_TO_STORE_METRICS = 10
|
10
|
-
|
10
|
+
|
11
11
|
attr_accessor :metric_hash
|
12
12
|
attr_accessor :transaction_hash
|
13
13
|
attr_accessor :stack
|
14
14
|
attr_accessor :slow_transactions # array of slow transaction slow_transactions
|
15
15
|
attr_reader :slow_transaction_lock
|
16
|
-
|
16
|
+
|
17
17
|
def initialize
|
18
18
|
@metric_hash = Hash.new
|
19
19
|
# Stores aggregate metrics for the current transaction. When the transaction is finished, metrics
|
@@ -24,7 +24,7 @@ class ScoutApm::Store
|
|
24
24
|
@slow_transaction_lock = Mutex.new
|
25
25
|
@slow_transactions = Array.new
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
# Called when the last stack item completes for the current transaction to clear
|
29
29
|
# for the next run.
|
30
30
|
def reset_transaction!
|
@@ -33,13 +33,13 @@ class ScoutApm::Store
|
|
33
33
|
@transaction_hash = Hash.new
|
34
34
|
@stack = Array.new
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def ignore_transaction!
|
38
38
|
Thread::current[:scout_apm_ignore_transaction] = true
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Called at the start of Tracer#instrument:
|
42
|
-
# (1) Either finds an existing MetricStats object in the metric_hash or
|
42
|
+
# (1) Either finds an existing MetricStats object in the metric_hash or
|
43
43
|
# initialize a new one. An existing MetricStats object is present if this +metric_name+ has already been instrumented.
|
44
44
|
# (2) Adds a StackItem to the stack. This StackItem is returned and later used to validate the item popped off the stack
|
45
45
|
# when an instrumented code block completes.
|
@@ -48,19 +48,19 @@ class ScoutApm::Store
|
|
48
48
|
stack << item
|
49
49
|
item
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
# Options:
|
53
53
|
# * :scope - If specified, sets the sub-scope for the metric. We allow additional scope level. This is used
|
54
54
|
# * uri - the request uri
|
55
55
|
def stop_recording(sanity_check_item, options={})
|
56
56
|
item = stack.pop
|
57
57
|
stack_empty = stack.empty?
|
58
|
-
# if ignoring the transaction, the item is popped but nothing happens.
|
58
|
+
# if ignoring the transaction, the item is popped but nothing happens.
|
59
59
|
if Thread::current[:scout_apm_ignore_transaction]
|
60
60
|
return
|
61
61
|
end
|
62
|
-
# unbalanced stack check - unreproducable cases have seen this occur. when it does, sets a Thread variable
|
63
|
-
# so we ignore further recordings. +Store#reset_transaction!+ resets this.
|
62
|
+
# unbalanced stack check - unreproducable cases have seen this occur. when it does, sets a Thread variable
|
63
|
+
# so we ignore further recordings. +Store#reset_transaction!+ resets this.
|
64
64
|
if item != sanity_check_item
|
65
65
|
ScoutApm::Agent.instance.logger.warn "Scope [#{Thread::current[:scout_apm_scope_name]}] Popped off stack: #{item.inspect} Expected: #{sanity_check_item.inspect}. Aborting."
|
66
66
|
ignore_transaction!
|
@@ -72,7 +72,7 @@ class ScoutApm::Store
|
|
72
72
|
end
|
73
73
|
meta = ScoutApm::MetricMeta.new(item.metric_name, :desc => options[:desc])
|
74
74
|
meta.scope = nil if stack_empty
|
75
|
-
|
75
|
+
|
76
76
|
# add backtrace for slow calls ... how is exclusive time handled?
|
77
77
|
if duration > ScoutApm::SlowTransaction::BACKTRACE_THRESHOLD and !stack_empty
|
78
78
|
meta.extra = {:backtrace => ScoutApm::SlowTransaction.backtrace_parser(caller)}
|
@@ -80,20 +80,20 @@ class ScoutApm::Store
|
|
80
80
|
stat = transaction_hash[meta] || ScoutApm::MetricStats.new(!stack_empty)
|
81
81
|
stat.update!(duration,duration-item.children_time)
|
82
82
|
transaction_hash[meta] = stat if store_metric?(stack_empty)
|
83
|
-
|
83
|
+
|
84
84
|
# Uses controllers as the entry point for a transaction. Otherwise, stats are ignored.
|
85
85
|
if stack_empty and meta.metric_name.match(/\AController\//)
|
86
86
|
aggs=aggregate_calls(transaction_hash.dup,meta)
|
87
|
-
store_slow(options[:uri],transaction_hash.dup.merge(aggs),meta,stat)
|
88
|
-
# deep duplicate
|
87
|
+
store_slow(options[:uri],transaction_hash.dup.merge(aggs),meta,stat)
|
88
|
+
# deep duplicate
|
89
89
|
duplicate = aggs.dup
|
90
90
|
duplicate.each_pair do |k,v|
|
91
91
|
duplicate[k.dup] = v.dup
|
92
|
-
end
|
93
|
-
merge_metrics(duplicate.merge({meta.dup => stat.dup})) # aggregrates + controller
|
92
|
+
end
|
93
|
+
merge_metrics(duplicate.merge({meta.dup => stat.dup})) # aggregrates + controller
|
94
94
|
end
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
# TODO - Move more logic to SlowTransaction
|
98
98
|
#
|
99
99
|
# Limits the size of the transaction hash to prevent a large transactions. The final item on the stack
|
@@ -101,19 +101,19 @@ class ScoutApm::Store
|
|
101
101
|
def store_metric?(stack_empty)
|
102
102
|
transaction_hash.size < ScoutApm::SlowTransaction::MAX_SIZE or stack_empty
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
# Returns the top-level category names used in the +metrics+ hash.
|
106
106
|
def categories(metrics)
|
107
107
|
cats = Set.new
|
108
108
|
metrics.keys.each do |meta|
|
109
109
|
next if meta.scope.nil? # ignore controller
|
110
|
-
if match=meta.metric_name.match(/\A([\w
|
110
|
+
if match=meta.metric_name.match(/\A([\w]+)\//)
|
111
111
|
cats << match[1]
|
112
112
|
end
|
113
113
|
end # metrics.each
|
114
114
|
cats
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# Takes a metric_hash of calls and generates aggregates for ActiveRecord and View calls.
|
118
118
|
def aggregate_calls(metrics,parent_meta)
|
119
119
|
categories = categories(metrics)
|
@@ -124,30 +124,30 @@ class ScoutApm::Store
|
|
124
124
|
agg_stats = ScoutApm::MetricStats.new
|
125
125
|
metrics.each do |meta,stats|
|
126
126
|
if meta.metric_name =~ /\A#{cat}\//
|
127
|
-
agg_stats.combine!(stats)
|
127
|
+
agg_stats.combine!(stats)
|
128
128
|
end
|
129
129
|
end # metrics.each
|
130
130
|
aggregates[agg_meta] = agg_stats unless agg_stats.call_count.zero?
|
131
|
-
end # categories.each
|
131
|
+
end # categories.each
|
132
132
|
aggregates
|
133
133
|
end
|
134
|
-
|
134
|
+
|
135
135
|
# Stores slow transactions. This will be sent to the server.
|
136
|
-
def store_slow(uri,transaction_hash,parent_meta,parent_stat,options = {})
|
136
|
+
def store_slow(uri,transaction_hash,parent_meta,parent_stat,options = {})
|
137
137
|
@slow_transaction_lock.synchronize do
|
138
138
|
# tree map of all slow transactions
|
139
139
|
if parent_stat.total_call_time >= 2
|
140
140
|
@slow_transactions.push(ScoutApm::SlowTransaction.new(uri,parent_meta.metric_name,parent_stat.total_call_time,transaction_hash.dup,ScoutApm::Context.current,Thread::current[:scout_apm_trace_time]))
|
141
|
-
ScoutApm::Agent.instance.logger.debug "Slow transaction sample added. [URI: #{uri}] [Context: #{ScoutApm::Context.current.to_hash}] Array Size: #{@slow_transactions.size}"
|
141
|
+
ScoutApm::Agent.instance.logger.debug "Slow transaction sample added. [URI: #{uri}] [Context: #{ScoutApm::Context.current.to_hash}] Array Size: #{@slow_transactions.size}"
|
142
142
|
end
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
146
|
-
# Finds or creates the metric w/the given name in the metric_hash, and updates the time. Primarily used to
|
145
|
+
|
146
|
+
# Finds or creates the metric w/the given name in the metric_hash, and updates the time. Primarily used to
|
147
147
|
# record sampled metrics. For instrumented methods, #record and #stop_recording are used.
|
148
148
|
#
|
149
149
|
# Options:
|
150
|
-
# :scope => If provided, overrides the default scope.
|
150
|
+
# :scope => If provided, overrides the default scope.
|
151
151
|
# :exclusive_time => Sets the exclusive time for the method. If not provided, uses +call_time+.
|
152
152
|
def track!(metric_name,call_time,options = {})
|
153
153
|
meta = ScoutApm::MetricMeta.new(metric_name)
|
@@ -156,12 +156,12 @@ class ScoutApm::Store
|
|
156
156
|
stat.update!(call_time,options[:exclusive_time] || call_time)
|
157
157
|
metric_hash[meta] = stat
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
# Combines old and current data
|
161
161
|
def merge_data(old_data)
|
162
162
|
{:metrics => merge_metrics(old_data[:metrics]), :slow_transactions => merge_slow_transactions(old_data[:slow_transactions])}
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
# Merges old and current data, clears the current in-memory metric hash, and returns
|
166
166
|
# the merged data
|
167
167
|
def merge_data_and_clear(old_data)
|
@@ -198,4 +198,4 @@ class ScoutApm::Store
|
|
198
198
|
end
|
199
199
|
self.slow_transactions
|
200
200
|
end
|
201
|
-
end # class Store
|
201
|
+
end # class Store
|
data/lib/scout_apm/tracer.rb
CHANGED
@@ -1,123 +1,134 @@
|
|
1
|
-
# Contains the methods that instrument blocks of code.
|
2
|
-
#
|
1
|
+
# Contains the methods that instrument blocks of code.
|
2
|
+
#
|
3
3
|
# When a code block is wrapped inside #instrument(metric_name):
|
4
4
|
# * The #instrument method pushes a StackItem onto Store#stack
|
5
5
|
# * When a code block is finished, #instrument pops the last item off the stack and verifies it's the StackItem
|
6
|
-
# we created earlier.
|
6
|
+
# we created earlier.
|
7
7
|
# * Once verified, the metrics for the recording session are merged into the in-memory Store#metric_hash. The current scope
|
8
8
|
# is also set for the metric (if Thread::current[:scout_apm_scope_name] isn't nil).
|
9
|
-
module ScoutApm
|
10
|
-
|
11
|
-
klass
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
9
|
+
module ScoutApm
|
10
|
+
module Tracer
|
11
|
+
def self.included(klass)
|
12
|
+
klass.extend ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
# Use to trace a method call, possibly reporting slow transaction traces to Scout.
|
18
|
+
# Options:
|
19
|
+
# * uri - the request uri
|
20
|
+
# * ip - the remote ip of the user. This is merged into the User context.
|
21
|
+
def scout_apm_trace(metric_name, options = {}, &block)
|
22
|
+
# TODO - wrap a lot of this into a Trace class, store that as a Thread var.
|
23
|
+
ScoutApm::Agent.instance.store.reset_transaction!
|
24
|
+
ScoutApm::Context.current.add_user(:ip => options[:ip]) if options[:ip]
|
25
|
+
Thread::current[:scout_apm_trace_time] = Time.now.utc
|
26
|
+
ScoutApm::Agent.instance.capacity.start_transaction!
|
27
|
+
e = nil
|
28
|
+
instrument(metric_name, options) do
|
29
|
+
Thread::current[:scout_apm_scope_name] = metric_name
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
rescue Exception => e
|
33
|
+
ScoutApm::Agent.instance.store.track!("Errors/#{metric_name}",1, :scope => nil)
|
34
|
+
end
|
35
|
+
Thread::current[:scout_apm_scope_name] = nil
|
36
|
+
end
|
37
|
+
Thread::current[:scout_apm_trace_time] = nil
|
38
|
+
ScoutApm::Agent.instance.capacity.finish_transaction!
|
39
|
+
# The context is cleared after instrumentation (rather than before) as tracing controller-actions doesn't occur until the controller-action is called.
|
40
|
+
# It does not trace before filters, which is a likely spot to add context. This means that any context applied during before_filters would be cleared.
|
41
|
+
ScoutApm::Context.clear!
|
42
|
+
raise e if e
|
43
|
+
end
|
44
|
+
|
45
|
+
# Options:
|
46
|
+
# * :scope - If specified, sets the sub-scope for the metric. We allow additional scope level. This is used
|
47
|
+
# * uri - the request uri
|
48
|
+
# * :ignore_children - will not instrument any method calls beneath this call. Example use case: InfluxDB uses Net::HTTP, which is instrumented. However, we can provide more specific data if we know we're doing an influx call, so we'd rather just instrument the Influx call and ignore Net::HTTP.
|
49
|
+
# when rendering the transaction tree in the UI.
|
50
|
+
def instrument(metric_name, options={}, &block)
|
51
|
+
# don't instrument if (1) NOT inside a transaction and (2) NOT a Controller metric.
|
52
|
+
if !Thread::current[:scout_apm_scope_name] and metric_name !~ /\AController\//
|
53
|
+
return yield
|
54
|
+
elsif Thread::current[:scout_ignore_children]
|
55
|
+
return yield
|
56
|
+
end
|
57
|
+
if options.delete(:scope)
|
58
|
+
Thread::current[:scout_apm_sub_scope] = metric_name
|
59
|
+
end
|
60
|
+
if options[:ignore_children]
|
61
|
+
Thread::current[:scout_ignore_children] = true
|
62
|
+
end
|
63
|
+
stack_item = ScoutApm::Agent.instance.store.record(metric_name)
|
29
64
|
begin
|
30
65
|
yield
|
31
|
-
|
32
|
-
|
66
|
+
ensure
|
67
|
+
Thread::current[:scout_apm_sub_scope] = nil if Thread::current[:scout_apm_sub_scope] == metric_name
|
68
|
+
if options[:ignore_children]
|
69
|
+
Thread::current[:scout_ignore_children] = nil
|
70
|
+
end
|
71
|
+
ScoutApm::Agent.instance.store.stop_recording(stack_item,options)
|
33
72
|
end
|
34
|
-
Thread::current[:scout_apm_scope_name] = nil
|
35
73
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# * :scope - If specified, sets the sub-scope for the metric. We allow additional scope level. This is used
|
46
|
-
# * uri - the request uri
|
47
|
-
# when rendering the transaction tree in the UI.
|
48
|
-
def instrument(metric_name, options={}, &block)
|
49
|
-
# don't instrument if (1) NOT inside a transaction and (2) NOT a Controller metric.
|
50
|
-
if !Thread::current[:scout_apm_scope_name] and metric_name !~ /\AController\//
|
51
|
-
return yield
|
74
|
+
|
75
|
+
def instrument_method(method,options = {})
|
76
|
+
ScoutApm::Agent.instance.logger.info "Instrumenting #{method}"
|
77
|
+
metric_name = options[:metric_name] || default_metric_name(method)
|
78
|
+
return if !instrumentable?(method) or instrumented?(method,metric_name)
|
79
|
+
class_eval instrumented_method_string(method, {:metric_name => metric_name, :scope => options[:scope]}), __FILE__, __LINE__
|
80
|
+
|
81
|
+
alias_method _uninstrumented_method_name(method, metric_name), method
|
82
|
+
alias_method method, _instrumented_method_name(method, metric_name)
|
52
83
|
end
|
53
|
-
|
54
|
-
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def instrumented_method_string(method, options)
|
88
|
+
klass = (self === Module) ? "self" : "self.class"
|
89
|
+
"def #{_instrumented_method_name(method, options[:metric_name])}(*args, &block)
|
90
|
+
result = #{klass}.instrument(\"#{options[:metric_name]}\",{:scope => #{options[:scope] || false}}) do
|
91
|
+
#{_uninstrumented_method_name(method, options[:metric_name])}(*args, &block)
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end"
|
55
95
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
96
|
+
|
97
|
+
# The method must exist to be instrumented.
|
98
|
+
def instrumentable?(method)
|
99
|
+
exists = method_defined?(method) || private_method_defined?(method)
|
100
|
+
ScoutApm::Agent.instance.logger.warn "The method [#{self.name}##{method}] does not exist and will not be instrumented" unless exists
|
101
|
+
exists
|
102
|
+
end
|
103
|
+
|
104
|
+
# +True+ if the method is already instrumented.
|
105
|
+
def instrumented?(method,metric_name)
|
106
|
+
instrumented = method_defined?(_instrumented_method_name(method, metric_name))
|
107
|
+
ScoutApm::Agent.instance.logger.warn "The method [#{self.name}##{method}] has already been instrumented" if instrumented
|
108
|
+
instrumented
|
109
|
+
end
|
110
|
+
|
111
|
+
def default_metric_name(method)
|
112
|
+
"Custom/#{self.name}/#{method.to_s}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# given a method and a metric, this method returns the
|
116
|
+
# untraced alias of the method name
|
117
|
+
def _uninstrumented_method_name(method, metric_name)
|
118
|
+
"#{_sanitize_name(method)}_without_scout_instrument_#{_sanitize_name(metric_name)}"
|
119
|
+
end
|
120
|
+
|
121
|
+
# given a method and a metric, this method returns the traced
|
122
|
+
# alias of the method name
|
123
|
+
def _instrumented_method_name(method, metric_name)
|
124
|
+
"#{_sanitize_name(method)}_with_scout_instrument_#{_sanitize_name(metric_name)}"
|
125
|
+
end
|
126
|
+
|
127
|
+
# Method names like +any?+ or +replace!+ contain a trailing character that would break when
|
128
|
+
# eval'd as ? and ! aren't allowed inside method names.
|
129
|
+
def _sanitize_name(name)
|
130
|
+
name.to_s.tr_s('^a-zA-Z0-9', '_')
|
62
131
|
end
|
63
132
|
end
|
64
|
-
|
65
|
-
|
66
|
-
ScoutApm::Agent.instance.logger.info "Instrumenting #{method}"
|
67
|
-
metric_name = options[:metric_name] || default_metric_name(method)
|
68
|
-
return if !instrumentable?(method) or instrumented?(method,metric_name)
|
69
|
-
class_eval instrumented_method_string(method, {:metric_name => metric_name, :scope => options[:scope]}), __FILE__, __LINE__
|
70
|
-
|
71
|
-
alias_method _uninstrumented_method_name(method, metric_name), method
|
72
|
-
alias_method method, _instrumented_method_name(method, metric_name)
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def instrumented_method_string(method, options)
|
78
|
-
klass = (self === Module) ? "self" : "self.class"
|
79
|
-
"def #{_instrumented_method_name(method, options[:metric_name])}(*args, &block)
|
80
|
-
result = #{klass}.instrument(\"#{options[:metric_name]}\",{:scope => #{options[:scope] || false}}) do
|
81
|
-
#{_uninstrumented_method_name(method, options[:metric_name])}(*args, &block)
|
82
|
-
end
|
83
|
-
result
|
84
|
-
end"
|
85
|
-
end
|
86
|
-
|
87
|
-
# The method must exist to be instrumented.
|
88
|
-
def instrumentable?(method)
|
89
|
-
exists = method_defined?(method) || private_method_defined?(method)
|
90
|
-
ScoutApm::Agent.instance.logger.warn "The method [#{self.name}##{method}] does not exist and will not be instrumented" unless exists
|
91
|
-
exists
|
92
|
-
end
|
93
|
-
|
94
|
-
# +True+ if the method is already instrumented.
|
95
|
-
def instrumented?(method,metric_name)
|
96
|
-
instrumented = method_defined?(_instrumented_method_name(method, metric_name))
|
97
|
-
ScoutApm::Agent.instance.logger.warn "The method [#{self.name}##{method}] has already been instrumented" if instrumented
|
98
|
-
instrumented
|
99
|
-
end
|
100
|
-
|
101
|
-
def default_metric_name(method)
|
102
|
-
"Custom/#{self.name}/#{method.to_s}"
|
103
|
-
end
|
104
|
-
|
105
|
-
# given a method and a metric, this method returns the
|
106
|
-
# untraced alias of the method name
|
107
|
-
def _uninstrumented_method_name(method, metric_name)
|
108
|
-
"#{_sanitize_name(method)}_without_scout_instrument_#{_sanitize_name(metric_name)}"
|
109
|
-
end
|
110
|
-
|
111
|
-
# given a method and a metric, this method returns the traced
|
112
|
-
# alias of the method name
|
113
|
-
def _instrumented_method_name(method, metric_name)
|
114
|
-
name = "#{_sanitize_name(method)}_with_scout_instrument_#{_sanitize_name(metric_name)}"
|
115
|
-
end
|
116
|
-
|
117
|
-
# Method names like +any?+ or +replace!+ contain a trailing character that would break when
|
118
|
-
# eval'd as ? and ! aren't allowed inside method names.
|
119
|
-
def _sanitize_name(name)
|
120
|
-
name.to_s.tr_s('^a-zA-Z0-9', '_')
|
121
|
-
end
|
122
|
-
end # ClassMethods
|
123
|
-
end # module Tracer
|
133
|
+
end
|
134
|
+
end
|