Class: ScoutApm::TrackedRequest
- Inherits:
-
Object
- Object
- ScoutApm::TrackedRequest
- Defined in:
- lib/scout_apm/tracked_request.rb
Constant Summary collapse
- BACKTRACE_THRESHOLD =
the minimum threshold in seconds to record the backtrace for a metric.
0.5
- BACKTRACE_BLACKLIST =
["Controller", "Job"]
Instance Attribute Summary collapse
-
#annotations ⇒ Object
readonly
As we go through a request, instrumentation can mark more general data into the Request Known Keys: :uri - the full URI requested by the user :queue_latency - how long a background Job spent in the queue before starting processing.
-
#call_counts ⇒ Object
This maintains a lookup hash of Layer names and call counts.
-
#context ⇒ Object
readonly
Context is application defined extra information.
-
#headers ⇒ Object
readonly
Headers as recorded by rails Can be nil if we never reach a Rails Controller.
-
#request_type ⇒ Object
readonly
What kind of request is this? A trace of a web request, or a background job? Use job! and web! to set, and job? and web? to query.
-
#root_layer ⇒ Object
readonly
The first layer registered with this request.
Instance Method Summary collapse
- #acknowledge_children! ⇒ Object
-
#annotate_request(hsh) ⇒ Object
As we learn things about this request, we can add data here.
- #capture_backtrace?(layer) ⇒ Boolean
-
#error! ⇒ Object
This request had an exception.
- #error? ⇒ Boolean
-
#finalized? ⇒ Boolean
Are we finished with this request? We’re done if we have no layers left after popping one off.
-
#ignore_children! ⇒ Object
Enable this when you would otherwise double track something interesting.
- #ignoring_children? ⇒ Boolean
-
#initialize ⇒ TrackedRequest
constructor
A new instance of TrackedRequest.
- #job! ⇒ Object
- #job? ⇒ Boolean
-
#record! ⇒ Object
Convert this request to the appropriate structure, then report it into the peristent Store object.
-
#recorded? ⇒ Boolean
Have we already persisted this request? Used to know when we should just create a new one (don’t attempt to add data to an already-recorded request).
- #set_headers(headers) ⇒ Object
- #start_layer(layer) ⇒ Object
-
#start_request(layer) ⇒ Object
Run at the beginning of the whole request.
- #stop_layer ⇒ Object
-
#stop_request ⇒ Object
Run at the end of the whole request.
-
#unique_name ⇒ Object
Only call this after the request is complete.
-
#update_call_counts!(layer) ⇒ Object
Maintains a lookup Hash of call counts by layer name.
- #web! ⇒ Object
- #web? ⇒ Boolean
Constructor Details
#initialize ⇒ TrackedRequest
Returns a new instance of TrackedRequest.
40 41 42 43 44 45 46 47 48 |
# File 'lib/scout_apm/tracked_request.rb', line 40 def initialize @layers = [] @call_counts = Hash.new { |h, k| h[k] = CallSet.new } @annotations = {} @ignoring_children = false @context = Context.new @root_layer = nil @error = false end |
Instance Attribute Details
#annotations ⇒ Object (readonly)
As we go through a request, instrumentation can mark more general data into the Request Known Keys:
:uri - the full URI requested by the user
:queue_latency - how long a background Job spent in the queue before starting processing
23 24 25 |
# File 'lib/scout_apm/tracked_request.rb', line 23 def annotations @annotations end |
#call_counts ⇒ Object
This maintains a lookup hash of Layer names and call counts. It’s used to trigger fetching a backtrace on n+1 calls. Note that layer names might not be Strings - can alse be Utils::ActiveRecordMetricName. Also, this would fail for layers with same names across multiple types.
36 37 38 |
# File 'lib/scout_apm/tracked_request.rb', line 36 def call_counts @call_counts end |
#context ⇒ Object (readonly)
Context is application defined extra information. (ie, which user, what is their email/ip, what plan are they on, what locale are they using, etc) See documentation for examples on how to set this from a before_filter
13 14 15 |
# File 'lib/scout_apm/tracked_request.rb', line 13 def context @context end |
#headers ⇒ Object (readonly)
Headers as recorded by rails Can be nil if we never reach a Rails Controller
27 28 29 |
# File 'lib/scout_apm/tracked_request.rb', line 27 def headers @headers end |
#request_type ⇒ Object (readonly)
What kind of request is this? A trace of a web request, or a background job? Use job! and web! to set, and job? and web? to query
31 32 33 |
# File 'lib/scout_apm/tracked_request.rb', line 31 def request_type @request_type end |
#root_layer ⇒ Object (readonly)
The first layer registered with this request. All other layers will be children of this layer.
17 18 19 |
# File 'lib/scout_apm/tracked_request.rb', line 17 def root_layer @root_layer end |
Instance Method Details
#acknowledge_children! ⇒ Object
245 246 247 |
# File 'lib/scout_apm/tracked_request.rb', line 245 def acknowledge_children! @ignoring_children = false end |
#annotate_request(hsh) ⇒ Object
As we learn things about this request, we can add data here. For instance, when we know where Rails routed this request to, we can store that scope info. Or as soon as we know which URI it was directed at, we can store that.
This data is internal to ScoutApm, to add custom information, use the Context api.
135 136 137 |
# File 'lib/scout_apm/tracked_request.rb', line 135 def annotate_request(hsh) @annotations.merge!(hsh) end |
#capture_backtrace?(layer) ⇒ Boolean
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/scout_apm/tracked_request.rb', line 77 def capture_backtrace?(layer) # Never capture backtraces for this kind of layer. The backtrace will # always be 100% framework code. return false if BACKTRACE_BLACKLIST.include?(layer.type) # Only capture backtraces if we're in a real "request". Otherwise we # can spend lot of time capturing backtraces from the internals of # Sidekiq, only to throw them away immediately. return false unless (web? || job?) # Capture any individually slow layer. return true if layer.total_exclusive_time > BACKTRACE_THRESHOLD # Capture any layer that we've seen many times. Captures n+1 problems return true if @call_counts[layer.name].capture_backtrace? # Don't capture otherwise false end |
#error! ⇒ Object
This request had an exception. Mark it down as an error
140 141 142 |
# File 'lib/scout_apm/tracked_request.rb', line 140 def error! @error = true end |
#error? ⇒ Boolean
144 145 146 |
# File 'lib/scout_apm/tracked_request.rb', line 144 def error? @error end |
#finalized? ⇒ Boolean
Are we finished with this request? We’re done if we have no layers left after popping one off
108 109 110 |
# File 'lib/scout_apm/tracked_request.rb', line 108 def finalized? @layers.none? end |
#ignore_children! ⇒ Object
Enable this when you would otherwise double track something interesting. This came up when we implemented InfluxDB instrumentation, which is more specific, and useful than the fact that InfluxDB happens to use Net::HTTP internally
When enabled, new layers won’t be added to the current Request.
Do not forget to turn if off when leaving a layer, it is the instrumentation’s task to do that.
241 242 243 |
# File 'lib/scout_apm/tracked_request.rb', line 241 def ignore_children! @ignoring_children = true end |
#ignoring_children? ⇒ Boolean
249 250 251 |
# File 'lib/scout_apm/tracked_request.rb', line 249 def ignoring_children? @ignoring_children end |
#job! ⇒ Object
152 153 154 |
# File 'lib/scout_apm/tracked_request.rb', line 152 def job! @request_type = "job" end |
#job? ⇒ Boolean
156 157 158 |
# File 'lib/scout_apm/tracked_request.rb', line 156 def job? request_type == "job" end |
#record! ⇒ Object
Convert this request to the appropriate structure, then report it into the peristent Store object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/scout_apm/tracked_request.rb', line 174 def record! @recorded = true # Update immediate and long-term histograms for both job and web requests if unique_name != :unknown ScoutApm::Agent.instance.request_histograms.add(unique_name, root_layer.total_call_time) ScoutApm::Agent.instance.request_histograms_by_time[ScoutApm::Agent.instance.store.]. add(unique_name, root_layer.total_call_time) end metrics = LayerConverters::MetricConverter.new(self).call ScoutApm::Agent.instance.store.track!(metrics) error_metrics = LayerConverters::ErrorConverter.new(self).call ScoutApm::Agent.instance.store.track!(error_metrics) if web? # Don't #call this - that's the job of the ScoredItemSet later. slow_converter = LayerConverters::SlowRequestConverter.new(self) ScoutApm::Agent.instance.store.track_slow_transaction!(slow_converter) queue_time_metrics = LayerConverters::RequestQueueTimeConverter.new(self).call ScoutApm::Agent.instance.store.track!(queue_time_metrics) end if job? job_metrics = LayerConverters::JobConverter.new(self).call ScoutApm::Agent.instance.store.track_job!(job_metrics) job_converter = LayerConverters::SlowJobConverter.new(self) ScoutApm::Agent.instance.store.track_slow_job!(job_converter) end end |
#recorded? ⇒ Boolean
Have we already persisted this request? Used to know when we should just create a new one (don’t attempt to add data to an already-recorded request). See RequestManager
223 224 225 |
# File 'lib/scout_apm/tracked_request.rb', line 223 def recorded? @recorded end |
#set_headers(headers) ⇒ Object
148 149 150 |
# File 'lib/scout_apm/tracked_request.rb', line 148 def set_headers(headers) @headers = headers end |
#start_layer(layer) ⇒ Object
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/scout_apm/tracked_request.rb', line 50 def start_layer(layer) if ignoring_children? return end start_request(layer) unless @root_layer update_call_counts!(layer) @layers[-1].add_child(layer) if @layers.any? @layers.push(layer) end |
#start_request(layer) ⇒ Object
Run at the beginning of the whole request
-
Capture the first layer as the root_layer
115 116 117 |
# File 'lib/scout_apm/tracked_request.rb', line 115 def start_request(layer) @root_layer = layer unless @root_layer # capture root layer end |
#stop_layer ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/scout_apm/tracked_request.rb', line 61 def stop_layer return if ignoring_children? layer = @layers.pop layer.record_stop_time! if capture_backtrace?(layer) layer.capture_backtrace! end if finalized? stop_request end end |
#stop_request ⇒ Object
Run at the end of the whole request
-
Send the request off to be stored
122 123 124 |
# File 'lib/scout_apm/tracked_request.rb', line 122 def stop_request record! end |
#unique_name ⇒ Object
Only call this after the request is complete
209 210 211 212 213 214 215 216 217 218 |
# File 'lib/scout_apm/tracked_request.rb', line 209 def unique_name @unique_name ||= begin scope_layer = LayerConverters::ConverterBase.new(self).scope_layer if scope_layer scope_layer.legacy_metric_name else :unknown end end end |
#update_call_counts!(layer) ⇒ Object
Maintains a lookup Hash of call counts by layer name. Used to determine if we should capture a backtrace.
98 99 100 |
# File 'lib/scout_apm/tracked_request.rb', line 98 def update_call_counts!(layer) @call_counts[layer.name].update!(layer.desc) end |
#web! ⇒ Object
160 161 162 |
# File 'lib/scout_apm/tracked_request.rb', line 160 def web! @request_type = "web" end |
#web? ⇒ Boolean
164 165 166 |
# File 'lib/scout_apm/tracked_request.rb', line 164 def web? request_type == "web" end |