scout_apm 4.0.4 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +4 -0
- data/CHANGELOG.markdown +33 -0
- data/LICENSE.md +21 -28
- data/gems/typhoeus.gemfile +3 -0
- data/lib/scout_apm/agent/preconditions.rb +3 -3
- data/lib/scout_apm/auto_instrument/rails.rb +0 -1
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +2 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +13 -2
- data/lib/scout_apm/config.rb +10 -0
- data/lib/scout_apm/error_service/middleware.rb +2 -2
- data/lib/scout_apm/error_service/payload.rb +1 -1
- data/lib/scout_apm/error_service.rb +3 -1
- data/lib/scout_apm/external_service_metric_set.rb +97 -0
- data/lib/scout_apm/external_service_metric_stats.rb +85 -0
- data/lib/scout_apm/fake_store.rb +3 -0
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +5 -1
- data/lib/scout_apm/git_revision.rb +9 -0
- data/lib/scout_apm/ignored_uris.rb +3 -1
- data/lib/scout_apm/instant/middleware.rb +2 -0
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +5 -2
- data/lib/scout_apm/instruments/action_view.rb +13 -5
- data/lib/scout_apm/instruments/active_record.rb +2 -0
- data/lib/scout_apm/instruments/elasticsearch.rb +2 -0
- data/lib/scout_apm/instruments/sinatra.rb +2 -0
- data/lib/scout_apm/instruments/typhoeus.rb +8 -6
- data/lib/scout_apm/layer_converters/external_service_converter.rb +65 -0
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +4 -0
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +2 -0
- data/lib/scout_apm/logger.rb +2 -2
- data/lib/scout_apm/reporting.rb +2 -1
- data/lib/scout_apm/serializers/external_service_serializer_to_json.rb +15 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +4 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +4 -1
- data/lib/scout_apm/store.rb +21 -1
- data/lib/scout_apm/tracked_request.rb +7 -1
- data/lib/scout_apm/utils/sql_sanitizer.rb +32 -6
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +5 -0
- data/scout_apm.gemspec +0 -2
- data/test/unit/auto_instrument/anonymous_block_value.rb +7 -0
- data/test/unit/auto_instrument/hanging_method.rb +6 -0
- data/test/unit/auto_instrument_test.rb +8 -0
- data/test/unit/external_service_metric_set_test.rb +67 -0
- data/test/unit/external_service_metric_stats_test.rb +106 -0
- data/test/unit/ignored_uris_test.rb +6 -0
- data/test/unit/instruments/typhoeus_test.rb +42 -0
- data/test/unit/remote/{test_message.rb → message_test.rb} +0 -0
- data/test/unit/remote/{test_router.rb → route_test.rb} +0 -0
- data/test/unit/remote/{test_server.rb → server_test.rb} +4 -1
- data/test/unit/serializers/payload_serializer_test.rb +3 -3
- data/test/unit/sql_sanitizer_test.rb +19 -2
- metadata +16 -9
- data/.travis.yml +0 -22
- data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -32
- data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9a94a38d6b06a02e34ce7923e8084976739cc8d0fba1adfbc606f88b0bdf4d7
|
4
|
+
data.tar.gz: aebe73f5379911e8691be2771b3a3fc3f5fbdd4779f2038f6a6f6e0ed38e1e20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8850b1365747879effcfe383d0b438070069d2b625365dc8ea61db7c72cf6840cdf73675c95aea7a7688750dbcb41efec1b7320232312172bc49be9b207d421
|
7
|
+
data.tar.gz: f3d389aa73f8b4090627843410c610edc1c5fa88740587136dbd8277d3ddfa5360e61d0d256ae23db7cf53f20b482d3f283541c077efcd2d12032e5f3f72f4e2
|
data/.github/workflows/test.yml
CHANGED
@@ -26,6 +26,9 @@ jobs:
|
|
26
26
|
- ruby: 2.4
|
27
27
|
- ruby: 2.5
|
28
28
|
- ruby: 2.6
|
29
|
+
- ruby: 2.6
|
30
|
+
gemfile: gems/typhoeus.gemfile
|
31
|
+
test_features: "typhoeus"
|
29
32
|
- ruby: 2.6
|
30
33
|
gemfile: gems/octoshark.gemfile
|
31
34
|
- ruby: 2.6
|
@@ -36,6 +39,7 @@ jobs:
|
|
36
39
|
|
37
40
|
env:
|
38
41
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
42
|
+
SCOUT_TEST_FEATURES: ${{ matrix.test_features }}
|
39
43
|
|
40
44
|
runs-on: ubuntu-latest
|
41
45
|
|
data/CHANGELOG.markdown
CHANGED
@@ -1,9 +1,42 @@
|
|
1
|
+
# 5.0.0
|
2
|
+
|
3
|
+
* Add External Service metrics reporting (#403)
|
4
|
+
* Relicense to MIT (#429)
|
5
|
+
* Opt out of frozen string literals in select files (#427)
|
6
|
+
* Fall back when logger can't write to destination (#423)
|
7
|
+
* Avoid exception on race condition (#407)
|
8
|
+
* Add Mina deploy tracking support (#327)
|
9
|
+
|
10
|
+
# 4.1.2
|
11
|
+
|
12
|
+
* Add record_queue_time configuration (PR #422)
|
13
|
+
|
14
|
+
# 4.1.1
|
15
|
+
|
16
|
+
* Fix issue with Typheous Hydra instrument (#418)
|
17
|
+
|
18
|
+
# 4.1.0
|
19
|
+
|
20
|
+
* Preload Celluloid in Shoryuken instrumentation (#331)
|
21
|
+
* Fix deprecation warning in Rails 6.1+ (#365)
|
22
|
+
* Set Typheous's desc more directly (#392)
|
23
|
+
* Delegate to ActiveRecord #log more intelligently (#394)
|
24
|
+
* Don't delay starting agent when possible (#397)
|
25
|
+
* Fix template naming issue in Rails 6+ (#399)
|
26
|
+
* Avoid double-counting issue with AutoInstruments (#405)
|
27
|
+
* Renaming test files for Remote::{Server|Route|Message} to be included in test run (#409)
|
28
|
+
* More robust naming of Sidekiq jobs (#412)
|
29
|
+
* Allow render_template instruments to work with older Rails (#413)
|
30
|
+
* Fix function to manually capture exceptions (#415)
|
31
|
+
* Enhance SQL Sanitization (#417)
|
32
|
+
|
1
33
|
# 4.0.4
|
2
34
|
|
3
35
|
* Add Faktory Support (#385)
|
4
36
|
* Remove Regexp hack for 1.8.7 (no longer supported) (#384)
|
5
37
|
* More robust DelayedJob detection (#382)
|
6
38
|
* Fix kwargs handling in Tracing module (#381)
|
39
|
+
|
7
40
|
# 4.0.3
|
8
41
|
|
9
42
|
* Handle edge case with nil Typhoeus current-layer (#380)
|
data/LICENSE.md
CHANGED
@@ -1,31 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
This license shall be automatically terminated and revoked if you exceed the scope or violate any terms and conditions of this license.
|
23
|
-
|
24
|
-
ALL LICENSED SOFTWARE, DOCUMENTATION AND OTHER PRODUCTS, INFORMATION, MATERIALS AND SERVICES PROVIDED BY ZIMUTH ARE PROVIDED HERE “AS IS.” ZIMUTH DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHER (INCLUDING ALL WARRANTIES ARISING FROM COURSE OF DEALING, USAGE OR TRADE PRACTICE), AND SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. WITHOUT LIMITING THE FOREGOING, ZIMUTH MAKES NO WARRANTY OF ANY KIND THAT THE LICENSED SOFTWARE OR DOCUMENTATION, OR ANY OTHER LICENSOR OR THIRD-PARTY GOODS, SERVICES, TECHNOLOGIES OR MATERIALS, WILL MEET YOUR REQUIREMENTS, OPERATE WITHOUT INTERRUPTION, ACHIEVE ANY INTENDED RESULT, BE COMPATIBLE OR WORK WITH ANY OTHER GOODS, SERVICES, TECHNOLOGIES OR MATERIALS (INCLUDING ANY SOFTWARE, HARDWARE, SYSTEM OR NETWORK) EXCEPT IF AND TO THE EXTENT EXPRESSLY SET FORTH IN THE DOCUMENTATION, OR BE SECURE, ACCURATE, COMPLETE, FREE OF HARMFUL CODE OR ERROR FREE. ALL OPEN-SOURCE COMPONENTS AND OTHER THIRD-PARTY MATERIALS ARE PROVIDED “AS IS” AND ANY REPRESENTATION OR WARRANTY OF OR CONCERNING ANY OF THEM IS STRICTLY BETWEEN LICENSEE AND THE THIRD-PARTY OWNER OR DISTRIBUTOR OF SUCH OPEN-SOURCE COMPONENTS AND THIRD-PARTY MATERIALS.
|
25
|
-
|
26
|
-
IN NO EVENT WILL ZIMUTH BE LIABLE UNDER OR IN CONNECTION WITH THIS LICENSE OR ITS SUBJECT MATTER UNDER ANY LEGAL OR EQUITABLE THEORY, INCLUDING BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY AND OTHERWISE, FOR ANY CONSEQUENTIAL, INCIDENTAL, INDIRECT, EXEMPLARY, SPECIAL, ENHANCED OR PUNITIVE DAMAGES, IN EACH CASE REGARDLESS OF WHETHER SUCH PERSONS WERE ADVISED OF THE POSSIBILITY OF SUCH LOSSES OR DAMAGES OR SUCH LOSSES OR DAMAGES WERE OTHERWISE FORESEEABLE, AND NOTWITHSTANDING THE FAILURE OF ANY AGREED OR OTHER REMEDY OF ITS ESSENTIAL PURPOSE.
|
27
|
-
|
28
|
-
## OPEN SOURCE COMPONENTS
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015-2021 Zimuth, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
29
22
|
|
30
23
|
This product includes `rusage`, which inherits the Artistic License 2.0 from proc/wait3.
|
31
24
|
See http://www.rubydoc.info/gems/rusage/0.2.0.
|
@@ -27,10 +27,10 @@ module ScoutApm
|
|
27
27
|
PRECONDITION_DETECTED_SERVER = {
|
28
28
|
:message => proc {|environ| "Deferring agent start. Standing by for first request" },
|
29
29
|
:check => proc { |context|
|
30
|
-
|
31
|
-
|
30
|
+
app_server_found = context.environment.app_server_integration(true).found?
|
31
|
+
background_job_integration_found = context.environment.background_job_integrations.length > 0
|
32
32
|
|
33
|
-
|
33
|
+
app_server_found || background_job_integration_found
|
34
34
|
},
|
35
35
|
:severity => :info,
|
36
36
|
},
|
@@ -37,6 +37,8 @@ module ScoutApm
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def install_processor
|
40
|
+
# celluloid has not loaded by this point and older versions of `shorykuen/processor` assume that it did
|
41
|
+
require 'celluloid' if defined?(::Shoryuken::VERSION) && ::Shoryuken::VERSION < '3'
|
40
42
|
require 'shoryuken/processor' # sidekiq v4 has not loaded this file by this point
|
41
43
|
|
42
44
|
::Shoryuken::Processor.class_eval do
|
@@ -80,12 +80,23 @@ module ScoutApm
|
|
80
80
|
DELAYED_WRAPPER_KLASS = 'Sidekiq::Extensions::DelayedClass'.freeze
|
81
81
|
|
82
82
|
|
83
|
+
# Capturing the class name is a little tricky, since we need to handle several cases:
|
84
|
+
# 1. ActiveJob, with the class in the key 'wrapped'
|
85
|
+
# 2. ActiveJob, but the 'wrapped' key is wrong (due to YAJL serializing weirdly), find it in args.job_class
|
86
|
+
# 3. DelayedJob wrapper, deserializing using YAML into the real object, which can be introspected
|
87
|
+
# 4. No wrapper, just sidekiq's class
|
83
88
|
def job_class(msg)
|
84
89
|
job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
|
85
90
|
|
86
|
-
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
|
91
|
+
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped') && msg['wrapped'].is_a?(String)
|
87
92
|
begin
|
88
|
-
job_class = msg['wrapped']
|
93
|
+
job_class = msg['wrapped'].to_s
|
94
|
+
rescue
|
95
|
+
ACTIVE_JOB_KLASS
|
96
|
+
end
|
97
|
+
elsif job_class == ACTIVE_JOB_KLASS && msg.try(:[], 'args').try(:[], 'job_class')
|
98
|
+
begin
|
99
|
+
job_class = msg['args']['job_class'].to_s
|
89
100
|
rescue
|
90
101
|
ACTIVE_JOB_KLASS
|
91
102
|
end
|
data/lib/scout_apm/config.rb
CHANGED
@@ -29,6 +29,7 @@ require 'scout_apm/environment'
|
|
29
29
|
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
30
30
|
# scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
|
31
31
|
# uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
|
32
|
+
# record_queue_time - true/false to enable recording of queuetime.
|
32
33
|
# remote_agent_host - Internal: What host to bind to, and also send messages to for remote. Default: 127.0.0.1.
|
33
34
|
# remote_agent_port - What port to bind the remote webserver to
|
34
35
|
# start_resque_server_instrument - Used in special situations with certain Resque installs
|
@@ -55,6 +56,8 @@ module ScoutApm
|
|
55
56
|
'direct_host',
|
56
57
|
'disabled_instruments',
|
57
58
|
'enable_background_jobs',
|
59
|
+
'external_service_metric_limit',
|
60
|
+
'external_service_metric_report_limit',
|
58
61
|
'host',
|
59
62
|
'hostname',
|
60
63
|
'ignore',
|
@@ -69,6 +72,7 @@ module ScoutApm
|
|
69
72
|
'name',
|
70
73
|
'profile',
|
71
74
|
'proxy',
|
75
|
+
'record_queue_time',
|
72
76
|
'remote_agent_host',
|
73
77
|
'remote_agent_port',
|
74
78
|
'report_format',
|
@@ -177,7 +181,10 @@ module ScoutApm
|
|
177
181
|
'compress_payload' => BooleanCoercion.new,
|
178
182
|
'database_metric_limit' => IntegerCoercion.new,
|
179
183
|
'database_metric_report_limit' => IntegerCoercion.new,
|
184
|
+
'external_service_metric_limit' => IntegerCoercion.new,
|
185
|
+
'external_service_metric_report_limit' => IntegerCoercion.new,
|
180
186
|
'instrument_http_url_length' => IntegerCoercion.new,
|
187
|
+
'record_queue_time' => BooleanCoercion.new,
|
181
188
|
'start_resque_server_instrument' => BooleanCoercion.new,
|
182
189
|
'timeline_traces' => BooleanCoercion.new,
|
183
190
|
'auto_instruments' => BooleanCoercion.new,
|
@@ -289,9 +296,12 @@ module ScoutApm
|
|
289
296
|
'remote_agent_port' => 7721, # picked at random
|
290
297
|
'database_metric_limit' => 5000, # The hard limit on db metrics
|
291
298
|
'database_metric_report_limit' => 1000,
|
299
|
+
'external_service_metric_limit' => 5000, # The hard limit on external service metrics
|
300
|
+
'external_service_metric_report_limit' => 1000,
|
292
301
|
'instrument_http_url_length' => 300,
|
293
302
|
'start_resque_server_instrument' => true, # still only starts if Resque is detected
|
294
303
|
'collect_remote_ip' => true,
|
304
|
+
'record_queue_time' => true,
|
295
305
|
'timeline_traces' => true,
|
296
306
|
'auto_instruments' => false,
|
297
307
|
'auto_instruments_ignore' => [],
|
@@ -9,10 +9,10 @@ module ScoutApm
|
|
9
9
|
begin
|
10
10
|
response = @app.call(env)
|
11
11
|
rescue Exception => exception
|
12
|
-
puts "[Scout Error Service] Caught Exception: #{exception.class.name}"
|
13
|
-
|
14
12
|
context = ScoutApm::Agent.instance.context
|
15
13
|
|
14
|
+
context.logger.debug "[Scout Error Service] Caught Exception: #{exception.class.name}"
|
15
|
+
|
16
16
|
# Bail out early, and reraise if the error is not interesting.
|
17
17
|
if context.ignored_exceptions.ignored?(exception)
|
18
18
|
raise
|
@@ -16,7 +16,9 @@ module ScoutApm
|
|
16
16
|
# Used internally by SidekiqException
|
17
17
|
def self.capture(exception, params = {})
|
18
18
|
return if disabled?
|
19
|
-
|
19
|
+
|
20
|
+
context = ScoutApm::Agent.instance.context
|
21
|
+
return if context.ignored_exceptions.ignore?(exception)
|
20
22
|
|
21
23
|
context.errors_buffer.capture(exception, env)
|
22
24
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Note, this class must be Marshal Dumpable
|
2
|
+
module ScoutApm
|
3
|
+
class ExternalServiceMetricSet
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :metrics # the raw metrics. You probably want #metrics_to_report
|
7
|
+
|
8
|
+
def marshal_dump
|
9
|
+
[ @metrics ]
|
10
|
+
end
|
11
|
+
|
12
|
+
def marshal_load(array)
|
13
|
+
@metrics = array.first
|
14
|
+
@context = ScoutApm::Agent.instance.context
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(context)
|
18
|
+
@context = context
|
19
|
+
|
20
|
+
# A hash of ExternalServiceMetricStats values, keyed by ExternalServiceMetricStats.key
|
21
|
+
@metrics = Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Need to look this up again if we end up as nil. Which I guess can happen
|
25
|
+
# after a Marshal load?
|
26
|
+
def context
|
27
|
+
@context ||= ScoutApm::Agent.instance.context
|
28
|
+
end
|
29
|
+
|
30
|
+
def each
|
31
|
+
metrics.each do |_key, external_service_metric_stat|
|
32
|
+
yield external_service_metric_stat
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Looks up a ExternalServiceMetricStats instance in the +@metrics+ hash. Sets the value to +other+ if no key
|
37
|
+
# Returns a ExternalServiceMetricStats instance
|
38
|
+
def lookup(other)
|
39
|
+
metrics[other.key] ||= other
|
40
|
+
end
|
41
|
+
|
42
|
+
# Take another set, and merge it with this one
|
43
|
+
def combine!(other)
|
44
|
+
other.each do |metric|
|
45
|
+
self << metric
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Add a single ExternalServiceMetricStats object to this set.
|
51
|
+
#
|
52
|
+
# Looks up an existing one under this key and merges, or just saves a new
|
53
|
+
# one under the key
|
54
|
+
def <<(stat)
|
55
|
+
existing_stat = metrics[stat.key]
|
56
|
+
if existing_stat
|
57
|
+
existing_stat.combine!(stat)
|
58
|
+
elsif at_limit?
|
59
|
+
# We're full up, can't add any more.
|
60
|
+
# Should I log this? It may get super noisy?
|
61
|
+
else
|
62
|
+
metrics[stat.key] = stat
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def increment_transaction_count!
|
67
|
+
metrics.each do |_key, external_service_metric_stat|
|
68
|
+
external_service_metric_stat.increment_transaction_count!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def metrics_to_report
|
73
|
+
report_limit = context.config.value('external_service_metric_report_limit')
|
74
|
+
if metrics.size > report_limit
|
75
|
+
metrics.
|
76
|
+
values.
|
77
|
+
sort_by {|stat| stat.call_time }.
|
78
|
+
reverse.
|
79
|
+
take(report_limit)
|
80
|
+
else
|
81
|
+
metrics.values
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
metrics.map {|key, metric|
|
87
|
+
"#{key.inspect} - Count: #{metric.call_count}, Total Time: #{"%.2f" % metric.call_time}"
|
88
|
+
}.join("\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
def at_limit?
|
92
|
+
@limit ||= context.config.value('external_service_metric_limit')
|
93
|
+
metrics.size >= @limit
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
class ExternalServiceMetricStats
|
3
|
+
|
4
|
+
DEFAULT_HISTOGRAM_SIZE = 50
|
5
|
+
|
6
|
+
attr_reader :domain_name
|
7
|
+
attr_reader :operation
|
8
|
+
attr_reader :scope
|
9
|
+
|
10
|
+
attr_reader :transaction_count
|
11
|
+
|
12
|
+
attr_reader :call_count
|
13
|
+
attr_reader :call_time
|
14
|
+
|
15
|
+
attr_reader :min_call_time
|
16
|
+
attr_reader :max_call_time
|
17
|
+
|
18
|
+
attr_reader :histogram
|
19
|
+
|
20
|
+
def initialize(domain_name, operation, scope, call_count, call_time)
|
21
|
+
@domain_name = domain_name
|
22
|
+
@operation = operation
|
23
|
+
|
24
|
+
@call_count = call_count
|
25
|
+
|
26
|
+
@call_time = call_time
|
27
|
+
@min_call_time = call_time
|
28
|
+
@max_call_time = call_time
|
29
|
+
|
30
|
+
# This histogram is for call_time
|
31
|
+
@histogram = NumericHistogram.new(DEFAULT_HISTOGRAM_SIZE)
|
32
|
+
@histogram.add(call_time)
|
33
|
+
|
34
|
+
@transaction_count = 0
|
35
|
+
|
36
|
+
@scope = scope
|
37
|
+
end
|
38
|
+
|
39
|
+
# Merge data in this scope. Used in ExternalServiceMetricSet
|
40
|
+
def key
|
41
|
+
@key ||= [domain_name, operation, scope]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Combine data from another ExternalServiceMetricStats into +self+. Modifies and returns +self+
|
45
|
+
def combine!(other)
|
46
|
+
return self if other == self
|
47
|
+
|
48
|
+
@transaction_count += other.transaction_count
|
49
|
+
@call_count += other.call_count
|
50
|
+
@call_time += other.call_time
|
51
|
+
|
52
|
+
@min_call_time = other.min_call_time if @min_call_time.zero? or other.min_call_time < @min_call_time
|
53
|
+
@max_call_time = other.max_call_time if other.max_call_time > @max_call_time
|
54
|
+
|
55
|
+
@histogram.combine!(other.histogram)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def as_json
|
60
|
+
json_attributes = [
|
61
|
+
:domain_name,
|
62
|
+
:operation,
|
63
|
+
:scope,
|
64
|
+
|
65
|
+
:transaction_count,
|
66
|
+
:call_count,
|
67
|
+
|
68
|
+
:histogram,
|
69
|
+
:call_time,
|
70
|
+
:max_call_time,
|
71
|
+
:min_call_time,
|
72
|
+
]
|
73
|
+
|
74
|
+
ScoutApm::AttributeArranger.call(self, json_attributes)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Called by the Set on each ExternalServiceMetricStats object that it holds, only
|
78
|
+
# once during the recording of a transaction.
|
79
|
+
#
|
80
|
+
# Don't call elsewhere, and don't set to 1 in the initializer.
|
81
|
+
def increment_transaction_count!
|
82
|
+
@transaction_count += 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/scout_apm/fake_store.rb
CHANGED
@@ -71,7 +71,11 @@ module ScoutApm
|
|
71
71
|
#
|
72
72
|
# We avoid this issue by not calling .respond_to? here, and instead using the less optimal `rescue nil` approach
|
73
73
|
def raw_database_adapter
|
74
|
-
adapter = ActiveRecord::Base.
|
74
|
+
adapter = ActiveRecord::Base.connection_db_config.configuration_hash[:adapter] rescue nil
|
75
|
+
|
76
|
+
if adapter.nil?
|
77
|
+
adapter = ActiveRecord::Base.connection_config[:adapter].to_s rescue nil
|
78
|
+
end
|
75
79
|
|
76
80
|
if adapter.nil?
|
77
81
|
adapter = ActiveRecord::Base.configurations[env]["adapter"]
|
@@ -20,6 +20,7 @@ module ScoutApm
|
|
20
20
|
detect_from_config ||
|
21
21
|
detect_from_heroku ||
|
22
22
|
detect_from_capistrano ||
|
23
|
+
detect_from_mina ||
|
23
24
|
detect_from_git
|
24
25
|
end
|
25
26
|
|
@@ -43,6 +44,14 @@ module ScoutApm
|
|
43
44
|
nil
|
44
45
|
end
|
45
46
|
|
47
|
+
# https://github.com/mina-deploy/mina
|
48
|
+
def detect_from_mina
|
49
|
+
File.read(File.join(app_root, '.mina_git_revision')).strip
|
50
|
+
rescue
|
51
|
+
logger.debug "Unable to detect Git Revision from Mina: #{$!.message}"
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
46
55
|
def detect_from_git
|
47
56
|
if File.directory?(".git")
|
48
57
|
`git rev-parse --short HEAD`.strip
|
@@ -4,7 +4,9 @@ module ScoutApm
|
|
4
4
|
attr_reader :regex
|
5
5
|
|
6
6
|
def initialize(prefixes)
|
7
|
-
regexes = Array(prefixes).
|
7
|
+
regexes = Array(prefixes).
|
8
|
+
reject{|prefix| prefix == ""}.
|
9
|
+
map {|prefix| %r{\A#{prefix}} }
|
8
10
|
@regex = Regexp.union(*regexes)
|
9
11
|
end
|
10
12
|
|
@@ -95,8 +95,11 @@ module ScoutApm
|
|
95
95
|
req.instant_key = instant_key
|
96
96
|
end
|
97
97
|
|
98
|
-
|
99
|
-
|
98
|
+
# Don't start a new layer if ActionController::API or
|
99
|
+
# ActionController::Base handled it already. Needs to account for
|
100
|
+
# any layers started during a around_action (most likely
|
101
|
+
# AutoInstrument, but could be another custom instrument)
|
102
|
+
if current_layer && (current_layer.type == "Controller" || current_layer.type == "AutoInstrument" || req.web?)
|
100
103
|
super
|
101
104
|
else
|
102
105
|
begin
|
@@ -83,7 +83,7 @@ module ScoutApm
|
|
83
83
|
maybe_template = args[1]
|
84
84
|
|
85
85
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
86
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3
|
86
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3.5
|
87
87
|
template_name ||= "Unknown Partial"
|
88
88
|
|
89
89
|
layer_name = template_name + "/Rendering"
|
@@ -105,7 +105,10 @@ module ScoutApm
|
|
105
105
|
def collection_with_template(*args, **kwargs)
|
106
106
|
req = ScoutApm::RequestManager.lookup
|
107
107
|
|
108
|
-
|
108
|
+
maybe_template = args[1]
|
109
|
+
|
110
|
+
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
111
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3.5
|
109
112
|
template_name ||= "Unknown Collection"
|
110
113
|
layer_name = template_name + "/Rendering"
|
111
114
|
|
@@ -126,10 +129,15 @@ module ScoutApm
|
|
126
129
|
end
|
127
130
|
|
128
131
|
module ActionViewTemplateRendererInstruments
|
129
|
-
|
132
|
+
# Don't forward kwargs here, since Rails 3, 4, 5, 6 don't use them, and
|
133
|
+
# it causes annoyances in the instrumentation
|
134
|
+
def render_template(*args)
|
130
135
|
req = ScoutApm::RequestManager.lookup
|
131
136
|
|
132
|
-
|
137
|
+
maybe_template = args[1]
|
138
|
+
|
139
|
+
template_name = args[0].virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
140
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.1.3
|
133
141
|
template_name ||= "Unknown"
|
134
142
|
layer_name = template_name + "/Rendering"
|
135
143
|
|
@@ -138,7 +146,7 @@ module ScoutApm
|
|
138
146
|
|
139
147
|
begin
|
140
148
|
req.start_layer(layer)
|
141
|
-
super(*args
|
149
|
+
super(*args)
|
142
150
|
ensure
|
143
151
|
req.stop_layer
|
144
152
|
end
|
@@ -215,6 +215,7 @@ module ScoutApm
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
end
|
218
|
+
ruby2_keywords :log if respond_to?(:ruby2_keywords, true)
|
218
219
|
end
|
219
220
|
|
220
221
|
module ActiveRecordInstruments
|
@@ -267,6 +268,7 @@ module ScoutApm
|
|
267
268
|
end
|
268
269
|
end
|
269
270
|
end
|
271
|
+
ruby2_keywords :log if respond_to?(:ruby2_keywords, true)
|
270
272
|
end
|
271
273
|
|
272
274
|
################################################################################
|
@@ -29,10 +29,11 @@ module ScoutApm
|
|
29
29
|
|
30
30
|
module TyphoeusHydraInstrumentation
|
31
31
|
def run(*args, &block)
|
32
|
+
layer = ScoutApm::Layer.new("HTTP", "Hydra")
|
33
|
+
layer.desc = scout_desc
|
34
|
+
|
32
35
|
req = ScoutApm::RequestManager.lookup
|
33
|
-
req.start_layer(
|
34
|
-
current_layer = req.current_layer
|
35
|
-
current_layer.desc = scout_desc if current_layer
|
36
|
+
req.start_layer(layer)
|
36
37
|
|
37
38
|
begin
|
38
39
|
super(*args, &block)
|
@@ -50,10 +51,11 @@ module ScoutApm
|
|
50
51
|
|
51
52
|
module TyphoeusInstrumentation
|
52
53
|
def run(*args, &block)
|
54
|
+
layer = ScoutApm::Layer.new("HTTP", scout_request_verb)
|
55
|
+
layer.desc = scout_desc(scout_request_verb, scout_request_url)
|
56
|
+
|
53
57
|
req = ScoutApm::RequestManager.lookup
|
54
|
-
req.start_layer(
|
55
|
-
current_layer = req.current_layer
|
56
|
-
current_layer.desc = scout_desc(scout_request_verb, scout_request_url) if current_layer
|
58
|
+
req.start_layer(layer)
|
57
59
|
|
58
60
|
begin
|
59
61
|
super(*args, &block)
|