scout_apm 5.6.4 → 5.7.0
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 +4 -4
- data/CHANGELOG.markdown +9 -0
- data/ext/allocations/allocations.c +20 -33
- data/ext/allocations/extconf.rb +1 -2
- data/lib/scout_apm/config.rb +6 -0
- data/lib/scout_apm/error.rb +65 -11
- data/lib/scout_apm/error_service/error_record.rb +4 -0
- data/lib/scout_apm/error_service/payload.rb +2 -0
- data/lib/scout_apm/sampling.rb +2 -2
- data/lib/scout_apm/version.rb +1 -1
- data/test/test_helper.rb +5 -0
- data/test/unit/error_test.rb +69 -0
- data/test/unit/sampling_test.rb +2 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2182f0bacb1555c0eb3323f8200db7f8d93068f018ce57c82bb4975361d0581
|
4
|
+
data.tar.gz: fd8b5694f8cacf2a648bb7a9e4440d16cb70be93c84b17db439781b01f2d5d48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44b2bcab8ec4d818faec5b87b4068ce3be0dd37afe97e123c4920cb4d5aa3a931c550bfb80cad1e9d9b6aea6102172cedd68afaee4a1497b3948675f61e2bec
|
7
|
+
data.tar.gz: a2e1dd693d0fc1f43e9f68d7a3b019c1d98d879a7671c9bc38944056390d6d8f7e0af6546ec39fc95d568edc3cca12bb73621ffd39e6c6eb80295c6a34cc3c20
|
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 5.7.0
|
4
|
+
- Fix native extension compilation with GCC 15 (#552)
|
5
|
+
- Handle non Rails/ActiveSupport applications w/ sampling (#557)
|
6
|
+
|
7
|
+
# 5.6.5
|
8
|
+
- Improve error capture API (#555)
|
9
|
+
- Add git sha and agent time tracking to error payloads (#554)
|
10
|
+
- Add support for named spaced jobs in sampling (#549)
|
11
|
+
|
3
12
|
# 5.6.4
|
4
13
|
- Use new ActiveJob class name and timestamp units for Sidekiq 8+ (#544)
|
5
14
|
|
@@ -1,26 +1,38 @@
|
|
1
|
-
#ifdef HAVE_RUBY_RUBY_H
|
2
|
-
#include <ruby/ruby.h>
|
3
|
-
#else // Ruby <= 1.8.7
|
4
1
|
#include <ruby.h>
|
5
|
-
#endif
|
6
2
|
|
7
3
|
VALUE mScoutApm;
|
8
4
|
VALUE mInstruments;
|
9
5
|
VALUE cAllocations;
|
10
6
|
|
11
|
-
#
|
7
|
+
#ifdef _WIN32
|
8
|
+
|
9
|
+
#define ALLOCATIONS_ENABLED Qfalse
|
10
|
+
|
11
|
+
static VALUE
|
12
|
+
get_allocation_count(VALUE klass) {
|
13
|
+
return ULL2NUM(0);
|
14
|
+
}
|
15
|
+
|
16
|
+
void
|
17
|
+
Init_hooks(VALUE module)
|
18
|
+
{
|
19
|
+
}
|
20
|
+
|
21
|
+
#else // _WIN32
|
12
22
|
|
13
23
|
#include <sys/resource.h> // is this needed?
|
14
24
|
#include <sys/time.h>
|
15
25
|
#include <ruby/debug.h>
|
16
26
|
|
27
|
+
#define ALLOCATIONS_ENABLED Qtrue
|
28
|
+
|
17
29
|
static __thread uint64_t endpoint_allocations;
|
18
30
|
void increment_allocations() {
|
19
31
|
endpoint_allocations++;
|
20
32
|
}
|
21
33
|
|
22
34
|
static VALUE
|
23
|
-
get_allocation_count() {
|
35
|
+
get_allocation_count(VALUE klass) {
|
24
36
|
return ULL2NUM(endpoint_allocations);
|
25
37
|
}
|
26
38
|
|
@@ -50,28 +62,7 @@ Init_hooks(VALUE module)
|
|
50
62
|
set_gc_hook(RUBY_INTERNAL_EVENT_NEWOBJ);
|
51
63
|
}
|
52
64
|
|
53
|
-
|
54
|
-
{
|
55
|
-
mScoutApm = rb_define_module("ScoutApm");
|
56
|
-
mInstruments = rb_define_module_under(mScoutApm, "Instruments");
|
57
|
-
cAllocations = rb_define_class_under(mInstruments, "Allocations", rb_cObject);
|
58
|
-
rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
|
59
|
-
rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
|
60
|
-
rb_define_const(cAllocations, "ENABLED", Qtrue);
|
61
|
-
Init_hooks(mScoutApm);
|
62
|
-
}
|
63
|
-
|
64
|
-
#else
|
65
|
-
|
66
|
-
static VALUE
|
67
|
-
get_allocation_count() {
|
68
|
-
return ULL2NUM(0);
|
69
|
-
}
|
70
|
-
|
71
|
-
void
|
72
|
-
Init_hooks(VALUE module)
|
73
|
-
{
|
74
|
-
}
|
65
|
+
#endif // _WIN32
|
75
66
|
|
76
67
|
void Init_allocations()
|
77
68
|
{
|
@@ -79,10 +70,6 @@ void Init_allocations()
|
|
79
70
|
mInstruments = rb_define_module_under(mScoutApm, "Instruments");
|
80
71
|
cAllocations = rb_define_class_under(mInstruments, "Allocations", rb_cObject);
|
81
72
|
rb_define_singleton_method(cAllocations, "count", get_allocation_count, 0);
|
82
|
-
|
83
|
-
rb_define_const(cAllocations, "ENABLED", Qfalse);
|
73
|
+
rb_define_const(cAllocations, "ENABLED", ALLOCATIONS_ENABLED);
|
84
74
|
Init_hooks(mScoutApm);
|
85
75
|
}
|
86
|
-
|
87
|
-
#endif //#ifdef RUBY_INTERNAL_EVENT_NEWOBJ
|
88
|
-
|
data/ext/allocations/extconf.rb
CHANGED
data/lib/scout_apm/config.rb
CHANGED
@@ -290,6 +290,12 @@ module ScoutApm
|
|
290
290
|
coercion.coerce(raw_value)
|
291
291
|
end
|
292
292
|
|
293
|
+
# https://github.com/rails/rails/blob/aca037411eab504a48c41472e696547da1543a19/activesupport/lib/active_support/core_ext/object/blank.rb#L25
|
294
|
+
def value_present?(key)
|
295
|
+
val = value(key)
|
296
|
+
val.respond_to?(:empty?) ? !val.empty? : false
|
297
|
+
end
|
298
|
+
|
293
299
|
# Did we load anything for configuration?
|
294
300
|
def any_keys_found?
|
295
301
|
@overlays.any? { |overlay| overlay.any_keys_found? }
|
data/lib/scout_apm/error.rb
CHANGED
@@ -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
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
17
|
-
|
18
|
-
return false
|
57
|
+
def log_warning(message)
|
58
|
+
ScoutApm::Agent.instance.context.logger.warn(message)
|
19
59
|
end
|
20
60
|
|
21
|
-
|
22
|
-
|
61
|
+
def validate_or_create_exception(exception, name)
|
62
|
+
return exception if exception.is_a?(Exception) && exception.backtrace
|
23
63
|
|
24
|
-
|
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
|
data/lib/scout_apm/sampling.rb
CHANGED
@@ -8,7 +8,7 @@ module ScoutApm
|
|
8
8
|
# jobs matched explicitly by name
|
9
9
|
|
10
10
|
# for now still support old config key ('ignore') for backwards compatibility
|
11
|
-
@ignore_endpoints = config.
|
11
|
+
@ignore_endpoints = config.value_present?('ignore') ? config.value('ignore') : config.value('ignore_endpoints')
|
12
12
|
@sample_endpoints = individual_sample_to_hash(config.value('sample_endpoints'))
|
13
13
|
@endpoint_sample_rate = config.value('endpoint_sample_rate')
|
14
14
|
|
@@ -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.
|
61
|
+
path, _, rate = sample.rpartition(':')
|
62
62
|
sample_hash[path] = rate.to_i
|
63
63
|
end
|
64
64
|
sample_hash
|
data/lib/scout_apm/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -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
|
data/test/unit/sampling_test.rb
CHANGED
@@ -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.
|
4
|
+
version: 5.7.0
|
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:
|
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.
|
512
|
+
rubygems_version: 3.6.9
|
512
513
|
specification_version: 4
|
513
514
|
summary: Ruby application performance monitoring
|
514
515
|
test_files: []
|