scout_apm 0.1.12 → 0.1.13

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.
@@ -1,14 +1,37 @@
1
- if defined?(::Net) && defined?(Net::HTTP)
2
- ScoutApm::Agent.instance.logger.debug "Instrumenting Net::HTTP"
3
- Net::HTTP.class_eval do
4
- include ScoutApm::Tracer
5
-
6
- def request_with_scout_instruments(*args,&block)
7
- self.class.instrument("HTTP/request", :desc => "#{(@address+args.first.path.split('?').first)[0..99]}") do
8
- request_without_scout_instruments(*args,&block)
1
+ module ScoutApm
2
+ module Instruments
3
+ class NetHttp
4
+ attr_reader :logger
5
+
6
+ def initalize(logger=ScoutApm::Agent.instance.logger)
7
+ @logger = logger
8
+ @installed = false
9
+ end
10
+
11
+ def installed?
12
+ @installed
13
+ end
14
+
15
+ def install
16
+ @installed = true
17
+
18
+ if defined?(::Net) && defined?(::Net::HTTP)
19
+ ScoutApm::Agent.instance.logger.debug "Instrumenting Net::HTTP"
20
+
21
+ ::Net::HTTP.class_eval do
22
+ include ScoutApm::Tracer
23
+
24
+ def request_with_scout_instruments(*args,&block)
25
+ self.class.instrument("HTTP/request", :desc => "#{(@address+args.first.path.split('?').first)[0..99]}") do
26
+ request_without_scout_instruments(*args,&block)
27
+ end
28
+ end
29
+ alias request_without_scout_instruments request
30
+ alias request request_with_scout_instruments
31
+ end
32
+ end
33
+
9
34
  end
10
35
  end
11
- alias request_without_scout_instruments request
12
- alias request request_with_scout_instruments
13
36
  end
14
37
  end
@@ -4,97 +4,101 @@
4
4
  #
5
5
  # Data is stored in a Hash, where the keys are Time.to_i on the minute. The value is a Hash {:metrics => Hash, :slow_transactions => Array}.
6
6
  # When depositing data, the new data is either merged with an existing time or placed in a new key.
7
- class ScoutApm::Layaway
8
- attr_accessor :file
9
- def initialize
10
- @file = ScoutApm::LayawayFile.new
11
- end
7
+ module ScoutApm
8
+ class Layaway
9
+ attr_accessor :file
10
+
11
+ def initialize
12
+ @file = ScoutApm::LayawayFile.new
13
+ end
14
+
15
+ def deposit_and_deliver
16
+ new_metrics = ScoutApm::Agent.instance.store.metric_hash
17
+ log_deposited_metrics(new_metrics)
18
+ log_deposited_slow_transactions(ScoutApm::Agent.instance.store.slow_transactions)
19
+ to_deliver = {}
20
+ file.read_and_write do |old_data|
21
+ old_data ||= Hash.new
22
+ # merge data
23
+ # if (1) there's data in the file and (2) there isn't any data yet for the current minute, this means we've
24
+ # collected all metrics for the previous slots and we're ready to deliver.
25
+ #
26
+ # Example w/2 processes:
27
+ #
28
+ # 12:00:34 ---
29
+ # Process 1: old_data.any? => false, so deposits.
30
+ # Process 2: old_data_any? => true and old_data[12:00].nil? => false, so deposits.
31
+ #
32
+ # 12:01:34 ---
33
+ # Process 1: old_data.any? => true and old_data[12:01].nil? => true, so delivers metrics.
34
+ # Process 2: old_data.any? => true and old_data[12:01].nil? => false, so deposits.
35
+ if old_data.any? and old_data[slot].nil?
36
+ to_deliver = old_data
37
+ old_data = Hash.new
38
+ elsif old_data.any?
39
+ ScoutApm::Agent.instance.logger.debug "Not yet time to deliver payload for slot [#{Utils::Time.to_s(old_data.keys.sort.last)}]"
40
+ else
41
+ ScoutApm::Agent.instance.logger.debug "There is no data in the layaway file to deliver."
42
+ end
43
+ old_data[slot]=ScoutApm::Agent.instance.store.merge_data_and_clear(old_data[slot] || {:metrics => {}, :slow_transactions => []})
44
+ log_saved_data(old_data,new_metrics)
45
+ old_data
46
+ end
47
+ to_deliver.any? ? validate_data(to_deliver) : {}
48
+ end
12
49
 
13
- def deposit_and_deliver
14
- new_metrics = ScoutApm::Agent.instance.store.metric_hash
15
- log_deposited_metrics(new_metrics)
16
- log_deposited_slow_transactions(ScoutApm::Agent.instance.store.slow_transactions)
17
- to_deliver = {}
18
- file.read_and_write do |old_data|
19
- old_data ||= Hash.new
20
- # merge data
21
- # if (1) there's data in the file and (2) there isn't any data yet for the current minute, this means we've
22
- # collected all metrics for the previous slots and we're ready to deliver.
23
- #
24
- # Example w/2 processes:
25
- #
26
- # 12:00:34 ---
27
- # Process 1: old_data.any? => false, so deposits.
28
- # Process 2: old_data_any? => true and old_data[12:00].nil? => false, so deposits.
29
- #
30
- # 12:01:34 ---
31
- # Process 1: old_data.any? => true and old_data[12:01].nil? => true, so delivers metrics.
32
- # Process 2: old_data.any? => true and old_data[12:01].nil? => false, so deposits.
33
- if old_data.any? and old_data[slot].nil?
34
- to_deliver = old_data
35
- old_data = Hash.new
36
- elsif old_data.any?
37
- ScoutApm::Agent.instance.logger.debug "Not yet time to deliver payload for slot [#{Time.at(old_data.keys.sort.last).strftime("%m/%d/%y %H:%M:%S %z")}]"
50
+ # Ensures the data we're sending to the server isn't stale.
51
+ # This can occur if the agent is collecting data, and app server goes down w/data in the local storage.
52
+ # When it is restarted later data will remain in local storage but it won't be for the current reporting interval.
53
+ #
54
+ # If the data is stale, an empty Hash is returned. Otherwise, the data from the most recent slot is returned.
55
+ def validate_data(data)
56
+ data = data.to_a.sort
57
+ now = Time.now
58
+ if (most_recent = data.first.first) < now.to_i - 2*60
59
+ ScoutApm::Agent.instance.logger.debug "Local Storage is stale (#{Utils::Time.to_s(most_recent)}). Not sending data."
60
+ {}
38
61
  else
39
- ScoutApm::Agent.instance.logger.debug "There is no data in the layaway file to deliver."
62
+ data.first.last
40
63
  end
41
- old_data[slot]=ScoutApm::Agent.instance.store.merge_data_and_clear(old_data[slot] || {:metrics => {}, :slow_transactions => []})
42
- log_saved_data(old_data,new_metrics)
43
- old_data
64
+ rescue
65
+ ScoutApm::Agent.instance.logger.debug $!.message
66
+ ScoutApm::Agent.instance.logger.debug $!.backtrace
44
67
  end
45
- to_deliver.any? ? validate_data(to_deliver) : {}
46
- end
47
68
 
48
- # Ensures the data we're sending to the server isn't stale.
49
- # This can occur if the agent is collecting data, and app server goes down w/data in the local storage.
50
- # When it is restarted later data will remain in local storage but it won't be for the current reporting interval.
51
- #
52
- # If the data is stale, an empty Hash is returned. Otherwise, the data from the most recent slot is returned.
53
- def validate_data(data)
54
- data = data.to_a.sort
55
- now = Time.now
56
- if (most_recent = data.first.first) < now.to_i - 2*60
57
- ScoutApm::Agent.instance.logger.debug "Local Storage is stale (#{Time.at(most_recent).strftime("%m/%d/%y %H:%M:%S %z")}). Not sending data."
58
- {}
59
- else
60
- data.first.last
69
+ # Data is stored under timestamp-keys, aligned to the beginning of the
70
+ # current minute
71
+ def slot
72
+ t = Time.now
73
+ t -= t.sec
74
+ t.to_i
61
75
  end
62
- rescue
63
- ScoutApm::Agent.instance.logger.debug $!.message
64
- ScoutApm::Agent.instance.logger.debug $!.backtrace
65
- end
66
76
 
67
- # Data is stored under timestamp-keys (without the second).
68
- def slot
69
- t = Time.now
70
- t -= t.sec
71
- t.to_i
72
- end
77
+ def log_deposited_metrics(new_metrics)
78
+ request_count = new_metrics.
79
+ to_a.
80
+ select { |meta, stats| meta.metric_name =~ /\AController/ }.
81
+ map { |meta, stats| stats.call_count }.
82
+ inject(0) { |total, i| total + i }
73
83
 
74
- def log_deposited_metrics(new_metrics)
75
- controller_count = 0
76
- new_metrics.each do |meta,stats|
77
- if meta.metric_name =~ /\AController/
78
- controller_count += stats.call_count
79
- end
84
+ ScoutApm::Agent.instance.logger.debug "Depositing #{request_count} requests into #{Utils::Time.to_s(slot)} slot."
80
85
  end
81
- ScoutApm::Agent.instance.logger.debug "Depositing #{controller_count} requests into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
82
- end
83
86
 
84
- def log_deposited_slow_transactions(new_slow_transactions)
85
- ScoutApm::Agent.instance.logger.debug "Depositing #{new_slow_transactions.size} slow transactions into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
86
- end
87
+ def log_deposited_slow_transactions(new_slow_transactions)
88
+ ScoutApm::Agent.instance.logger.debug "Depositing #{new_slow_transactions.size} slow transactions into #{Utils::Time.to_s(slot)} slot."
89
+ end
87
90
 
88
- def log_saved_data(old_data,new_metrics)
89
- ScoutApm::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
90
- old_data.each do |k,v|
91
- controller_count = 0
92
- new_metrics.each do |meta,stats|
93
- if meta.metric_name =~ /\AController/
94
- controller_count += stats.call_count
91
+ def log_saved_data(old_data,new_metrics)
92
+ ScoutApm::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
93
+ old_data.each do |k,v|
94
+ controller_count = 0
95
+ new_metrics.each do |meta,stats|
96
+ if meta.metric_name =~ /\AController/
97
+ controller_count += stats.call_count
98
+ end
95
99
  end
100
+ ScoutApm::Agent.instance.logger.debug "#{Utils::Time.to_s(k)} => #{controller_count} requests and #{v[:slow_transactions].size} slow transactions"
96
101
  end
97
- ScoutApm::Agent.instance.logger.debug "#{Time.at(k).strftime("%m/%d/%y %H:%M:%S %z")} => #{controller_count} requests and #{v[:slow_transactions].size} slow transactions"
98
102
  end
99
103
  end
100
104
  end
@@ -1,72 +1,74 @@
1
1
  # Logic for the serialized file access
2
- class ScoutApm::LayawayFile
3
- def path
4
- "#{ScoutApm::Agent.instance.default_log_path}/scout_apm.db"
5
- end
2
+ module ScoutApm
3
+ class LayawayFile
4
+ def path
5
+ "#{ScoutApm::Agent.instance.default_log_path}/scout_apm.db"
6
+ end
6
7
 
7
- def dump(object)
8
- Marshal.dump(object)
9
- end
8
+ def dump(object)
9
+ Marshal.dump(object)
10
+ end
10
11
 
11
- def load(dump)
12
- if dump.size == 0
13
- ScoutApm::Agent.instance.logger.debug("No data in layaway file.")
14
- return nil
12
+ def load(dump)
13
+ if dump.size == 0
14
+ ScoutApm::Agent.instance.logger.debug("No data in layaway file.")
15
+ return nil
16
+ end
17
+ Marshal.load(dump)
18
+ rescue ArgumentError, TypeError => e
19
+ ScoutApm::Agent.instance.logger.debug("Error loading data from layaway file: #{e.inspect}")
20
+ ScoutApm::Agent.instance.logger.debug(e.backtrace.inspect)
21
+ nil
15
22
  end
16
- Marshal.load(dump)
17
- rescue ArgumentError, TypeError => e
18
- ScoutApm::Agent.instance.logger.debug("Error loading data from layaway file: #{e.inspect}")
19
- ScoutApm::Agent.instance.logger.debug(e.backtrace.inspect)
20
- nil
21
- end
22
23
 
23
- def read_and_write
24
- File.open(path, File::RDWR | File::CREAT) do |f|
25
- f.flock(File::LOCK_EX)
26
- begin
27
- result = (yield get_data(f))
28
- f.rewind
29
- f.truncate(0)
30
- if result
31
- write(f, dump(result))
24
+ def read_and_write
25
+ File.open(path, File::RDWR | File::CREAT) do |f|
26
+ f.flock(File::LOCK_EX)
27
+ begin
28
+ result = (yield get_data(f))
29
+ f.rewind
30
+ f.truncate(0)
31
+ if result
32
+ write(f, dump(result))
33
+ end
34
+ ensure
35
+ f.flock(File::LOCK_UN)
32
36
  end
33
- ensure
34
- f.flock(File::LOCK_UN)
35
37
  end
38
+ rescue Errno::ENOENT, Exception => e
39
+ ScoutApm::Agent.instance.logger.error("Unable to access the layaway file [#{e.message}]. The user running the app must have read+write access.")
40
+ ScoutApm::Agent.instance.logger.debug(e.backtrace.split("\n"))
41
+ # ensure the in-memory metric hash is cleared so data doesn't continue to accumulate.
42
+ ScoutApm::Agent.instance.store.metric_hash = {}
36
43
  end
37
- rescue Errno::ENOENT, Exception => e
38
- ScoutApm::Agent.instance.logger.error("Unable to access the layaway file [#{e.message}]. The user running the app must have read+write access.")
39
- ScoutApm::Agent.instance.logger.debug(e.backtrace.split("\n"))
40
- # ensure the in-memory metric hash is cleared so data doesn't continue to accumulate.
41
- ScoutApm::Agent.instance.store.metric_hash = {}
42
- end
43
44
 
44
- def get_data(f)
45
- data = read_until_end(f)
46
- result = load(data)
47
- f.truncate(0)
48
- result
49
- end
45
+ def get_data(f)
46
+ data = read_until_end(f)
47
+ result = load(data)
48
+ f.truncate(0)
49
+ result
50
+ end
50
51
 
51
- def write(f, string)
52
- result = 0
53
- while (result < string.length)
54
- result += f.write_nonblock(string)
52
+ def write(f, string)
53
+ result = 0
54
+ while (result < string.length)
55
+ result += f.write_nonblock(string)
56
+ end
57
+ rescue Errno::EAGAIN, Errno::EINTR
58
+ IO.select(nil, [f])
59
+ retry
55
60
  end
56
- rescue Errno::EAGAIN, Errno::EINTR
57
- IO.select(nil, [f])
58
- retry
59
- end
60
61
 
61
- def read_until_end(f)
62
- contents = ""
63
- while true
64
- contents << f.read_nonblock(10_000)
62
+ def read_until_end(f)
63
+ contents = ""
64
+ while true
65
+ contents << f.read_nonblock(10_000)
66
+ end
67
+ rescue Errno::EAGAIN, Errno::EINTR
68
+ IO.select([f])
69
+ retry
70
+ rescue EOFError
71
+ contents
65
72
  end
66
- rescue Errno::EAGAIN, Errno::EINTR
67
- IO.select([f])
68
- retry
69
- rescue EOFError
70
- contents
71
73
  end
72
74
  end
@@ -11,7 +11,14 @@ module ScoutApm
11
11
  :unicorn
12
12
  end
13
13
 
14
- def forking?; true; end
14
+ def forking?
15
+ return true unless (defined?(::Unicorn) && defined?(::Unicorn::Configurator))
16
+ ObjectSpace.each_object(::Unicorn::Configurator).first[:preload_app].tap {|x|
17
+ logger.info "Unicorn is forking? #{x}"
18
+ }
19
+ rescue
20
+ true
21
+ end
15
22
 
16
23
  def present?
17
24
  if defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
@@ -37,3 +44,4 @@ module ScoutApm
37
44
  end
38
45
  end
39
46
  end
47
+