scout_apm 2.6.6 → 2.6.10

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +23 -0
  3. data/Gemfile +4 -0
  4. data/lib/scout_apm.rb +15 -0
  5. data/lib/scout_apm/agent.rb +22 -0
  6. data/lib/scout_apm/agent_context.rb +12 -0
  7. data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
  8. data/lib/scout_apm/config.rb +17 -2
  9. data/lib/scout_apm/detailed_trace.rb +2 -1
  10. data/lib/scout_apm/error.rb +27 -0
  11. data/lib/scout_apm/error_service.rb +32 -0
  12. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  13. data/lib/scout_apm/error_service/error_record.rb +211 -0
  14. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  15. data/lib/scout_apm/error_service/middleware.rb +32 -0
  16. data/lib/scout_apm/error_service/notifier.rb +33 -0
  17. data/lib/scout_apm/error_service/payload.rb +47 -0
  18. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  19. data/lib/scout_apm/error_service/railtie.rb +11 -0
  20. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  21. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  22. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +47 -26
  23. data/lib/scout_apm/instruments/action_view.rb +7 -2
  24. data/lib/scout_apm/middleware.rb +1 -1
  25. data/lib/scout_apm/reporter.rb +8 -3
  26. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +28 -10
  27. data/lib/scout_apm/utils/sql_sanitizer.rb +1 -0
  28. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +1 -1
  29. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +1 -0
  30. data/lib/scout_apm/version.rb +1 -1
  31. data/test/unit/agent_context_test.rb +15 -0
  32. data/test/unit/error_service/error_buffer_test.rb +25 -0
  33. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  34. data/test/unit/serializers/payload_serializer_test.rb +36 -0
  35. data/test/unit/sql_sanitizer_test.rb +7 -0
  36. metadata +18 -58
@@ -26,7 +26,7 @@ module ScoutApm
26
26
  ScoutApm::Agent.instance.start
27
27
  @started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
28
28
  rescue => e
29
- ScoutApm::Agent.instance.context.logger("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
29
+ ScoutApm::Agent.instance.context.logger.info("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
30
30
  end
31
31
  end
32
32
  end
@@ -2,7 +2,6 @@ require 'openssl'
2
2
 
3
3
  module ScoutApm
4
4
  class Reporter
5
- CA_FILE = File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] )
6
5
  VERIFY_MODE = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
7
6
 
8
7
  attr_reader :type
@@ -23,6 +22,7 @@ module ScoutApm
23
22
  context.logger
24
23
  end
25
24
 
25
+ # The fully serialized string payload to be sent
26
26
  def report(payload, headers = {})
27
27
  hosts = determine_hosts
28
28
 
@@ -36,6 +36,7 @@ module ScoutApm
36
36
  logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
37
37
  end
38
38
 
39
+ logger.info("Posting payload to #{hosts.inspect}")
39
40
  post_payload(hosts, payload, headers)
40
41
  end
41
42
 
@@ -52,6 +53,8 @@ module ScoutApm
52
53
  URI.parse("#{host}/apps/deploy.scout?key=#{key}&name=#{encoded_app_name}")
53
54
  when :instant_trace
54
55
  URI.parse("#{host}/apps/instant_trace.scout?key=#{key}&name=#{encoded_app_name}&instant_key=#{instant_key}")
56
+ when :errors
57
+ URI.parse("#{host}/apps/error.scout?key=#{key}&name=#{encoded_app_name}")
55
58
  end.tap { |u| logger.debug("Posting to #{u}") }
56
59
  end
57
60
 
@@ -90,7 +93,7 @@ module ScoutApm
90
93
  logger.debug "got response: #{response.inspect}"
91
94
  case response
92
95
  when Net::HTTPSuccess, Net::HTTPNotModified
93
- logger.debug "/#{type} OK"
96
+ logger.debug "#{type} OK"
94
97
  when Net::HTTPBadRequest
95
98
  logger.warn "/#{type} FAILED: The Account Key [#{config.value('key')}] is invalid."
96
99
  when Net::HTTPUnprocessableEntity
@@ -123,7 +126,7 @@ module ScoutApm
123
126
  proxy_uri.password).new(url.host, url.port)
124
127
  if url.is_a?(URI::HTTPS)
125
128
  http.use_ssl = true
126
- http.ca_file = CA_FILE
129
+ http.ca_file = config.value("ssl_cert_file")
127
130
  http.verify_mode = VERIFY_MODE
128
131
  end
129
132
  http
@@ -142,6 +145,8 @@ module ScoutApm
142
145
  def determine_hosts
143
146
  if [:deploy_hook, :instant_trace].include?(type)
144
147
  config.value('direct_host')
148
+ elsif [:errors].include?(type)
149
+ config.value('errors_host')
145
150
  else
146
151
  config.value('host')
147
152
  end
@@ -45,18 +45,36 @@ module ScoutApm
45
45
  "{#{str_parts.join(",")}}"
46
46
  end
47
47
 
48
- ESCAPE_MAPPINGS = {
49
- "\b" => '\\b',
50
- "\t" => '\\t',
51
- "\n" => '\\n',
52
- "\f" => '\\f',
53
- "\r" => '\\r',
54
- '"' => '\\"',
55
- '\\' => '\\\\',
56
- }
48
+ # Ruby 1.8.7 seems to be fundamentally different in how gsub or regexes
49
+ # work. This is a hack and will be removed as soon as we can drop
50
+ # support
51
+ if RUBY_VERSION == "1.8.7"
52
+ ESCAPE_MAPPINGS = {
53
+ "\b" => '\\b',
54
+ "\t" => '\\t',
55
+ "\n" => '\\n',
56
+ "\f" => '\\f',
57
+ "\r" => '\\r',
58
+ '"' => '\\"',
59
+ '\\' => '\\\\',
60
+ }
61
+ else
62
+ ESCAPE_MAPPINGS = {
63
+ # Stackoverflow answer on gsub matches and backslashes - https://stackoverflow.com/a/4149087/2705125
64
+ '\\' => '\\\\\\\\',
65
+ "\b" => '\\b',
66
+ "\t" => '\\t',
67
+ "\n" => '\\n',
68
+ "\f" => '\\f',
69
+ "\r" => '\\r',
70
+ '"' => '\\"',
71
+ }
72
+ end
57
73
 
58
74
  def escape(string)
59
- ESCAPE_MAPPINGS.inject(string.to_s) {|s, (bad, good)| s.gsub(bad, good) }
75
+ ESCAPE_MAPPINGS.inject(string.to_s) {|s, (bad, good)|
76
+ s.gsub(bad, good)
77
+ }
60
78
  end
61
79
 
62
80
  def format_by_type(formatee)
@@ -51,6 +51,7 @@ module ScoutApm
51
51
  sql.gsub!(PSQL_PLACEHOLDER, '?')
52
52
  sql.gsub!(PSQL_VAR_INTERPOLATION, '')
53
53
  sql.gsub!(PSQL_AFTER_WHERE) {|c| c.gsub(PSQL_REMOVE_STRINGS, '?')}
54
+ sql.gsub!(PSQL_AFTER_SET) {|c| c.gsub(PSQL_REMOVE_STRINGS, '?')}
54
55
  sql.gsub!(PSQL_REMOVE_INTEGERS, '?')
55
56
  sql.gsub!(PSQL_IN_CLAUSE, 'IN (?)')
56
57
  sql.gsub!(MULTIPLE_SPACES, ' ')
@@ -5,13 +5,13 @@ module ScoutApm
5
5
  MULTIPLE_SPACES = %r|\s+|.freeze
6
6
  MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
7
7
 
8
-
9
8
  PSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
10
9
  PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
11
10
  PSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
12
11
  PSQL_PLACEHOLDER = /\$\d+/.freeze
13
12
  PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
14
13
  PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|$)/i.freeze
14
+ PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|$)/i.freeze
15
15
 
16
16
  MYSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
17
17
  MYSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
@@ -11,6 +11,7 @@ module ScoutApm
11
11
  PSQL_PLACEHOLDER = /\$\d+/.freeze
12
12
  PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
13
13
  PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|$)/i.freeze
14
+ PSQL_AFTER_SET = /(?:SET\s+).*?(?:WHERE|$)/i.freeze
14
15
 
15
16
  MYSQL_VAR_INTERPOLATION = %r|\[\[.*\]\]\s*$|.freeze
16
17
  MYSQL_REMOVE_INTEGERS = /\b\d+\b/.freeze
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "2.6.6"
2
+ VERSION = "2.6.10"
3
3
  end
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+
3
+ require "scout_apm/agent_context"
4
+
5
+ class AgentContextTest < Minitest::Test
6
+ def test_has_error_service_ignored_exceptions
7
+ context = ScoutApm::AgentContext.new
8
+ assert ScoutApm::ErrorService::IgnoredExceptions, context.ignored_exceptions.class
9
+ end
10
+
11
+ def test_has_error_buffer
12
+ context = ScoutApm::AgentContext.new
13
+ assert ScoutApm::ErrorService::ErrorBuffer, context.error_buffer.class
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+
3
+ class ErrorBufferTest < Minitest::Test
4
+ class FakeError < StandardError
5
+ end
6
+
7
+ def test_captures_and_stores_exceptions_and_env
8
+ eb = ScoutApm::ErrorService::ErrorBuffer.new(context)
9
+ eb.capture(ex, env)
10
+ end
11
+
12
+ #### Helpers
13
+
14
+ def context
15
+ ScoutApm::AgentContext.new
16
+ end
17
+
18
+ def env
19
+ {}
20
+ end
21
+
22
+ def ex(msg="Whoops")
23
+ FakeError.new(msg)
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ require "test_helper"
2
+
3
+ class IgnoredExceptionsTest < Minitest::Test
4
+ class FakeError < StandardError
5
+ end
6
+
7
+ class SubclassFakeError < FakeError
8
+ end
9
+
10
+ def test_ignores_with_string_match
11
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["RuntimeError"])
12
+ assert ig.ignored?(RuntimeError.new("something went wrong"))
13
+ assert !ig.ignored?(FakeError.new("something went wrong"))
14
+ end
15
+
16
+ def test_ignores_with_block
17
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, [])
18
+ ig.add_callback { |e| e.message == "ignore me" }
19
+
20
+ should_ignore = RuntimeError.new("ignore me")
21
+ should_not_ignore = RuntimeError.new("super legit")
22
+
23
+ assert ig.ignored?(should_ignore)
24
+ assert !ig.ignored?(should_not_ignore)
25
+ end
26
+
27
+ def test_ignores_subclasses
28
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["IgnoredExceptionsTest::FakeError"])
29
+ assert ig.ignored?(SubclassFakeError.new("Subclass"))
30
+ end
31
+
32
+ # Check that a bad exception in the list doesn't stop the whole thing from working
33
+ def test_does_not_consider_unknown_errors
34
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, ["ThisDoesNotExist", "IgnoredExceptionsTest::FakeError"])
35
+ assert ig.ignored?(FakeError.new("ignore this one"))
36
+ end
37
+
38
+ def test_add_module
39
+ ig = ScoutApm::ErrorService::IgnoredExceptions.new(context, [])
40
+ ig.add(IgnoredExceptionsTest::FakeError)
41
+ assert ig.ignored?(FakeError.new("ignore this one"))
42
+ end
43
+
44
+ #### Helpers
45
+
46
+ def context
47
+ ScoutApm::AgentContext.new
48
+ end
49
+ end
@@ -108,4 +108,40 @@ class PayloadSerializerTest < Minitest::Test
108
108
  json = { "foo" => "\bbar\nbaz\r" }
109
109
  assert_equal json, JSON.parse(ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json))
110
110
  end
111
+
112
+ def test_escapes_escaped_quotes
113
+ # Some escapes haven't ever worked on 1.8.7, and is not the issue I'm
114
+ # fixing now. Remove this when we drop support for ancient ruby
115
+ skip if RUBY_VERSION == "1.8.7"
116
+
117
+ json = {"foo" => %q|`additional_details` = '{\"amount\":1}'|}
118
+ result = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json)
119
+ assert_equal json, JSON.parse(result)
120
+ end
121
+
122
+ def test_escapes_various_special_characters
123
+ # Some escapes haven't ever worked on 1.8.7, and is not the issue I'm
124
+ # fixing now. Remove this when we drop support for ancient ruby
125
+ skip if RUBY_VERSION == "1.8.7"
126
+
127
+ json = {"foo" => [
128
+ %Q|\fbar|,
129
+ %Q|\rbar|,
130
+ %Q|\nbar|,
131
+ %Q|\tbar|,
132
+ %Q|"bar|,
133
+ %Q|'bar|,
134
+ %Q|{bar|,
135
+ %Q|}bar|,
136
+ %Q|\\bar|,
137
+ if RUBY_VERSION == '1.8.7'
138
+ ""
139
+ else
140
+ %Q|\\\nbar|
141
+ end,
142
+ ]}
143
+
144
+ result = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(json)
145
+ assert_equal json, JSON.parse(result)
146
+ end
111
147
  end
@@ -139,6 +139,13 @@ module ScoutApm
139
139
  assert_equal %q|SELECT `blogs`.* FROM `blogs` WHERE (title = ?)|, ss.to_s
140
140
  end
141
141
 
142
+ def test_set_columns
143
+ sql = %q|UPDATE "mytable" SET "myfield" = 'fieldcontent', "countofthings" = 10 WHERE "user_id" = 10|
144
+
145
+ ss = SqlSanitizer.new(sql).tap{ |it| it.database_engine = :postgres }
146
+ assert_equal %q|UPDATE "mytable" SET "myfield" = ?, "countofthings" = ? WHERE "user_id" = ?|, ss.to_s
147
+ end
148
+
142
149
  def assert_faster_than(target_seconds)
143
150
  t1 = ::Time.now
144
151
  yield
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: 2.6.6
4
+ version: 2.6.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-12-19 00:00:00.000000000 Z
12
+ date: 2020-10-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -267,6 +267,17 @@ files:
267
267
  - lib/scout_apm/debug.rb
268
268
  - lib/scout_apm/detailed_trace.rb
269
269
  - lib/scout_apm/environment.rb
270
+ - lib/scout_apm/error.rb
271
+ - lib/scout_apm/error_service.rb
272
+ - lib/scout_apm/error_service/error_buffer.rb
273
+ - lib/scout_apm/error_service/error_record.rb
274
+ - lib/scout_apm/error_service/ignored_exceptions.rb
275
+ - lib/scout_apm/error_service/middleware.rb
276
+ - lib/scout_apm/error_service/notifier.rb
277
+ - lib/scout_apm/error_service/payload.rb
278
+ - lib/scout_apm/error_service/periodic_work.rb
279
+ - lib/scout_apm/error_service/railtie.rb
280
+ - lib/scout_apm/error_service/sidekiq.rb
270
281
  - lib/scout_apm/extensions/config.rb
271
282
  - lib/scout_apm/extensions/transaction_callback_payload.rb
272
283
  - lib/scout_apm/fake_store.rb
@@ -281,7 +292,6 @@ files:
281
292
  - lib/scout_apm/instant/middleware.rb
282
293
  - lib/scout_apm/instant_reporting.rb
283
294
  - lib/scout_apm/instrument_manager.rb
284
- - lib/scout_apm/instruments/.DS_Store
285
295
  - lib/scout_apm/instruments/action_controller_rails_2.rb
286
296
  - lib/scout_apm/instruments/action_controller_rails_3_rails4.rb
287
297
  - lib/scout_apm/instruments/action_view.rb
@@ -391,6 +401,7 @@ files:
391
401
  - test/data/config_test_1.yml
392
402
  - test/test_helper.rb
393
403
  - test/tmp/README.md
404
+ - test/unit/agent_context_test.rb
394
405
  - test/unit/agent_test.rb
395
406
  - test/unit/auto_instrument/assignments-instrumented.rb
396
407
  - test/unit/auto_instrument/assignments.rb
@@ -406,6 +417,8 @@ files:
406
417
  - test/unit/db_query_metric_set_test.rb
407
418
  - test/unit/db_query_metric_stats_test.rb
408
419
  - test/unit/environment_test.rb
420
+ - test/unit/error_service/error_buffer_test.rb
421
+ - test/unit/error_service/ignored_exceptions_test.rb
409
422
  - test/unit/extensions/periodic_callbacks_test.rb
410
423
  - test/unit/extensions/transaction_callbacks_test.rb
411
424
  - test/unit/fake_store_test.rb
@@ -461,61 +474,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
461
474
  - !ruby/object:Gem::Version
462
475
  version: '0'
463
476
  requirements: []
464
- rubygems_version: 3.0.4
477
+ rubygems_version: 3.0.8
465
478
  signing_key:
466
479
  specification_version: 4
467
480
  summary: Ruby application performance monitoring
468
- test_files:
469
- - test/data/config_test_1.yml
470
- - test/test_helper.rb
471
- - test/tmp/README.md
472
- - test/unit/agent_test.rb
473
- - test/unit/auto_instrument/assignments-instrumented.rb
474
- - test/unit/auto_instrument/assignments.rb
475
- - test/unit/auto_instrument/controller-ast.txt
476
- - test/unit/auto_instrument/controller-instrumented.rb
477
- - test/unit/auto_instrument/controller.rb
478
- - test/unit/auto_instrument/rescue_from-instrumented.rb
479
- - test/unit/auto_instrument/rescue_from.rb
480
- - test/unit/auto_instrument_test.rb
481
- - test/unit/background_job_integrations/sidekiq_test.rb
482
- - test/unit/config_test.rb
483
- - test/unit/context_test.rb
484
- - test/unit/db_query_metric_set_test.rb
485
- - test/unit/db_query_metric_stats_test.rb
486
- - test/unit/environment_test.rb
487
- - test/unit/extensions/periodic_callbacks_test.rb
488
- - test/unit/extensions/transaction_callbacks_test.rb
489
- - test/unit/fake_store_test.rb
490
- - test/unit/git_revision_test.rb
491
- - test/unit/histogram_test.rb
492
- - test/unit/ignored_uris_test.rb
493
- - test/unit/instruments/active_record_test.rb
494
- - test/unit/instruments/net_http_test.rb
495
- - test/unit/instruments/percentile_sampler_test.rb
496
- - test/unit/layaway_test.rb
497
- - test/unit/layer_children_set_test.rb
498
- - test/unit/layer_converters/depth_first_walker_test.rb
499
- - test/unit/layer_converters/metric_converter_test.rb
500
- - test/unit/layer_converters/stubs.rb
501
- - test/unit/limited_layer_test.rb
502
- - test/unit/logger_test.rb
503
- - test/unit/metric_set_test.rb
504
- - test/unit/remote/test_message.rb
505
- - test/unit/remote/test_router.rb
506
- - test/unit/remote/test_server.rb
507
- - test/unit/request_histograms_test.rb
508
- - test/unit/scored_item_set_test.rb
509
- - test/unit/serializers/payload_serializer_test.rb
510
- - test/unit/slow_job_policy_test.rb
511
- - test/unit/slow_request_policy_test.rb
512
- - test/unit/sql_sanitizer_test.rb
513
- - test/unit/store_test.rb
514
- - test/unit/tracer_test.rb
515
- - test/unit/tracked_request_test.rb
516
- - test/unit/transaction_test.rb
517
- - test/unit/transaction_time_consumed_test.rb
518
- - test/unit/utils/active_record_metric_name_test.rb
519
- - test/unit/utils/backtrace_parser_test.rb
520
- - test/unit/utils/numbers_test.rb
521
- - test/unit/utils/scm.rb
481
+ test_files: []