scout_apm 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+