scout_apm 2.6.6 → 2.6.10

Sign up to get free protection for your applications and to get access to all the features.
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: []