appsignal 2.11.4-java → 2.11.9-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2437240f8298f3750115f38e730027652c282e9f259c651bd8111c3ca155bbd7
4
- data.tar.gz: 0b0e58c57c07ea20524c4f40a57ad3390834707e6f5319a20559b8285ff7d550
3
+ metadata.gz: df7fae0d072de1948a84fc6c546b15da9e76bfde32f70e28d4242ba2dbc98be2
4
+ data.tar.gz: '092acba4f404fafd21333649bdd65c190da7b31507172f7da91c9a56debe2c5a'
5
5
  SHA512:
6
- metadata.gz: 3e803aadead924e985168c693f8aaa7bd160febbd3bf371d5e4ea507449ce5e4ad2ce19be526ce56b118d39959b11cbdbadeb535e576f9a075b249be946999fa
7
- data.tar.gz: fa3192e852d1e6e77ab137c18d3c75ca2b67cd751d0de97cc1bacc8512e49eb76a2c01100a232c0dc6de1fd67a91b05f56d0a236104edab7321d44a110b47f4f
6
+ metadata.gz: e6f79d7c05a1504874bb7b86816d8c2c405e1499ff765839984db085d3b3bb26da03af7749c78e6a7013bf3afe52fc95badd66bc8787c5b7c0587f4d12b1ca70
7
+ data.tar.gz: 843ccd65902958efcd641d09d8c65e9ec55ac0a908ac16651a4016fb99329bfa6b1c903472a59182e21f56f83ac738e7dd0eb03d957bbaed4bba550b8a8ce76b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ # 2.11.9
4
+ - Fix and simplify Ruby method delegation for object method instrumentation in
5
+ the different Ruby versions. PR #706
6
+
7
+ # 2.11.8
8
+ - Mark minutely probe thread as fork-safe by @pixeltrix. PR #704
9
+
10
+ # 2.11.7
11
+ - Fix ActionCable integration in test environment using `stub_connection`.
12
+ PR #705
13
+
14
+ # 2.11.6
15
+ - Prepend Sidekiq middleware to wrap all Sidekiq middleware. Catches more
16
+ errors and provide more complete performance measurements. PR #698
17
+
18
+ # 2.11.5
19
+ - Add more detailed logging to finish_event calls when the event is unknown, so
20
+ we know what event is being tried to finish. Commit
21
+ c888a04d1b9ac947652b29c111c650fb5a5cf71c
22
+
3
23
  # 2.11.4
4
24
  - Support Ruby 3.0 for Object method instrumentation with keyword arguments
5
25
  (https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html)
data/ext/agent.yml CHANGED
@@ -1,62 +1,62 @@
1
1
  ---
2
- version: 361340a
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: 4e08cb0cef0ea7e30f8d507380b923f6cfa14adaea12c81804e118acd6395b57
9
+ checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
10
10
  filename: appsignal-x86_64-darwin-all-static.tar.gz
11
11
  dynamic:
12
- checksum: 1a9c3e26bd453fe60a2f511d536e64bdeddb1f939664bda90d6a41eaeedf5250
12
+ checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
13
13
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
14
14
  universal-darwin:
15
15
  static:
16
- checksum: 4e08cb0cef0ea7e30f8d507380b923f6cfa14adaea12c81804e118acd6395b57
16
+ checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
17
17
  filename: appsignal-x86_64-darwin-all-static.tar.gz
18
18
  dynamic:
19
- checksum: 1a9c3e26bd453fe60a2f511d536e64bdeddb1f939664bda90d6a41eaeedf5250
19
+ checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
20
20
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
21
21
  i686-linux:
22
22
  static:
23
- checksum: 01c027b3e472cb39d844284fcc8ba532628c00731b912e0e9718646ed124ae6e
23
+ checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
24
24
  filename: appsignal-i686-linux-all-static.tar.gz
25
25
  dynamic:
26
- checksum: 30696eac3ae5646bcb21ff86a1824dd4511a41dd323514e4a97a41b5ff09e2f8
26
+ checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
27
27
  filename: appsignal-i686-linux-all-dynamic.tar.gz
28
28
  x86-linux:
29
29
  static:
30
- checksum: 01c027b3e472cb39d844284fcc8ba532628c00731b912e0e9718646ed124ae6e
30
+ checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
31
31
  filename: appsignal-i686-linux-all-static.tar.gz
32
32
  dynamic:
33
- checksum: 30696eac3ae5646bcb21ff86a1824dd4511a41dd323514e4a97a41b5ff09e2f8
33
+ checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
34
34
  filename: appsignal-i686-linux-all-dynamic.tar.gz
35
35
  x86_64-linux:
36
36
  static:
37
- checksum: fe2038d6fa468fc23900fea6d8179d2b37d41d54f4ff33c573116183ac1cb491
37
+ checksum: d6c280e992d74f97d59da9827ec5707ca4f6776b0568cde1c083c1113e4b7104
38
38
  filename: appsignal-x86_64-linux-all-static.tar.gz
39
39
  dynamic:
40
- checksum: bcbf9546b078fb8bbaae5e7df4ed84831346c814bc865a47d32a3a095a84acbb
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: b2ee5579a62b76a1d2f41f4e06749c4c549f9aaa40764862eff49c5a6a8841f1
44
+ checksum: 9cf8ad34392662746a45cfce18113ad19cc29954789e2058f30e527163f2e98a
45
45
  filename: appsignal-x86_64-linux-musl-all-static.tar.gz
46
46
  dynamic:
47
- checksum: 53cd2464853e61c2d8f911ec32ff98cb48f824293ffd575da6743bc34625a7eb
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: f228dd2f2cf951c9eb9f04487be50fdfdc3d29a956e639787a4022bd73f02e53
51
+ checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
52
52
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
53
53
  dynamic:
54
- checksum: 030b322b2cb15607260cbb0424c03f6e41646dca7aacc43a30279ad63a336541
54
+ checksum: a1faae80ae09a6588c4cd35cb91dfa4b2176fc3cb17fbf79db80194c21e75bf9
55
55
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
56
56
  amd64-freebsd:
57
57
  static:
58
- checksum: f228dd2f2cf951c9eb9f04487be50fdfdc3d29a956e639787a4022bd73f02e53
58
+ checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
59
59
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
60
60
  dynamic:
61
- checksum: 030b322b2cb15607260cbb0424c03f6e41646dca7aacc43a30279ad63a336541
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
- env = channel.connection.env
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
- env = channel.connection.env
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.add Appsignal::Hooks::SidekiqPlugin
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
@@ -1,56 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
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"
15
10
  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
11
+ Appsignal.instrument name do
12
+ send "appsignal_uninstrumented_#{method_name}", *args, &block
27
13
  end
28
14
  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
15
+ if singleton_class.respond_to?(:ruby2_keywords, true)
16
+ singleton_class.send(:ruby2_keywords, method_name)
41
17
  end
18
+ end
42
19
 
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
20
+ def self.appsignal_instrument_method(method_name, options = {})
21
+ alias_method "appsignal_uninstrumented_#{method_name}", method_name
22
+ define_method method_name do |*args, &block|
23
+ name = options.fetch(:name) do
24
+ "#{method_name}.#{appsignal_reverse_class_name}.other"
25
+ end
26
+ Appsignal.instrument name do
27
+ send "appsignal_uninstrumented_#{method_name}", *args, &block
52
28
  end
53
29
  end
30
+ ruby2_keywords method_name if respond_to?(:ruby2_keywords, true)
54
31
  end
55
32
 
56
33
  def self.appsignal_reverse_class_name
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.4".freeze
4
+ VERSION = "2.11.9".freeze
5
5
  end
@@ -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 SidekiqMiddlewareMock < Set
20
- def exists?(middleware)
21
- include?(middleware)
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 ||= SidekiqMiddlewareMock.new
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
- it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
45
- described_class.new.install
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
- expect(Sidekiq.server_middleware.exists?(Appsignal::Hooks::SidekiqPlugin)).to be(true)
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
@@ -30,12 +30,57 @@ describe Object do
30
30
  before do
31
31
  Appsignal.config = project_fixture_config
32
32
  expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
33
+ expect(Appsignal.active?).to be_truthy
33
34
  end
34
35
  after { Appsignal.config = nil }
35
36
 
37
+ context "with different kind of arguments" do
38
+ let(:klass) do
39
+ Class.new do
40
+ def positional_arguments(param1, param2)
41
+ [param1, param2]
42
+ end
43
+ appsignal_instrument_method :positional_arguments
44
+
45
+ def positional_arguments_splat(*params)
46
+ params
47
+ end
48
+ appsignal_instrument_method :positional_arguments_splat
49
+
50
+ def keyword_arguments(a: nil, b: nil)
51
+ [a, b]
52
+ end
53
+ appsignal_instrument_method :keyword_arguments
54
+
55
+ def keyword_arguments_splat(**kwargs)
56
+ kwargs
57
+ end
58
+ appsignal_instrument_method :keyword_arguments_splat
59
+
60
+ def splat(*args, **kwargs)
61
+ [args, kwargs]
62
+ end
63
+ appsignal_instrument_method :splat
64
+ end
65
+ end
66
+
67
+ it "instruments the method and calls it" do
68
+ expect(instance.positional_arguments("abc", "def")).to eq(["abc", "def"])
69
+ expect(instance.positional_arguments_splat("abc", "def")).to eq(["abc", "def"])
70
+ expect(instance.keyword_arguments(:a => "a", :b => "b")).to eq(["a", "b"])
71
+ expect(instance.keyword_arguments_splat(:a => "a", :b => "b"))
72
+ .to eq(:a => "a", :b => "b")
73
+
74
+ expect(instance.splat).to eq([[], {}])
75
+ expect(instance.splat(:a => "a", :b => "b")).to eq([[], { :a => "a", :b => "b" }])
76
+ expect(instance.splat("abc", "def")).to eq([["abc", "def"], {}])
77
+ expect(instance.splat("abc", "def", :a => "a", :b => "b"))
78
+ .to eq([["abc", "def"], { :a => "a", :b => "b" }])
79
+ end
80
+ end
81
+
36
82
  context "with anonymous class" do
37
83
  it "instruments the method and calls it" do
38
- expect(Appsignal.active?).to be_truthy
39
84
  expect(transaction).to receive(:start_event)
40
85
  expect(transaction).to receive(:finish_event).with \
41
86
  "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
@@ -56,7 +101,6 @@ describe Object do
56
101
  let(:klass) { NamedClass }
57
102
 
58
103
  it "instruments the method and calls it" do
59
- expect(Appsignal.active?).to be_truthy
60
104
  expect(transaction).to receive(:start_event)
61
105
  expect(transaction).to receive(:finish_event).with \
62
106
  "foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
@@ -81,7 +125,6 @@ describe Object do
81
125
  let(:klass) { MyModule::NestedModule::NamedClass }
82
126
 
83
127
  it "instruments the method and calls it" do
84
- expect(Appsignal.active?).to be_truthy
85
128
  expect(transaction).to receive(:start_event)
86
129
  expect(transaction).to receive(:finish_event).with \
87
130
  "bar.NamedClass.NestedModule.MyModule.other", nil, nil,
@@ -101,7 +144,6 @@ describe Object do
101
144
  end
102
145
 
103
146
  it "instruments with custom name" do
104
- expect(Appsignal.active?).to be_truthy
105
147
  expect(transaction).to receive(:start_event)
106
148
  expect(transaction).to receive(:finish_event).with \
107
149
  "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
@@ -162,6 +204,51 @@ describe Object do
162
204
  end
163
205
  after { Appsignal.config = nil }
164
206
 
207
+ context "with different kind of arguments" do
208
+ let(:klass) do
209
+ Class.new do
210
+ def self.positional_arguments(param1, param2)
211
+ [param1, param2]
212
+ end
213
+ appsignal_instrument_class_method :positional_arguments
214
+
215
+ def self.positional_arguments_splat(*params)
216
+ params
217
+ end
218
+ appsignal_instrument_class_method :positional_arguments_splat
219
+
220
+ def self.keyword_arguments(a: nil, b: nil)
221
+ [a, b]
222
+ end
223
+ appsignal_instrument_class_method :keyword_arguments
224
+
225
+ def self.keyword_arguments_splat(**kwargs)
226
+ kwargs
227
+ end
228
+ appsignal_instrument_class_method :keyword_arguments_splat
229
+
230
+ def self.splat(*args, **kwargs)
231
+ [args, kwargs]
232
+ end
233
+ appsignal_instrument_class_method :splat
234
+ end
235
+ end
236
+
237
+ it "instruments the method and calls it" do
238
+ expect(klass.positional_arguments("abc", "def")).to eq(["abc", "def"])
239
+ expect(klass.positional_arguments_splat("abc", "def")).to eq(["abc", "def"])
240
+ expect(klass.keyword_arguments(:a => "a", :b => "b")).to eq(["a", "b"])
241
+ expect(klass.keyword_arguments_splat(:a => "a", :b => "b"))
242
+ .to eq(:a => "a", :b => "b")
243
+
244
+ expect(klass.splat).to eq([[], {}])
245
+ expect(klass.splat(:a => "a", :b => "b")).to eq([[], { :a => "a", :b => "b" }])
246
+ expect(klass.splat("abc", "def")).to eq([["abc", "def"], {}])
247
+ expect(klass.splat("abc", "def", :a => "a", :b => "b"))
248
+ .to eq([["abc", "def"], { :a => "a", :b => "b" }])
249
+ end
250
+ end
251
+
165
252
  context "with anonymous class" do
166
253
  it "instruments the method and calls it" do
167
254
  expect(Appsignal.active?).to be_truthy
@@ -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
4
+ version: 2.11.9
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-01-21 00:00:00.000000000 Z
13
+ date: 2021-02-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -432,7 +432,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
432
432
  - !ruby/object:Gem::Version
433
433
  version: '0'
434
434
  requirements: []
435
- rubygems_version: 3.2.6
435
+ rubygems_version: 3.2.8
436
436
  signing_key:
437
437
  specification_version: 4
438
438
  summary: Logs performance and exception data from your app to appsignal.com