scout_apm 5.7.0 → 5.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +4 -0
- data/lib/scout_apm/config.rb +11 -0
- data/lib/scout_apm/error.rb +16 -5
- data/lib/scout_apm/error_service/error_record.rb +14 -42
- data/lib/scout_apm/error_service/payload.rb +0 -1
- data/lib/scout_apm/version.rb +1 -1
- data/test/unit/error_service/error_buffer_test.rb +46 -1
- data/test/unit/error_test.rb +32 -7
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e96ebe1b7d9a1fb4bd1427ba0564e645cb9bed376da56bc92f9d86f0515adf7
|
4
|
+
data.tar.gz: d1c4692af9e37bb6107f14064ce89f94626d987bf55d845dea19b522fa910ce1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 958530e614d6ad7ac0ef8805d40843883fae446d5dbfdee7f982a504eff838e0f6abe9fc70e7ead42f0e3a7075540291954264704da73694f132674ef2edc48b
|
7
|
+
data.tar.gz: d35f05a9f2c6094b87e1df04a95fa54138c901b25b479ce5962c616d54806fea1a25ab5e89feb5d98c0ecf0d5e13163e97f990c3e74229544751f3b6e0d4fe9f
|
data/CHANGELOG.markdown
CHANGED
data/lib/scout_apm/config.rb
CHANGED
@@ -50,6 +50,14 @@ require 'scout_apm/environment'
|
|
50
50
|
# endpoint_sample_rate - Rate to sample all endpoints. An integer between 0 and 100. 0 means no traces are sent, 100 means all traces are sent. (supercedes 'sample_rate')
|
51
51
|
# job_sample_rate - Rate to sample all jobs. An integer between 0 and 100. 0 means no traces are sent, 100 means all traces are sent. (supercedes 'sample_rate')
|
52
52
|
#
|
53
|
+
#
|
54
|
+
# Errors Service Configuration
|
55
|
+
# errors_enabled - true/false to enable the errors service
|
56
|
+
# errors_ignored_exceptions - An array of exception classes to ignore.
|
57
|
+
# errors_filtered_params - An array of parameter names to filter/redact out of error reports.
|
58
|
+
# errors_env_capture - An array of environment variables to capture in error reports.
|
59
|
+
# errors_host - The host to send error reports to.
|
60
|
+
#
|
53
61
|
# Any of these config settings can be set with an environment variable prefixed
|
54
62
|
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
55
63
|
|
@@ -113,6 +121,7 @@ module ScoutApm
|
|
113
121
|
'errors_enabled',
|
114
122
|
'errors_ignored_exceptions',
|
115
123
|
'errors_filtered_params',
|
124
|
+
'errors_env_capture',
|
116
125
|
'errors_host',
|
117
126
|
]
|
118
127
|
|
@@ -232,6 +241,7 @@ module ScoutApm
|
|
232
241
|
'errors_enabled' => BooleanCoercion.new,
|
233
242
|
'errors_ignored_exceptions' => JsonCoercion.new,
|
234
243
|
'errors_filtered_params' => JsonCoercion.new,
|
244
|
+
'errors_env_capture' => JsonCoercion.new,
|
235
245
|
}
|
236
246
|
|
237
247
|
|
@@ -365,6 +375,7 @@ module ScoutApm
|
|
365
375
|
'errors_enabled' => false,
|
366
376
|
'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
|
367
377
|
'errors_filtered_params' => %w(password s3-key),
|
378
|
+
'errors_env_capture' => %w(),
|
368
379
|
'errors_host' => 'https://errors.scoutapm.com',
|
369
380
|
}.freeze
|
370
381
|
|
data/lib/scout_apm/error.rb
CHANGED
@@ -9,11 +9,11 @@ module ScoutApm
|
|
9
9
|
class Custom < ScoutDefined; end
|
10
10
|
|
11
11
|
class << self
|
12
|
-
def capture(exception,
|
13
|
-
|
12
|
+
def capture(exception, context={}, env: {}, name: "ScoutApm::Error::Custom")
|
13
|
+
agent_context = ScoutApm::Agent.instance.context
|
14
14
|
|
15
15
|
# Skip if error monitoring isn't enabled at all
|
16
|
-
if !
|
16
|
+
if ! agent_context.config.value("errors_enabled")
|
17
17
|
return false
|
18
18
|
end
|
19
19
|
|
@@ -21,12 +21,23 @@ module ScoutApm
|
|
21
21
|
return false unless exception
|
22
22
|
|
23
23
|
# Skip if this one error is ignored
|
24
|
-
if
|
24
|
+
if agent_context.ignored_exceptions.ignored?(exception)
|
25
25
|
return false
|
26
26
|
end
|
27
27
|
|
28
|
+
unless env.is_a?(Hash)
|
29
|
+
log_warning("Expected env to be a Hash, got #{env.class}")
|
30
|
+
env = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
unless context.is_a?(Hash)
|
34
|
+
log_warning("Expected context to be a Hash, got #{context.class}")
|
35
|
+
context = {}
|
36
|
+
end
|
37
|
+
ScoutApm::Context.add(context)
|
38
|
+
|
28
39
|
# Capture the error for further processing and shipping
|
29
|
-
|
40
|
+
agent_context.error_buffer.capture(exception, env)
|
30
41
|
|
31
42
|
return true
|
32
43
|
end
|
@@ -7,7 +7,6 @@ module ScoutApm
|
|
7
7
|
attr_reader :message
|
8
8
|
attr_reader :request_uri
|
9
9
|
attr_reader :request_params
|
10
|
-
attr_reader :request_session
|
11
10
|
attr_reader :environment
|
12
11
|
attr_reader :trace
|
13
12
|
attr_reader :request_components
|
@@ -28,8 +27,7 @@ module ScoutApm
|
|
28
27
|
@message = LengthLimit.new(exception.message, 100).to_s
|
29
28
|
@request_uri = LengthLimit.new(rack_request_url(env), 200).to_s
|
30
29
|
@request_params = clean_params(env["action_dispatch.request.parameters"])
|
31
|
-
@
|
32
|
-
@environment = clean_params(strip_env(env))
|
30
|
+
@environment = clean_params(env_captured(env))
|
33
31
|
@trace = clean_backtrace(exception.backtrace)
|
34
32
|
@request_components = components(env)
|
35
33
|
@agent_time = Time.now.iso8601
|
@@ -92,46 +90,20 @@ module ScoutApm
|
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
"rack.request.form_hash",
|
102
|
-
"rack.request.form_vars",
|
103
|
-
"async.callback",
|
104
|
-
|
105
|
-
# Security related items
|
106
|
-
"action_dispatch.secret_key_base",
|
107
|
-
"action_dispatch.http_auth_salt",
|
108
|
-
"action_dispatch.signed_cookie_salt",
|
109
|
-
"action_dispatch.encrypted_cookie_salt",
|
110
|
-
"action_dispatch.encrypted_signed_cookie_salt",
|
111
|
-
"action_dispatch.authenticated_encrypted_cookie_salt",
|
112
|
-
|
113
|
-
# Raw data from the URL & parameters. Would bypass our normal params filtering
|
114
|
-
"QUERY_STRING",
|
115
|
-
"REQUEST_URI",
|
116
|
-
"REQUEST_PATH",
|
117
|
-
"ORIGINAL_FULLPATH",
|
118
|
-
"action_dispatch.request.query_parameters",
|
119
|
-
"action_dispatch.request.parameters",
|
120
|
-
"rack.request.query_string",
|
121
|
-
"rack.request.query_hash",
|
93
|
+
# Capture params from env
|
94
|
+
KEYS_TO_KEEP = [
|
95
|
+
"HTTP_USER_AGENT",
|
96
|
+
"HTTP_REFERER",
|
97
|
+
"HTTP_ACCEPT_ENCODING",
|
98
|
+
"HTTP_ORIGIN",
|
122
99
|
]
|
123
|
-
def
|
124
|
-
env.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
if session.respond_to?(:to_hash)
|
132
|
-
session.to_hash
|
133
|
-
else
|
134
|
-
session.data
|
100
|
+
def env_captured(env)
|
101
|
+
env.select { |k, v| KEYS_TO_KEEP.include?(k) }.tap do |filtered_env|
|
102
|
+
filtered_env["HTTP_X_FORWARDED_FOR"] = env["HTTP_X_FORWARDED_FOR"] if @agent_context.config.value('collect_remote_ip') && env["HTTP_X_FORWARDED_FOR"]
|
103
|
+
|
104
|
+
@agent_context.config.value('errors_env_capture').each do |key|
|
105
|
+
filtered_env[key] = env[key] if env[key]
|
106
|
+
end
|
135
107
|
end
|
136
108
|
end
|
137
109
|
|
@@ -35,7 +35,6 @@ module ScoutApm
|
|
35
35
|
:message => error_record.message,
|
36
36
|
:request_uri => error_record.request_uri,
|
37
37
|
:request_params => error_record.request_params,
|
38
|
-
:request_session => error_record.request_session,
|
39
38
|
:environment => error_record.environment,
|
40
39
|
:trace => error_record.trace,
|
41
40
|
:request_components => error_record.request_components,
|
data/lib/scout_apm/version.rb
CHANGED
@@ -9,6 +9,34 @@ class ErrorBufferTest < Minitest::Test
|
|
9
9
|
eb.capture(ex, env)
|
10
10
|
end
|
11
11
|
|
12
|
+
def test_only_captures_relevant_environment
|
13
|
+
config = make_fake_config(
|
14
|
+
'errors_enabled' => true,
|
15
|
+
'errors_env_capture' => %w(ANOTHER_HEADER),
|
16
|
+
'collect_remote_ip' => true
|
17
|
+
)
|
18
|
+
|
19
|
+
test_context = ScoutApm::AgentContext.new().tap { |c| c.config = config }
|
20
|
+
ScoutApm::Agent.instance.stub(:context, test_context) do
|
21
|
+
eb = ScoutApm::ErrorService::ErrorBuffer.new(test_context)
|
22
|
+
eb.capture(ex, env)
|
23
|
+
exceptions = eb.instance_variable_get(:@error_records)
|
24
|
+
assert_equal 1, exceptions.length
|
25
|
+
|
26
|
+
exception = exceptions[0]
|
27
|
+
expected_env_keys = [
|
28
|
+
"ANOTHER_HEADER",
|
29
|
+
"HTTP_X_FORWARDED_FOR",
|
30
|
+
"HTTP_USER_AGENT",
|
31
|
+
"HTTP_REFERER",
|
32
|
+
"HTTP_ACCEPT_ENCODING",
|
33
|
+
"HTTP_ORIGIN",
|
34
|
+
].to_set
|
35
|
+
|
36
|
+
assert_equal expected_env_keys, exception.environment.keys.to_set
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
12
40
|
#### Helpers
|
13
41
|
|
14
42
|
def context
|
@@ -16,7 +44,24 @@ class ErrorBufferTest < Minitest::Test
|
|
16
44
|
end
|
17
45
|
|
18
46
|
def env
|
19
|
-
{
|
47
|
+
{
|
48
|
+
"REQUEST_METHOD" => "GET",
|
49
|
+
"SERVER_NAME" => "localhost",
|
50
|
+
"SERVER_PORT" => "3000",
|
51
|
+
"PATH_INFO" => "/test",
|
52
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
53
|
+
"HTTP_USER_AGENT" => "TestAgent",
|
54
|
+
"HTTP_ACCEPT" => "text/html",
|
55
|
+
"HTTP_HOST" => "localhost:3000",
|
56
|
+
"HTTP_X_FORWARDED_FOR" => "123.345.67.89",
|
57
|
+
"HTTP_X_FORWARDED_PROTO" => "http",
|
58
|
+
"rack.url_scheme" => "http",
|
59
|
+
"REMOTE_ADDR" => "123.345.67.89",
|
60
|
+
"ANOTHER_HEADER" => "value",
|
61
|
+
"HTTP_REFERER" => "http://example.com",
|
62
|
+
"HTTP_ACCEPT_ENCODING" => "gzip, deflate",
|
63
|
+
"HTTP_ORIGIN" => "http://example.com",
|
64
|
+
}
|
20
65
|
end
|
21
66
|
|
22
67
|
def ex(msg="Whoops")
|
data/test/unit/error_test.rb
CHANGED
@@ -7,6 +7,7 @@ class ErrorTest < Minitest::Test
|
|
7
7
|
def test_captures_and_stores_exceptions_and_env
|
8
8
|
config = make_fake_config(
|
9
9
|
'errors_enabled' => true,
|
10
|
+
'errors_env_capture' => %w(),
|
10
11
|
)
|
11
12
|
test_context = ScoutApm::AgentContext.new().tap{|c| c.config = config }
|
12
13
|
ScoutApm::Agent.instance.stub(:context, test_context) do
|
@@ -15,12 +16,19 @@ class ErrorTest < Minitest::Test
|
|
15
16
|
end
|
16
17
|
assert_equal true, ScoutApm::Error.capture("Oh no an error") # Will be of type ScoutApm::Error::Custom
|
17
18
|
assert_equal true, ScoutApm::Error.capture("Oh no another error") # Will be of type ScoutApm::Error::Custom
|
19
|
+
|
18
20
|
assert_equal true, ScoutApm::Error.capture("Something went boom", {"boom" => "yes"}, name: "boom_error")
|
19
|
-
|
20
|
-
|
21
|
-
assert_equal true, ScoutApm::Error.capture(
|
22
|
-
|
23
|
-
assert_equal true, ScoutApm::Error.capture(
|
21
|
+
ScoutApm::Context.current.instance_variable_set(:@extra, {}) # Need to reset as the context/request won't change.
|
22
|
+
|
23
|
+
assert_equal true, ScoutApm::Error.capture("No context or env", name: "another error")
|
24
|
+
|
25
|
+
assert_equal true, ScoutApm::Error.capture(ex, context)
|
26
|
+
ScoutApm::Context.current.instance_variable_set(:@extra, {})
|
27
|
+
|
28
|
+
assert_equal true, ScoutApm::Error.capture(exwbt, env: env)
|
29
|
+
|
30
|
+
assert_equal false, ScoutApm::Error.capture(Class, env: env)
|
31
|
+
assert_equal true, ScoutApm::Error.capture("Name collision", context, env: env, name: "ScoutApm")
|
24
32
|
|
25
33
|
begin
|
26
34
|
raise StandardError, "Whoops"
|
@@ -43,24 +51,41 @@ class ErrorTest < Minitest::Test
|
|
43
51
|
|
44
52
|
assert_equal "Something went boom", exceptions[2].message
|
45
53
|
assert_equal "BoomError", exceptions[2].exception_class
|
54
|
+
assert_equal "yes", exceptions[2].context["boom"]
|
46
55
|
|
47
|
-
assert_equal "No env", exceptions[3].message
|
56
|
+
assert_equal "No context or env", exceptions[3].message
|
48
57
|
assert_equal "AnotherError", exceptions[3].exception_class
|
58
|
+
assert_equal assert_empty_context, exceptions[3].context
|
49
59
|
|
50
60
|
assert_equal "Whoops", exceptions[4].message
|
51
61
|
assert_equal "ErrorTest::FakeError", exceptions[4].exception_class
|
62
|
+
assert_equal 123, exceptions[4].context["user_id"]
|
52
63
|
|
53
64
|
assert_equal "/path/to/file.rb:10:in `method_name'", exceptions[5].trace.first
|
54
65
|
|
55
66
|
assert_equal "ScoutApm::Error::Custom", exceptions[6].exception_class
|
67
|
+
assert_equal 123, exceptions[6].context["user_id"]
|
68
|
+
assert_equal "TestAgent", exceptions[6].environment["HTTP_USER_AGENT"]
|
56
69
|
|
57
70
|
assert_equal "StandardError", exceptions[7].exception_class
|
58
71
|
end
|
59
72
|
end
|
60
73
|
|
61
74
|
#### Helpers
|
75
|
+
def context
|
76
|
+
{
|
77
|
+
"user_id" => 123,
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
62
81
|
def env
|
63
|
-
{
|
82
|
+
{
|
83
|
+
"HTTP_USER_AGENT" => "TestAgent",
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def assert_empty_context
|
88
|
+
{user: {}}
|
64
89
|
end
|
65
90
|
|
66
91
|
def ex(msg="Whoops")
|