scout_apm 3.0.0.pre13 → 3.0.0.pre14
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.
- checksums.yaml +4 -4
- data/lib/scout_apm.rb +20 -10
- data/lib/scout_apm/agent.rb +114 -319
- data/lib/scout_apm/agent/exit_handler.rb +66 -0
- data/lib/scout_apm/agent/preconditions.rb +69 -0
- data/lib/scout_apm/agent_context.rb +234 -0
- data/lib/scout_apm/app_server_load.rb +24 -14
- data/lib/scout_apm/background_job_integrations/resque.rb +7 -8
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
- data/lib/scout_apm/background_recorder.rb +8 -3
- data/lib/scout_apm/background_worker.rb +14 -7
- data/lib/scout_apm/config.rb +35 -26
- data/lib/scout_apm/context.rb +11 -4
- data/lib/scout_apm/db_query_metric_set.rb +17 -5
- data/lib/scout_apm/debug.rb +1 -1
- data/lib/scout_apm/environment.rb +10 -14
- data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
- data/lib/scout_apm/git_revision.rb +13 -8
- data/lib/scout_apm/histogram.rb +1 -1
- data/lib/scout_apm/instant/middleware.rb +7 -7
- data/lib/scout_apm/instant_reporting.rb +7 -7
- data/lib/scout_apm/instrument_manager.rb +87 -0
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +12 -7
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +17 -12
- data/lib/scout_apm/instruments/action_view.rb +11 -7
- data/lib/scout_apm/instruments/active_record.rb +25 -11
- data/lib/scout_apm/instruments/elasticsearch.rb +10 -6
- data/lib/scout_apm/instruments/grape.rb +12 -8
- data/lib/scout_apm/instruments/http_client.rb +10 -6
- data/lib/scout_apm/instruments/influxdb.rb +10 -6
- data/lib/scout_apm/instruments/middleware_detailed.rb +11 -5
- data/lib/scout_apm/instruments/middleware_summary.rb +11 -5
- data/lib/scout_apm/instruments/mongoid.rb +10 -5
- data/lib/scout_apm/instruments/moped.rb +11 -6
- data/lib/scout_apm/instruments/net_http.rb +10 -6
- data/lib/scout_apm/instruments/percentile_sampler.rb +8 -6
- data/lib/scout_apm/instruments/process/process_cpu.rb +8 -4
- data/lib/scout_apm/instruments/process/process_memory.rb +15 -10
- data/lib/scout_apm/instruments/rails_router.rb +12 -6
- data/lib/scout_apm/instruments/redis.rb +10 -6
- data/lib/scout_apm/instruments/samplers.rb +11 -0
- data/lib/scout_apm/instruments/sinatra.rb +5 -4
- data/lib/scout_apm/layaway.rb +21 -20
- data/lib/scout_apm/layaway_file.rb +8 -3
- data/lib/scout_apm/layer.rb +3 -3
- data/lib/scout_apm/layer_converters/converter_base.rb +6 -7
- data/lib/scout_apm/layer_converters/database_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/histograms.rb +2 -2
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +4 -3
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +5 -4
- data/lib/scout_apm/logger.rb +143 -0
- data/lib/scout_apm/middleware.rb +7 -9
- data/lib/scout_apm/periodic_work.rb +28 -0
- data/lib/scout_apm/reporter.rb +14 -8
- data/lib/scout_apm/reporting.rb +135 -0
- data/lib/scout_apm/request_manager.rb +4 -6
- data/lib/scout_apm/serializers/payload_serializer.rb +1 -1
- data/lib/scout_apm/slow_job_policy.rb +6 -2
- data/lib/scout_apm/slow_job_record.rb +5 -5
- data/lib/scout_apm/slow_request_policy.rb +6 -2
- data/lib/scout_apm/slow_transaction.rb +5 -5
- data/lib/scout_apm/store.rb +22 -16
- data/lib/scout_apm/synchronous_recorder.rb +7 -3
- data/lib/scout_apm/tasks/doctor.rb +75 -0
- data/lib/scout_apm/tasks/support.rb +22 -0
- data/lib/scout_apm/tracer.rb +5 -5
- data/lib/scout_apm/tracked_request.rb +43 -19
- data/lib/scout_apm/utils/active_record_metric_name.rb +66 -8
- data/lib/scout_apm/utils/backtrace_parser.rb +1 -1
- data/lib/scout_apm/utils/installed_gems.rb +7 -3
- data/lib/scout_apm/utils/klass_helper.rb +8 -2
- data/lib/scout_apm/utils/scm.rb +1 -1
- data/lib/scout_apm/utils/sql_sanitizer.rb +3 -3
- data/lib/scout_apm/version.rb +1 -1
- data/lib/tasks/doctor.rake +11 -0
- data/scout_apm.gemspec +1 -0
- data/test/test_helper.rb +17 -2
- data/test/unit/agent_test.rb +1 -54
- data/test/unit/config_test.rb +9 -5
- data/test/unit/context_test.rb +4 -4
- data/test/unit/db_query_metric_set_test.rb +11 -4
- data/test/unit/fake_store_test.rb +1 -1
- data/test/unit/git_revision_test.rb +3 -3
- data/test/unit/instruments/net_http_test.rb +2 -1
- data/test/unit/instruments/percentile_sampler_test.rb +5 -9
- data/test/unit/layaway_test.rb +10 -5
- data/test/unit/layer_converters/metric_converter_test.rb +2 -2
- data/test/unit/slow_request_policy_test.rb +7 -3
- data/test/unit/sql_sanitizer_test.rb +0 -6
- data/test/unit/store_test.rb +11 -8
- data/test/unit/utils/active_record_metric_name_test.rb +45 -7
- metadata +27 -5
- data/lib/scout_apm/agent/logging.rb +0 -74
- data/lib/scout_apm/agent/reporting.rb +0 -129
- data/lib/scout_apm/utils/null_logger.rb +0 -13
data/lib/scout_apm/middleware.rb
CHANGED
|
@@ -5,8 +5,10 @@ module ScoutApm
|
|
|
5
5
|
def initialize(app)
|
|
6
6
|
@app = app
|
|
7
7
|
@attempts = 0
|
|
8
|
-
@enabled = ScoutApm::Agent.instance.apm_enabled?
|
|
9
|
-
|
|
8
|
+
# @enabled = ScoutApm::Agent.instance.context.apm_enabled?
|
|
9
|
+
# XXX: Figure out if this middleware should even know
|
|
10
|
+
@enabled = true
|
|
11
|
+
@started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
# If we get a web request in, then we know we're running in some sort of app server
|
|
@@ -21,14 +23,10 @@ module ScoutApm
|
|
|
21
23
|
|
|
22
24
|
def attempt_to_start_agent
|
|
23
25
|
@attempts += 1
|
|
24
|
-
ScoutApm::Agent.instance.start
|
|
25
|
-
ScoutApm::Agent.instance.
|
|
26
|
-
@started = ScoutApm::Agent.instance.started? && ScoutApm::Agent.instance.background_worker_running?
|
|
26
|
+
ScoutApm::Agent.instance.start
|
|
27
|
+
@started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
|
|
27
28
|
rescue => e
|
|
28
|
-
|
|
29
|
-
if ENV["SCOUT_LOG_LEVEL"] == "debug"
|
|
30
|
-
STDOUT.puts "Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
31
|
-
end
|
|
29
|
+
ScoutApm::Agent.instance.context.logger("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
class PeriodicWork
|
|
3
|
+
attr_reader :context
|
|
4
|
+
|
|
5
|
+
def initialize(context)
|
|
6
|
+
@context = context
|
|
7
|
+
@reporting = ScoutApm::Reporting.new(context)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Expected to be called many times over the life of the agent
|
|
11
|
+
def run
|
|
12
|
+
ScoutApm::Debug.instance.call_periodic_hooks
|
|
13
|
+
@reporting.process_metrics
|
|
14
|
+
clean_old_percentiles
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# XXX: Move logic into a RequestHistogramsByTime class that can keep the timeout logic in it
|
|
20
|
+
def clean_old_percentiles
|
|
21
|
+
context.
|
|
22
|
+
request_histograms_by_time.
|
|
23
|
+
keys.
|
|
24
|
+
select {|timestamp| timestamp.age_in_seconds > 60 * 10 }.
|
|
25
|
+
each {|old_timestamp| context.request_histograms_by_time.delete(old_timestamp) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/scout_apm/reporter.rb
CHANGED
|
@@ -5,18 +5,24 @@ module ScoutApm
|
|
|
5
5
|
CA_FILE = File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] )
|
|
6
6
|
VERIFY_MODE = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
|
7
7
|
|
|
8
|
-
attr_reader :config
|
|
9
|
-
attr_reader :logger
|
|
10
8
|
attr_reader :type
|
|
9
|
+
attr_reader :context
|
|
11
10
|
attr_reader :instant_key
|
|
12
11
|
|
|
13
|
-
def initialize(
|
|
14
|
-
@
|
|
15
|
-
@logger = logger
|
|
12
|
+
def initialize(context, type, instant_key=nil)
|
|
13
|
+
@context = context
|
|
16
14
|
@type = type
|
|
17
15
|
@instant_key = instant_key
|
|
18
16
|
end
|
|
19
17
|
|
|
18
|
+
def config
|
|
19
|
+
context.config
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def logger
|
|
23
|
+
context.logger
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
def report(payload, headers = {})
|
|
21
27
|
hosts = determine_hosts
|
|
22
28
|
|
|
@@ -27,14 +33,14 @@ module ScoutApm
|
|
|
27
33
|
headers.merge!(compression_headers)
|
|
28
34
|
|
|
29
35
|
compress_payload_size = payload.length
|
|
30
|
-
|
|
36
|
+
logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
post_payload(hosts, payload, headers)
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
def uri(host)
|
|
37
|
-
encoded_app_name = CGI.escape(
|
|
43
|
+
encoded_app_name = CGI.escape(context.environment.application_name)
|
|
38
44
|
key = config.value('key')
|
|
39
45
|
|
|
40
46
|
case type
|
|
@@ -100,7 +106,7 @@ module ScoutApm
|
|
|
100
106
|
|
|
101
107
|
# Headers passed up with all API requests.
|
|
102
108
|
def default_http_headers
|
|
103
|
-
{ "Agent-Hostname" =>
|
|
109
|
+
{ "Agent-Hostname" => context.environment.hostname,
|
|
104
110
|
"Content-Type" => "application/octet-stream",
|
|
105
111
|
"Agent-Version" => ScoutApm::VERSION,
|
|
106
112
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Methods related to sending metrics to scoutapp.com.
|
|
2
|
+
module ScoutApm
|
|
3
|
+
class Reporting
|
|
4
|
+
attr_reader :context
|
|
5
|
+
|
|
6
|
+
def initialize(context)
|
|
7
|
+
@context = context
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def logger
|
|
11
|
+
context.logger
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def reporter
|
|
15
|
+
@reporter ||= ScoutApm::Reporter.new(context, :checkin)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The data moves through a treadmill of reporting, coordinating several Rails processes by using an external file.
|
|
19
|
+
# * During the minute it is being recorded by the instruments, it gets
|
|
20
|
+
# recorded into the ram of each process (in the Store class).
|
|
21
|
+
# * The minute after, each process writes its own metrics to a shared LayawayFile
|
|
22
|
+
# * The minute after that, the first process to wake up pushes the combined
|
|
23
|
+
# data to the server, and wipes it. Next processes don't have anything to do.
|
|
24
|
+
#
|
|
25
|
+
# At any given point, there is data in each of those steps, moving its way through the process
|
|
26
|
+
def process_metrics
|
|
27
|
+
# Write the previous minute's data to the shared-across-process layaway file.
|
|
28
|
+
context.store.write_to_layaway(context.layaway)
|
|
29
|
+
|
|
30
|
+
# Attempt to send 2 minutes ago's data up to the server. This
|
|
31
|
+
# only acctually occurs if this process is the first to wake up this
|
|
32
|
+
# minute.
|
|
33
|
+
report_to_server
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# In a running app, one process will get the period ready for delivery, the others will see 0.
|
|
37
|
+
def report_to_server
|
|
38
|
+
period_to_report = ScoutApm::StoreReportingPeriodTimestamp.minutes_ago(2)
|
|
39
|
+
|
|
40
|
+
logger.debug("Attempting to claim #{period_to_report.to_s}")
|
|
41
|
+
|
|
42
|
+
did_write = context.layaway.with_claim(period_to_report) do |rps|
|
|
43
|
+
logger.debug("Succeeded claiming #{period_to_report.to_s}")
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
merged = rps.inject { |memo, rp| memo.merge(rp) }
|
|
47
|
+
logger.debug("Merged #{rps.length} reporting periods, delivering")
|
|
48
|
+
deliver_period(merged)
|
|
49
|
+
true
|
|
50
|
+
rescue => e
|
|
51
|
+
logger.debug("Error merging reporting periods #{e.message}")
|
|
52
|
+
logger.debug("Error merging reporting periods #{e.backtrace}")
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if !did_write
|
|
59
|
+
logger.debug("Failed to obtain claim for #{period_to_report.to_s}")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def deliver_period(reporting_period)
|
|
64
|
+
metrics = reporting_period.metrics_payload
|
|
65
|
+
slow_transactions = reporting_period.slow_transactions_payload
|
|
66
|
+
jobs = reporting_period.jobs
|
|
67
|
+
slow_jobs = reporting_period.slow_jobs_payload
|
|
68
|
+
histograms = reporting_period.histograms
|
|
69
|
+
db_query_metrics = reporting_period.db_query_metrics_payload
|
|
70
|
+
|
|
71
|
+
metadata = {
|
|
72
|
+
:app_root => context.environment.root.to_s,
|
|
73
|
+
:unique_id => ScoutApm::Utils::UniqueId.simple,
|
|
74
|
+
:agent_version => ScoutApm::VERSION,
|
|
75
|
+
:agent_time => reporting_period.timestamp.to_s,
|
|
76
|
+
:agent_pid => Process.pid,
|
|
77
|
+
:platform => "ruby",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
log_deliver(metrics, slow_transactions, metadata, slow_jobs, histograms)
|
|
81
|
+
|
|
82
|
+
payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
|
|
83
|
+
logger.debug("Sending payload w/ Headers: #{headers.inspect}")
|
|
84
|
+
|
|
85
|
+
reporter.report(payload, headers)
|
|
86
|
+
rescue => e
|
|
87
|
+
logger.warn "Error on checkin"
|
|
88
|
+
logger.info e.message
|
|
89
|
+
logger.debug e.backtrace
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def log_deliver(metrics, slow_transactions, metadata, jobs_traces, histograms)
|
|
93
|
+
total_request_count = metrics.
|
|
94
|
+
select { |meta,stats| meta.metric_name =~ /\AController/ }.
|
|
95
|
+
inject(0) {|sum, (_, stat)| sum + stat.call_count }
|
|
96
|
+
|
|
97
|
+
memory = metrics.
|
|
98
|
+
find {|meta,stats| meta.metric_name =~ /\AMemory/ }
|
|
99
|
+
process_log_str = if memory
|
|
100
|
+
"Recorded from #{memory.last.call_count} processes"
|
|
101
|
+
else
|
|
102
|
+
"Recorded across (unknown) processes"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
time_clause = "[#{Time.parse(metadata[:agent_time]).strftime("%H:%M")}]"
|
|
106
|
+
metrics_clause = "#{metrics.length} Metrics for #{total_request_count} requests"
|
|
107
|
+
slow_trans_clause = "#{slow_transactions.length} Slow Transaction Traces"
|
|
108
|
+
job_clause = "#{jobs_traces.length} Job Traces"
|
|
109
|
+
histogram_clause = "#{histograms.length} Histograms"
|
|
110
|
+
|
|
111
|
+
logger.info "#{time_clause} Delivering #{metrics_clause} and #{slow_trans_clause} and #{job_clause}, #{process_log_str}."
|
|
112
|
+
logger.debug("\n\nMetrics: #{metrics.pretty_inspect}\nSlowTrans: #{slow_transactions.pretty_inspect}\nMetadata: #{metadata.inspect.pretty_inspect}\n\n")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# TODO: Move this into PayloadSerializer?
|
|
116
|
+
# XXX: Remove non-json report format entirely
|
|
117
|
+
def headers
|
|
118
|
+
if ScoutApm::Agent.instance.context.config.value("report_format") == 'json'
|
|
119
|
+
headers = {'Content-Type' => 'application/json'}
|
|
120
|
+
else
|
|
121
|
+
headers = {}
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Before reporting, lookup metric_id for each MetricMeta. This speeds up
|
|
126
|
+
# reporting on the server-side.
|
|
127
|
+
def add_metric_ids(metrics)
|
|
128
|
+
metrics.each do |meta,stats|
|
|
129
|
+
if metric_id = metric_lookup[meta]
|
|
130
|
+
meta.metric_id = metric_id
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -19,13 +19,11 @@ module ScoutApm
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# Create a new TrackedRequest object for this thread
|
|
22
|
+
# XXX: Figure out who is in charge of creating a `FakeStore` - previously was here
|
|
22
23
|
def self.create
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ScoutApm::FakeStore.new
|
|
27
|
-
end
|
|
28
|
-
Thread.current[:scout_request] = TrackedRequest.new(store)
|
|
24
|
+
agent_context = ScoutApm::Agent.instance.context
|
|
25
|
+
store = agent_context.store
|
|
26
|
+
Thread.current[:scout_request] = TrackedRequest.new(agent_context, store)
|
|
29
27
|
end
|
|
30
28
|
end
|
|
31
29
|
end
|
|
@@ -3,7 +3,7 @@ module ScoutApm
|
|
|
3
3
|
module Serializers
|
|
4
4
|
class PayloadSerializer
|
|
5
5
|
def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
|
|
6
|
-
if ScoutApm::Agent.instance.config.value("report_format") == 'json'
|
|
6
|
+
if ScoutApm::Agent.instance.context.config.value("report_format") == 'json'
|
|
7
7
|
ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
|
|
8
8
|
else
|
|
9
9
|
metadata = metadata.dup
|
|
@@ -24,8 +24,12 @@ module ScoutApm
|
|
|
24
24
|
# has been running.
|
|
25
25
|
attr_reader :last_seen
|
|
26
26
|
|
|
27
|
+
# The AgentContext we're running in
|
|
28
|
+
attr_reader :context
|
|
29
|
+
|
|
30
|
+
def initialize(context)
|
|
31
|
+
@context = context
|
|
27
32
|
|
|
28
|
-
def initialize
|
|
29
33
|
zero_time = Time.now
|
|
30
34
|
@last_seen = Hash.new { |h, k| h[k] = zero_time }
|
|
31
35
|
end
|
|
@@ -54,7 +58,7 @@ module ScoutApm
|
|
|
54
58
|
age = Time.now - last_seen[unique_name]
|
|
55
59
|
|
|
56
60
|
# What approximate percentile was this request?
|
|
57
|
-
percentile =
|
|
61
|
+
percentile = context.request_histograms.approximate_quantile_of_value(unique_name, total_time)
|
|
58
62
|
|
|
59
63
|
return speed_points(total_time) + percentile_points(percentile) + age_points(age)
|
|
60
64
|
end
|
|
@@ -23,7 +23,7 @@ module ScoutApm
|
|
|
23
23
|
attr_reader :git_sha
|
|
24
24
|
attr_reader :truncated_metrics
|
|
25
25
|
|
|
26
|
-
def initialize(queue_name, job_name, time, total_time, exclusive_time, context, metrics, allocation_metrics, mem_delta, allocations, score, truncated_metrics)
|
|
26
|
+
def initialize(agent_context, queue_name, job_name, time, total_time, exclusive_time, context, metrics, allocation_metrics, mem_delta, allocations, score, truncated_metrics)
|
|
27
27
|
@queue_name = queue_name
|
|
28
28
|
@job_name = job_name
|
|
29
29
|
@time = time
|
|
@@ -34,13 +34,13 @@ module ScoutApm
|
|
|
34
34
|
@allocation_metrics = allocation_metrics
|
|
35
35
|
@mem_delta = mem_delta
|
|
36
36
|
@allocations = allocations
|
|
37
|
-
@seconds_since_startup = (Time.now -
|
|
38
|
-
@hostname =
|
|
39
|
-
@git_sha =
|
|
37
|
+
@seconds_since_startup = (Time.now - agent_context.process_start_time)
|
|
38
|
+
@hostname = agent_context.environment.hostname
|
|
39
|
+
@git_sha = agent_context.environment.git_revision.sha
|
|
40
40
|
@score = score
|
|
41
41
|
@truncated_metrics = truncated_metrics
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
agent_context.logger.debug { "Slow Job [#{metric_name}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta}"}
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def metric_name
|
|
@@ -24,8 +24,12 @@ module ScoutApm
|
|
|
24
24
|
# has been running.
|
|
25
25
|
attr_reader :last_seen
|
|
26
26
|
|
|
27
|
+
# The AgentContext we're running in
|
|
28
|
+
attr_reader :context
|
|
29
|
+
|
|
30
|
+
def initialize(context)
|
|
31
|
+
@context = context
|
|
27
32
|
|
|
28
|
-
def initialize
|
|
29
33
|
zero_time = Time.now
|
|
30
34
|
@last_seen = Hash.new { |h, k| h[k] = zero_time }
|
|
31
35
|
end
|
|
@@ -54,7 +58,7 @@ module ScoutApm
|
|
|
54
58
|
age = Time.now - last_seen[unique_name]
|
|
55
59
|
|
|
56
60
|
# What approximate percentile was this request?
|
|
57
|
-
percentile =
|
|
61
|
+
percentile = context.request_histograms.approximate_quantile_of_value(unique_name, total_time)
|
|
58
62
|
|
|
59
63
|
return speed_points(total_time) + percentile_points(percentile) + age_points(age)
|
|
60
64
|
end
|
|
@@ -19,7 +19,7 @@ module ScoutApm
|
|
|
19
19
|
|
|
20
20
|
attr_reader :truncated_metrics # True/False that says if we had to truncate the metrics of this trace
|
|
21
21
|
|
|
22
|
-
def initialize(uri, metric_name, total_call_time, metrics, allocation_metrics, context, time, raw_stackprof, mem_delta, allocations, score, truncated_metrics)
|
|
22
|
+
def initialize(agent_context, uri, metric_name, total_call_time, metrics, allocation_metrics, context, time, raw_stackprof, mem_delta, allocations, score, truncated_metrics)
|
|
23
23
|
@uri = uri
|
|
24
24
|
@metric_name = metric_name
|
|
25
25
|
@total_call_time = total_call_time
|
|
@@ -30,13 +30,13 @@ module ScoutApm
|
|
|
30
30
|
@prof = []
|
|
31
31
|
@mem_delta = mem_delta
|
|
32
32
|
@allocations = allocations
|
|
33
|
-
@seconds_since_startup = (Time.now -
|
|
34
|
-
@hostname =
|
|
33
|
+
@seconds_since_startup = (Time.now - agent_context.process_start_time)
|
|
34
|
+
@hostname = agent_context.environment.hostname
|
|
35
35
|
@score = score
|
|
36
|
-
@git_sha =
|
|
36
|
+
@git_sha = agent_context.environment.git_revision.sha
|
|
37
37
|
@truncated_metrics = truncated_metrics
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
agent_context.logger.debug { "Slow Request [#{uri}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta} Score: #{score}"}
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Used to remove metrics when the payload will be too large.
|
data/lib/scout_apm/store.rb
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
# the layaway file for cross-process aggregation.
|
|
4
4
|
module ScoutApm
|
|
5
5
|
class Store
|
|
6
|
-
def initialize
|
|
6
|
+
def initialize(context)
|
|
7
|
+
@context = context
|
|
7
8
|
@mutex = Mutex.new
|
|
8
|
-
@reporting_periods = Hash.new { |h,k| h[k] = StoreReportingPeriod.new(k) }
|
|
9
|
+
@reporting_periods = Hash.new { |h,k| h[k] = StoreReportingPeriod.new(k, @context) }
|
|
9
10
|
@samplers = []
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def current_timestamp
|
|
13
|
-
StoreReportingPeriodTimestamp.new
|
|
14
|
+
StoreReportingPeriodTimestamp.new(Time.now)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def current_period
|
|
@@ -84,11 +85,11 @@ module ScoutApm
|
|
|
84
85
|
# current-minute metrics. Useful when we are shutting down the agent
|
|
85
86
|
# during a restart.
|
|
86
87
|
def write_to_layaway(layaway, force=false)
|
|
87
|
-
|
|
88
|
+
logger.debug("Writing to layaway#{" (Forced)" if force}")
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
@reporting_periods.select { |time, rp| force || (time.timestamp < current_timestamp.timestamp) }.
|
|
91
|
+
each { |time, rp| collect_samplers(rp) }.
|
|
92
|
+
each { |time, rp| write_reporting_period(layaway, time, rp) }
|
|
92
93
|
end
|
|
93
94
|
|
|
94
95
|
def write_reporting_period(layaway, time, rp)
|
|
@@ -96,18 +97,18 @@ module ScoutApm
|
|
|
96
97
|
layaway.write_reporting_period(rp)
|
|
97
98
|
}
|
|
98
99
|
rescue => e
|
|
99
|
-
|
|
100
|
+
logger.warn("Failed writing data to layaway file: #{e.message} / #{e.backtrace}")
|
|
100
101
|
ensure
|
|
101
|
-
|
|
102
|
+
logger.debug("Before delete, reporting periods length: #{@reporting_periods.size}")
|
|
102
103
|
deleted_items = @reporting_periods.delete(time)
|
|
103
|
-
|
|
104
|
+
logger.debug("After delete, reporting periods length: #{@reporting_periods.size}. Did delete #{deleted_items}")
|
|
104
105
|
end
|
|
105
106
|
private :write_reporting_period
|
|
106
107
|
|
|
107
108
|
######################################
|
|
108
109
|
# Sampler support
|
|
109
|
-
def add_sampler(
|
|
110
|
-
@samplers <<
|
|
110
|
+
def add_sampler(sampler_klass)
|
|
111
|
+
@samplers << sampler_klass.new(@context)
|
|
111
112
|
end
|
|
112
113
|
|
|
113
114
|
def collect_samplers(rp)
|
|
@@ -115,12 +116,17 @@ module ScoutApm
|
|
|
115
116
|
begin
|
|
116
117
|
sampler.metrics(rp.timestamp, self)
|
|
117
118
|
rescue => e
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
logger.info "Error reading #{sampler.human_name} for period: #{rp}"
|
|
120
|
+
logger.debug "#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
|
120
121
|
end
|
|
121
122
|
end
|
|
122
123
|
end
|
|
123
124
|
private :collect_samplers
|
|
125
|
+
|
|
126
|
+
def logger
|
|
127
|
+
@context.logger
|
|
128
|
+
end
|
|
129
|
+
private :logger
|
|
124
130
|
end
|
|
125
131
|
|
|
126
132
|
# A timestamp, normalized to the beginning of a minute. Used as a hash key to
|
|
@@ -191,7 +197,7 @@ module ScoutApm
|
|
|
191
197
|
|
|
192
198
|
attr_reader :db_query_metric_set
|
|
193
199
|
|
|
194
|
-
def initialize(timestamp)
|
|
200
|
+
def initialize(timestamp, context)
|
|
195
201
|
@timestamp = timestamp
|
|
196
202
|
|
|
197
203
|
@request_traces = ScoredItemSet.new
|
|
@@ -200,7 +206,7 @@ module ScoutApm
|
|
|
200
206
|
@histograms = []
|
|
201
207
|
|
|
202
208
|
@metric_set = MetricSet.new
|
|
203
|
-
@db_query_metric_set = DbQueryMetricSet.new
|
|
209
|
+
@db_query_metric_set = DbQueryMetricSet.new(context)
|
|
204
210
|
|
|
205
211
|
@jobs = Hash.new
|
|
206
212
|
end
|