appsignal 3.1.3 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16fff6fc92481e9164ad7611fdb1de6d0ebee1d28906ebc95d7fe10100d69cfd
4
- data.tar.gz: 790ec312c3349ee2b6889d8fca238e6cea7daa537c71cf81b7438152c2a2a2cb
3
+ metadata.gz: b9c72c3c1f23e52406ea915fe3dd35cf45648c7dd712cebc8cdf908e304be60e
4
+ data.tar.gz: 90ddbe20ee5243338aefdfbefbb1989bbe1df5cc57a56a4f277bcfa789086b41
5
5
  SHA512:
6
- metadata.gz: c20ef4e52d38822a25880f8be5d88ea1f703b7c165813cdcd3d959383c03ff668f47a9c0bab41fe904eb8bf746911d4eb445f90a1804227847172bdc45e80c38
7
- data.tar.gz: c5705bfeca7ec423ee1888572f84ef70fd15abdb5501cd0c9e73c2d5855b498d7e03ac8c50ab1aa88137d3335376cd2d0b767503c0ec17d9b2c7b209cd04e80f
6
+ metadata.gz: 3f7aaf99cae0ca17beab0064e259c8a2cbe1f95b1bcaabc9a074b3033ead5fc847d714d2e84b0140874ef1efc57c098ad3c46596be87f09ead2a5f2d148d77ea
7
+ data.tar.gz: bfe585b6c6ce5f8cc5e470c9a164e6690fc1834e40bb4bd27491f719cbdaf6ebd4b92d1f1ba2bc3001e157f5c35786f12143b29d9908e9ce73d9ca9712c7c5ef
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.1.5
4
+
5
+ ### Changed
6
+
7
+ - [4035c3c2](https://github.com/appsignal/appsignal-ruby/commit/4035c3c2d5c0b002119054014daddd193bd820f0) patch - Bump agent to version 813a59b
8
+
9
+ - Fix http proxy config option parsing for port 80.
10
+ - Fix the return value for appsignal_import_opentelemetry_span extension
11
+ function in `appsignal.h`.
12
+
13
+ ### Fixed
14
+
15
+ - [feb60fb8](https://github.com/appsignal/appsignal-ruby/commit/feb60fb877a2b264e587fe3d5d546e40d86c9c38) patch - Fix NoMethodError for AppSignal Puma plugin for Puma 6. Puma 5 is also still supported.
16
+
17
+ ## 3.1.4
18
+
19
+ ### Added
20
+
21
+ - [ffe49cfe](https://github.com/appsignal/appsignal-ruby/commit/ffe49cfe94f5269e59d6f168a73114f7a3914f79) patch - Support temporarily disabling GC profiling without reporting inaccurate `gc_time` metric durations. The MRI probe's `gc_time` will not report any value when the `GC::Profiler.enabled?` returns `false`.
22
+
23
+ ### Changed
24
+
25
+ - [af7e666c](https://github.com/appsignal/appsignal-ruby/commit/af7e666cf173ec1f42e9cf3fce2ab6c8e658440c) patch - Listen if the Ruby Garbage Collection profiler is enabled and collect how long the GC is running for the Ruby VM magic dashboard. An app will need to call `GC::Profiler.enable` to enable the GC profiler. Do not enable this in production environments, or at least not for long, because this can negatively impact performance of apps.
26
+
27
+ ### Fixed
28
+
29
+ - [b3a163be](https://github.com/appsignal/appsignal-ruby/commit/b3a163be154796e1f358c5061eaee99845c960ee) patch - Fix the MRI probe using the Garbage Collection profiler instead of the NilProfiler when garbage collection instrumentation is not enabled for MRI probe. This caused unnecessary overhead.
30
+
3
31
  ## 3.1.3
4
32
 
5
33
  ### Added
data/ext/agent.yml CHANGED
@@ -3,99 +3,99 @@
3
3
  # appsignal-agent repository.
4
4
  # Modifications to this file will be overwritten with the next agent release.
5
5
  ---
6
- version: '06391fb'
6
+ version: 813a59b
7
7
  mirrors:
8
8
  - https://appsignal-agent-releases.global.ssl.fastly.net
9
9
  - https://d135dj0rjqvssy.cloudfront.net
10
10
  triples:
11
11
  x86_64-darwin:
12
12
  static:
13
- checksum: 9bf41c183d94c80e980f57ea2e29d08bae97e8097b5284a2b91a5484bf866f8c
13
+ checksum: c8919a19a28950f726221829ea4d2b3312f1595a5e28ea134f8c41ed0814d7cd
14
14
  filename: appsignal-x86_64-darwin-all-static.tar.gz
15
15
  dynamic:
16
- checksum: 4d3789e65cf00e446600e883d95d097323ebb3835703c67c8d09f434f09ab496
16
+ checksum: 1c10139bc4df56048a71766b64eba76462ee38d3cc814dc230e0fdb3c7e4fba3
17
17
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
18
18
  universal-darwin:
19
19
  static:
20
- checksum: 9bf41c183d94c80e980f57ea2e29d08bae97e8097b5284a2b91a5484bf866f8c
20
+ checksum: c8919a19a28950f726221829ea4d2b3312f1595a5e28ea134f8c41ed0814d7cd
21
21
  filename: appsignal-x86_64-darwin-all-static.tar.gz
22
22
  dynamic:
23
- checksum: 4d3789e65cf00e446600e883d95d097323ebb3835703c67c8d09f434f09ab496
23
+ checksum: 1c10139bc4df56048a71766b64eba76462ee38d3cc814dc230e0fdb3c7e4fba3
24
24
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
25
25
  aarch64-darwin:
26
26
  static:
27
- checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
27
+ checksum: 40a38896132f418362af9fb2e9796eb4479e13cc0691b61f0f0b81b77e66ded6
28
28
  filename: appsignal-aarch64-darwin-all-static.tar.gz
29
29
  dynamic:
30
- checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
30
+ checksum: 40ec0c7db246cfc9b8eeebc882b07ba625948f376a53d8e24add7148d0f8c067
31
31
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
32
32
  arm64-darwin:
33
33
  static:
34
- checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
34
+ checksum: 40a38896132f418362af9fb2e9796eb4479e13cc0691b61f0f0b81b77e66ded6
35
35
  filename: appsignal-aarch64-darwin-all-static.tar.gz
36
36
  dynamic:
37
- checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
37
+ checksum: 40ec0c7db246cfc9b8eeebc882b07ba625948f376a53d8e24add7148d0f8c067
38
38
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
39
39
  arm-darwin:
40
40
  static:
41
- checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
41
+ checksum: 40a38896132f418362af9fb2e9796eb4479e13cc0691b61f0f0b81b77e66ded6
42
42
  filename: appsignal-aarch64-darwin-all-static.tar.gz
43
43
  dynamic:
44
- checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
44
+ checksum: 40ec0c7db246cfc9b8eeebc882b07ba625948f376a53d8e24add7148d0f8c067
45
45
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
46
46
  aarch64-linux:
47
47
  static:
48
- checksum: 0f2430e637eb77ce2093f021777087e87cb1e7be7c86a53771172696791c4879
48
+ checksum: c73b6e9de849a40290a0d90eaad43ea41a9a0293ba4b8bf99f69965c45c85514
49
49
  filename: appsignal-aarch64-linux-all-static.tar.gz
50
50
  dynamic:
51
- checksum: 0e4f9305aeaaa2d7847e83be04227b865723a0591574108d78040b5921a677a7
51
+ checksum: 90226eefe2e2f66833ca3e31c69ce70763ed57916bd0b5c1809bd99d61ff3429
52
52
  filename: appsignal-aarch64-linux-all-dynamic.tar.gz
53
53
  i686-linux:
54
54
  static:
55
- checksum: 449ba623aaa1853c2d211bf1e2d3a14e5ae09225a62457cbdbcc0983a5713a52
55
+ checksum: 6741b9a068dc405b3d6d07953fab7fc876c21b4add1cbb2b4c4c4dfdeca5d387
56
56
  filename: appsignal-i686-linux-all-static.tar.gz
57
57
  dynamic:
58
- checksum: dae994292d602eaf0910bd2ce53f0163e19767a4cbb8e5d0db99c0010d6df486
58
+ checksum: 1af902b37af378a06251365fb637f86298380d3627c54f2945a85c1b7f075fda
59
59
  filename: appsignal-i686-linux-all-dynamic.tar.gz
60
60
  x86-linux:
61
61
  static:
62
- checksum: 449ba623aaa1853c2d211bf1e2d3a14e5ae09225a62457cbdbcc0983a5713a52
62
+ checksum: 6741b9a068dc405b3d6d07953fab7fc876c21b4add1cbb2b4c4c4dfdeca5d387
63
63
  filename: appsignal-i686-linux-all-static.tar.gz
64
64
  dynamic:
65
- checksum: dae994292d602eaf0910bd2ce53f0163e19767a4cbb8e5d0db99c0010d6df486
65
+ checksum: 1af902b37af378a06251365fb637f86298380d3627c54f2945a85c1b7f075fda
66
66
  filename: appsignal-i686-linux-all-dynamic.tar.gz
67
67
  x86_64-linux:
68
68
  static:
69
- checksum: 394796c0ddeb4881c9f2e6ce82f840e66bcb69e027324f6c04f6671067445fbb
69
+ checksum: 8355b017093db606014023cc617d84d6375d503d7ffa54f62c7b3dc56fb64ead
70
70
  filename: appsignal-x86_64-linux-all-static.tar.gz
71
71
  dynamic:
72
- checksum: 9ca4762c464482b0a5a89898a839388597dd57a17a21527a67f3e3db0e540a03
72
+ checksum: 7c239a7ffe18cb173120bd67fb96563f4a81f0744bbbb47082f077a38ccbe5f1
73
73
  filename: appsignal-x86_64-linux-all-dynamic.tar.gz
74
74
  x86_64-linux-musl:
75
75
  static:
76
- checksum: 673271c8c5fd55053d8a719bcd307f787db4ca4633baf8cf961c442bf1805614
76
+ checksum: e9d98ed23b872dbf1e67a081473918cf88c4af775b1caadbfd93deda2635d9f8
77
77
  filename: appsignal-x86_64-linux-musl-all-static.tar.gz
78
78
  dynamic:
79
- checksum: 609d59376d6633652015e838eb649229fe2523d443a5471232b869f48eb99640
79
+ checksum: 5538172a95dfca1a4cf8e111ba61eab5e9c16314fa902259711cb9e8e0d2f85e
80
80
  filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
81
81
  aarch64-linux-musl:
82
82
  static:
83
- checksum: e90ca19bf61596be022ba04897e8902b3401add58f351a40a3d3a7af241d0bbb
83
+ checksum: 0fb3eacfb8c8bc01c4acc8916327626720de376bcdd95104be71bb11a4ff9215
84
84
  filename: appsignal-aarch64-linux-musl-all-static.tar.gz
85
85
  dynamic:
86
- checksum: afb66c65fb82b672887bc6b6e82d82f09d9855a5497a7abb06b438dadea97aca
86
+ checksum: b87bcedaa2aa886acf3a93ce6e32762a843f3bee1ca7a8f9e0d17ca32f7a7d39
87
87
  filename: appsignal-aarch64-linux-musl-all-dynamic.tar.gz
88
88
  x86_64-freebsd:
89
89
  static:
90
- checksum: cb45da91c51123859e5ef5cea850460c28d6e77dfa08b90375178d9017162ba8
90
+ checksum: b7d3c244b7068213840f5970df2e318d98f7909eb3b2b4ab42441d064ffb19ee
91
91
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
92
92
  dynamic:
93
- checksum: 6a03e02c2526e05edaa7fa932b2e764318c63ec93d517c6c00f6b7541bfe71f3
93
+ checksum: 0a739134f11d50318d14f247df1f8cc0f8aec1fbcb70a8bc48d5e1f22dc4aaba
94
94
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
95
95
  amd64-freebsd:
96
96
  static:
97
- checksum: cb45da91c51123859e5ef5cea850460c28d6e77dfa08b90375178d9017162ba8
97
+ checksum: b7d3c244b7068213840f5970df2e318d98f7909eb3b2b4ab42441d064ffb19ee
98
98
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
99
99
  dynamic:
100
- checksum: 6a03e02c2526e05edaa7fa932b2e764318c63ec93d517c6c00f6b7541bfe71f3
100
+ checksum: 0a739134f11d50318d14f247df1f8cc0f8aec1fbcb70a8bc48d5e1f22dc4aaba
101
101
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
@@ -1,6 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'padrino', "~> 0.15"
4
- gem 'rack'
4
+ gem 'rack', "~> 2"
5
+ gem 'sinatra', "~> 2"
5
6
 
6
7
  gemspec :path => '../'
data/gemfiles/que.gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'que'
3
+ gem 'que', "~> 1.0"
4
4
 
5
5
  gemspec :path => '../'
@@ -15,7 +15,6 @@ module Appsignal
15
15
  :debug => false,
16
16
  :dns_servers => [],
17
17
  :enable_allocation_tracking => true,
18
- :enable_gc_instrumentation => false,
19
18
  :enable_host_metrics => true,
20
19
  :enable_minutely_probes => true,
21
20
  :enable_statsd => true,
@@ -63,7 +62,6 @@ module Appsignal
63
62
  "APPSIGNAL_DEBUG" => :debug,
64
63
  "APPSIGNAL_DNS_SERVERS" => :dns_servers,
65
64
  "APPSIGNAL_ENABLE_ALLOCATION_TRACKING" => :enable_allocation_tracking,
66
- "APPSIGNAL_ENABLE_GC_INSTRUMENTATION" => :enable_gc_instrumentation,
67
65
  "APPSIGNAL_ENABLE_HOST_METRICS" => :enable_host_metrics,
68
66
  "APPSIGNAL_ENABLE_MINUTELY_PROBES" => :enable_minutely_probes,
69
67
  "APPSIGNAL_ENABLE_STATSD" => :enable_statsd,
@@ -114,7 +112,6 @@ module Appsignal
114
112
  APPSIGNAL_ACTIVE
115
113
  APPSIGNAL_DEBUG
116
114
  APPSIGNAL_ENABLE_ALLOCATION_TRACKING
117
- APPSIGNAL_ENABLE_GC_INSTRUMENTATION
118
115
  APPSIGNAL_ENABLE_HOST_METRICS
119
116
  APPSIGNAL_ENABLE_MINUTELY_PROBES
120
117
  APPSIGNAL_ENABLE_STATSD
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module GarbageCollection
6
+ # Return the GC profiler wrapper.
7
+ #
8
+ # Returns {Profiler} if the Ruby Garbage Collection profiler is enabled.
9
+ # This is checked by calling `GC::Profiler.enabled?`.
10
+ #
11
+ # GC profiling is disabled by default due to the overhead it causes. Do not
12
+ # enable this in production for long periods of time.
13
+ def self.profiler
14
+ # Cached instances so it doesn't create a new object every time this
15
+ # method is called. Especially necessary for the {Profiler} because a new
16
+ # instance will have a new internal time counter.
17
+ @real_profiler ||= Profiler.new
18
+ @nil_profiler ||= NilProfiler.new
19
+
20
+ enabled? ? @real_profiler : @nil_profiler
21
+ end
22
+
23
+ # Check if Garbage Collection is enabled at the moment.
24
+ #
25
+ # @return [Boolean]
26
+ def self.enabled?
27
+ GC::Profiler.enabled?
28
+ end
29
+
30
+ # Unset the currently cached profilers.
31
+ #
32
+ # @return [void]
33
+ def self.clear_profiler!
34
+ @real_profiler = nil
35
+ @nil_profiler = nil
36
+ end
37
+
38
+ # A wrapper around Ruby's `GC::Profiler` that tracks garbage collection
39
+ # time, while clearing `GC::Profiler`'s total_time to make sure it doesn't
40
+ # leak memory by keeping garbage collection run samples in memory.
41
+ class Profiler
42
+ def self.lock
43
+ @lock ||= Mutex.new
44
+ end
45
+
46
+ def initialize
47
+ @total_time = 0
48
+ end
49
+
50
+ # Whenever {#total_time} is called, the current `GC::Profiler#total_time`
51
+ # gets added to `@total_time`, after which `GC::Profiler.clear` is called
52
+ # to prevent it from leaking memory. A class-level lock is used to make
53
+ # sure garbage collection time is never counted more than once.
54
+ #
55
+ # Whenever `@total_time` gets above two billion milliseconds (about 23
56
+ # days), it's reset to make sure the result fits in a signed 32-bit
57
+ # integer.
58
+ #
59
+ # @return [Integer]
60
+ def total_time
61
+ lock.synchronize do
62
+ @total_time += (internal_profiler.total_time * 1000).round
63
+ internal_profiler.clear
64
+ end
65
+
66
+ @total_time = 0 if @total_time > 2_000_000_000
67
+
68
+ @total_time
69
+ end
70
+
71
+ private
72
+
73
+ def internal_profiler
74
+ GC::Profiler
75
+ end
76
+
77
+ def lock
78
+ self.class.lock
79
+ end
80
+ end
81
+
82
+ # A dummy profiler that always returns 0 as the total time. Used when GC
83
+ # profiler is disabled.
84
+ class NilProfiler
85
+ def total_time
86
+ 0
87
+ end
88
+ end
89
+ end
90
+ end
@@ -8,7 +8,7 @@ module Appsignal
8
8
  defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
9
9
  end
10
10
 
11
- def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollectionProfiler.new)
11
+ def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollection.profiler)
12
12
  Appsignal.logger.debug("Initializing VM probe")
13
13
  @appsignal = appsignal
14
14
  @gc_profiler = gc_profiler
@@ -31,8 +31,10 @@ module Appsignal
31
31
  )
32
32
 
33
33
  set_gauge("thread_count", Thread.list.size)
34
- gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
35
- set_gauge("gc_time", gc_time) if gc_time > 0
34
+ if Appsignal::GarbageCollection.enabled?
35
+ gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
36
+ set_gauge("gc_time", gc_time) if gc_time > 0
37
+ end
36
38
  end
37
39
 
38
40
  gc_stats = GC.stat
@@ -66,11 +66,6 @@ module Appsignal
66
66
  def clear_current_transaction!
67
67
  Thread.current[:appsignal_transaction] = nil
68
68
  end
69
-
70
- def garbage_collection_profiler
71
- @garbage_collection_profiler ||=
72
- Appsignal.config[:enable_gc_instrumentation] ? Appsignal::GarbageCollectionProfiler.new : NilGarbageCollectionProfiler.new
73
- end
74
69
  end
75
70
 
76
71
  attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded, :breadcrumbs
@@ -103,7 +98,7 @@ module Appsignal
103
98
  @ext = Appsignal::Extension.start_transaction(
104
99
  @transaction_id,
105
100
  @namespace,
106
- self.class.garbage_collection_profiler.total_time
101
+ 0
107
102
  ) || Appsignal::Extension::MockTransaction.new
108
103
  end
109
104
 
@@ -117,9 +112,7 @@ module Appsignal
117
112
  "because it was manually discarded."
118
113
  return
119
114
  end
120
- if @ext.finish(self.class.garbage_collection_profiler.total_time)
121
- sample_data
122
- end
115
+ sample_data if @ext.finish(0)
123
116
  @ext.complete
124
117
  end
125
118
 
@@ -355,7 +348,7 @@ module Appsignal
355
348
 
356
349
  def start_event
357
350
  return if paused?
358
- @ext.start_event(self.class.garbage_collection_profiler.total_time)
351
+ @ext.start_event(0)
359
352
  end
360
353
 
361
354
  def finish_event(name, title, body, body_format = Appsignal::EventFormatter::DEFAULT)
@@ -365,7 +358,7 @@ module Appsignal
365
358
  title || BLANK,
366
359
  body || BLANK,
367
360
  body_format || Appsignal::EventFormatter::DEFAULT,
368
- self.class.garbage_collection_profiler.total_time
361
+ 0
369
362
  )
370
363
  end
371
364
 
@@ -377,7 +370,7 @@ module Appsignal
377
370
  body || BLANK,
378
371
  body_format || Appsignal::EventFormatter::DEFAULT,
379
372
  duration,
380
- self.class.garbage_collection_profiler.total_time
373
+ 0
381
374
  )
382
375
  end
383
376
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.1.3".freeze
4
+ VERSION = "3.1.5".freeze
5
5
  end
data/lib/appsignal.rb CHANGED
@@ -115,11 +115,6 @@ module Appsignal
115
115
  Appsignal::Environment.report_enabled("allocation_tracking")
116
116
  end
117
117
 
118
- if config[:enable_gc_instrumentation]
119
- GC::Profiler.enable
120
- Appsignal::Environment.report_enabled("gc_instrumentation")
121
- end
122
-
123
118
  Appsignal::Minutely.start if config[:enable_minutely_probes]
124
119
 
125
120
  collect_environment_metadata
@@ -298,7 +293,7 @@ require "appsignal/hooks"
298
293
  require "appsignal/probes"
299
294
  require "appsignal/marker"
300
295
  require "appsignal/minutely"
301
- require "appsignal/garbage_collection_profiler"
296
+ require "appsignal/garbage_collection"
302
297
  require "appsignal/integrations/railtie" if defined?(::Rails)
303
298
  require "appsignal/transaction"
304
299
  require "appsignal/version"
@@ -11,9 +11,9 @@ require "json"
11
11
  Puma::Plugin.create do # rubocop:disable Metrics/BlockLength
12
12
  def start(launcher)
13
13
  @launcher = launcher
14
- @launcher.events.debug "AppSignal: Puma plugin start."
14
+ log_debug "AppSignal: Puma plugin start."
15
15
  in_background do
16
- @launcher.events.debug "AppSignal: Start Puma stats collection loop."
16
+ log_debug "AppSignal: Start Puma stats collection loop."
17
17
  plugin = AppsignalPumaPlugin.new
18
18
 
19
19
  loop do
@@ -24,12 +24,12 @@ Puma::Plugin.create do # rubocop:disable Metrics/BlockLength
24
24
  # metrics.
25
25
  sleep sleep_time
26
26
 
27
- @launcher.events.debug "AppSignal: Collecting Puma stats."
27
+ log_debug "AppSignal: Collecting Puma stats."
28
28
  stats = fetch_puma_stats
29
29
  if stats
30
30
  plugin.call(stats)
31
31
  else
32
- @launcher.events.log "AppSignal: No Puma stats to report."
32
+ log_debug "AppSignal: No Puma stats to report."
33
33
  end
34
34
  rescue StandardError => error
35
35
  log_error "Error while processing metrics.", error
@@ -44,8 +44,20 @@ Puma::Plugin.create do # rubocop:disable Metrics/BlockLength
44
44
  60 # seconds
45
45
  end
46
46
 
47
+ def logger
48
+ if @launcher.respond_to? :log_writer
49
+ @launcher.log_writer
50
+ else
51
+ @launcher.events
52
+ end
53
+ end
54
+
55
+ def log_debug(message)
56
+ logger.debug message
57
+ end
58
+
47
59
  def log_error(message, error)
48
- @launcher.events.log "AppSignal: #{message}\n" \
60
+ logger.error "AppSignal: #{message}\n" \
49
61
  "#{error.class}: #{error.message}\n#{error.backtrace.join("\n")}"
50
62
  end
51
63
 
@@ -156,7 +156,6 @@ describe Appsignal::Config do
156
156
  :debug => false,
157
157
  :dns_servers => [],
158
158
  :enable_allocation_tracking => true,
159
- :enable_gc_instrumentation => false,
160
159
  :enable_host_metrics => true,
161
160
  :enable_minutely_probes => true,
162
161
  :enable_statsd => true,
@@ -1,11 +1,33 @@
1
- describe Appsignal::GarbageCollectionProfiler do
1
+ describe Appsignal::GarbageCollection do
2
+ describe ".profiler" do
3
+ before do
4
+ # Unset the internal memoized variable to avoid state leaking
5
+ described_class.clear_profiler!
6
+ end
7
+
8
+ context "when GC instrumentation is disabled" do
9
+ it "returns the NilProfiler" do
10
+ expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::NilProfiler)
11
+ end
12
+ end
13
+
14
+ context "when GC profiling is enabled" do
15
+ before { GC::Profiler.enable }
16
+ after { GC::Profiler.disable }
17
+
18
+ it "returns the Profiler" do
19
+ expect(described_class.profiler).to be_a(Appsignal::GarbageCollection::Profiler)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe Appsignal::GarbageCollection::Profiler do
2
26
  let(:internal_profiler) { FakeGCProfiler.new }
3
27
  let(:profiler) { described_class.new }
4
28
 
5
29
  before do
6
- allow_any_instance_of(described_class)
7
- .to receive(:internal_profiler)
8
- .and_return(internal_profiler)
30
+ stub_const("GC::Profiler", internal_profiler)
9
31
  end
10
32
 
11
33
  context "on initialization" do
@@ -54,7 +76,7 @@ describe Appsignal::GarbageCollectionProfiler do
54
76
 
55
77
  2.times do
56
78
  threads << Thread.new do
57
- profiler = Appsignal::GarbageCollectionProfiler.new
79
+ profiler = Appsignal::GarbageCollection::Profiler.new
58
80
  results << profiler.total_time
59
81
  end
60
82
  end
@@ -65,7 +87,7 @@ describe Appsignal::GarbageCollectionProfiler do
65
87
  end
66
88
  end
67
89
 
68
- describe Appsignal::NilGarbageCollectionProfiler do
90
+ describe Appsignal::GarbageCollection::NilProfiler do
69
91
  let(:profiler) { described_class.new }
70
92
 
71
93
  describe "#total_time" do
@@ -39,6 +39,7 @@ describe Appsignal::Probes::MriProbe do
39
39
  let(:hostname) { nil }
40
40
  before do
41
41
  allow(gc_profiler_mock).to receive(:total_time)
42
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
42
43
  end
43
44
 
44
45
  it "should track vm metrics" do
@@ -70,6 +71,40 @@ describe Appsignal::Probes::MriProbe do
70
71
  end
71
72
  end
72
73
 
74
+ context "when GC profiling is disabled" do
75
+ it "does not report a gc_time metric" do
76
+ allow(GC::Profiler).to receive(:enabled?).and_return(false)
77
+ expect(gc_profiler_mock).to_not receive(:total_time)
78
+ probe.call # Normal call, create a cache
79
+ probe.call # Report delta value based on cached value
80
+ metrics = appsignal_mock.gauges.map { |(key)| key }
81
+ expect(metrics).to_not include("gc_time")
82
+ end
83
+
84
+ it "does not report a gc_time metric while temporarily disabled" do
85
+ # While enabled
86
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
87
+ expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15)
88
+ probe.call # Normal call, create a cache
89
+ probe.call # Report delta value based on cached value
90
+ expect_gauges([["gc_time", 5]])
91
+
92
+ # While disabled
93
+ allow(GC::Profiler).to receive(:enabled?).and_return(false)
94
+ probe.call # Call twice to make sure any caches resets wouldn't mess up the assertion
95
+ probe.call
96
+ # Does not include any newly reported metrics
97
+ expect_gauges([["gc_time", 5]])
98
+
99
+ # When enabled after being disabled for a while, it only reports the
100
+ # newly reported time since it was renabled
101
+ allow(GC::Profiler).to receive(:enabled?).and_return(true)
102
+ expect(gc_profiler_mock).to receive(:total_time).and_return(25)
103
+ probe.call
104
+ expect_gauges([["gc_time", 5], ["gc_time", 10]])
105
+ end
106
+ end
107
+
73
108
  it "tracks GC run count" do
74
109
  expect(GC).to receive(:count).and_return(10, 15)
75
110
  expect(GC).to receive(:stat).and_return(
@@ -683,43 +683,36 @@ describe Appsignal::Transaction do
683
683
  end
684
684
 
685
685
  describe "#sample_data" do
686
- it "should sample data" do
687
- expect(transaction.ext).to receive(:set_sample_data).with(
688
- "environment",
689
- Appsignal::Utils::Data.generate(
690
- "CONTENT_LENGTH" => "0",
691
- "REQUEST_METHOD" => "GET",
692
- "SERVER_NAME" => "example.org",
693
- "SERVER_PORT" => "80",
694
- "PATH_INFO" => "/blog"
695
- )
696
- ).once
697
- expect(transaction.ext).to receive(:set_sample_data).with(
698
- "session_data",
699
- Appsignal::Utils::Data.generate({})
700
- ).once
701
- expect(transaction.ext).to receive(:set_sample_data).with(
702
- "params",
703
- Appsignal::Utils::Data.generate(
704
- "controller" => "blog_posts",
705
- "action" => "show",
706
- "id" => "1"
707
- )
708
- ).once
709
- expect(transaction.ext).to receive(:set_sample_data).with(
710
- "metadata",
711
- Appsignal::Utils::Data.generate("key" => "value")
712
- ).once
713
- expect(transaction.ext).to receive(:set_sample_data).with(
714
- "tags",
715
- Appsignal::Utils::Data.generate({})
716
- ).once
717
- expect(transaction.ext).to receive(:set_sample_data).with(
718
- "breadcrumbs",
719
- Appsignal::Utils::Data.generate([])
720
- ).once
686
+ let(:env) { { "rack.session" => { "session" => "value" } } }
721
687
 
688
+ it "sets sample data" do
689
+ transaction.set_tags "tag" => "value"
690
+ transaction.add_breadcrumb "category", "action", "message", "key" => "value"
722
691
  transaction.sample_data
692
+
693
+ sample_data = transaction.to_h["sample_data"]
694
+ expect(sample_data["environment"]).to include(
695
+ "CONTENT_LENGTH" => "0",
696
+ "REQUEST_METHOD" => "GET",
697
+ "SERVER_NAME" => "example.org",
698
+ "SERVER_PORT" => "80",
699
+ "PATH_INFO" => "/blog"
700
+ )
701
+ expect(sample_data["session_data"]).to eq("session" => "value")
702
+ expect(sample_data["params"]).to eq(
703
+ "controller" => "blog_posts",
704
+ "action" => "show",
705
+ "id" => "1"
706
+ )
707
+ expect(sample_data["metadata"]).to eq("key" => "value")
708
+ expect(sample_data["tags"]).to eq("tag" => "value")
709
+ expect(sample_data["breadcrumbs"]).to contain_exactly(
710
+ "action" => "action",
711
+ "category" => "category",
712
+ "message" => "message",
713
+ "metadata" => { "key" => "value" },
714
+ "time" => kind_of(Integer)
715
+ )
723
716
  end
724
717
  end
725
718
 
@@ -792,23 +785,6 @@ describe Appsignal::Transaction do
792
785
  end
793
786
  end
794
787
 
795
- describe "#garbage_collection_profiler" do
796
- before { Appsignal::Transaction.instance_variable_set(:@garbage_collection_profiler, nil) }
797
-
798
- it "returns the NilGarbageCollectionProfiler" do
799
- expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::NilGarbageCollectionProfiler)
800
- end
801
-
802
- context "when gc profiling is enabled" do
803
- before { Appsignal.config.config_hash[:enable_gc_instrumentation] = true }
804
- after { Appsignal.config.config_hash[:enable_gc_instrumentation] = false }
805
-
806
- it "returns the GarbageCollectionProfiler" do
807
- expect(Appsignal::Transaction.garbage_collection_profiler).to be_a(Appsignal::GarbageCollectionProfiler)
808
- end
809
- end
810
- end
811
-
812
788
  describe "#start_event" do
813
789
  it "starts the event in the extension" do
814
790
  expect(transaction.ext).to receive(:start_event).with(0).and_call_original
@@ -827,11 +803,7 @@ describe Appsignal::Transaction do
827
803
  end
828
804
 
829
805
  describe "#finish_event" do
830
- let(:fake_gc_time) { 123 }
831
- before do
832
- expect(described_class.garbage_collection_profiler)
833
- .to receive(:total_time).at_least(:once).and_return(fake_gc_time)
834
- end
806
+ let(:fake_gc_time) { 0 }
835
807
 
836
808
  it "should finish the event in the extension" do
837
809
  expect(transaction.ext).to receive(:finish_event).with(
@@ -878,11 +850,7 @@ describe Appsignal::Transaction do
878
850
  end
879
851
 
880
852
  describe "#record_event" do
881
- let(:fake_gc_time) { 123 }
882
- before do
883
- expect(described_class.garbage_collection_profiler)
884
- .to receive(:total_time).at_least(:once).and_return(fake_gc_time)
885
- end
853
+ let(:fake_gc_time) { 0 }
886
854
 
887
855
  it "should record the event in the extension" do
888
856
  expect(transaction.ext).to receive(:record_event).with(
@@ -54,20 +54,12 @@ describe Appsignal do
54
54
  Appsignal.start
55
55
  end
56
56
 
57
- context "when allocation tracking and gc instrumentation have been enabled" do
57
+ context "when allocation tracking has been enabled" do
58
58
  before do
59
- allow(GC::Profiler).to receive(:enable)
60
59
  Appsignal.config.config_hash[:enable_allocation_tracking] = true
61
- Appsignal.config.config_hash[:enable_gc_instrumentation] = true
62
60
  capture_environment_metadata_report_calls
63
61
  end
64
62
 
65
- it "should enable Ruby's GC::Profiler" do
66
- expect(GC::Profiler).to receive(:enable)
67
- Appsignal.start
68
- expect_environment_metadata("ruby_gc_instrumentation_enabled", "true")
69
- end
70
-
71
63
  unless DependencyHelper.running_jruby?
72
64
  it "installs the allocation event hook" do
73
65
  expect(Appsignal::Extension).to receive(:install_allocation_event_hook)
@@ -78,29 +70,17 @@ describe Appsignal do
78
70
  end
79
71
  end
80
72
 
81
- context "when allocation tracking and gc instrumentation have been disabled" do
73
+ context "when allocation tracking has been disabled" do
82
74
  before do
83
75
  Appsignal.config.config_hash[:enable_allocation_tracking] = false
84
- Appsignal.config.config_hash[:enable_gc_instrumentation] = false
85
76
  capture_environment_metadata_report_calls
86
77
  end
87
78
 
88
- it "should not enable Ruby's GC::Profiler" do
89
- expect(GC::Profiler).not_to receive(:enable)
90
- Appsignal.start
91
- end
92
-
93
79
  it "should not install the allocation event hook" do
94
- expect(Appsignal::Minutely).not_to receive(:install_allocation_event_hook)
80
+ expect(Appsignal::Extension).not_to receive(:install_allocation_event_hook)
95
81
  Appsignal.start
96
82
  expect_not_environment_metadata("ruby_allocation_tracking_enabled")
97
83
  end
98
-
99
- it "should not add the gc probe to minutely" do
100
- expect(Appsignal::Minutely).not_to receive(:register_garbage_collection_probe)
101
- Appsignal.start
102
- expect_not_environment_metadata("ruby_gc_instrumentation_enabled")
103
- end
104
84
  end
105
85
 
106
86
  context "when minutely metrics has been enabled" do
@@ -2,14 +2,14 @@ RSpec.describe "Puma plugin" do
2
2
  include WaitForHelper
3
3
 
4
4
  class MockPumaLauncher
5
- def events
6
- return @events if defined?(@events)
5
+ def log_writer
6
+ return @log_writer if defined?(@log_writer)
7
7
 
8
- @events = MockPumaEvents.new
8
+ @log_writer = MockPumaLogWriter.new
9
9
  end
10
10
  end
11
11
 
12
- class MockPumaEvents
12
+ class MockPumaLogWriter
13
13
  attr_reader :logs
14
14
 
15
15
  def initialize
@@ -153,7 +153,7 @@ RSpec.describe "Puma plugin" do
153
153
  end
154
154
 
155
155
  def logs
156
- launcher.events.logs
156
+ launcher.log_writer.logs
157
157
  end
158
158
 
159
159
  def messages
@@ -281,7 +281,7 @@ RSpec.describe "Puma plugin" do
281
281
  it "does not fetch metrics" do
282
282
  run_plugin(appsignal_plugin) do
283
283
  expect(logs).to_not include([:error, kind_of(String)])
284
- expect(logs).to include([:log, "AppSignal: No Puma stats to report."])
284
+ expect(logs).to include([:debug, "AppSignal: No Puma stats to report."])
285
285
  expect(messages).to be_empty
286
286
  end
287
287
  end
@@ -296,4 +296,43 @@ RSpec.describe "Puma plugin" do
296
296
  end
297
297
  end
298
298
  end
299
+
300
+ context "with Puma < 6 Events class" do
301
+ class MockPumaEvents
302
+ attr_reader :logs
303
+
304
+ def initialize
305
+ @logs = []
306
+ end
307
+
308
+ def log(message)
309
+ @logs << [:log, message]
310
+ end
311
+
312
+ def debug(message)
313
+ @logs << [:debug, message]
314
+ end
315
+
316
+ def error(message)
317
+ @logs << [:error, message]
318
+ end
319
+ end
320
+
321
+ let(:launcher) do
322
+ Class.new do
323
+ def events
324
+ return @events if defined?(@events)
325
+
326
+ @events = MockPumaEvents.new
327
+ end
328
+ end.new
329
+ end
330
+ let(:stats_data) { { :max_threads => 5 } }
331
+
332
+ it "logs messages to the events class" do
333
+ run_plugin(appsignal_plugin) do
334
+ expect(launcher.events.logs).to_not be_empty
335
+ end
336
+ end
337
+ end
299
338
  end
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: 3.1.3
4
+ version: 3.1.5
5
5
  platform: ruby
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: 2022-08-04 00:00:00.000000000 Z
13
+ date: 2022-10-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -204,7 +204,7 @@ files:
204
204
  - lib/appsignal/event_formatter/sequel/sql_formatter.rb
205
205
  - lib/appsignal/extension.rb
206
206
  - lib/appsignal/extension/jruby.rb
207
- - lib/appsignal/garbage_collection_profiler.rb
207
+ - lib/appsignal/garbage_collection.rb
208
208
  - lib/appsignal/helpers/instrumentation.rb
209
209
  - lib/appsignal/helpers/metrics.rb
210
210
  - lib/appsignal/hooks.rb
@@ -305,7 +305,7 @@ files:
305
305
  - spec/lib/appsignal/extension/jruby_spec.rb
306
306
  - spec/lib/appsignal/extension_install_failure_spec.rb
307
307
  - spec/lib/appsignal/extension_spec.rb
308
- - spec/lib/appsignal/garbage_collection_profiler_spec.rb
308
+ - spec/lib/appsignal/garbage_collection_spec.rb
309
309
  - spec/lib/appsignal/hooks/action_cable_spec.rb
310
310
  - spec/lib/appsignal/hooks/action_mailer_spec.rb
311
311
  - spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
@@ -427,7 +427,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
427
427
  - !ruby/object:Gem::Version
428
428
  version: '0'
429
429
  requirements: []
430
- rubygems_version: 3.3.12
430
+ rubygems_version: 3.3.7
431
431
  signing_key:
432
432
  specification_version: 4
433
433
  summary: Logs performance and exception data from your app to appsignal.com
@@ -457,7 +457,7 @@ test_files:
457
457
  - spec/lib/appsignal/extension/jruby_spec.rb
458
458
  - spec/lib/appsignal/extension_install_failure_spec.rb
459
459
  - spec/lib/appsignal/extension_spec.rb
460
- - spec/lib/appsignal/garbage_collection_profiler_spec.rb
460
+ - spec/lib/appsignal/garbage_collection_spec.rb
461
461
  - spec/lib/appsignal/hooks/action_cable_spec.rb
462
462
  - spec/lib/appsignal/hooks/action_mailer_spec.rb
463
463
  - spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Appsignal
4
- # {Appsignal::GarbageCollectionProfiler} wraps Ruby's `GC::Profiler` to be
5
- # able to track garbage collection time for multiple transactions, while
6
- # constantly clearing `GC::Profiler`'s total_time to make sure it doesn't
7
- # leak memory by keeping garbage collection run samples in memory.
8
- #
9
- # @api private
10
- class GarbageCollectionProfiler
11
- def self.lock
12
- @lock ||= Mutex.new
13
- end
14
-
15
- def initialize
16
- @total_time = 0
17
- end
18
-
19
- # Whenever {#total_time} is called, the current `GC::Profiler#total_time`
20
- # gets added to `@total_time`, after which `GC::Profiler.clear` is called
21
- # to prevent it from leaking memory. A class-level lock is used to make
22
- # sure garbage collection time is never counted more than once.
23
- #
24
- # Whenever `@total_time` gets above two billion milliseconds (about 23
25
- # days), it's reset to make sure the result fits in a signed 32-bit
26
- # integer.
27
- #
28
- # @return [Integer]
29
- def total_time
30
- lock.synchronize do
31
- @total_time += (internal_profiler.total_time * 1000).round
32
- internal_profiler.clear
33
- end
34
-
35
- @total_time = 0 if @total_time > 2_000_000_000
36
-
37
- @total_time
38
- end
39
-
40
- private
41
-
42
- def internal_profiler
43
- GC::Profiler
44
- end
45
-
46
- def lock
47
- self.class.lock
48
- end
49
- end
50
-
51
- # {Appsignal::NilGarbageCollectionProfiler} is a dummy profiler
52
- # that always returns 0 as the total time.
53
- # Used when we don't want any profile information
54
- #
55
- # @api private
56
- class NilGarbageCollectionProfiler
57
- def total_time
58
- 0
59
- end
60
- end
61
- end