scout_apm 2.6.10 → 3.0.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -2
- data/.rubocop.yml +3 -11
- data/CHANGELOG.markdown +4 -362
- data/Gemfile +1 -14
- data/README.markdown +7 -52
- data/Rakefile +1 -0
- data/ext/allocations/allocations.c +1 -7
- data/ext/allocations/extconf.rb +0 -1
- data/ext/rusage/rusage.c +0 -26
- data/ext/stacks/extconf.rb +37 -0
- data/ext/stacks/scout_atomics.h +86 -0
- data/ext/stacks/stacks.c +811 -0
- data/lib/scout_apm/agent/logging.rb +69 -0
- data/lib/scout_apm/agent/reporting.rb +126 -0
- data/lib/scout_apm/agent.rb +259 -138
- data/lib/scout_apm/app_server_load.rb +15 -41
- data/lib/scout_apm/attribute_arranger.rb +3 -14
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -70
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +24 -31
- data/lib/scout_apm/background_worker.rb +12 -23
- data/lib/scout_apm/capacity.rb +57 -0
- data/lib/scout_apm/config.rb +37 -206
- data/lib/scout_apm/context.rb +4 -20
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
- data/lib/scout_apm/environment.rb +28 -42
- data/lib/scout_apm/fake_store.rb +0 -12
- data/lib/scout_apm/framework_integrations/rails_2.rb +1 -2
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +6 -17
- data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
- data/lib/scout_apm/histogram.rb +3 -12
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +54 -202
- data/lib/scout_apm/instant_reporting.rb +7 -7
- data/lib/scout_apm/instruments/.DS_Store +0 -0
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +9 -15
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +76 -124
- data/lib/scout_apm/instruments/active_record.rb +29 -324
- data/lib/scout_apm/instruments/delayed_job.rb +57 -0
- data/lib/scout_apm/instruments/elasticsearch.rb +6 -10
- data/lib/scout_apm/instruments/grape.rb +9 -12
- data/lib/scout_apm/instruments/http_client.rb +7 -14
- data/lib/scout_apm/instruments/influxdb.rb +6 -10
- data/lib/scout_apm/instruments/middleware_detailed.rb +11 -15
- data/lib/scout_apm/instruments/middleware_summary.rb +5 -11
- data/lib/scout_apm/instruments/mongoid.rb +8 -39
- data/lib/scout_apm/instruments/moped.rb +6 -11
- data/lib/scout_apm/instruments/net_http.rb +9 -27
- data/lib/scout_apm/instruments/percentile_sampler.rb +23 -42
- data/lib/scout_apm/instruments/process/process_cpu.rb +6 -11
- data/lib/scout_apm/instruments/process/process_memory.rb +12 -17
- data/lib/scout_apm/instruments/rails_router.rb +6 -12
- data/lib/scout_apm/instruments/redis.rb +6 -10
- data/lib/scout_apm/instruments/sinatra.rb +4 -5
- data/lib/scout_apm/job_record.rb +2 -4
- data/lib/scout_apm/layaway.rb +34 -88
- data/lib/scout_apm/layaway_file.rb +3 -13
- data/lib/scout_apm/layer.rb +60 -25
- data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +6 -7
- data/lib/scout_apm/layer_converters/converter_base.rb +14 -203
- data/lib/scout_apm/layer_converters/depth_first_walker.rb +10 -22
- data/lib/scout_apm/layer_converters/error_converter.rb +8 -8
- data/lib/scout_apm/layer_converters/job_converter.rb +50 -37
- data/lib/scout_apm/layer_converters/metric_converter.rb +19 -18
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +13 -13
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +116 -52
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +120 -51
- data/lib/scout_apm/metric_meta.rb +5 -0
- data/lib/scout_apm/metric_set.rb +1 -9
- data/lib/scout_apm/metric_stats.rb +8 -7
- data/lib/scout_apm/middleware.rb +9 -7
- data/lib/scout_apm/reporter.rb +24 -71
- data/lib/scout_apm/request_histograms.rb +0 -12
- data/lib/scout_apm/request_manager.rb +7 -5
- data/lib/scout_apm/scored_item_set.rb +0 -7
- data/lib/scout_apm/serializers/app_server_load_serializer.rb +0 -4
- data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
- data/lib/scout_apm/serializers/directive_serializer.rb +0 -4
- data/lib/scout_apm/serializers/payload_serializer.rb +4 -11
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +16 -35
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
- data/lib/scout_apm/server_integrations/passenger.rb +1 -1
- data/lib/scout_apm/server_integrations/puma.rb +2 -5
- data/lib/scout_apm/slow_job_policy.rb +13 -25
- data/lib/scout_apm/slow_job_record.rb +4 -13
- data/lib/scout_apm/slow_request_policy.rb +13 -25
- data/lib/scout_apm/slow_transaction.rb +5 -25
- data/lib/scout_apm/store.rb +32 -99
- data/lib/scout_apm/trace_compactor.rb +312 -0
- data/lib/scout_apm/tracer.rb +31 -35
- data/lib/scout_apm/tracked_request.rb +95 -262
- data/lib/scout_apm/utils/active_record_metric_name.rb +13 -88
- data/lib/scout_apm/utils/backtrace_parser.rb +4 -7
- data/lib/scout_apm/utils/fake_stacks.rb +87 -0
- data/lib/scout_apm/utils/installed_gems.rb +3 -7
- data/lib/scout_apm/utils/klass_helper.rb +2 -8
- data/lib/scout_apm/utils/null_logger.rb +13 -0
- data/lib/scout_apm/utils/sql_sanitizer.rb +5 -16
- data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -7
- data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -6
- data/lib/scout_apm/utils/unique_id.rb +0 -27
- data/lib/scout_apm/version.rb +2 -1
- data/lib/scout_apm.rb +25 -84
- data/scout_apm.gemspec +3 -17
- data/test/test_helper.rb +3 -57
- data/test/unit/agent_test.rb +54 -1
- data/test/unit/background_job_integrations/sidekiq_test.rb +3 -0
- data/test/unit/config_test.rb +12 -25
- data/test/unit/context_test.rb +4 -4
- data/test/unit/histogram_test.rb +4 -25
- data/test/unit/ignored_uris_test.rb +1 -1
- data/test/unit/instruments/active_record_instruments_test.rb +5 -0
- data/test/unit/layaway_test.rb +2 -62
- data/test/unit/serializers/payload_serializer_test.rb +15 -43
- data/test/unit/slow_request_policy_test.rb +6 -15
- data/test/unit/sql_sanitizer_test.rb +6 -53
- data/test/unit/store_test.rb +4 -73
- data/test/unit/utils/active_record_metric_name_test.rb +5 -59
- data/test/unit/utils/backtrace_parser_test.rb +1 -6
- data/tester.rb +53 -0
- metadata +28 -229
- data/.travis.yml +0 -26
- data/Guardfile +0 -43
- data/gems/README.md +0 -28
- data/gems/octoshark.gemfile +0 -4
- data/gems/rails3.gemfile +0 -5
- data/gems/rails4.gemfile +0 -4
- data/gems/rails5.gemfile +0 -4
- data/gems/rails6.gemfile +0 -4
- data/lib/scout_apm/agent/exit_handler.rb +0 -65
- data/lib/scout_apm/agent/preconditions.rb +0 -81
- data/lib/scout_apm/agent_context.rb +0 -261
- data/lib/scout_apm/auto_instrument/instruction_sequence.rb +0 -31
- data/lib/scout_apm/auto_instrument/layer.rb +0 -23
- data/lib/scout_apm/auto_instrument/parser.rb +0 -27
- data/lib/scout_apm/auto_instrument/rails.rb +0 -175
- data/lib/scout_apm/auto_instrument.rb +0 -5
- data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +0 -55
- data/lib/scout_apm/background_job_integrations/que.rb +0 -134
- data/lib/scout_apm/background_job_integrations/resque.rb +0 -88
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +0 -124
- data/lib/scout_apm/background_job_integrations/sneakers.rb +0 -87
- data/lib/scout_apm/background_recorder.rb +0 -48
- data/lib/scout_apm/db_query_metric_set.rb +0 -97
- data/lib/scout_apm/db_query_metric_stats.rb +0 -102
- data/lib/scout_apm/debug.rb +0 -37
- data/lib/scout_apm/detailed_trace.rb +0 -217
- data/lib/scout_apm/error.rb +0 -27
- data/lib/scout_apm/error_service/error_buffer.rb +0 -39
- data/lib/scout_apm/error_service/error_record.rb +0 -211
- data/lib/scout_apm/error_service/ignored_exceptions.rb +0 -66
- data/lib/scout_apm/error_service/middleware.rb +0 -32
- data/lib/scout_apm/error_service/notifier.rb +0 -33
- data/lib/scout_apm/error_service/payload.rb +0 -47
- data/lib/scout_apm/error_service/periodic_work.rb +0 -17
- data/lib/scout_apm/error_service/railtie.rb +0 -11
- data/lib/scout_apm/error_service/sidekiq.rb +0 -80
- data/lib/scout_apm/error_service.rb +0 -32
- data/lib/scout_apm/extensions/config.rb +0 -87
- data/lib/scout_apm/extensions/transaction_callback_payload.rb +0 -74
- data/lib/scout_apm/git_revision.rb +0 -59
- data/lib/scout_apm/instrument_manager.rb +0 -88
- data/lib/scout_apm/instruments/action_view.rb +0 -141
- data/lib/scout_apm/instruments/http.rb +0 -48
- data/lib/scout_apm/instruments/memcached.rb +0 -43
- data/lib/scout_apm/instruments/resque.rb +0 -39
- data/lib/scout_apm/instruments/samplers.rb +0 -11
- data/lib/scout_apm/layer_children_set.rb +0 -86
- data/lib/scout_apm/layer_converters/database_converter.rb +0 -70
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +0 -38
- data/lib/scout_apm/layer_converters/histograms.rb +0 -15
- data/lib/scout_apm/layer_converters/trace_converter.rb +0 -184
- data/lib/scout_apm/limited_layer.rb +0 -126
- data/lib/scout_apm/logger.rb +0 -158
- data/lib/scout_apm/periodic_work.rb +0 -47
- data/lib/scout_apm/rack.rb +0 -26
- data/lib/scout_apm/remote/message.rb +0 -27
- data/lib/scout_apm/remote/recorder.rb +0 -57
- data/lib/scout_apm/remote/router.rb +0 -49
- data/lib/scout_apm/remote/server.rb +0 -60
- data/lib/scout_apm/reporting.rb +0 -143
- data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +0 -15
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
- data/lib/scout_apm/synchronous_recorder.rb +0 -30
- data/lib/scout_apm/tasks/doctor.rb +0 -75
- data/lib/scout_apm/tasks/support.rb +0 -22
- data/lib/scout_apm/transaction.rb +0 -13
- data/lib/scout_apm/transaction_time_consumed.rb +0 -51
- data/lib/scout_apm/utils/gzip_helper.rb +0 -24
- data/lib/scout_apm/utils/marshal_logging.rb +0 -90
- data/lib/scout_apm/utils/numbers.rb +0 -14
- data/lib/scout_apm/utils/scm.rb +0 -14
- data/lib/tasks/doctor.rake +0 -11
- data/test/tmp/README.md +0 -17
- data/test/unit/agent_context_test.rb +0 -15
- data/test/unit/auto_instrument/assignments-instrumented.rb +0 -31
- data/test/unit/auto_instrument/assignments.rb +0 -31
- data/test/unit/auto_instrument/controller-ast.txt +0 -57
- data/test/unit/auto_instrument/controller-instrumented.rb +0 -49
- data/test/unit/auto_instrument/controller.rb +0 -49
- data/test/unit/auto_instrument/rescue_from-instrumented.rb +0 -13
- data/test/unit/auto_instrument/rescue_from.rb +0 -13
- data/test/unit/auto_instrument_test.rb +0 -54
- data/test/unit/db_query_metric_set_test.rb +0 -67
- data/test/unit/db_query_metric_stats_test.rb +0 -113
- data/test/unit/error_service/error_buffer_test.rb +0 -25
- data/test/unit/error_service/ignored_exceptions_test.rb +0 -49
- data/test/unit/extensions/periodic_callbacks_test.rb +0 -58
- data/test/unit/extensions/transaction_callbacks_test.rb +0 -58
- data/test/unit/fake_store_test.rb +0 -10
- data/test/unit/git_revision_test.rb +0 -15
- data/test/unit/instruments/active_record_test.rb +0 -40
- data/test/unit/instruments/net_http_test.rb +0 -27
- data/test/unit/instruments/percentile_sampler_test.rb +0 -133
- data/test/unit/layer_children_set_test.rb +0 -97
- data/test/unit/layer_converters/depth_first_walker_test.rb +0 -70
- data/test/unit/layer_converters/metric_converter_test.rb +0 -22
- data/test/unit/layer_converters/stubs.rb +0 -33
- data/test/unit/limited_layer_test.rb +0 -53
- data/test/unit/logger_test.rb +0 -69
- data/test/unit/remote/test_message.rb +0 -13
- data/test/unit/remote/test_router.rb +0 -33
- data/test/unit/remote/test_server.rb +0 -15
- data/test/unit/request_histograms_test.rb +0 -17
- data/test/unit/tracer_test.rb +0 -76
- data/test/unit/tracked_request_test.rb +0 -71
- data/test/unit/transaction_test.rb +0 -14
- data/test/unit/transaction_time_consumed_test.rb +0 -46
- data/test/unit/utils/numbers_test.rb +0 -15
- data/test/unit/utils/scm.rb +0 -17
data/lib/scout_apm/layaway.rb
CHANGED
@@ -7,24 +7,23 @@
|
|
7
7
|
#
|
8
8
|
module ScoutApm
|
9
9
|
class Layaway
|
10
|
+
# How old a file needs to be in Seconds before it gets reported.
|
11
|
+
REPORTING_AGE = 120
|
12
|
+
|
10
13
|
# How long to let a stale file sit before deleting it.
|
11
14
|
# Letting it sit a bit may be useful for debugging
|
12
15
|
STALE_AGE = 10 * 60
|
13
16
|
|
14
|
-
# Failsafe to prevent writing layaway files if for some reason they are not being cleaned up
|
15
|
-
MAX_FILES_LIMIT = 5000
|
16
|
-
|
17
17
|
# A strftime format string for how we render timestamps in filenames.
|
18
18
|
# Must be sortable as an integer
|
19
19
|
TIME_FORMAT = "%Y%m%d%H%M"
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
@context = context
|
24
|
-
end
|
21
|
+
attr_accessor :config
|
22
|
+
attr_reader :environment
|
25
23
|
|
26
|
-
def
|
27
|
-
|
24
|
+
def initialize(config, environment)
|
25
|
+
@config = config
|
26
|
+
@environment = environment
|
28
27
|
end
|
29
28
|
|
30
29
|
# Returns a Pathname object with the fully qualified directory where the layaway files can be placed.
|
@@ -35,54 +34,33 @@ module ScoutApm
|
|
35
34
|
def directory
|
36
35
|
return @directory if @directory
|
37
36
|
|
38
|
-
data_file =
|
37
|
+
data_file = config.value("data_file")
|
39
38
|
data_file = File.dirname(data_file) if data_file && !File.directory?(data_file)
|
40
39
|
|
41
40
|
candidates = [
|
42
41
|
data_file,
|
43
|
-
"#{
|
42
|
+
"#{environment.root}/tmp",
|
44
43
|
"/tmp"
|
45
44
|
].compact
|
46
45
|
|
47
46
|
found = candidates.detect { |dir| File.writable?(dir) }
|
48
|
-
logger.debug("Storing Layaway Files in #{found}")
|
47
|
+
ScoutApm::Agent.instance.logger.debug("Storing Layaway Files in #{found}")
|
49
48
|
@directory = Pathname.new(found)
|
50
49
|
end
|
51
50
|
|
52
|
-
def write_reporting_period(reporting_period
|
53
|
-
if at_layaway_file_limit?(files_limit)
|
54
|
-
# This will happen constantly once we hit this case, so only log the first time
|
55
|
-
@wrote_layaway_limit_error_message ||= logger.error("Layaway: Hit layaway file limit. Not writing to layaway file")
|
56
|
-
return false
|
57
|
-
end
|
58
|
-
logger.debug("Layaway: wrote time period: #{reporting_period.timestamp}")
|
51
|
+
def write_reporting_period(reporting_period)
|
59
52
|
filename = file_for(reporting_period.timestamp)
|
60
|
-
layaway_file = LayawayFile.new(
|
53
|
+
layaway_file = LayawayFile.new(filename)
|
61
54
|
layaway_file.write(reporting_period)
|
62
|
-
rescue => e
|
63
|
-
logger.debug("Layaway: error writing: #{e.message}, #{e.backtrace.inspect}")
|
64
|
-
raise e
|
65
55
|
end
|
66
56
|
|
67
|
-
# Claims a given timestamp
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# Once the 'claim' is obtained:
|
72
|
-
# * load and yield each ReportingPeriod from the layaway files.
|
73
|
-
# * if there are reporting periods:
|
74
|
-
# * yields any ReportingPeriods collected up from all the files.
|
75
|
-
# * deletes all of the layaway files (including the coordinator) for the timestamp
|
76
|
-
# * if not
|
77
|
-
# * delete the coordinator
|
78
|
-
# * remove any stale layaway files that may be hanging around.
|
79
|
-
# * Finally unlock and ensure the coordinator file is cleared.
|
80
|
-
#
|
81
|
-
# If a claim file can't be obtained, return false without doing any work
|
82
|
-
# Another process is handling the reporting.
|
57
|
+
# Claims a given timestamp (getting a lock on a particular filename),
|
58
|
+
# then yields ReportingPeriods collected up from all the files.
|
59
|
+
# If the yield returns truthy, delete the layaway files that made it up.
|
83
60
|
def with_claim(timestamp)
|
84
61
|
coordinator_file = glob_pattern(timestamp, :coordinator)
|
85
62
|
|
63
|
+
|
86
64
|
begin
|
87
65
|
# This file gets deleted only by a process that successfully created and obtained the exclusive lock
|
88
66
|
f = File.open(coordinator_file, File::RDWR | File::CREAT | File::EXCL | File::NONBLOCK)
|
@@ -92,35 +70,25 @@ module ScoutApm
|
|
92
70
|
|
93
71
|
begin
|
94
72
|
if f
|
95
|
-
begin
|
96
|
-
logger.debug("Obtained Reporting Lock")
|
97
|
-
|
98
|
-
log_layaway_file_information
|
99
73
|
|
100
|
-
|
101
|
-
rps = files.map{ |layaway| LayawayFile.new(context, layaway).load }.compact
|
102
|
-
if rps.any?
|
103
|
-
yield rps
|
74
|
+
ScoutApm::Agent.instance.logger.debug("Obtained Reporting Lock")
|
104
75
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
logger.debug("Layaway: No files to report")
|
110
|
-
end
|
76
|
+
files = all_files_for(timestamp).reject{|l| l.to_s == coordinator_file.to_s }
|
77
|
+
rps = files.map{ |layaway| LayawayFile.new(layaway).load }.compact
|
78
|
+
if rps.any?
|
79
|
+
yield rps
|
111
80
|
|
112
|
-
|
81
|
+
delete_files_for(timestamp) # also removes the coodinator_file
|
113
82
|
delete_stale_files(timestamp.to_time - STALE_AGE)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
logger.debug("Layaway: Caught an exception in with_claim, with the coordination file locked: #{e.message}, #{e.backtrace.inspect}")
|
118
|
-
raise
|
119
|
-
ensure
|
120
|
-
# Unlock the file when done!
|
121
|
-
f.flock(File::LOCK_UN | File::LOCK_NB)
|
122
|
-
f.close
|
83
|
+
else
|
84
|
+
File.unlink(coordinator_file)
|
85
|
+
ScoutApm::Agent.instance.logger.debug("No layaway files to report")
|
123
86
|
end
|
87
|
+
|
88
|
+
# Unlock the file when done!
|
89
|
+
f.flock(File::LOCK_UN | File::LOCK_NB)
|
90
|
+
f.close
|
91
|
+
true
|
124
92
|
else
|
125
93
|
# Didn't obtain lock, another process is reporting. Return false from this function, but otherwise no work
|
126
94
|
false
|
@@ -129,10 +97,7 @@ module ScoutApm
|
|
129
97
|
end
|
130
98
|
|
131
99
|
def delete_files_for(timestamp)
|
132
|
-
all_files_for(timestamp).each { |layaway|
|
133
|
-
logger.debug("Layaway: Deleting file: #{layaway}")
|
134
|
-
File.unlink(layaway)
|
135
|
-
}
|
100
|
+
all_files_for(timestamp).each { |layaway| File.unlink(layaway) }
|
136
101
|
end
|
137
102
|
|
138
103
|
def delete_stale_files(older_than)
|
@@ -141,10 +106,8 @@ module ScoutApm
|
|
141
106
|
compact.
|
142
107
|
uniq.
|
143
108
|
select { |timestamp| timestamp.to_i < older_than.strftime(TIME_FORMAT).to_i }.
|
144
|
-
tap { |timestamps| logger.debug("
|
109
|
+
tap { |timestamps| ScoutApm::Agent.instance.logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
|
145
110
|
map { |timestamp| delete_files_for(timestamp) }
|
146
|
-
rescue => e
|
147
|
-
logger.debug("Layaway: Problem deleting stale files: #{e.message}, #{e.backtrace.inspect}")
|
148
111
|
end
|
149
112
|
|
150
113
|
private
|
@@ -188,30 +151,13 @@ module ScoutApm
|
|
188
151
|
end
|
189
152
|
|
190
153
|
def timestamp_from_filename(filename)
|
191
|
-
match = filename.match(%r{scout_(
|
154
|
+
match = filename.match(%r{scout_(.*)_.*\.data})
|
192
155
|
if match
|
193
156
|
match[1]
|
194
157
|
else
|
195
158
|
nil
|
196
159
|
end
|
197
160
|
end
|
198
|
-
|
199
|
-
def at_layaway_file_limit?(files_limit = MAX_FILES_LIMIT)
|
200
|
-
all_files_for(:all).count >= files_limit
|
201
|
-
end
|
202
|
-
|
203
|
-
def log_layaway_file_information
|
204
|
-
files_in_temp = Dir["#{directory}/*"].count
|
205
|
-
|
206
|
-
all_filenames = all_files_for(:all)
|
207
|
-
count_per_timestamp = Hash[
|
208
|
-
all_filenames.
|
209
|
-
group_by {|f| timestamp_from_filename(f) }.
|
210
|
-
map{ |timestamp, list| [timestamp, list.length] }
|
211
|
-
]
|
212
|
-
|
213
|
-
logger.debug("Layaway: Total Files in #{directory}: #{files_in_temp}. Total Layaway Files: #{all_filenames.size}. By Timestamp: #{count_per_timestamp.inspect}")
|
214
|
-
end
|
215
161
|
end
|
216
162
|
end
|
217
163
|
|
@@ -2,15 +2,9 @@
|
|
2
2
|
module ScoutApm
|
3
3
|
class LayawayFile
|
4
4
|
attr_reader :path
|
5
|
-
attr_reader :context
|
6
5
|
|
7
|
-
def initialize(
|
6
|
+
def initialize(path)
|
8
7
|
@path = path
|
9
|
-
@context = context
|
10
|
-
end
|
11
|
-
|
12
|
-
def logger
|
13
|
-
context.logger
|
14
8
|
end
|
15
9
|
|
16
10
|
def load
|
@@ -18,8 +12,8 @@ module ScoutApm
|
|
18
12
|
deserialize(data)
|
19
13
|
rescue NameError, ArgumentError, TypeError => e
|
20
14
|
# Marshal error
|
21
|
-
logger.info("
|
22
|
-
logger.debug("#{e.message}, #{e.backtrace.join("\n\t")}")
|
15
|
+
ScoutApm::Agent.instance.logger.info("Unable to load data from Layaway file, resetting.")
|
16
|
+
ScoutApm::Agent.instance.logger.debug("#{e.message}, #{e.backtrace.join("\n\t")}")
|
23
17
|
nil
|
24
18
|
end
|
25
19
|
|
@@ -30,10 +24,6 @@ module ScoutApm
|
|
30
24
|
|
31
25
|
def serialize(data)
|
32
26
|
Marshal.dump(data)
|
33
|
-
rescue
|
34
|
-
ScoutApm::Agent.instance.logger.info("Failed Marshalling LayawayFile")
|
35
|
-
ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
|
36
|
-
raise
|
37
27
|
end
|
38
28
|
|
39
29
|
def deserialize(data)
|
data/lib/scout_apm/layer.rb
CHANGED
@@ -13,17 +13,13 @@ module ScoutApm
|
|
13
13
|
# instrumentation for an example of how this is useful
|
14
14
|
attr_accessor :name
|
15
15
|
|
16
|
-
# An array of children layers
|
16
|
+
# An array of children layers, in call order.
|
17
17
|
# For instance, if we are in a middleware, there will likely be only a single
|
18
18
|
# child, which is another middleware. In a Controller, we may have a handful
|
19
19
|
# of children: [ActiveRecord, ActiveRecord, View, HTTP Call].
|
20
20
|
#
|
21
21
|
# This useful to get actual time spent in this layer vs. children time
|
22
|
-
|
23
|
-
# TODO: Check callers for compatibility w/ nil to avoid making an empty array
|
24
|
-
def children
|
25
|
-
@children || LayerChildrenSet.new
|
26
|
-
end
|
22
|
+
attr_reader :children
|
27
23
|
|
28
24
|
# Time objects recording the start & stop times of this layer
|
29
25
|
attr_reader :start_time, :stop_time
|
@@ -36,42 +32,47 @@ module ScoutApm
|
|
36
32
|
|
37
33
|
# If this layer took longer than a fixed amount of time, store the
|
38
34
|
# backtrace of where it occurred.
|
39
|
-
|
40
|
-
|
41
|
-
# The file name associated with the layer. Only used for autoinstruments overhead logging.
|
42
|
-
attr_accessor :file_name
|
35
|
+
attr_reader :backtrace
|
43
36
|
|
44
37
|
# As we go through a part of a request, instrumentation can store additional data
|
45
38
|
# Known Keys:
|
46
39
|
# :record_count - The number of rows returned by an AR query (From notification instantiation.active_record)
|
47
40
|
# :class_name - The ActiveRecord class name (From notification instantiation.active_record)
|
48
|
-
#
|
49
|
-
# If no annotations are ever set, this will return nil
|
50
41
|
attr_reader :annotations
|
51
42
|
|
52
|
-
|
43
|
+
# ScoutProf - trace_index is an index into the Stack structure in the C
|
44
|
+
# code, used to store captured traces.
|
45
|
+
attr_reader :trace_index
|
46
|
+
|
47
|
+
# ScoutProf - frame_index is an optimization to not capture a few frames
|
48
|
+
# during scoutprof instrumentation
|
49
|
+
attr_reader :frame_index
|
50
|
+
|
51
|
+
# Captured backtraces from ScoutProf. This is distinct from the backtrace
|
52
|
+
# attribute, which gets the ruby backtrace of any given layer. StackProf
|
53
|
+
# focuses on Controller layers, and requires a native extension and a
|
54
|
+
# reasonably recent Ruby.
|
55
|
+
attr_reader :traces
|
53
56
|
|
54
57
|
BACKTRACE_CALLER_LIMIT = 50 # maximum number of lines to send thru for backtrace analysis
|
55
58
|
|
56
59
|
def initialize(type, name, start_time = Time.now)
|
57
60
|
@type = type
|
58
61
|
@name = name
|
62
|
+
@annotations = {}
|
59
63
|
@start_time = start_time
|
60
64
|
@allocations_start = ScoutApm::Instruments::Allocations.count
|
61
65
|
@allocations_stop = 0
|
62
|
-
|
63
|
-
# initialize these only on first use
|
64
|
-
@children = nil
|
65
|
-
@annotations = nil
|
66
|
+
@children = [] # In order of calls
|
66
67
|
@desc = nil
|
67
|
-
end
|
68
68
|
|
69
|
-
|
70
|
-
|
69
|
+
@traces = ScoutApm::TraceSet.new
|
70
|
+
@raw_frames = []
|
71
|
+
@frame_index = ScoutApm::Instruments::Stacks.current_frame_index # For efficiency sake, try to skip the bottom X frames when collecting traces
|
72
|
+
@trace_index = ScoutApm::Instruments::Stacks.current_trace_index
|
71
73
|
end
|
72
74
|
|
73
75
|
def add_child(child)
|
74
|
-
@children ||= LayerChildrenSet.new
|
75
76
|
@children << child
|
76
77
|
end
|
77
78
|
|
@@ -90,7 +91,6 @@ module ScoutApm
|
|
90
91
|
|
91
92
|
# This data is internal to ScoutApm, to add custom information, use the Context api.
|
92
93
|
def annotate_layer(hsh)
|
93
|
-
@annotations ||= {}
|
94
94
|
@annotations.merge!(hsh)
|
95
95
|
end
|
96
96
|
|
@@ -102,6 +102,14 @@ module ScoutApm
|
|
102
102
|
@subscopable
|
103
103
|
end
|
104
104
|
|
105
|
+
def traced!
|
106
|
+
@traced = true
|
107
|
+
end
|
108
|
+
|
109
|
+
def traced?
|
110
|
+
@traced
|
111
|
+
end
|
112
|
+
|
105
113
|
# This is the old style name. This function is used for now, but should be
|
106
114
|
# removed, and the new type & name split should be enforced through the
|
107
115
|
# app.
|
@@ -116,13 +124,42 @@ module ScoutApm
|
|
116
124
|
# In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.
|
117
125
|
def caller_array
|
118
126
|
# omits the first several callers which are in the ScoutAPM stack.
|
119
|
-
if ScoutApm::
|
127
|
+
if ScoutApm::Environment.instance.ruby_2?
|
120
128
|
caller(3...BACKTRACE_CALLER_LIMIT)
|
121
129
|
else
|
122
130
|
caller[3...BACKTRACE_CALLER_LIMIT]
|
123
131
|
end
|
124
132
|
end
|
125
133
|
|
134
|
+
# Set the name of the file that this action is coming from.
|
135
|
+
# TraceSet uses this to more accurately filter backtraces
|
136
|
+
def set_root_class(klass_name)
|
137
|
+
@traces.set_root_class(klass_name)
|
138
|
+
end
|
139
|
+
|
140
|
+
def start_sampling
|
141
|
+
if ScoutApm::Agent.instance.config.value('profile') && traced?
|
142
|
+
ScoutApm::Instruments::Stacks.update_indexes(frame_index, trace_index)
|
143
|
+
ScoutApm::Instruments::Stacks.start_sampling
|
144
|
+
else
|
145
|
+
ScoutApm::Instruments::Stacks.stop_sampling(false)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def record_traces!
|
150
|
+
if ScoutApm::Agent.instance.config.value('profile')
|
151
|
+
ScoutApm::Instruments::Stacks.stop_sampling(false)
|
152
|
+
if traced?
|
153
|
+
traces.raw_traces = ScoutApm::Instruments::Stacks.profile_frames
|
154
|
+
traces.skipped_in_gc = ScoutApm::Instruments::Stacks.skipped_in_gc
|
155
|
+
traces.skipped_in_handler = ScoutApm::Instruments::Stacks.skipped_in_handler
|
156
|
+
traces.skipped_in_job_registered = ScoutApm::Instruments::Stacks.skipped_in_job_registered
|
157
|
+
traces.skipped_in_not_running = ScoutApm::Instruments::Stacks.skipped_in_not_running
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
126
163
|
######################################
|
127
164
|
# Debugging Helpers
|
128
165
|
######################################
|
@@ -163,7 +200,6 @@ module ScoutApm
|
|
163
200
|
map { |child| child.total_call_time }.
|
164
201
|
inject(0) { |sum, time| sum + time }
|
165
202
|
end
|
166
|
-
private :child_time
|
167
203
|
|
168
204
|
######################################
|
169
205
|
# Allocation Calculations
|
@@ -189,6 +225,5 @@ module ScoutApm
|
|
189
225
|
map { |child| child.total_allocations }.
|
190
226
|
inject(0) { |sum, obj| sum + obj }
|
191
227
|
end
|
192
|
-
private :child_allocations
|
193
228
|
end
|
194
229
|
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module LayerConverters
|
3
3
|
class AllocationMetricConverter < ConverterBase
|
4
|
-
def
|
5
|
-
|
6
|
-
return unless
|
4
|
+
def call
|
5
|
+
scope = scope_layer
|
6
|
+
return {} unless scope
|
7
|
+
return {} unless ScoutApm::Instruments::Allocations::ENABLED
|
7
8
|
|
8
|
-
meta = MetricMeta.new("ObjectAllocations", {:scope =>
|
9
|
+
meta = MetricMeta.new("ObjectAllocations", {:scope => scope.legacy_metric_name})
|
9
10
|
stat = MetricStats.new
|
10
11
|
stat.update!(root_layer.total_allocations)
|
11
|
-
metrics = { meta => stat }
|
12
12
|
|
13
|
-
|
14
|
-
nil # not returning anything in the layer results ... not used
|
13
|
+
{ meta => stat }
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -1,220 +1,31 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module LayerConverters
|
3
3
|
class ConverterBase
|
4
|
-
|
5
|
-
attr_reader :context
|
4
|
+
attr_reader :walker
|
6
5
|
attr_reader :request
|
7
6
|
attr_reader :root_layer
|
8
|
-
attr_reader :layer_finder
|
9
7
|
|
10
|
-
def initialize(
|
11
|
-
@context = context
|
8
|
+
def initialize(request)
|
12
9
|
@request = request
|
13
|
-
@layer_finder = layer_finder
|
14
|
-
@store = store
|
15
|
-
|
16
10
|
@root_layer = request.root_layer
|
17
|
-
@
|
18
|
-
@limited = false
|
11
|
+
@walker = DepthFirstWalker.new(root_layer)
|
19
12
|
end
|
20
13
|
|
14
|
+
# Scope is determined by the first Controller we hit. Most of the time
|
15
|
+
# there will only be 1 anyway. But if you have a controller that calls
|
16
|
+
# another controller method, we may pick that up:
|
17
|
+
# def update
|
18
|
+
# show
|
19
|
+
# render :update
|
20
|
+
# end
|
21
21
|
def scope_layer
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
################################################################################
|
26
|
-
# Subscoping
|
27
|
-
################################################################################
|
28
|
-
#
|
29
|
-
# Keep a list of subscopes, but only ever use the front one. The rest
|
30
|
-
# get pushed/popped in cases when we have many levels of subscopable
|
31
|
-
# layers. This lets us push/pop without otherwise keeping track very closely.
|
32
|
-
def register_hooks(walker)
|
33
|
-
@subscope_layers = []
|
34
|
-
|
35
|
-
walker.before do |layer|
|
36
|
-
if layer.subscopable?
|
37
|
-
@subscope_layers.push(layer)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
walker.after do |layer|
|
42
|
-
if layer.subscopable?
|
43
|
-
@subscope_layers.pop
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def subscoped?(layer)
|
49
|
-
@subscope_layers.first && layer != @subscope_layers.first # Don't scope under ourself.
|
50
|
-
end
|
51
|
-
|
52
|
-
def subscope_name
|
53
|
-
@subscope_layers.first.legacy_metric_name
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
################################################################################
|
58
|
-
# Backtrace Handling
|
59
|
-
################################################################################
|
60
|
-
#
|
61
|
-
# Because we get several layers for the same thing if you call an
|
62
|
-
# instrumented thing repeatedly, and only some of them may have
|
63
|
-
# backtraces captured, we store the backtraces off into another spot
|
64
|
-
# during processing, then at the end, we loop over those saved
|
65
|
-
# backtraces, putting them back into the metrics hash.
|
66
|
-
#
|
67
|
-
# This comes up most often when capturing n+1 backtraces. Because the
|
68
|
-
# query may be fast enough to evade our time-limit based backtrace
|
69
|
-
# capture, only the Nth item (see TrackedRequest for more detail) has a
|
70
|
-
# backtrack captured. This sequence makes sure that we report up that
|
71
|
-
# backtrace in the aggregated set of metrics around that call.
|
72
|
-
|
73
|
-
# Call this as you are processing each layer. It will store off backtraces
|
74
|
-
def store_backtrace(layer, meta)
|
75
|
-
return unless layer.backtrace
|
76
|
-
|
77
|
-
bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
|
78
|
-
if bt.any?
|
79
|
-
meta.backtrace = bt
|
80
|
-
@backtraces << meta
|
81
|
-
end
|
22
|
+
@scope_layer ||= find_first_layer_of_type("Controller") || find_first_layer_of_type("Job")
|
82
23
|
end
|
83
24
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
@backtraces.each do |meta_with_backtrace|
|
88
|
-
metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
|
25
|
+
def find_first_layer_of_type(layer_type)
|
26
|
+
walker.walk do |layer|
|
27
|
+
return layer if layer.type == layer_type
|
89
28
|
end
|
90
|
-
metric_hash
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
################################################################################
|
95
|
-
# Limit Handling
|
96
|
-
################################################################################
|
97
|
-
|
98
|
-
# To prevent huge traces from being generated, we should stop collecting
|
99
|
-
# detailed metrics as we go beyond some reasonably large count.
|
100
|
-
#
|
101
|
-
# We should still add up the /all aggregates.
|
102
|
-
|
103
|
-
MAX_METRICS = 500
|
104
|
-
|
105
|
-
def over_metric_limit?(metric_hash)
|
106
|
-
if metric_hash.size > MAX_METRICS
|
107
|
-
@limited = true
|
108
|
-
else
|
109
|
-
false
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def limited?
|
114
|
-
!! @limited
|
115
|
-
end
|
116
|
-
|
117
|
-
################################################################################
|
118
|
-
# Meta Scope
|
119
|
-
################################################################################
|
120
|
-
|
121
|
-
# When we make MetricMeta records, we need to determine a few things from layer.
|
122
|
-
def make_meta_options(layer)
|
123
|
-
scope_hash = make_meta_options_scope(layer)
|
124
|
-
desc_hash = make_meta_options_desc_hash(layer)
|
125
|
-
|
126
|
-
scope_hash.merge(desc_hash)
|
127
|
-
end
|
128
|
-
|
129
|
-
def make_meta_options_scope(layer)
|
130
|
-
# This layer is scoped under another thing. Typically that means this is a layer under a view.
|
131
|
-
# Like: Controller -> View/users/show -> ActiveRecord/user/find
|
132
|
-
# in that example, the scope is the View/users/show
|
133
|
-
if subscoped?(layer)
|
134
|
-
{:scope => subscope_name}
|
135
|
-
|
136
|
-
# We don't scope the controller under itself
|
137
|
-
elsif layer == scope_layer
|
138
|
-
{}
|
139
|
-
|
140
|
-
# This layer is a top level metric ("ActiveRecord", or "HTTP" or
|
141
|
-
# whatever, directly under the controller), so scope to the
|
142
|
-
# Controller
|
143
|
-
else
|
144
|
-
{:scope => scope_layer.legacy_metric_name}
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def make_meta_options_desc_hash(layer, max_desc_length=32768)
|
149
|
-
if layer.desc
|
150
|
-
desc_s = layer.desc.to_s
|
151
|
-
trimmed_desc = desc_s[0 .. max_desc_length]
|
152
|
-
{:desc => trimmed_desc}
|
153
|
-
else
|
154
|
-
{}
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
################################################################################
|
160
|
-
# Storing metrics into the hashes
|
161
|
-
################################################################################
|
162
|
-
|
163
|
-
# This is the detailed metric - type, name, backtrace, annotations, etc.
|
164
|
-
def store_specific_metric(layer, metric_hash, allocation_metric_hash)
|
165
|
-
return false if over_metric_limit?(metric_hash)
|
166
|
-
|
167
|
-
meta_options = make_meta_options(layer)
|
168
|
-
|
169
|
-
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
170
|
-
meta.extra.merge!(layer.annotations) if layer.annotations
|
171
|
-
|
172
|
-
store_backtrace(layer, meta)
|
173
|
-
|
174
|
-
metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope))
|
175
|
-
allocation_metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope))
|
176
|
-
|
177
|
-
# timing
|
178
|
-
stat = metric_hash[meta]
|
179
|
-
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
180
|
-
|
181
|
-
# allocations
|
182
|
-
stat = allocation_metric_hash[meta]
|
183
|
-
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
184
|
-
|
185
|
-
if LimitedLayer === layer
|
186
|
-
metric_hash[meta].call_count = layer.count
|
187
|
-
allocation_metric_hash[meta].call_count = layer.count
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
# Merged Metric - no specifics, just sum up by type (ActiveRecord, View, HTTP, etc)
|
192
|
-
def store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
|
193
|
-
meta = MetricMeta.new("#{layer.type}/all")
|
194
|
-
|
195
|
-
metric_hash[meta] ||= MetricStats.new(false)
|
196
|
-
allocation_metric_hash[meta] ||= MetricStats.new(false)
|
197
|
-
|
198
|
-
# timing
|
199
|
-
stat = metric_hash[meta]
|
200
|
-
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
201
|
-
|
202
|
-
# allocations
|
203
|
-
stat = allocation_metric_hash[meta]
|
204
|
-
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
205
|
-
end
|
206
|
-
|
207
|
-
################################################################################
|
208
|
-
# Misc Helpers
|
209
|
-
################################################################################
|
210
|
-
|
211
|
-
# Sometimes we start capturing a layer without knowing if we really
|
212
|
-
# want to make an entry for it. See ActiveRecord instrumentation for
|
213
|
-
# an example. We start capturing before we know if a query is cached
|
214
|
-
# or not, and want to skip any cached queries.
|
215
|
-
def skip_layer?(layer)
|
216
|
-
return false if layer.annotations.nil?
|
217
|
-
return true if layer.annotations[:ignorable]
|
218
29
|
end
|
219
30
|
end
|
220
31
|
end
|