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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2182f0bacb1555c0eb3323f8200db7f8d93068f018ce57c82bb4975361d0581
4
- data.tar.gz: fd8b5694f8cacf2a648bb7a9e4440d16cb70be93c84b17db439781b01f2d5d48
3
+ metadata.gz: 2e96ebe1b7d9a1fb4bd1427ba0564e645cb9bed376da56bc92f9d86f0515adf7
4
+ data.tar.gz: d1c4692af9e37bb6107f14064ce89f94626d987bf55d845dea19b522fa910ce1
5
5
  SHA512:
6
- metadata.gz: e44b2bcab8ec4d818faec5b87b4068ce3be0dd37afe97e123c4920cb4d5aa3a931c550bfb80cad1e9d9b6aea6102172cedd68afaee4a1497b3948675f61e2bec
7
- data.tar.gz: a2e1dd693d0fc1f43e9f68d7a3b019c1d98d879a7671c9bc38944056390d6d8f7e0af6546ec39fc95d568edc3cca12bb73621ffd39e6c6eb80295c6a34cc3c20
6
+ metadata.gz: 958530e614d6ad7ac0ef8805d40843883fae446d5dbfdee7f982a504eff838e0f6abe9fc70e7ead42f0e3a7075540291954264704da73694f132674ef2edc48b
7
+ data.tar.gz: d35f05a9f2c6094b87e1df04a95fa54138c901b25b479ce5962c616d54806fea1a25ab5e89feb5d98c0ecf0d5e13163e97f990c3e74229544751f3b6e0d4fe9f
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,9 @@
1
1
  # Unreleased
2
2
 
3
+ # 5.7.1
4
+ - Update error capture API to use context (#560)
5
+ - Filter env in error records (#559)
6
+
3
7
  # 5.7.0
4
8
  - Fix native extension compilation with GCC 15 (#552)
5
9
  - Handle non Rails/ActiveSupport applications w/ sampling (#557)
@@ -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
 
@@ -9,11 +9,11 @@ module ScoutApm
9
9
  class Custom < ScoutDefined; end
10
10
 
11
11
  class << self
12
- def capture(exception, env={}, name: "ScoutApm::Error::Custom")
13
- context = ScoutApm::Agent.instance.context
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 ! context.config.value("errors_enabled")
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 context.ignored_exceptions.ignored?(exception)
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
- context.error_buffer.capture(exception, env)
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
- @request_session = clean_params(session_data(env))
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
- # Deletes params from env
96
- #
97
- # These are not configurable, and will leak PII info up to Scout if
98
- # allowed through. Things like specific parameters can be exposed with
99
- # the ScoutApm::Context interface.
100
- KEYS_TO_REMOVE = [
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 strip_env(env)
124
- env.reject { |k, v| KEYS_TO_REMOVE.include?(k) }
125
- end
126
-
127
- def session_data(env)
128
- session = env["action_dispatch.request.session"]
129
- return if session.nil?
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,
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "5.7.0"
2
+ VERSION = "5.7.1"
3
3
  end
@@ -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")
@@ -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
- assert_equal true, ScoutApm::Error.capture("No env", name: "another error")
20
- assert_equal true, ScoutApm::Error.capture(ex, env)
21
- assert_equal true, ScoutApm::Error.capture(exwbt, env)
22
- assert_equal false, ScoutApm::Error.capture(Class, env)
23
- assert_equal true, ScoutApm::Error.capture("Name collision, but", env, name: "ScoutApm")
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")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.7.0
4
+ version: 5.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes