scout_apm 5.6.4 → 5.6.5

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: 9c929de92cd0a3b2690bac6ef665c8c4d7187dd3ecc838cc881033d8cf5c413c
4
- data.tar.gz: 3148b06815beb3e506e271bb560fb700b5882d503c1a94f646011117ee424d9b
3
+ metadata.gz: 1a45c15600501200c54adb79600c0ff8c2c7229c6f4c8c6292804f2f10c716e2
4
+ data.tar.gz: d068883fec42f88524e0ccac0d84da6941a8c981c6eecb5b7e525affddb3ea77
5
5
  SHA512:
6
- metadata.gz: 28afece432426708eb8ce9bdb527dddd2820261574ca299887ba1294ecc98b34663c04dc15c9cf0ffb78f5211abb9b8d78ba6e202c7a6d28867edda65727b902
7
- data.tar.gz: 3e81497fa32092e4f037d272ee1a76f555bd9a557c9855a304e96acc93cdedaf24e0a4b21d86e895dddb1db569ec35954cbff30340f8e9803b7d799b28866522
6
+ metadata.gz: 92c538b493815dd07fed71cf43bf610c109b988a8aa597647e5b0d5c854120c13c9d8f96b8c04740ece120b952327ac920d41d18589422cfd23fe84ee1cded38
7
+ data.tar.gz: f69b0f43b36cc020600f91c97aafe3ed523225ac87bb9b62b2764d797e8d40380381b08032303a2d1c54d5a2f02164a8086a1b55bc7ad20d12e5ccf189db2921
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,10 @@
1
1
  # Unreleased
2
2
 
3
+ # 5.6.5
4
+ - Improve error capture API (#555)
5
+ - Add git sha and agent time tracking to error payloads (#554)
6
+ - Add support for named spaced jobs in sampling (#549)
7
+
3
8
  # 5.6.4
4
9
  - Use new ActiveJob class name and timestamp units for Sidekiq 8+ (#544)
5
10
 
@@ -5,23 +5,77 @@ module ScoutApm
5
5
  module Error
6
6
  # Capture an exception, optionally with an environment hash. This may be a
7
7
  # Rack environment, but is not required.
8
- def self.capture(exception, env={})
9
- context = ScoutApm::Agent.instance.context
8
+ class ScoutDefined < Exception; end
9
+ class Custom < ScoutDefined; end
10
+
11
+ class << self
12
+ def capture(exception, env={}, name: "ScoutApm::Error::Custom")
13
+ context = ScoutApm::Agent.instance.context
10
14
 
11
- # Skip if error monitoring isn't enabled at all
12
- if ! context.config.value("errors_enabled")
13
- return false
15
+ # Skip if error monitoring isn't enabled at all
16
+ if ! context.config.value("errors_enabled")
17
+ return false
18
+ end
19
+
20
+ exception = validate_or_create_exception(exception, name)
21
+ return false unless exception
22
+
23
+ # Skip if this one error is ignored
24
+ if context.ignored_exceptions.ignored?(exception)
25
+ return false
26
+ end
27
+
28
+ # Capture the error for further processing and shipping
29
+ context.error_buffer.capture(exception, env)
30
+
31
+ return true
32
+ end
33
+
34
+ private
35
+
36
+ def get_caller_location
37
+ caller_locations(0, 10)
38
+ .reject { |loc| loc.absolute_path == __FILE__ }
39
+ .map(&:to_s)
40
+ end
41
+
42
+ def define_error_class(name_str)
43
+ # e.g., "some_error" → "SomeError", "some error" → "SomeError"
44
+ class_name = name_str.gsub(/(?:^|[_\s])([a-zA-Z])/) { $1.upcase }
45
+
46
+ if Object.const_defined?(class_name)
47
+ klass = Object.const_get(class_name)
48
+ return klass if klass.ancestors.include?(ScoutApm::Error::ScoutDefined)
49
+
50
+ log_warning("Error class name '#{class_name}' is already defined. Falling back to ScoutApm::Error::Custom.")
51
+ return Custom
52
+ else
53
+ Object.const_set(class_name, Class.new(ScoutDefined))
54
+ end
14
55
  end
15
56
 
16
- # Skip if this one error is ignored
17
- if context.ignored_exceptions.ignored?(exception)
18
- return false
57
+ def log_warning(message)
58
+ ScoutApm::Agent.instance.context.logger.warn(message)
19
59
  end
20
60
 
21
- # Capture the error for further processing and shipping
22
- context.error_buffer.capture(exception, env)
61
+ def validate_or_create_exception(exception, name)
62
+ return exception if exception.is_a?(Exception) && exception.backtrace
23
63
 
24
- return true
64
+ if exception.is_a?(Exception)
65
+ exception.tap do |e|
66
+ e.set_backtrace(get_caller_location)
67
+ end
68
+
69
+ elsif exception.is_a?(String)
70
+ define_error_class(name).new(exception).tap do |e|
71
+ e.set_backtrace(get_caller_location)
72
+ end
73
+
74
+ else
75
+ log_warning("Invalid exception type: #{exception.class}. Expected Exception or String.")
76
+ nil
77
+ end
78
+ end
25
79
  end
26
80
  end
27
81
  end
@@ -12,6 +12,8 @@ module ScoutApm
12
12
  attr_reader :trace
13
13
  attr_reader :request_components
14
14
  attr_reader :context
15
+ attr_reader :agent_time
16
+ attr_reader :git_sha
15
17
 
16
18
  def initialize(agent_context, exception, env, context=nil)
17
19
  @agent_context = agent_context
@@ -30,6 +32,8 @@ module ScoutApm
30
32
  @environment = clean_params(strip_env(env))
31
33
  @trace = clean_backtrace(exception.backtrace)
32
34
  @request_components = components(env)
35
+ @agent_time = Time.now.iso8601
36
+ @git_sha = @agent_context.environment.git_revision.sha.to_s
33
37
  end
34
38
 
35
39
  # TODO: This is rails specific
@@ -40,6 +40,8 @@ module ScoutApm
40
40
  :trace => error_record.trace,
41
41
  :request_components => error_record.request_components,
42
42
  :context => error_record.context,
43
+ :agent_time => error_record.agent_time,
44
+ :git_sha => error_record.git_sha,
43
45
  }
44
46
  end
45
47
  end
@@ -58,7 +58,7 @@ module ScoutApm
58
58
  # config looks like ['/foo:50','/bar:100']. parse it into hash of string: integer
59
59
  sample_hash = {}
60
60
  sampling_config.each do |sample|
61
- path, rate = sample.split(':')
61
+ path, _, rate = sample.rpartition(':')
62
62
  sample_hash[path] = rate.to_i
63
63
  end
64
64
  sample_hash
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "5.6.4"
2
+ VERSION = "5.6.5"
3
3
  end
@@ -0,0 +1,69 @@
1
+ require "test_helper"
2
+
3
+ class ErrorTest < Minitest::Test
4
+ class FakeError < StandardError
5
+ end
6
+
7
+ def test_captures_and_stores_exceptions_and_env
8
+ config = make_fake_config(
9
+ 'errors_enabled' => true,
10
+ )
11
+ test_context = ScoutApm::AgentContext.new().tap{|c| c.config = config }
12
+ ScoutApm::Agent.instance.stub(:context, test_context) do
13
+ exwbt = ex.tap do |e|
14
+ e.set_backtrace(["/path/to/file.rb:10:in `method_name'"])
15
+ end
16
+ assert_equal true, ScoutApm::Error.capture("Oh no an error") # Will be of type ScoutApm::Error::Custom
17
+ assert_equal true, ScoutApm::Error.capture("Oh no another error") # Will be of type ScoutApm::Error::Custom
18
+ 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")
24
+
25
+ begin
26
+ raise StandardError, "Whoops"
27
+ rescue StandardError => e
28
+ assert_equal true, ScoutApm::Error.capture(e, env)
29
+ end
30
+
31
+ exceptions = ScoutApm::Agent.instance.context.error_buffer.instance_variable_get(:@error_records)
32
+
33
+ assert_equal 8, exceptions.length
34
+ assert_equal "Oh no an error", exceptions[0].message
35
+ assert_equal "ScoutApm::Error::Custom", exceptions[0].exception_class
36
+
37
+ # Ensure we capture time and git sha
38
+ refute_nil exceptions[0].git_sha
39
+ assert exceptions[0].agent_time.is_a?(String)
40
+
41
+ assert_equal "Oh no another error", exceptions[1].message
42
+ assert_equal "ScoutApm::Error::Custom", exceptions[1].exception_class
43
+
44
+ assert_equal "Something went boom", exceptions[2].message
45
+ assert_equal "BoomError", exceptions[2].exception_class
46
+
47
+ assert_equal "No env", exceptions[3].message
48
+ assert_equal "AnotherError", exceptions[3].exception_class
49
+
50
+ assert_equal "Whoops", exceptions[4].message
51
+ assert_equal "ErrorTest::FakeError", exceptions[4].exception_class
52
+
53
+ assert_equal "/path/to/file.rb:10:in `method_name'", exceptions[5].trace.first
54
+
55
+ assert_equal "ScoutApm::Error::Custom", exceptions[6].exception_class
56
+
57
+ assert_equal "StandardError", exceptions[7].exception_class
58
+ end
59
+ end
60
+
61
+ #### Helpers
62
+ def env
63
+ {}
64
+ end
65
+
66
+ def ex(msg="Whoops")
67
+ FakeError.new(msg)
68
+ end
69
+ end
@@ -15,7 +15,7 @@ class SamplingTest < Minitest::Test
15
15
  {
16
16
  'sample_endpoints' => ['/foo/bar:100', '/foo:50', '/bar/zap:80'],
17
17
  'ignore_endpoints' => ['/baz'],
18
- 'sample_jobs' => ['joba:50'],
18
+ 'sample_jobs' => ['joba:50', 'Foo::BarJob:95'],
19
19
  'ignore_jobs' => 'jobb,jobc',
20
20
  }
21
21
  )
@@ -59,6 +59,7 @@ class SamplingTest < Minitest::Test
59
59
  def test_job_sample
60
60
  sampling = ScoutApm::Sampling.new(@individual_config)
61
61
  assert_equal 50, sampling.job_sample_rate('joba')
62
+ assert_equal 95, sampling.job_sample_rate('Foo::BarJob')
62
63
  assert_nil sampling.job_sample_rate('jobb')
63
64
  end
64
65
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.4
4
+ version: 5.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
8
8
  - Andre Lewis
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-11 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -443,6 +443,7 @@ files:
443
443
  - test/unit/environment_test.rb
444
444
  - test/unit/error_service/error_buffer_test.rb
445
445
  - test/unit/error_service/ignored_exceptions_test.rb
446
+ - test/unit/error_test.rb
446
447
  - test/unit/extensions/periodic_callbacks_test.rb
447
448
  - test/unit/extensions/transaction_callbacks_test.rb
448
449
  - test/unit/external_service_metric_set_test.rb
@@ -508,7 +509,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
508
509
  - !ruby/object:Gem::Version
509
510
  version: '0'
510
511
  requirements: []
511
- rubygems_version: 3.6.2
512
+ rubygems_version: 3.6.9
512
513
  specification_version: 4
513
514
  summary: Ruby application performance monitoring
514
515
  test_files: []