scout_apm 1.6.1 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.markdown +5 -0
- data/LICENSE.md +9 -0
- data/lib/scout_apm/agent/reporting.rb +24 -6
- data/lib/scout_apm/layaway.rb +134 -31
- data/lib/scout_apm/layaway_file.rb +27 -74
- data/lib/scout_apm/store.rb +19 -3
- data/lib/scout_apm/version.rb +1 -1
- metadata +3 -4
- data/lib/allocations.bundle +0 -0
- data/lib/stacks.bundle +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dd32dd69f13bbb9d647caa56a2e17cfe89340e2
|
4
|
+
data.tar.gz: 4663534020a6d20226f566abd8ce9e3ba90d9263
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 595e782a27c8721ba8fc7dc4760b672b94f37aed634b1ed68eced23d4d72401052dd2792b5889534cd287cecfb9fdcfbffdbf081fab38876b40c477346f5816d
|
7
|
+
data.tar.gz: 562921bd5380679a1efbf24dedced0becd8ab2da347897550b09468f6c967e9eada8e5702954d52c7dafcdbfd666ae79b1056c574656c61cbc8d269e6cca4293
|
data/.gitignore
CHANGED
data/CHANGELOG.markdown
CHANGED
data/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
All components of this product, except where noted, are Copyright (c) 2013-2016 Zimuth, Inc DBA Scout. All rights reserved.
|
2
|
+
|
3
|
+
Certain inventions disclosed in this file may be claimed within patents owned or patent applications filed by Zimuth, Inc. or third parties.
|
4
|
+
|
5
|
+
Subject to the terms of this notice, Zimuth grants you a nonexclusive, nontransferable license, without the right to sublicense, to (a) install and execute one copy of these files on any number of workstations owned or controlled by you and (b) distribute verbatim copies of these files to third parties. As a condition to the foregoing grant, you must provide this notice along with each copy you distribute and you must not remove, alter, or obscure this notice. All other use, reproduction, modification, distribution, or other exploitation of these files is strictly prohibited, except as may be set forth in a separate written license agreement between you and Zimuth. The terms of any such license agreement will control over this notice. The license stated above will be automatically terminated and revoked if you exceed its scope or violate any of the terms of this notice.
|
6
|
+
|
7
|
+
This License does not grant permission to use the trade names, trademarks, service marks, or product names of Zimuth, except as required for reasonable and customary use in describing the origin of this file and reproducing the content of this notice. You may not mark or brand this file with any trade name, trademarks, service marks, or product names other than the original brand (if any) provided by Zimuth.
|
8
|
+
|
9
|
+
Unless otherwise expressly agreed by Zimuth in a separate written license agreement, these files are provided AS IS, WITHOUT WARRANTY OF ANY KIND, including without any implied warranties of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, or NON-INFRINGEMENT. As a condition to your use of these files, you are solely responsible for such use. Zimuth will have no liability to you for direct, indirect, consequential, incidental, special, or punitive damages or for lost profits or data.
|
@@ -24,17 +24,35 @@ module ScoutApm
|
|
24
24
|
report_to_server
|
25
25
|
end
|
26
26
|
|
27
|
-
MAX_AGE_TO_REPORT = (10 * 60) # ten minutes as seconds
|
28
|
-
|
29
27
|
# In a running app, one process will get one period ready for delivery, the others will see 0.
|
30
28
|
def report_to_server
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
period_to_report = ScoutApm::StoreReportingPeriodTimestamp.minutes_ago(2)
|
30
|
+
|
31
|
+
logger.debug("Attempting to claim #{period_to_report.to_s}")
|
32
|
+
|
33
|
+
did_write = layaway.with_claim(period_to_report) do |rps|
|
34
|
+
logger.debug("Succeeded claiming #{period_to_report.to_s}")
|
35
|
+
|
36
|
+
begin
|
37
|
+
merged = rps.inject { |memo, rp| memo.merge(rp) }
|
38
|
+
logger.debug("Merged #{rps.length} reporting periods, delivering")
|
39
|
+
deliver_period(merged)
|
40
|
+
true
|
41
|
+
rescue => e
|
42
|
+
logger.debug("Error merging reporting periods #{e.message}")
|
43
|
+
logger.debug("Error merging reporting periods #{e.backtrace}")
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
if !did_write
|
50
|
+
logger.debug("Failed to obtain claim for #{period_to_report.to_s}")
|
35
51
|
end
|
52
|
+
|
36
53
|
end
|
37
54
|
|
55
|
+
|
38
56
|
def deliver_period(reporting_period)
|
39
57
|
metrics = reporting_period.metrics_payload
|
40
58
|
slow_transactions = reporting_period.slow_transactions_payload
|
data/lib/scout_apm/layaway.rb
CHANGED
@@ -1,53 +1,156 @@
|
|
1
|
-
# Stores StoreReportingPeriod objects in a file before sending them to the server.
|
2
|
-
#
|
1
|
+
# Stores StoreReportingPeriod objects in a per-process file before sending them to the server.
|
2
|
+
# Coordinates a single process to collect up all individual files, merge them, then send.
|
3
|
+
#
|
4
|
+
# Each layaway file is named basedir/scout_#{timestamp}_#{pid}.data
|
5
|
+
# Where timestamp is in the format:
|
6
|
+
# And PID is the process id of the running process
|
7
|
+
#
|
3
8
|
module ScoutApm
|
4
9
|
class Layaway
|
5
|
-
|
10
|
+
# How old a file needs to be in Seconds before it gets reported.
|
11
|
+
REPORTING_AGE = 120
|
6
12
|
|
7
|
-
|
8
|
-
|
13
|
+
# How long to let a stale file sit before deleting it.
|
14
|
+
# Letting it sit a bit may be useful for debugging
|
15
|
+
STALE_AGE = 10 * 60
|
16
|
+
|
17
|
+
# A strftime format string for how we render timestamps in filenames.
|
18
|
+
# Must be sortable as an integer
|
19
|
+
TIME_FORMAT = "%Y%m%d%H%M"
|
20
|
+
|
21
|
+
def initialize(directory=nil)
|
22
|
+
@directory = directory
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a Pathname object with the fully qualified directory where the layaway files can be placed.
|
26
|
+
# That directory must be writable by this process.
|
27
|
+
#
|
28
|
+
# Don't set this in initializer, since it relies on agent instance existing to figure out the value.
|
29
|
+
#
|
30
|
+
def directory
|
31
|
+
return @directory if @directory
|
32
|
+
|
33
|
+
data_file = ScoutApm::Agent.instance.config.value("data_file")
|
34
|
+
data_file = File.dirname(data_file) if data_file && !File.directory?
|
35
|
+
|
36
|
+
candidates = [
|
37
|
+
data_file,
|
38
|
+
"#{ScoutApm::Agent.instance.environment.root}/tmp",
|
39
|
+
"/tmp"
|
40
|
+
].compact
|
41
|
+
|
42
|
+
found = candidates.detect { |dir| File.writable?(dir) }
|
43
|
+
ScoutApm::Agent.instance.logger.debug("Storing Layaway Files in #{found}")
|
44
|
+
@directory = Pathname.new(found)
|
45
|
+
end
|
46
|
+
|
47
|
+
def write_reporting_period(reporting_period)
|
48
|
+
filename = file_for(reporting_period.timestamp)
|
49
|
+
layaway_file = LayawayFile.new(filename)
|
50
|
+
layaway_file.write(reporting_period)
|
9
51
|
end
|
10
52
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
53
|
+
# Claims a given timestamp (getting a lock on a particular filename),
|
54
|
+
# then yields ReportingPeriods collected up from all the files.
|
55
|
+
# If the yield returns truthy, delete the layaway files that made it up.
|
56
|
+
def with_claim(timestamp)
|
57
|
+
coordinator_file = glob_pattern(timestamp, :coordinator)
|
58
|
+
|
59
|
+
|
60
|
+
# This file gets deleted only by a process that successfully obtained a lock
|
61
|
+
f = File.open(coordinator_file, File::RDWR | File::CREAT)
|
62
|
+
begin
|
63
|
+
# Nonblocking, Exclusive lock.
|
64
|
+
if f.flock(File::LOCK_EX | File::LOCK_NB)
|
65
|
+
|
66
|
+
ScoutApm::Agent.instance.logger.debug("Obtained Reporting Lock")
|
15
67
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
68
|
+
files = all_files_for(timestamp).reject{|l| l.to_s == coordinator_file.to_s }
|
69
|
+
rps = files.map{ |layaway| LayawayFile.new(layaway).load }.compact
|
70
|
+
if rps.any?
|
71
|
+
yield rps
|
20
72
|
|
21
|
-
|
22
|
-
|
73
|
+
delete_files_for(timestamp) # also removes the coodinator_file
|
74
|
+
delete_stale_files(timestamp.to_time - STALE_AGE)
|
75
|
+
else
|
76
|
+
File.unlink(coordinator_file)
|
77
|
+
ScoutApm::Agent.instance.logger.debug("No layaway files to report")
|
78
|
+
end
|
23
79
|
|
24
|
-
|
25
|
-
|
80
|
+
# Unlock the file when done!
|
81
|
+
f.flock(File::LOCK_UN | File::LOCK_NB)
|
82
|
+
f.close
|
83
|
+
true
|
84
|
+
else
|
85
|
+
# Didn't obtain lock, another process is reporting. Return false from this function, but otherwise no work
|
86
|
+
f.close
|
87
|
+
false
|
88
|
+
end
|
26
89
|
end
|
27
90
|
end
|
28
91
|
|
29
|
-
|
92
|
+
def delete_files_for(timestamp)
|
93
|
+
all_files_for(timestamp).each { |layaway| File.unlink(layaway) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete_stale_files(older_than)
|
97
|
+
all_files_for(:all).
|
98
|
+
map { |filename| timestamp_from_filename(filename) }.
|
99
|
+
compact.
|
100
|
+
uniq.
|
101
|
+
select { |timestamp| timestamp.to_i < older_than.strftime(TIME_FORMAT).to_i }.
|
102
|
+
tap { |timestamps| ScoutApm::Agent.instance.logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
|
103
|
+
map { |timestamp| delete_files_for(timestamp) }
|
104
|
+
end
|
30
105
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
106
|
+
private
|
107
|
+
|
108
|
+
##########################################
|
109
|
+
# Looking up files
|
110
|
+
|
111
|
+
def file_for(timestamp)
|
112
|
+
glob_pattern(timestamp)
|
113
|
+
end
|
36
114
|
|
37
|
-
|
115
|
+
def all_files_for(timestamp)
|
116
|
+
Dir[glob_pattern(timestamp, :all)]
|
117
|
+
end
|
38
118
|
|
39
|
-
|
119
|
+
# Timestamp should be either :all or a Time-ish object that responds to strftime (StoreReportingPeriodTimestamp does)
|
120
|
+
# if timestamp == :all then find all timestamps, otherwise format it.
|
121
|
+
# if pid == :all, get the files for all
|
122
|
+
def glob_pattern(timestamp, pid=$$)
|
123
|
+
timestamp_pattern = format_timestamp(timestamp)
|
124
|
+
pid_pattern = format_pid(pid)
|
125
|
+
directory + "scout_#{timestamp_pattern}_#{pid_pattern}.data"
|
126
|
+
end
|
40
127
|
|
41
|
-
|
42
|
-
|
128
|
+
def format_timestamp(timestamp)
|
129
|
+
if timestamp == :all
|
130
|
+
"*"
|
131
|
+
elsif timestamp.respond_to?(:strftime)
|
132
|
+
timestamp.strftime(TIME_FORMAT)
|
133
|
+
else
|
134
|
+
timestamp.to_s
|
43
135
|
end
|
136
|
+
end
|
44
137
|
|
45
|
-
|
138
|
+
def format_pid(pid)
|
139
|
+
if pid == :all
|
140
|
+
"*"
|
141
|
+
else
|
142
|
+
pid.to_s
|
143
|
+
end
|
46
144
|
end
|
47
145
|
|
48
|
-
|
49
|
-
|
50
|
-
|
146
|
+
def timestamp_from_filename(filename)
|
147
|
+
match = filename.match(%r{scout_(.*)_.*\.data})
|
148
|
+
if match
|
149
|
+
match[1]
|
150
|
+
else
|
151
|
+
nil
|
152
|
+
end
|
51
153
|
end
|
52
154
|
end
|
53
155
|
end
|
156
|
+
|
@@ -1,94 +1,36 @@
|
|
1
|
-
#
|
1
|
+
# A single layaway file. See Layaway for the management of the group of files.
|
2
2
|
module ScoutApm
|
3
3
|
class LayawayFile
|
4
|
-
|
5
|
-
return @path if @path
|
4
|
+
attr_reader :path
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
"#{ScoutApm::Agent.instance.default_log_path}/scout_apm.db",
|
10
|
-
"#{ScoutApm::Agent.instance.environment.root}/tmp/scout_apm.db"
|
11
|
-
]
|
12
|
-
|
13
|
-
candidates.each do |candidate|
|
14
|
-
next if candidate.nil?
|
15
|
-
|
16
|
-
begin
|
17
|
-
ScoutApm::Agent.instance.logger.debug("Checking Layaway File Location: #{candidate}")
|
18
|
-
File.open(candidate, "w") { |f| } # Open & Close to check that we can
|
19
|
-
|
20
|
-
# No exception, it is valid
|
21
|
-
ScoutApm::Agent.instance.logger.info("Layaway File location found: #{candidate}")
|
22
|
-
@path = candidate
|
23
|
-
return @path
|
24
|
-
rescue Exception
|
25
|
-
ScoutApm::Agent.instance.logger.debug("Couldn't open layaway file for test write at #{candidate}")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
ScoutApm::Agent.instance.logger.error("No valid layaway file found, please set a location in the configuration key `data_file`")
|
30
|
-
nil
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
31
8
|
end
|
32
9
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def load(dump)
|
38
|
-
if dump.size == 0
|
39
|
-
ScoutApm::Agent.instance.logger.debug("No data in layaway file.")
|
40
|
-
return nil
|
41
|
-
end
|
42
|
-
Marshal.load(dump)
|
10
|
+
def load
|
11
|
+
data = File.open(path, "r") { |f| read_raw(f) }
|
12
|
+
deserialize(data)
|
43
13
|
rescue NameError, ArgumentError, TypeError => e
|
14
|
+
# Marshal error
|
44
15
|
ScoutApm::Agent.instance.logger.info("Unable to load data from Layaway file, resetting.")
|
45
16
|
ScoutApm::Agent.instance.logger.debug("#{e.message}, #{e.backtrace.join("\n\t")}")
|
46
17
|
nil
|
47
18
|
end
|
48
19
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
begin
|
53
|
-
result = (yield get_data(f))
|
54
|
-
f.rewind
|
55
|
-
f.truncate(0)
|
56
|
-
if result
|
57
|
-
write(f, dump(result))
|
58
|
-
end
|
59
|
-
ensure
|
60
|
-
f.flock(File::LOCK_UN)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
rescue Errno::ENOENT, Exception => e
|
64
|
-
ScoutApm::Agent.instance.logger.error("Unable to access the layaway file [#{e.class} - #{e.message}]. " +
|
65
|
-
"The user running the app must have read & write access. " +
|
66
|
-
"Change the path by setting the `data_file` key in scout_apm.yml"
|
67
|
-
)
|
68
|
-
ScoutApm::Agent.instance.logger.debug(e.backtrace.join("\n\t"))
|
69
|
-
|
70
|
-
# ensure the in-memory metric hash is cleared so data doesn't continue to accumulate.
|
71
|
-
# ScoutApm::Agent.instance.store.metric_hash = {}
|
20
|
+
def write(data)
|
21
|
+
serialized_data = serialize(data)
|
22
|
+
File.open(path, "w") { |f| write_raw(f, serialized_data) }
|
72
23
|
end
|
73
24
|
|
74
|
-
def
|
75
|
-
data
|
76
|
-
result = load(data)
|
77
|
-
f.truncate(0)
|
78
|
-
result
|
25
|
+
def serialize(data)
|
26
|
+
Marshal.dump(data)
|
79
27
|
end
|
80
28
|
|
81
|
-
def
|
82
|
-
|
83
|
-
while (result < string.length)
|
84
|
-
result += f.write_nonblock(string)
|
85
|
-
end
|
86
|
-
rescue Errno::EAGAIN, Errno::EINTR
|
87
|
-
IO.select(nil, [f])
|
88
|
-
retry
|
29
|
+
def deserialize(data)
|
30
|
+
Marshal.load(data)
|
89
31
|
end
|
90
32
|
|
91
|
-
def
|
33
|
+
def read_raw(f)
|
92
34
|
contents = ""
|
93
35
|
while true
|
94
36
|
contents << f.read_nonblock(10_000)
|
@@ -99,5 +41,16 @@ module ScoutApm
|
|
99
41
|
rescue EOFError
|
100
42
|
contents
|
101
43
|
end
|
44
|
+
|
45
|
+
def write_raw(f, data)
|
46
|
+
result = 0
|
47
|
+
while (result < data.length)
|
48
|
+
result += f.write_nonblock(data)
|
49
|
+
end
|
50
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
51
|
+
IO.select(nil, [f])
|
52
|
+
retry
|
53
|
+
end
|
102
54
|
end
|
103
55
|
end
|
56
|
+
|
data/lib/scout_apm/store.rb
CHANGED
@@ -71,11 +71,10 @@ module ScoutApm
|
|
71
71
|
reporting_periods.select { |time, rp| force || time.timestamp < current_timestamp.timestamp}.
|
72
72
|
each { |time, rp|
|
73
73
|
collect_samplers(rp)
|
74
|
-
layaway.
|
74
|
+
layaway.write_reporting_period(rp)
|
75
75
|
reporting_periods.delete(time)
|
76
76
|
}
|
77
77
|
}
|
78
|
-
ScoutApm::Agent.instance.logger.debug("Finished writing to layaway")
|
79
78
|
end
|
80
79
|
|
81
80
|
######################################
|
@@ -108,8 +107,25 @@ module ScoutApm
|
|
108
107
|
@timestamp = @raw_time.to_i - @raw_time.sec # The normalized time (integer) to compare by
|
109
108
|
end
|
110
109
|
|
110
|
+
def self.minutes_ago(min, base_time=Time.now)
|
111
|
+
adjusted = base_time - (min * 60)
|
112
|
+
new(adjusted)
|
113
|
+
end
|
114
|
+
|
111
115
|
def to_s
|
112
|
-
|
116
|
+
strftime
|
117
|
+
end
|
118
|
+
|
119
|
+
def strftime(pattern=nil)
|
120
|
+
if pattern.nil?
|
121
|
+
to_time.iso8601
|
122
|
+
else
|
123
|
+
to_time.strftime(pattern)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_time
|
128
|
+
Time.at(@timestamp)
|
113
129
|
end
|
114
130
|
|
115
131
|
def eql?(o)
|
data/lib/scout_apm/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-06-
|
12
|
+
date: 2016-06-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -77,10 +77,10 @@ files:
|
|
77
77
|
- ".gitignore"
|
78
78
|
- CHANGELOG.markdown
|
79
79
|
- Gemfile
|
80
|
+
- LICENSE.md
|
80
81
|
- README.markdown
|
81
82
|
- Rakefile
|
82
83
|
- data/cacert.pem
|
83
|
-
- lib/allocations.bundle
|
84
84
|
- lib/scout_apm.rb
|
85
85
|
- lib/scout_apm/agent.rb
|
86
86
|
- lib/scout_apm/agent/logging.rb
|
@@ -181,7 +181,6 @@ files:
|
|
181
181
|
- lib/scout_apm/utils/time.rb
|
182
182
|
- lib/scout_apm/utils/unique_id.rb
|
183
183
|
- lib/scout_apm/version.rb
|
184
|
-
- lib/stacks.bundle
|
185
184
|
- scout_apm.gemspec
|
186
185
|
- test/data/config_test_1.yml
|
187
186
|
- test/test_helper.rb
|
data/lib/allocations.bundle
DELETED
Binary file
|
data/lib/stacks.bundle
DELETED
Binary file
|