scout_apm 2.6.6 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -4
  3. data/.travis.yml +0 -6
  4. data/CHANGELOG.markdown +30 -0
  5. data/Gemfile +1 -8
  6. data/lib/scout_apm.rb +21 -1
  7. data/lib/scout_apm/agent.rb +22 -0
  8. data/lib/scout_apm/agent_context.rb +14 -2
  9. data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
  10. data/lib/scout_apm/config.rb +17 -2
  11. data/lib/scout_apm/detailed_trace.rb +2 -1
  12. data/lib/scout_apm/error.rb +27 -0
  13. data/lib/scout_apm/error_service.rb +32 -0
  14. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  15. data/lib/scout_apm/error_service/error_record.rb +211 -0
  16. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  17. data/lib/scout_apm/error_service/middleware.rb +32 -0
  18. data/lib/scout_apm/error_service/notifier.rb +33 -0
  19. data/lib/scout_apm/error_service/payload.rb +47 -0
  20. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  21. data/lib/scout_apm/error_service/railtie.rb +11 -0
  22. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  23. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  24. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +47 -26
  25. data/lib/scout_apm/instruments/action_view.rb +7 -2
  26. data/lib/scout_apm/instruments/active_record.rb +13 -28
  27. data/lib/scout_apm/middleware.rb +1 -1
  28. data/lib/scout_apm/reporter.rb +8 -3
  29. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +28 -10
  30. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  31. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  32. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  33. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  34. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  35. data/lib/scout_apm/slow_request_policy.rb +18 -77
  36. data/lib/scout_apm/utils/sql_sanitizer.rb +1 -0
  37. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +1 -1
  38. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +1 -0
  39. data/lib/scout_apm/version.rb +1 -1
  40. data/scout_apm.gemspec +6 -6
  41. data/test/unit/agent_context_test.rb +29 -0
  42. data/test/unit/error_service/error_buffer_test.rb +25 -0
  43. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  44. data/test/unit/serializers/payload_serializer_test.rb +36 -0
  45. data/test/unit/slow_request_policy_test.rb +41 -13
  46. data/test/unit/sql_sanitizer_test.rb +7 -0
  47. metadata +25 -62
  48. data/lib/scout_apm/slow_job_policy.rb +0 -111
  49. data/test/unit/slow_job_policy_test.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73af2b28e0b054233c1ccd48122741d7b8e27adf9c61ee93fea520f7e3a6b3c7
4
- data.tar.gz: 544c20bf5c56478269232f19a792dcbc00c125befb6e5e7b406be04dc954944e
3
+ metadata.gz: ffcf571075c7f443ebe029e8f07c726cc5cb6c12e156d47e2565c576ac671316
4
+ data.tar.gz: 684f8ed4ba52d2e5819ea319288333a83f8b5481491ae879be2e8031cdd5d6ee
5
5
  SHA512:
6
- metadata.gz: c8558cd998a6c54ca7cd3c3174ba96c28230b0041a49e2d494460116a6f9065ba28f998ce8a74b546f69a8f1d5ec5dd81b9be29e27f4a874f51e1352f02798f0
7
- data.tar.gz: 18f7152a257b7ff61cb0c4953542527a2af25220d726ac26ade9242421f817afc83ce18fa28ea018188215717cedb25336ba79a61572cb706f99f16278be3545
6
+ metadata.gz: a4d90917dc02469213092848211fe779f44ec09e7db4829aaaa737ca15cf1811137dc2a4ae00adaa76cc2be92916b0467f164f79a76fac501c7a4827ba5d6f7d
7
+ data.tar.gz: 776d064902d998cc69330e470fbc9bfb368ae401d8e8a341310e4dc2a9070a256a2e57370505ee3475761d26145e4efced633963243527e39b95ab584bf48b2a
data/.rubocop.yml CHANGED
@@ -10,7 +10,3 @@ AllCops:
10
10
  Metrics/LineLength:
11
11
  Enabled: false
12
12
  Max: 100
13
-
14
- Style/HashSyntax:
15
- Enabled: true
16
- EnforcedStyle: hash_rockets
data/.travis.yml CHANGED
@@ -4,12 +4,6 @@ cache: bundler
4
4
 
5
5
  matrix:
6
6
  include:
7
- - rvm: "1.8.7"
8
- gemfile: gems/rails3.gemfile
9
- - rvm: "1.9.3"
10
- gemfile: gems/rails3.gemfile
11
- - rvm: 2.0
12
- gemfile: gems/rails3.gemfile
13
7
  - rvm: 2.1
14
8
  gemfile: gems/rails3.gemfile
15
9
  - rvm: 2.2
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,33 @@
1
+ # 4.0.0
2
+
3
+ * Require Ruby >= 2.1 (#270)
4
+ * ErrorService reporting. Enable with `errors_enabled` config setting. (#347)
5
+ * Modular SlowRequestPolicy (#364)
6
+ * Fix deprecation warnings (#354)
7
+
8
+ # 2.6.10
9
+
10
+ * Fix an edge case in JSON serialization (#360)
11
+
12
+ # 2.6.9
13
+
14
+ * Add `ssl_cert_file` config option (#352)
15
+ * Improve sanitization of Postgres UPDATE SQL (#351)
16
+ * Allow custom URL sanitization (#341)
17
+
18
+ # 2.6.8
19
+
20
+ * Lock rake version for 1.8.7 to older version (#329)
21
+ * Delete unneeded .DS_Store file that snuck in (#334)
22
+ * Fix typo in "queue_time_ms"
23
+ * Fix Rails 6 deprecation warning at boot time (#337)
24
+ * Fix partial naming on Rails 6.0 (#339)
25
+ * Support Sidekiq 6.1 instrumentation (#340)
26
+
27
+ # 2.6.7
28
+
29
+ * Remove accidental call to `as_json`
30
+
1
31
  # 2.6.6
2
32
 
3
33
  * Add basic support for parsing Microsoft SQLServer queries (#317)
data/Gemfile CHANGED
@@ -3,11 +3,4 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in scout_apm.gemspec
4
4
  gemspec
5
5
 
6
- # Pin development dependencies more conservatively for Ruby 1.8.7
7
- if RUBY_VERSION <= "1.8.7"
8
- gem "activesupport", "~> 3.2"
9
- gem "i18n", "~> 0.6.11"
10
- gem "pry", "~> 0.9.12"
11
- gem "rake", "~> 10.5"
12
- gem "minitest", "~> 5.11.3"
13
- end
6
+ gem "rake", ">= 12.3.3"
data/lib/scout_apm.rb CHANGED
@@ -144,8 +144,13 @@ require 'scout_apm/slow_transaction'
144
144
  require 'scout_apm/slow_job_record'
145
145
  require 'scout_apm/detailed_trace'
146
146
  require 'scout_apm/scored_item_set'
147
+
147
148
  require 'scout_apm/slow_request_policy'
148
- require 'scout_apm/slow_job_policy'
149
+ require 'scout_apm/slow_policy/age_policy'
150
+ require 'scout_apm/slow_policy/speed_policy'
151
+ require 'scout_apm/slow_policy/percent_policy'
152
+ require 'scout_apm/slow_policy/percentile_policy'
153
+
149
154
  require 'scout_apm/job_record'
150
155
  require 'scout_apm/request_histograms'
151
156
  require 'scout_apm/transaction_time_consumed'
@@ -186,6 +191,16 @@ require 'scout_apm/tasks/support'
186
191
  require 'scout_apm/extensions/config'
187
192
  require 'scout_apm/extensions/transaction_callback_payload'
188
193
 
194
+ require 'scout_apm/error_service'
195
+ require 'scout_apm/error_service/middleware'
196
+ require 'scout_apm/error_service/notifier'
197
+ require 'scout_apm/error_service/sidekiq'
198
+ require 'scout_apm/error_service/ignored_exceptions'
199
+ require 'scout_apm/error_service/error_buffer'
200
+ require 'scout_apm/error_service/error_record'
201
+ require 'scout_apm/error_service/periodic_work'
202
+ require 'scout_apm/error_service/payload'
203
+
189
204
  if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
190
205
  module ScoutApm
191
206
  class Railtie < Rails::Railtie
@@ -205,6 +220,11 @@ if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR
205
220
  ScoutApm::Agent.instance.context.logger.debug("AutoInstruments is disabled.")
206
221
  end
207
222
 
223
+ if ScoutApm::Agent.instance.context.config.value("errors_enabled")
224
+ app.config.middleware.insert_after ActionDispatch::DebugExceptions, ScoutApm::ErrorService::Middleware
225
+ ScoutApm::ErrorService::Sidekiq.new.install
226
+ end
227
+
208
228
  # Install the middleware every time in development mode.
209
229
  # The middleware is a noop if dev_trace is not enabled in config
210
230
  if Rails.env.development?
@@ -66,6 +66,7 @@ module ScoutApm
66
66
 
67
67
  if context.started?
68
68
  start_background_worker unless background_worker_running?
69
+ start_error_service_background_worker unless error_service_background_worker_running?
69
70
  return
70
71
  end
71
72
 
@@ -81,6 +82,7 @@ module ScoutApm
81
82
  @app_server_load ||= AppServerLoad.new(context).run
82
83
 
83
84
  start_background_worker
85
+ start_error_service_background_worker
84
86
  end
85
87
 
86
88
  def instrument_manager
@@ -198,5 +200,25 @@ module ScoutApm
198
200
  @background_worker &&
199
201
  @background_worker.running?
200
202
  end
203
+
204
+ # seconds to batch error reports
205
+ ERROR_SEND_FREQUENCY = 5
206
+ def start_error_service_background_worker
207
+ periodic_work = ScoutApm::ErrorService::PeriodicWork.new(context)
208
+
209
+ @error_service_background_worker = ScoutApm::BackgroundWorker.new(context, ERROR_SEND_FREQUENCY)
210
+ @error_service_background_worker_thread = Thread.new do
211
+ @error_service_background_worker.start {
212
+ periodic_work.run
213
+ }
214
+ end
215
+ end
216
+
217
+ def error_service_background_worker_running?
218
+ @error_service_background_worker_thread &&
219
+ @error_service_background_worker_thread.alive? &&
220
+ @error_service_background_worker &&
221
+ @error_service_background_worker.running?
222
+ end
201
223
  end
202
224
  end
@@ -96,11 +96,11 @@ module ScoutApm
96
96
  end
97
97
 
98
98
  def slow_request_policy
99
- @slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self)
99
+ @slow_request_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
100
100
  end
101
101
 
102
102
  def slow_job_policy
103
- @slow_job_policy ||= ScoutApm::SlowJobPolicy.new(self)
103
+ @slow_job_policy ||= ScoutApm::SlowRequestPolicy.new(self).tap{|p| p.add_default_policies }
104
104
  end
105
105
 
106
106
  # Maintains a Histogram of insignificant/significant autoinstrument layers.
@@ -142,6 +142,18 @@ module ScoutApm
142
142
  config.value('dev_trace') && environment.env == "development"
143
143
  end
144
144
 
145
+ ###################
146
+ # Error Service #
147
+ ###################
148
+
149
+ def error_buffer
150
+ @error_buffer ||= ScoutApm::ErrorService::ErrorBuffer.new(self)
151
+ end
152
+
153
+ def ignored_exceptions
154
+ @ignored_exceptions ||= ScoutApm::ErrorService::IgnoredExceptions.new(self, config.value('errors_ignored_exceptions'))
155
+ end
156
+
145
157
  #############
146
158
  # Setters #
147
159
  #############
@@ -40,10 +40,10 @@ module ScoutApm
40
40
  require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
41
41
 
42
42
  ::Sidekiq::Processor.class_eval do
43
- def initialize_with_scout(boss)
43
+ def initialize_with_scout(*args)
44
44
  agent = ::ScoutApm::Agent.instance
45
45
  agent.start
46
- initialize_without_scout(boss)
46
+ initialize_without_scout(*args)
47
47
  end
48
48
 
49
49
  alias_method :initialize_without_scout, :initialize
@@ -75,11 +75,18 @@ module ScoutApm
75
75
  'revision_sha',
76
76
  'scm_subdirectory',
77
77
  'start_resque_server_instrument',
78
+ 'ssl_cert_file',
78
79
  'uri_reporting',
79
80
  'instrument_http_url_length',
80
81
  'timeline_traces',
81
82
  'auto_instruments',
82
- 'auto_instruments_ignore'
83
+ 'auto_instruments_ignore',
84
+
85
+ # Error Service Related Configuration
86
+ 'errors_enabled',
87
+ 'errors_ignored_exceptions',
88
+ 'errors_filtered_params',
89
+ 'errors_host',
83
90
  ]
84
91
 
85
92
  ################################################################################
@@ -175,6 +182,9 @@ module ScoutApm
175
182
  'timeline_traces' => BooleanCoercion.new,
176
183
  'auto_instruments' => BooleanCoercion.new,
177
184
  'auto_instruments_ignore' => JsonCoercion.new,
185
+ 'errors_enabled' => BooleanCoercion.new,
186
+ 'errors_ignored_exceptions' => JsonCoercion.new,
187
+ 'errors_filtered_params' => JsonCoercion.new,
178
188
  }
179
189
 
180
190
 
@@ -284,7 +294,12 @@ module ScoutApm
284
294
  'collect_remote_ip' => true,
285
295
  'timeline_traces' => true,
286
296
  'auto_instruments' => false,
287
- 'auto_instruments_ignore' => []
297
+ 'auto_instruments_ignore' => [],
298
+ 'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
299
+ 'errors_enabled' => false,
300
+ 'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
301
+ 'errors_filtered_params' => %w(password s3-key),
302
+ 'errors_host' => 'https://errors.scoutapm.com',
288
303
  }.freeze
289
304
 
290
305
  def value(key)
@@ -200,8 +200,9 @@ class DetailedTraceTags
200
200
  @tags = hash
201
201
  end
202
202
 
203
+ # @tags is already a hash, so no conversion needed
203
204
  def as_json(*)
204
- @tags.as_json
205
+ @tags
205
206
  end
206
207
  end
207
208
 
@@ -0,0 +1,27 @@
1
+ # Public API for the Scout Error Monitoring service
2
+ #
3
+ # See-Also ScoutApm::Transaction and ScoutApm::Tracing for APM related APIs
4
+ module ScoutApm
5
+ module Error
6
+ # Capture an exception, optionally with an environment hash. This may be a
7
+ # Rack environment, but is not required.
8
+ def self.capture(exception, env={})
9
+ context = ScoutApm::Agent.instance.context
10
+
11
+ # Skip if error monitoring isn't enabled at all
12
+ if ! context.config.value("errors_enabled")
13
+ return false
14
+ end
15
+
16
+ # Skip if this one error is ignored
17
+ if context.ignored_exceptions.ignored?(exception)
18
+ return false
19
+ end
20
+
21
+ # Capture the error for further processing and shipping
22
+ context.error_buffer.capture(exception, env)
23
+
24
+ return true
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require "net/http"
2
+ require "net/https"
3
+ require "uri"
4
+
5
+ module ScoutApm
6
+ module ErrorService
7
+ API_VERSION = "1"
8
+
9
+ HEADERS = {
10
+ "Content-type" => "application/json",
11
+ "Accept" => "application/json"
12
+ }
13
+
14
+ # Public API to force a given exception to be captured.
15
+ # Still obeys the ignore list
16
+ # Used internally by SidekiqException
17
+ def self.capture(exception, params = {})
18
+ return if disabled?
19
+ return if ScoutApm::Agent.instance.context.ignored_exceptions.ignore?(exception)
20
+
21
+ context.errors_buffer.capture(exception, env)
22
+ end
23
+
24
+ def self.enabled?
25
+ ScoutApm::Agent.instance.context.config.value("errors_enabled")
26
+ end
27
+
28
+ def self.disabled?
29
+ !enabled?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ # Holds onto exceptions, and moves them forward to shipping when appropriate
2
+ module ScoutApm
3
+ module ErrorService
4
+ class ErrorBuffer
5
+ include Enumerable
6
+
7
+ attr_reader :agent_context
8
+
9
+ def initialize(agent_context)
10
+ @agent_context = agent_context
11
+ @error_records = []
12
+ @mutex = Monitor.new
13
+ end
14
+
15
+ def capture(exception, env)
16
+ context = ScoutApm::Context.current
17
+
18
+ @mutex.synchronize {
19
+ @error_records << ErrorRecord.new(agent_context, exception, env, context)
20
+ }
21
+ end
22
+
23
+ def get_and_reset_error_records
24
+ @mutex.synchronize {
25
+ ret = @error_records
26
+ @error_records = []
27
+ ret
28
+ }
29
+ end
30
+
31
+ # Enables enumerable - for count and each and similar methods
32
+ def each
33
+ @error_records.each do |error_record|
34
+ yield error_record
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,211 @@
1
+ module ScoutApm
2
+ module ErrorService
3
+ # Converts the raw error data captured into the captured data, and holds it
4
+ # until it's ready to be reported.
5
+ class ErrorRecord
6
+ attr_reader :exception_class
7
+ attr_reader :message
8
+ attr_reader :request_uri
9
+ attr_reader :request_params
10
+ attr_reader :request_session
11
+ attr_reader :environment
12
+ attr_reader :trace
13
+ attr_reader :request_components
14
+ attr_reader :context
15
+
16
+ def initialize(agent_context, exception, env, context=nil)
17
+ @agent_context = agent_context
18
+
19
+ @context = if context
20
+ context.to_hash
21
+ else
22
+ {}
23
+ end
24
+
25
+ @exception_class = LengthLimit.new(exception.class.name).to_s
26
+ @message = LengthLimit.new(exception.message, 100).to_s
27
+ @request_uri = LengthLimit.new(rack_request_url(env), 200).to_s
28
+ @request_params = clean_params(env["action_dispatch.request.parameters"])
29
+ @request_session = clean_params(session_data(env))
30
+ @environment = clean_params(strip_env(env))
31
+ @trace = clean_backtrace(exception.backtrace)
32
+ @request_components = components(env)
33
+ end
34
+
35
+ # TODO: This is rails specific
36
+ def components(env)
37
+ components = {}
38
+ unless env["action_dispatch.request.parameters"].nil?
39
+ components[:controller] = env["action_dispatch.request.parameters"][:controller] || nil
40
+ components[:action] = env["action_dispatch.request.parameters"][:action] || nil
41
+ components[:module] = env["action_dispatch.request.parameters"][:module] || nil
42
+ end
43
+
44
+ # For background workers like sidekiq
45
+ # TODO: extract data creation for background jobs
46
+ components[:controller] ||= env[:custom_controller]
47
+
48
+ components
49
+ end
50
+
51
+ # TODO: Can I use the same thing we use in traces?
52
+ def rack_request_url(env)
53
+ protocol = rack_scheme(env)
54
+ protocol = protocol.nil? ? "" : "#{protocol}://"
55
+
56
+ host = env["SERVER_NAME"] || ""
57
+ path = env["REQUEST_URI"] || ""
58
+ port = env["SERVER_PORT"] || "80"
59
+ port = ["80", "443"].include?(port.to_s) ? "" : ":#{port}"
60
+
61
+ protocol.to_s + host.to_s + port.to_s + path.to_s
62
+ end
63
+
64
+ def rack_scheme(env)
65
+ if env["HTTPS"] == "on"
66
+ "https"
67
+ elsif env["HTTP_X_FORWARDED_PROTO"]
68
+ env["HTTP_X_FORWARDED_PROTO"].split(",")[0]
69
+ else
70
+ env["rack.url_scheme"]
71
+ end
72
+ end
73
+
74
+ # TODO: This name is too vague
75
+ def clean_params(params)
76
+ return if params.nil?
77
+
78
+ normalized = normalize_data(params)
79
+ filter_params(normalized)
80
+ end
81
+
82
+ # TODO: When was backtrace_cleaner introduced?
83
+ def clean_backtrace(backtrace)
84
+ if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
85
+ Rails.backtrace_cleaner.send(:filter, backtrace)
86
+ else
87
+ backtrace
88
+ end
89
+ end
90
+
91
+ # Deletes params from env
92
+ #
93
+ # These are not configurable, and will leak PII info up to Scout if
94
+ # allowed through. Things like specific parameters can be exposed with
95
+ # the ScoutApm::Context interface.
96
+ KEYS_TO_REMOVE = [
97
+ "rack.request.form_hash",
98
+ "rack.request.form_vars",
99
+ "async.callback",
100
+
101
+ # Security related items
102
+ "action_dispatch.secret_key_base",
103
+ "action_dispatch.http_auth_salt",
104
+ "action_dispatch.signed_cookie_salt",
105
+ "action_dispatch.encrypted_cookie_salt",
106
+ "action_dispatch.encrypted_signed_cookie_salt",
107
+ "action_dispatch.authenticated_encrypted_cookie_salt",
108
+
109
+ # Raw data from the URL & parameters. Would bypass our normal params filtering
110
+ "QUERY_STRING",
111
+ "REQUEST_URI",
112
+ "REQUEST_PATH",
113
+ "ORIGINAL_FULLPATH",
114
+ "action_dispatch.request.query_parameters",
115
+ "action_dispatch.request.parameters",
116
+ "rack.request.query_string",
117
+ "rack.request.query_hash",
118
+ ]
119
+ def strip_env(env)
120
+ env.reject { |k, v| KEYS_TO_REMOVE.include?(k) }
121
+ end
122
+
123
+ def session_data(env)
124
+ session = env["action_dispatch.request.session"]
125
+ return if session.nil?
126
+
127
+ if session.respond_to?(:to_hash)
128
+ session.to_hash
129
+ else
130
+ session.data
131
+ end
132
+ end
133
+
134
+ # TODO: Rename and make this clearer. I think it maps over the whole tree of a hash, and to_s each leaf node?
135
+ def normalize_data(hash)
136
+ new_hash = {}
137
+
138
+ hash.each do |key, value|
139
+ if value.respond_to?(:to_hash)
140
+ begin
141
+ new_hash[key] = normalize_data(value.to_hash)
142
+ rescue
143
+ new_hash[key] = LengthLimit.new(value.to_s).to_s
144
+ end
145
+ else
146
+ new_hash[key] = LengthLimit.new(value.to_s).to_s
147
+ end
148
+ end
149
+
150
+ new_hash
151
+ end
152
+
153
+ ###################
154
+ # Filtering Params
155
+ ###################
156
+
157
+ # Replaces parameter values with a string / set in config file
158
+ def filter_params(params)
159
+ return params unless filtered_params_config
160
+
161
+ params.each do |k, v|
162
+ if filter_key?(k)
163
+ params[k] = "[FILTERED]"
164
+ elsif v.respond_to?(:to_hash)
165
+ filter_params(params[k])
166
+ end
167
+ end
168
+
169
+ params
170
+ end
171
+
172
+ # Check, if a key should be filtered
173
+ def filter_key?(key)
174
+ params_to_filter.any? do |filter|
175
+ key.to_s == filter.to_s # key.to_s.include?(filter.to_s)
176
+ end
177
+ end
178
+
179
+ def params_to_filter
180
+ @params_to_filter ||= filtered_params_config + rails_filtered_params
181
+ end
182
+
183
+ # Accessor for the filtered params config value. Will be removed as we refactor and clean up this code.
184
+ # TODO: Flip this over to use a new class like filtered exceptions?
185
+ def filtered_params_config
186
+ @agent_context.config.value("errors_filtered_params")
187
+ end
188
+
189
+ def rails_filtered_params
190
+ return [] unless defined?(Rails)
191
+ Rails.configuration.filter_parameters
192
+ rescue
193
+ []
194
+ end
195
+
196
+ class LengthLimit
197
+ attr_reader :text
198
+ attr_reader :char_limit
199
+
200
+ def initialize(text, char_limit=100)
201
+ @text = text
202
+ @char_limit = char_limit
203
+ end
204
+
205
+ def to_s
206
+ text[0..char_limit]
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end