/
queue_time.rb
78 lines (67 loc) · 2.56 KB
/
queue_time.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# encoding: utf-8
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
module NewRelic
module Agent
module Instrumentation
# https://newrelic.com/docs/features/tracking-front-end-time
# Record queue time metrics based on any of three headers
# which can be set on the request.
module QueueTime
unless defined?(REQUEST_START_HEADER)
REQUEST_START_HEADER = 'HTTP_X_REQUEST_START'.freeze
QUEUE_START_HEADER = 'HTTP_X_QUEUE_START'.freeze
MIDDLEWARE_START_HEADER = 'HTTP_X_MIDDLEWARE_START'.freeze
ALL_QUEUE_METRIC = 'WebFrontend/QueueTime'.freeze
# any timestamps before this are thrown out and the parser
# will try again with a larger unit (2000/1/1 UTC)
EARLIEST_ACCEPTABLE_TIME = Time.at(946684800)
CANDIDATE_HEADERS = [
REQUEST_START_HEADER,
QUEUE_START_HEADER,
MIDDLEWARE_START_HEADER
].freeze
DIVISORS = [1_000_000, 1_000, 1]
end
module_function
def parse_frontend_timestamp(headers, now=Time.now)
earliest = nil
CANDIDATE_HEADERS.each do |header|
if headers[header]
parsed = parse_timestamp(timestamp_string_from_header_value(headers[header]))
if parsed && (!earliest || parsed < earliest)
earliest = parsed
end
end
end
if earliest && earliest > now
NewRelic::Agent.logger.debug("Negative queue time detected, treating as zero: start=#{earliest.to_f} > now=#{now.to_f}")
earliest = now
end
earliest
end
def timestamp_string_from_header_value(value)
case value
when /^\s*([\d+\.]+)\s*$/ then $1
# following regexp intentionally unanchored to handle
# (ie ignore) leading server names
when /t=([\d+\.]+)/ then $1
end
end
def parse_timestamp(string)
DIVISORS.each do |divisor|
begin
t = Time.at(string.to_f / divisor)
return t if t > EARLIEST_ACCEPTABLE_TIME
rescue RangeError
# On Ruby versions built with a 32-bit time_t, attempting to
# instantiate a Time object in the far future raises a RangeError,
# in which case we know we've chosen the wrong divisor.
end
end
nil
end
end
end
end
end