appsignal 2.11.3-java → 2.11.8-java
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/.rubocop.yml +2 -0
- data/CHANGELOG.md +21 -0
- data/Rakefile +9 -6
- data/ext/agent.yml +17 -17
- data/lib/appsignal/hooks/action_cable.rb +10 -2
- data/lib/appsignal/hooks/sidekiq.rb +5 -1
- data/lib/appsignal/integrations/object.rb +4 -34
- data/lib/appsignal/integrations/object_ruby_19.rb +37 -0
- data/lib/appsignal/integrations/object_ruby_modern.rb +64 -0
- data/lib/appsignal/minutely.rb +6 -0
- data/lib/appsignal/system.rb +4 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +88 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +61 -7
- data/spec/lib/appsignal/integrations/object_19_spec.rb +266 -0
- data/spec/lib/appsignal/integrations/object_spec.rb +29 -10
- data/spec/lib/puma/appsignal_spec.rb +28 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1e1ba61246de44136e06c57be6f4a877be8cc714dbcdafd1c7c8eb9bd994cbc4
|
|
4
|
+
data.tar.gz: ef465afb88df2f8f53015e84192159d464c4827e66a1ee19bed38b73712e810c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '060922d22213fb4b53b3656221d5ae1da5624682f0765f6631a2ef6c041ec12239729a38b1db5df383a316952628912d9a63680a8742211d404df9836cf2877f'
|
|
7
|
+
data.tar.gz: e7a8ba54779115a77703090837a9814e017884607d3b059b620a875cba9c52bc449fa56006ad443ecc076d1bf8bfc7b8c109bd7d61c811c8f5748e683b94f3d4
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# 2.11.8
|
|
4
|
+
- Mark minutely probe thread as fork-safe by @pixeltrix. PR #704
|
|
5
|
+
|
|
6
|
+
# 2.11.7
|
|
7
|
+
- Fix ActionCable integration in test environment using `stub_connection`.
|
|
8
|
+
PR #705
|
|
9
|
+
|
|
10
|
+
# 2.11.6
|
|
11
|
+
- Prepend Sidekiq middleware to wrap all Sidekiq middleware. Catches more
|
|
12
|
+
errors and provide more complete performance measurements. PR #698
|
|
13
|
+
|
|
14
|
+
# 2.11.5
|
|
15
|
+
- Add more detailed logging to finish_event calls when the event is unknown, so
|
|
16
|
+
we know what event is being tried to finish. Commit
|
|
17
|
+
c888a04d1b9ac947652b29c111c650fb5a5cf71c
|
|
18
|
+
|
|
19
|
+
# 2.11.4
|
|
20
|
+
- Support Ruby 3.0 for Object method instrumentation with keyword arguments
|
|
21
|
+
(https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html)
|
|
22
|
+
PR #693
|
|
23
|
+
|
|
3
24
|
# 2.11.3
|
|
4
25
|
- Support Shoryuken batch workers. PR #687
|
|
5
26
|
|
data/Rakefile
CHANGED
|
@@ -275,7 +275,8 @@ namespace :publish do
|
|
|
275
275
|
Dir.chdir("#{File.dirname(__FILE__)}/pkg") do
|
|
276
276
|
Dir["*.gem"].each do |gem_package|
|
|
277
277
|
puts "## Publishing gem package: #{gem_package}"
|
|
278
|
-
system "gem push #{gem_package}"
|
|
278
|
+
result = system "gem push #{gem_package}"
|
|
279
|
+
raise "Failed to Push gem" unless result
|
|
279
280
|
end
|
|
280
281
|
end
|
|
281
282
|
end
|
|
@@ -374,19 +375,21 @@ end
|
|
|
374
375
|
begin
|
|
375
376
|
require "rspec/core/rake_task"
|
|
376
377
|
is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
excludes = []
|
|
379
|
+
excludes << "spec/lib/appsignal/extension/jruby_spec.rb" unless is_jruby
|
|
380
|
+
is_ruby19 = RUBY_VERSION < "2.0"
|
|
381
|
+
excludes << "spec/lib/appsignal/integrations/object_spec.rb" if is_ruby19
|
|
382
|
+
exclude_pattern = "--exclude-pattern=#{excludes.join(",")}" if excludes.any?
|
|
380
383
|
|
|
381
384
|
desc "Run the AppSignal gem test suite."
|
|
382
385
|
RSpec::Core::RakeTask.new :test do |t|
|
|
383
|
-
t.rspec_opts =
|
|
386
|
+
t.rspec_opts = exclude_pattern
|
|
384
387
|
end
|
|
385
388
|
|
|
386
389
|
namespace :test do
|
|
387
390
|
desc "Run the Appsignal gem test in an extension failure scenario"
|
|
388
391
|
RSpec::Core::RakeTask.new :failure do |t|
|
|
389
|
-
t.rspec_opts = "#{
|
|
392
|
+
t.rspec_opts = "#{exclude_pattern} --tag extension_installation_failure"
|
|
390
393
|
end
|
|
391
394
|
end
|
|
392
395
|
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
data/ext/agent.yml
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
1
|
---
|
|
2
|
-
version:
|
|
2
|
+
version: d98461b
|
|
3
3
|
mirrors:
|
|
4
4
|
- https://appsignal-agent-releases.global.ssl.fastly.net
|
|
5
5
|
- https://d135dj0rjqvssy.cloudfront.net
|
|
6
6
|
triples:
|
|
7
7
|
x86_64-darwin:
|
|
8
8
|
static:
|
|
9
|
-
checksum:
|
|
9
|
+
checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
|
|
10
10
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
|
11
11
|
dynamic:
|
|
12
|
-
checksum:
|
|
12
|
+
checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
|
|
13
13
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
|
14
14
|
universal-darwin:
|
|
15
15
|
static:
|
|
16
|
-
checksum:
|
|
16
|
+
checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
|
|
17
17
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
|
18
18
|
dynamic:
|
|
19
|
-
checksum:
|
|
19
|
+
checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
|
|
20
20
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
|
21
21
|
i686-linux:
|
|
22
22
|
static:
|
|
23
|
-
checksum:
|
|
23
|
+
checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
|
|
24
24
|
filename: appsignal-i686-linux-all-static.tar.gz
|
|
25
25
|
dynamic:
|
|
26
|
-
checksum:
|
|
26
|
+
checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
|
|
27
27
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
|
28
28
|
x86-linux:
|
|
29
29
|
static:
|
|
30
|
-
checksum:
|
|
30
|
+
checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
|
|
31
31
|
filename: appsignal-i686-linux-all-static.tar.gz
|
|
32
32
|
dynamic:
|
|
33
|
-
checksum:
|
|
33
|
+
checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
|
|
34
34
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
|
35
35
|
x86_64-linux:
|
|
36
36
|
static:
|
|
37
|
-
checksum:
|
|
37
|
+
checksum: d6c280e992d74f97d59da9827ec5707ca4f6776b0568cde1c083c1113e4b7104
|
|
38
38
|
filename: appsignal-x86_64-linux-all-static.tar.gz
|
|
39
39
|
dynamic:
|
|
40
|
-
checksum:
|
|
40
|
+
checksum: ef1a3f5d4b2ed61ea2ae4d5cb1a261a0e685fb9d3e7ea9efe455498aad386e59
|
|
41
41
|
filename: appsignal-x86_64-linux-all-dynamic.tar.gz
|
|
42
42
|
x86_64-linux-musl:
|
|
43
43
|
static:
|
|
44
|
-
checksum:
|
|
44
|
+
checksum: 9cf8ad34392662746a45cfce18113ad19cc29954789e2058f30e527163f2e98a
|
|
45
45
|
filename: appsignal-x86_64-linux-musl-all-static.tar.gz
|
|
46
46
|
dynamic:
|
|
47
|
-
checksum:
|
|
47
|
+
checksum: 01bd76983227648d9bc41d035e1552fcf18d62f0d6818bf7bb7fc2e7c166513d
|
|
48
48
|
filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
|
|
49
49
|
x86_64-freebsd:
|
|
50
50
|
static:
|
|
51
|
-
checksum:
|
|
51
|
+
checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
|
|
52
52
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
|
53
53
|
dynamic:
|
|
54
|
-
checksum:
|
|
54
|
+
checksum: a1faae80ae09a6588c4cd35cb91dfa4b2176fc3cb17fbf79db80194c21e75bf9
|
|
55
55
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
|
56
56
|
amd64-freebsd:
|
|
57
57
|
static:
|
|
58
|
-
checksum:
|
|
58
|
+
checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
|
|
59
59
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
|
60
60
|
dynamic:
|
|
61
|
-
checksum:
|
|
61
|
+
checksum: a1faae80ae09a6588c4cd35cb91dfa4b2176fc3cb17fbf79db80194c21e75bf9
|
|
62
62
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
|
@@ -56,7 +56,11 @@ module Appsignal
|
|
|
56
56
|
def install_callbacks
|
|
57
57
|
ActionCable::Channel::Base.set_callback :subscribe, :around, :prepend => true do |channel, inner|
|
|
58
58
|
# The request is only the original websocket request
|
|
59
|
-
|
|
59
|
+
connection = channel.connection
|
|
60
|
+
# #env is not available on the Rails ConnectionStub class used in the
|
|
61
|
+
# Rails app test suite. If we call `#env` it causes an error to occur
|
|
62
|
+
# in apps' test suites.
|
|
63
|
+
env = connection.respond_to?(:env) ? connection.env : {}
|
|
60
64
|
request = ActionDispatch::Request.new(env)
|
|
61
65
|
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
|
|
62
66
|
request.request_id || SecureRandom.uuid
|
|
@@ -84,7 +88,11 @@ module Appsignal
|
|
|
84
88
|
|
|
85
89
|
ActionCable::Channel::Base.set_callback :unsubscribe, :around, :prepend => true do |channel, inner|
|
|
86
90
|
# The request is only the original websocket request
|
|
87
|
-
|
|
91
|
+
connection = channel.connection
|
|
92
|
+
# #env is not available on the Rails ConnectionStub class used in the
|
|
93
|
+
# Rails app test suite. If we call `#env` it causes an error to occur
|
|
94
|
+
# in apps' test suites.
|
|
95
|
+
env = connection.respond_to?(:env) ? connection.env : {}
|
|
88
96
|
request = ActionDispatch::Request.new(env)
|
|
89
97
|
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
|
|
90
98
|
request.request_id || SecureRandom.uuid
|
|
@@ -16,7 +16,11 @@ module Appsignal
|
|
|
16
16
|
|
|
17
17
|
::Sidekiq.configure_server do |config|
|
|
18
18
|
config.server_middleware do |chain|
|
|
19
|
-
chain.
|
|
19
|
+
if chain.respond_to? :prepend
|
|
20
|
+
chain.prepend Appsignal::Hooks::SidekiqPlugin
|
|
21
|
+
else
|
|
22
|
+
chain.add Appsignal::Hooks::SidekiqPlugin
|
|
23
|
+
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
22
26
|
end
|
|
@@ -4,38 +4,8 @@ if defined?(Appsignal)
|
|
|
4
4
|
Appsignal::Environment.report_enabled("object_instrumentation")
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
singleton_class.send(:define_method, method_name) do |*args, &block|
|
|
12
|
-
name = options.fetch(:name) do
|
|
13
|
-
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
|
14
|
-
end
|
|
15
|
-
Appsignal.instrument name do
|
|
16
|
-
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.appsignal_instrument_method(method_name, options = {})
|
|
22
|
-
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
|
23
|
-
define_method method_name do |*args, &block|
|
|
24
|
-
name = options.fetch(:name) do
|
|
25
|
-
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
|
26
|
-
end
|
|
27
|
-
Appsignal.instrument name do
|
|
28
|
-
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def self.appsignal_reverse_class_name
|
|
34
|
-
return "AnonymousClass" unless name
|
|
35
|
-
name.split("::").reverse.join(".")
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def appsignal_reverse_class_name
|
|
39
|
-
self.class.appsignal_reverse_class_name
|
|
40
|
-
end
|
|
7
|
+
if RUBY_VERSION < "2.0"
|
|
8
|
+
require "appsignal/integrations/object_ruby_19"
|
|
9
|
+
else
|
|
10
|
+
require "appsignal/integrations/object_ruby_modern"
|
|
41
11
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
|
5
|
+
singleton_class.send \
|
|
6
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
|
7
|
+
singleton_class.send(:define_method, method_name) do |*args, &block|
|
|
8
|
+
name = options.fetch(:name) do
|
|
9
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
|
10
|
+
end
|
|
11
|
+
Appsignal.instrument name do
|
|
12
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
|
18
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
|
19
|
+
define_method method_name do |*args, &block|
|
|
20
|
+
name = options.fetch(:name) do
|
|
21
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
|
22
|
+
end
|
|
23
|
+
Appsignal.instrument name do
|
|
24
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.appsignal_reverse_class_name
|
|
30
|
+
return "AnonymousClass" unless name
|
|
31
|
+
name.split("::").reverse.join(".")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def appsignal_reverse_class_name
|
|
35
|
+
self.class.appsignal_reverse_class_name
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
if Appsignal::System.ruby_2_7_or_newer?
|
|
5
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
|
6
|
+
singleton_class.send \
|
|
7
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
|
8
|
+
singleton_class.send(:define_method, method_name) do |*args, **kwargs, &block|
|
|
9
|
+
name = options.fetch(:name) do
|
|
10
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
|
11
|
+
end
|
|
12
|
+
Appsignal.instrument name do
|
|
13
|
+
send "appsignal_uninstrumented_#{method_name}", *args, **kwargs, &block
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
|
19
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
|
20
|
+
define_method method_name do |*args, **kwargs, &block|
|
|
21
|
+
name = options.fetch(:name) do
|
|
22
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
|
23
|
+
end
|
|
24
|
+
Appsignal.instrument name do
|
|
25
|
+
send "appsignal_uninstrumented_#{method_name}", *args, **kwargs, &block
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
|
31
|
+
singleton_class.send \
|
|
32
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
|
33
|
+
singleton_class.send(:define_method, method_name) do |*args, &block|
|
|
34
|
+
name = options.fetch(:name) do
|
|
35
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
|
36
|
+
end
|
|
37
|
+
Appsignal.instrument name do
|
|
38
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
|
44
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
|
45
|
+
define_method method_name do |*args, &block|
|
|
46
|
+
name = options.fetch(:name) do
|
|
47
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
|
48
|
+
end
|
|
49
|
+
Appsignal.instrument name do
|
|
50
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.appsignal_reverse_class_name
|
|
57
|
+
return "AnonymousClass" unless name
|
|
58
|
+
name.split("::").reverse.join(".")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def appsignal_reverse_class_name
|
|
62
|
+
self.class.appsignal_reverse_class_name
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/appsignal/minutely.rb
CHANGED
|
@@ -135,6 +135,12 @@ module Appsignal
|
|
|
135
135
|
def start
|
|
136
136
|
stop
|
|
137
137
|
@thread = Thread.new do
|
|
138
|
+
# Advise multi-threaded app servers to ignore this thread
|
|
139
|
+
# for the purposes of fork safety warnings
|
|
140
|
+
if Thread.current.respond_to?(:thread_variable_set)
|
|
141
|
+
Thread.current.thread_variable_set(:fork_safe, true)
|
|
142
|
+
end
|
|
143
|
+
|
|
138
144
|
sleep initial_wait_time
|
|
139
145
|
initialize_probes
|
|
140
146
|
loop do
|
data/lib/appsignal/system.rb
CHANGED
data/lib/appsignal/version.rb
CHANGED
|
@@ -2,6 +2,8 @@ describe Appsignal::Hooks::ActionCableHook do
|
|
|
2
2
|
if DependencyHelper.action_cable_present?
|
|
3
3
|
context "with ActionCable" do
|
|
4
4
|
require "action_cable/engine"
|
|
5
|
+
# Require test helper to test with ConnectionStub
|
|
6
|
+
require "action_cable/channel/test_case" if DependencyHelper.rails6_present?
|
|
5
7
|
|
|
6
8
|
describe ".dependencies_present?" do
|
|
7
9
|
subject { described_class.new.dependencies_present? }
|
|
@@ -262,6 +264,49 @@ describe Appsignal::Hooks::ActionCableHook do
|
|
|
262
264
|
)
|
|
263
265
|
end
|
|
264
266
|
end
|
|
267
|
+
|
|
268
|
+
if DependencyHelper.rails6_present?
|
|
269
|
+
context "with ConnectionStub" do
|
|
270
|
+
let(:connection) { ActionCable::Channel::ConnectionStub.new }
|
|
271
|
+
let(:transaction_id) { "Stubbed transaction id" }
|
|
272
|
+
before do
|
|
273
|
+
# Stub future (private AppSignal) transaction id generated by the hook.
|
|
274
|
+
expect(SecureRandom).to receive(:uuid).and_return(transaction_id)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it "does not fail on missing `#env` method on `ConnectionStub`" do
|
|
278
|
+
instance.subscribe_to_channel
|
|
279
|
+
|
|
280
|
+
expect(subject).to include(
|
|
281
|
+
"action" => "MyChannel#subscribed",
|
|
282
|
+
"error" => nil,
|
|
283
|
+
"id" => transaction_id,
|
|
284
|
+
"namespace" => Appsignal::Transaction::ACTION_CABLE,
|
|
285
|
+
"metadata" => {
|
|
286
|
+
"method" => "websocket",
|
|
287
|
+
"path" => "" # No path as the ConnectionStub doesn't have the real request env
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
expect(subject["events"].first).to include(
|
|
291
|
+
"allocation_count" => kind_of(Integer),
|
|
292
|
+
"body" => "",
|
|
293
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
294
|
+
"child_allocation_count" => kind_of(Integer),
|
|
295
|
+
"child_duration" => kind_of(Float),
|
|
296
|
+
"child_gc_duration" => kind_of(Float),
|
|
297
|
+
"count" => 1,
|
|
298
|
+
"gc_duration" => kind_of(Float),
|
|
299
|
+
"start" => kind_of(Float),
|
|
300
|
+
"duration" => kind_of(Float),
|
|
301
|
+
"name" => "subscribed.action_cable",
|
|
302
|
+
"title" => ""
|
|
303
|
+
)
|
|
304
|
+
expect(subject["sample_data"]).to include(
|
|
305
|
+
"params" => { "internal" => "true" }
|
|
306
|
+
)
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
265
310
|
end
|
|
266
311
|
|
|
267
312
|
describe "unsubscribe callback" do
|
|
@@ -349,6 +394,49 @@ describe Appsignal::Hooks::ActionCableHook do
|
|
|
349
394
|
)
|
|
350
395
|
end
|
|
351
396
|
end
|
|
397
|
+
|
|
398
|
+
if DependencyHelper.rails6_present?
|
|
399
|
+
context "with ConnectionStub" do
|
|
400
|
+
let(:connection) { ActionCable::Channel::ConnectionStub.new }
|
|
401
|
+
let(:transaction_id) { "Stubbed transaction id" }
|
|
402
|
+
before do
|
|
403
|
+
# Stub future (private AppSignal) transaction id generated by the hook.
|
|
404
|
+
expect(SecureRandom).to receive(:uuid).and_return(transaction_id)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
it "does not fail on missing `#env` method on `ConnectionStub`" do
|
|
408
|
+
instance.unsubscribe_from_channel
|
|
409
|
+
|
|
410
|
+
expect(subject).to include(
|
|
411
|
+
"action" => "MyChannel#unsubscribed",
|
|
412
|
+
"error" => nil,
|
|
413
|
+
"id" => transaction_id,
|
|
414
|
+
"namespace" => Appsignal::Transaction::ACTION_CABLE,
|
|
415
|
+
"metadata" => {
|
|
416
|
+
"method" => "websocket",
|
|
417
|
+
"path" => "" # No path as the ConnectionStub doesn't have the real request env
|
|
418
|
+
}
|
|
419
|
+
)
|
|
420
|
+
expect(subject["events"].first).to include(
|
|
421
|
+
"allocation_count" => kind_of(Integer),
|
|
422
|
+
"body" => "",
|
|
423
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
424
|
+
"child_allocation_count" => kind_of(Integer),
|
|
425
|
+
"child_duration" => kind_of(Float),
|
|
426
|
+
"child_gc_duration" => kind_of(Float),
|
|
427
|
+
"count" => 1,
|
|
428
|
+
"gc_duration" => kind_of(Float),
|
|
429
|
+
"start" => kind_of(Float),
|
|
430
|
+
"duration" => kind_of(Float),
|
|
431
|
+
"name" => "unsubscribed.action_cable",
|
|
432
|
+
"title" => ""
|
|
433
|
+
)
|
|
434
|
+
expect(subject["sample_data"]).to include(
|
|
435
|
+
"params" => { "internal" => "true" }
|
|
436
|
+
)
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
end
|
|
352
440
|
end
|
|
353
441
|
end
|
|
354
442
|
end
|
|
@@ -16,14 +16,31 @@ describe Appsignal::Hooks::SidekiqHook do
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
describe "#install" do
|
|
19
|
-
class
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
class SidekiqMiddlewareMockWithPrepend < Array
|
|
20
|
+
alias add <<
|
|
21
|
+
alias exists? include?
|
|
22
|
+
|
|
23
|
+
unless method_defined? :prepend
|
|
24
|
+
def prepend(middleware) # For Ruby < 2.5
|
|
25
|
+
insert(0, middleware)
|
|
26
|
+
end
|
|
22
27
|
end
|
|
23
28
|
end
|
|
29
|
+
|
|
30
|
+
class SidekiqMiddlewareMockWithoutPrepend < Array
|
|
31
|
+
alias add <<
|
|
32
|
+
alias exists? include?
|
|
33
|
+
|
|
34
|
+
undef_method :prepend if method_defined? :prepend # For Ruby >= 2.5
|
|
35
|
+
end
|
|
36
|
+
|
|
24
37
|
module SidekiqMock
|
|
38
|
+
def self.middleware_mock=(mock)
|
|
39
|
+
@middlewares = mock.new
|
|
40
|
+
end
|
|
41
|
+
|
|
25
42
|
def self.middlewares
|
|
26
|
-
@middlewares
|
|
43
|
+
@middlewares
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
def self.configure_server
|
|
@@ -36,15 +53,52 @@ describe Appsignal::Hooks::SidekiqHook do
|
|
|
36
53
|
end
|
|
37
54
|
end
|
|
38
55
|
|
|
56
|
+
def add_middleware(middleware)
|
|
57
|
+
Sidekiq.configure_server do |sidekiq_config|
|
|
58
|
+
sidekiq_config.middlewares.add(middleware)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
39
62
|
before do
|
|
40
63
|
Appsignal.config = project_fixture_config
|
|
41
64
|
stub_const "Sidekiq", SidekiqMock
|
|
42
65
|
end
|
|
43
66
|
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
context "when Sidekiq middleware responds to prepend method" do # Sidekiq 3.3.0 and newer
|
|
68
|
+
before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithPrepend }
|
|
69
|
+
|
|
70
|
+
it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain in the first position" do
|
|
71
|
+
user_middleware1 = proc {}
|
|
72
|
+
add_middleware(user_middleware1)
|
|
73
|
+
described_class.new.install
|
|
74
|
+
user_middleware2 = proc {}
|
|
75
|
+
add_middleware(user_middleware2)
|
|
46
76
|
|
|
47
|
-
|
|
77
|
+
expect(Sidekiq.server_middleware).to eql([
|
|
78
|
+
Appsignal::Hooks::SidekiqPlugin, # Prepend makes it the first entry
|
|
79
|
+
user_middleware1,
|
|
80
|
+
user_middleware2
|
|
81
|
+
])
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context "when Sidekiq middleware does not respond to prepend method" do
|
|
86
|
+
before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithoutPrepend }
|
|
87
|
+
|
|
88
|
+
it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
|
|
89
|
+
user_middleware1 = proc {}
|
|
90
|
+
add_middleware(user_middleware1)
|
|
91
|
+
described_class.new.install
|
|
92
|
+
user_middleware2 = proc {}
|
|
93
|
+
add_middleware(user_middleware2)
|
|
94
|
+
|
|
95
|
+
# Add middlewares in whatever order they were added
|
|
96
|
+
expect(Sidekiq.server_middleware).to eql([
|
|
97
|
+
user_middleware1,
|
|
98
|
+
Appsignal::Hooks::SidekiqPlugin,
|
|
99
|
+
user_middleware2
|
|
100
|
+
])
|
|
101
|
+
end
|
|
48
102
|
end
|
|
49
103
|
end
|
|
50
104
|
end
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
require "appsignal/integrations/object"
|
|
2
|
+
|
|
3
|
+
def is_ruby_19
|
|
4
|
+
RUBY_VERSION < "2.0"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
describe Object do
|
|
8
|
+
describe "#instrument_method" do
|
|
9
|
+
context "with instance method" do
|
|
10
|
+
let(:klass) do
|
|
11
|
+
Class.new do
|
|
12
|
+
def foo(param1, options = {})
|
|
13
|
+
[param1, options]
|
|
14
|
+
end
|
|
15
|
+
appsignal_instrument_method :foo
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
let(:instance) { klass.new }
|
|
19
|
+
|
|
20
|
+
def call_with_arguments
|
|
21
|
+
instance.foo(
|
|
22
|
+
"abc",
|
|
23
|
+
:foo => "bar"
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "when active" do
|
|
28
|
+
let(:transaction) { http_request_transaction }
|
|
29
|
+
before do
|
|
30
|
+
Appsignal.config = project_fixture_config
|
|
31
|
+
expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
|
|
32
|
+
end
|
|
33
|
+
after { Appsignal.config = nil }
|
|
34
|
+
|
|
35
|
+
context "with anonymous class" do
|
|
36
|
+
it "instruments the method and calls it" do
|
|
37
|
+
expect(Appsignal.active?).to be_truthy
|
|
38
|
+
expect(transaction).to receive(:start_event)
|
|
39
|
+
expect(transaction).to receive(:finish_event).with \
|
|
40
|
+
"foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
41
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "with named class" do
|
|
46
|
+
before do
|
|
47
|
+
class NamedClass
|
|
48
|
+
def foo
|
|
49
|
+
1
|
|
50
|
+
end
|
|
51
|
+
appsignal_instrument_method :foo
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
after { Object.send(:remove_const, :NamedClass) }
|
|
55
|
+
let(:klass) { NamedClass }
|
|
56
|
+
|
|
57
|
+
it "instruments the method and calls it" do
|
|
58
|
+
expect(Appsignal.active?).to be_truthy
|
|
59
|
+
expect(transaction).to receive(:start_event)
|
|
60
|
+
expect(transaction).to receive(:finish_event).with \
|
|
61
|
+
"foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
62
|
+
expect(instance.foo).to eq(1)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "with nested named class" do
|
|
67
|
+
before do
|
|
68
|
+
module MyModule
|
|
69
|
+
module NestedModule
|
|
70
|
+
class NamedClass
|
|
71
|
+
def bar
|
|
72
|
+
2
|
|
73
|
+
end
|
|
74
|
+
appsignal_instrument_method :bar
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
after { Object.send(:remove_const, :MyModule) }
|
|
80
|
+
let(:klass) { MyModule::NestedModule::NamedClass }
|
|
81
|
+
|
|
82
|
+
it "instruments the method and calls it" do
|
|
83
|
+
expect(Appsignal.active?).to be_truthy
|
|
84
|
+
expect(transaction).to receive(:start_event)
|
|
85
|
+
expect(transaction).to receive(:finish_event).with \
|
|
86
|
+
"bar.NamedClass.NestedModule.MyModule.other", nil, nil,
|
|
87
|
+
Appsignal::EventFormatter::DEFAULT
|
|
88
|
+
expect(instance.bar).to eq(2)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context "with custom name" do
|
|
93
|
+
let(:klass) do
|
|
94
|
+
Class.new do
|
|
95
|
+
def foo
|
|
96
|
+
1
|
|
97
|
+
end
|
|
98
|
+
appsignal_instrument_method :foo, :name => "my_method.group"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "instruments with custom name" do
|
|
103
|
+
expect(Appsignal.active?).to be_truthy
|
|
104
|
+
expect(transaction).to receive(:start_event)
|
|
105
|
+
expect(transaction).to receive(:finish_event).with \
|
|
106
|
+
"my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
107
|
+
expect(instance.foo).to eq(1)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context "with a method given a block" do
|
|
112
|
+
let(:klass) do
|
|
113
|
+
Class.new do
|
|
114
|
+
def foo
|
|
115
|
+
yield
|
|
116
|
+
end
|
|
117
|
+
appsignal_instrument_method :foo
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should yield the block" do
|
|
122
|
+
expect(instance.foo { 42 }).to eq(42)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "when not active" do
|
|
128
|
+
let(:transaction) { Appsignal::Transaction.current }
|
|
129
|
+
|
|
130
|
+
it "does not instrument, but still calls the method" do
|
|
131
|
+
expect(Appsignal.active?).to be_falsy
|
|
132
|
+
expect(transaction).to_not receive(:start_event)
|
|
133
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context "with class method" do
|
|
139
|
+
let(:klass) do
|
|
140
|
+
Class.new do
|
|
141
|
+
def self.bar(param1, options = {})
|
|
142
|
+
[param1, options]
|
|
143
|
+
end
|
|
144
|
+
appsignal_instrument_class_method :bar
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
def call_with_arguments
|
|
148
|
+
klass.bar(
|
|
149
|
+
"abc",
|
|
150
|
+
:foo => "bar"
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
context "when active" do
|
|
155
|
+
let(:transaction) { http_request_transaction }
|
|
156
|
+
before do
|
|
157
|
+
Appsignal.config = project_fixture_config
|
|
158
|
+
expect(Appsignal::Transaction).to receive(:current).at_least(:once)
|
|
159
|
+
.and_return(transaction)
|
|
160
|
+
end
|
|
161
|
+
after { Appsignal.config = nil }
|
|
162
|
+
|
|
163
|
+
context "with anonymous class" do
|
|
164
|
+
it "instruments the method and calls it" do
|
|
165
|
+
expect(Appsignal.active?).to be_truthy
|
|
166
|
+
expect(transaction).to receive(:start_event)
|
|
167
|
+
expect(transaction).to receive(:finish_event).with \
|
|
168
|
+
"bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
169
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context "with named class" do
|
|
174
|
+
before do
|
|
175
|
+
class NamedClass
|
|
176
|
+
def self.bar
|
|
177
|
+
2
|
|
178
|
+
end
|
|
179
|
+
appsignal_instrument_class_method :bar
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
after { Object.send(:remove_const, :NamedClass) }
|
|
183
|
+
let(:klass) { NamedClass }
|
|
184
|
+
|
|
185
|
+
it "instruments the method and calls it" do
|
|
186
|
+
expect(Appsignal.active?).to be_truthy
|
|
187
|
+
expect(transaction).to receive(:start_event)
|
|
188
|
+
expect(transaction).to receive(:finish_event).with \
|
|
189
|
+
"bar.class_method.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
190
|
+
expect(klass.bar).to eq(2)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
context "with nested named class" do
|
|
194
|
+
before do
|
|
195
|
+
module MyModule
|
|
196
|
+
module NestedModule
|
|
197
|
+
class NamedClass
|
|
198
|
+
def self.bar
|
|
199
|
+
2
|
|
200
|
+
end
|
|
201
|
+
appsignal_instrument_class_method :bar
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
after { Object.send(:remove_const, :MyModule) }
|
|
207
|
+
let(:klass) { MyModule::NestedModule::NamedClass }
|
|
208
|
+
|
|
209
|
+
it "instruments the method and calls it" do
|
|
210
|
+
expect(Appsignal.active?).to be_truthy
|
|
211
|
+
expect(transaction).to receive(:start_event)
|
|
212
|
+
expect(transaction).to receive(:finish_event).with \
|
|
213
|
+
"bar.class_method.NamedClass.NestedModule.MyModule.other", nil, nil,
|
|
214
|
+
Appsignal::EventFormatter::DEFAULT
|
|
215
|
+
expect(klass.bar).to eq(2)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
context "with custom name" do
|
|
221
|
+
let(:klass) do
|
|
222
|
+
Class.new do
|
|
223
|
+
def self.bar
|
|
224
|
+
2
|
|
225
|
+
end
|
|
226
|
+
appsignal_instrument_class_method :bar, :name => "my_method.group"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it "instruments with custom name" do
|
|
231
|
+
expect(Appsignal.active?).to be_truthy
|
|
232
|
+
expect(transaction).to receive(:start_event)
|
|
233
|
+
expect(transaction).to receive(:finish_event).with \
|
|
234
|
+
"my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
235
|
+
expect(klass.bar).to eq(2)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
context "with a method given a block" do
|
|
240
|
+
let(:klass) do
|
|
241
|
+
Class.new do
|
|
242
|
+
def self.bar
|
|
243
|
+
yield
|
|
244
|
+
end
|
|
245
|
+
appsignal_instrument_class_method :bar
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
it "should yield the block" do
|
|
250
|
+
expect(klass.bar { 42 }).to eq(42)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
context "when not active" do
|
|
256
|
+
let(:transaction) { Appsignal::Transaction.current }
|
|
257
|
+
|
|
258
|
+
it "does not instrument, but still call the method" do
|
|
259
|
+
expect(Appsignal.active?).to be_falsy
|
|
260
|
+
expect(transaction).to_not receive(:start_event)
|
|
261
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
require "appsignal/integrations/object"
|
|
2
2
|
|
|
3
|
+
def is_ruby_19
|
|
4
|
+
RUBY_VERSION < "2.0"
|
|
5
|
+
end
|
|
6
|
+
|
|
3
7
|
describe Object do
|
|
4
8
|
describe "#instrument_method" do
|
|
5
9
|
context "with instance method" do
|
|
6
10
|
let(:klass) do
|
|
7
11
|
Class.new do
|
|
8
|
-
def foo
|
|
9
|
-
|
|
12
|
+
def foo(param1, options = {}, keyword_param: 1)
|
|
13
|
+
[param1, options, keyword_param]
|
|
10
14
|
end
|
|
11
15
|
appsignal_instrument_method :foo
|
|
12
16
|
end
|
|
13
17
|
end
|
|
14
18
|
let(:instance) { klass.new }
|
|
15
19
|
|
|
20
|
+
def call_with_arguments
|
|
21
|
+
instance.foo(
|
|
22
|
+
"abc",
|
|
23
|
+
{ :foo => "bar" },
|
|
24
|
+
:keyword_param => 2
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
16
28
|
context "when active" do
|
|
17
29
|
let(:transaction) { http_request_transaction }
|
|
18
30
|
before do
|
|
@@ -27,7 +39,7 @@ describe Object do
|
|
|
27
39
|
expect(transaction).to receive(:start_event)
|
|
28
40
|
expect(transaction).to receive(:finish_event).with \
|
|
29
41
|
"foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
30
|
-
expect(
|
|
42
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
|
|
31
43
|
end
|
|
32
44
|
end
|
|
33
45
|
|
|
@@ -116,10 +128,10 @@ describe Object do
|
|
|
116
128
|
context "when not active" do
|
|
117
129
|
let(:transaction) { Appsignal::Transaction.current }
|
|
118
130
|
|
|
119
|
-
it "
|
|
131
|
+
it "does not instrument, but still calls the method" do
|
|
120
132
|
expect(Appsignal.active?).to be_falsy
|
|
121
133
|
expect(transaction).to_not receive(:start_event)
|
|
122
|
-
expect(
|
|
134
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
|
|
123
135
|
end
|
|
124
136
|
end
|
|
125
137
|
end
|
|
@@ -127,12 +139,19 @@ describe Object do
|
|
|
127
139
|
context "with class method" do
|
|
128
140
|
let(:klass) do
|
|
129
141
|
Class.new do
|
|
130
|
-
def self.bar
|
|
131
|
-
|
|
142
|
+
def self.bar(param1, options = {}, keyword_param: 1)
|
|
143
|
+
[param1, options, keyword_param]
|
|
132
144
|
end
|
|
133
145
|
appsignal_instrument_class_method :bar
|
|
134
146
|
end
|
|
135
147
|
end
|
|
148
|
+
def call_with_arguments
|
|
149
|
+
klass.bar(
|
|
150
|
+
"abc",
|
|
151
|
+
{ :foo => "bar" },
|
|
152
|
+
:keyword_param => 2
|
|
153
|
+
)
|
|
154
|
+
end
|
|
136
155
|
|
|
137
156
|
context "when active" do
|
|
138
157
|
let(:transaction) { http_request_transaction }
|
|
@@ -149,7 +168,7 @@ describe Object do
|
|
|
149
168
|
expect(transaction).to receive(:start_event)
|
|
150
169
|
expect(transaction).to receive(:finish_event).with \
|
|
151
170
|
"bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
|
|
152
|
-
expect(
|
|
171
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
|
|
153
172
|
end
|
|
154
173
|
end
|
|
155
174
|
|
|
@@ -238,10 +257,10 @@ describe Object do
|
|
|
238
257
|
context "when not active" do
|
|
239
258
|
let(:transaction) { Appsignal::Transaction.current }
|
|
240
259
|
|
|
241
|
-
it "
|
|
260
|
+
it "does not instrument, but still call the method" do
|
|
242
261
|
expect(Appsignal.active?).to be_falsy
|
|
243
262
|
expect(transaction).to_not receive(:start_event)
|
|
244
|
-
expect(
|
|
263
|
+
expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
|
|
245
264
|
end
|
|
246
265
|
end
|
|
247
266
|
end
|
|
@@ -23,6 +23,27 @@ RSpec.describe "Puma plugin" do
|
|
|
23
23
|
def self.stats
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def self.run
|
|
27
|
+
# Capture threads running before application is preloaded
|
|
28
|
+
before = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
|
29
|
+
|
|
30
|
+
# An abbreviated version of what happens in Puma::Cluster#run
|
|
31
|
+
launcher = MockPumaLauncher.new
|
|
32
|
+
plugin = Plugin.plugin.new
|
|
33
|
+
plugin.start(launcher)
|
|
34
|
+
launcher.events.on_booted.call
|
|
35
|
+
|
|
36
|
+
# Wait for minutely probe thread to finish starting
|
|
37
|
+
sleep 0.005
|
|
38
|
+
|
|
39
|
+
# Capture any new threads running after application is preloaded.
|
|
40
|
+
# Any threads created during the preloading phase will not be
|
|
41
|
+
# carried over into the forked workers. Puma warns about these
|
|
42
|
+
# but the minutely probe thread should only exist in the main process.
|
|
43
|
+
after = Thread.list.reject { |t| t.thread_variable_get(:fork_safe) }
|
|
44
|
+
$stdout.puts "! WARNING: Detected #{after.size - before.size} Thread(s) started in app boot" if after.size > before.size
|
|
45
|
+
end
|
|
46
|
+
|
|
26
47
|
class Plugin
|
|
27
48
|
class << self
|
|
28
49
|
attr_reader :plugin
|
|
@@ -68,6 +89,13 @@ RSpec.describe "Puma plugin" do
|
|
|
68
89
|
wait_for("enough probe calls") { probe.calls >= 2 }
|
|
69
90
|
end
|
|
70
91
|
|
|
92
|
+
it "marks the PumaProbe thread as fork-safe", :not_ruby19 do
|
|
93
|
+
out_stream = std_stream
|
|
94
|
+
capture_stdout(out_stream) { Puma.run }
|
|
95
|
+
|
|
96
|
+
expect(out_stream.read).not_to include("WARNING: Detected 1 Thread")
|
|
97
|
+
end
|
|
98
|
+
|
|
71
99
|
context "without Puma.stats" do
|
|
72
100
|
before { Puma.singleton_class.send(:remove_method, :stats) }
|
|
73
101
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: appsignal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.11.
|
|
4
|
+
version: 2.11.8
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Beekman
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2021-
|
|
13
|
+
date: 2021-02-18 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rack
|
|
@@ -247,6 +247,8 @@ files:
|
|
|
247
247
|
- lib/appsignal/integrations/grape.rb
|
|
248
248
|
- lib/appsignal/integrations/mongo_ruby_driver.rb
|
|
249
249
|
- lib/appsignal/integrations/object.rb
|
|
250
|
+
- lib/appsignal/integrations/object_ruby_19.rb
|
|
251
|
+
- lib/appsignal/integrations/object_ruby_modern.rb
|
|
250
252
|
- lib/appsignal/integrations/padrino.rb
|
|
251
253
|
- lib/appsignal/integrations/que.rb
|
|
252
254
|
- lib/appsignal/integrations/railtie.rb
|
|
@@ -336,6 +338,7 @@ files:
|
|
|
336
338
|
- spec/lib/appsignal/integrations/data_mapper_spec.rb
|
|
337
339
|
- spec/lib/appsignal/integrations/grape_spec.rb
|
|
338
340
|
- spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb
|
|
341
|
+
- spec/lib/appsignal/integrations/object_19_spec.rb
|
|
339
342
|
- spec/lib/appsignal/integrations/object_spec.rb
|
|
340
343
|
- spec/lib/appsignal/integrations/padrino_spec.rb
|
|
341
344
|
- spec/lib/appsignal/integrations/que_spec.rb
|
|
@@ -429,7 +432,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
429
432
|
- !ruby/object:Gem::Version
|
|
430
433
|
version: '0'
|
|
431
434
|
requirements: []
|
|
432
|
-
rubygems_version: 3.2.
|
|
435
|
+
rubygems_version: 3.2.8
|
|
433
436
|
signing_key:
|
|
434
437
|
specification_version: 4
|
|
435
438
|
summary: Logs performance and exception data from your app to appsignal.com
|
|
@@ -488,6 +491,7 @@ test_files:
|
|
|
488
491
|
- spec/lib/appsignal/integrations/data_mapper_spec.rb
|
|
489
492
|
- spec/lib/appsignal/integrations/grape_spec.rb
|
|
490
493
|
- spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb
|
|
494
|
+
- spec/lib/appsignal/integrations/object_19_spec.rb
|
|
491
495
|
- spec/lib/appsignal/integrations/object_spec.rb
|
|
492
496
|
- spec/lib/appsignal/integrations/padrino_spec.rb
|
|
493
497
|
- spec/lib/appsignal/integrations/que_spec.rb
|