Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 92 lines (80 sloc) 3.653 kB
c7bf1c3 @jaggederest queue time, initial header work
jaggederest authored
1 module NewRelic
2 module Agent
3 module Instrumentation
4 module QueueTime
5 MAIN_HEADER = 'X_REQUEST_START'
6
7 HEADER_REGEX = /([^\s\/,(t=)]+)? ?t=([0-9]+)/
8 SERVER_METRIC = 'WebFrontend/WebServer/'
9 ALL_METRIC = 'WebFrontend/WebServer/all'
10
11 # main method to extract queue time info from env hash,
12 # records individual server metrics and one roll-up for all servers
13 def parse_queue_time_from(env)
14 start_time = Time.now
15 matches = env[MAIN_HEADER].to_s.scan(HEADER_REGEX).map do |name, time|
16 [name, convert_from_microseconds(time.to_i)]
17 end
18 record_individual_server_stats(start_time, matches)
19 record_rollup_stat(start_time, matches)
20 end
21
22 private
23
24 # goes through the list of servers and records each one in
25 # reverse order, subtracting the time for each successive
26 # server from the earlier ones in the list.
27 # an example because it's complicated:
28 # start data:
29 # [['a', Time.at(1000)], ['b', Time.at(1001)]], start time: Time.at(1002)
30 # initial run: Time.at(1002), ['b', Time.at(1001)]
31 # next: Time.at(1001), ['a', Time.at(1000)]
32 # see tests for more
33 def record_individual_server_stats(end_time, matches) # (Time, [[String, Time]]) -> nil
34 matches = matches.sort_by {|name, time| time }
35 matches.reverse!
36 matches.inject(end_time) {|start_time, pair|
37 name, time = pair
38 record_queue_time_for(name, time, start_time)
39 time
40 }
41 end
42
43 # records the total time for all servers in a rollup metric
44 def record_rollup_stat(start_time, matches) # (Time, [String, Time]) -> nil
45 # default to the start time if we have no header
46 oldest_time = find_oldest_time(matches) || start_time
47 record_time_stat(ALL_METRIC, oldest_time, start_time)
48 end
49
50 # searches for the first server to touch a request
51 def find_oldest_time(matches) # [[String, Time]] -> Time
52 matches.map do |name, time|
53 time
54 end.min
55 end
56
57 # basically just assembles the metric name
58 def record_queue_time_for(name, time, start_time) # (Maybe String, Time, Time) -> nil
59 record_time_stat(SERVER_METRIC + name, time, start_time) if name
60 end
61
62 # Checks that the time is not negative, and does the actual
63 # data recording
64 def record_time_stat(name, start_time, end_time) # (String, Time, Time) -> nil
65 total_time = end_time - start_time
66 if total_time < 0
67 raise 'should not provide an end time less than start time'
68 else
69 NewRelic::Agent.get_stats(name).trace_call(total_time)
70 end
71 end
72
73 # convert a time to the value provided by the header, for convenience
74 def convert_to_microseconds(time) # Time -> Int
75 raise TypeError.new('Cannot convert a non-time into microseconds') unless time.is_a?(Time) || time.is_a?(Numeric)
76 return time if time.is_a?(Numeric)
77 (time.to_f * 1000000).to_i
78 end
79
80 # convert a time from the header value (time in microseconds)
81 # into a ruby time object
82 def convert_from_microseconds(int) # Int -> Time
83 raise TypeError.new('Cannot convert a non-number into a time') unless int.is_a?(Time) || int.is_a?(Numeric)
84 return int if int.is_a?(Time)
85 Time.at((int.to_f / 1000000))
86 end
87 end
88 end
89 end
90 end
91
Something went wrong with that request. Please try again.