appsignal 3.1.0-java → 3.1.3-java

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: c5b5a232369f0d030bf17e1397dc12e7f5b9cce53b2469468a65cde362ef7903
4
- data.tar.gz: eb490806ae18a93d7ec24eb071c63b22c1f599b62fbd82e5a47d0f00ebeee50c
3
+ metadata.gz: 5bb9d094ed8de471e90dea7cd631a31ffea0777c09dbe4bfac516cc1820cbdcf
4
+ data.tar.gz: 790ec312c3349ee2b6889d8fca238e6cea7daa537c71cf81b7438152c2a2a2cb
5
5
  SHA512:
6
- metadata.gz: a3f0c5ed5ab18b10c70145c748b674c33b0e70ec67715b800f05da4b2f829268ef1d7d9aa3173afa55927c746d11cebc6bd2571438ead0074a141b0fa168b32a
7
- data.tar.gz: 130d790bd57b4e39ee9011e56a11c60ff72dda6e266aa7335ceea0f63e1e41093c815a9c4286c5db2129198acf169c9d65a6b025eb15e4b930ac905e62439d8c
6
+ metadata.gz: a3ecfceba446f9a876c903b796a2b91c4ba44ab53856e9a6518d4681f86144547bdffd6d367a309ae05c5e15ebc30a52ca4b666dcec1c651b321d90c0eb8014f
7
+ data.tar.gz: c5705bfeca7ec423ee1888572f84ef70fd15abdb5501cd0c9e73c2d5855b498d7e03ac8c50ab1aa88137d3335376cd2d0b767503c0ec17d9b2c7b209cd04e80f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.1.3
4
+
5
+ ### Added
6
+
7
+ - [811a1082](https://github.com/appsignal/appsignal-ruby/commit/811a10825043ed584f23d870e3a420ee409eb151) patch - Add the `Transaction.current?` helper to determine if any Transaction is currently active or not. AppSignal `NilTransaction`s are not considered active transactions.
8
+
9
+ ### Changed
10
+
11
+ - [dc50d889](https://github.com/appsignal/appsignal-ruby/commit/dc50d8892699bf17b2399865ead8b27ce45b60ed) patch - Rename the (so far privately reported) `gc_total_time` metric to `gc_time`. It no longer reports the total time of Garbage Collection measured, but only the time between two (minutely) measurements.
12
+
13
+ ### Fixed
14
+
15
+ - [7cfed987](https://github.com/appsignal/appsignal-ruby/commit/7cfed98761cf81d475261c553486b24843460cf3) patch - Fix error on unknown HTTP request method. When a request is made with an unknown request method, triggering and `ActionController::UnknownHttpMethod`, it will no longer break the AppSignal instrumentation but omit the request method in the sample data.
16
+
17
+ ## 3.1.2
18
+
19
+ ### Changed
20
+
21
+ - [1b95bb4c](https://github.com/appsignal/appsignal-ruby/commit/1b95bb4c8df08128cfa2db0d918ffcb909e5ee4c) patch - Report Garbage Collection total time metric as the delta between measurements. This reports a more user friendly metric that doesn't always goes up until the app restarts or gets a new deploy. This metric is reported 0 by default without `GC::Profiler.enable` having been called.
22
+ - [61a78fb0](https://github.com/appsignal/appsignal-ruby/commit/61a78fb028b04ae6f0a4ca1fc469d744f23c5029) patch - Bump agent to 06391fb
23
+
24
+ - Accept "warning" value for the `log_level` config option.
25
+ - Add aarch64 Linux musl build.
26
+ - Improve debug logging from the extension.
27
+ - Fix high CPU issue for appsignal-agent when nothing could be read from the socket.
28
+
29
+ ## 3.1.1
30
+
31
+ ### Changed
32
+
33
+ - [e225c798](https://github.com/appsignal/appsignal-ruby/commit/e225c798c65aef6085bb689597b7f3359fe138f7) patch - Report all Ruby VM metrics as gauges. We previously reported some metrics as distributions, but all fields for those distributions would report the same values.
34
+
35
+ ### Fixed
36
+
37
+ - [31fd19c6](https://github.com/appsignal/appsignal-ruby/commit/31fd19c6019db2c68b359f1fc4ed3d5e4843e349) patch - Add hostname tag for Ruby VM metrics. This allows us to graph every host separately and multiple hosts won't overwrite each other metrics.
38
+
3
39
  ## 3.1.0
4
40
 
5
41
  ### Added
data/appsignal.gemspec CHANGED
@@ -39,12 +39,16 @@ Gem::Specification.new do |gem| # rubocop:disable Metrics/BlockLength
39
39
  gem.add_development_dependency "rake", ">= 12"
40
40
  gem.add_development_dependency "rspec", "~> 3.8"
41
41
  gem.add_development_dependency "timecop"
42
- gem.add_development_dependency "webmock"
43
42
  gem.add_development_dependency "yard", ">= 0.9.20"
44
43
  gem.add_development_dependency "pry"
45
44
 
46
45
  # Dependencies that need to be locked to a specific version in developement
47
46
  ruby_version = Gem::Version.new(RUBY_VERSION)
47
+ if ruby_version < Gem::Version.new("2.3.0")
48
+ gem.add_development_dependency "webmock", "3.14.0"
49
+ else
50
+ gem.add_development_dependency "webmock"
51
+ end
48
52
  if ruby_version > Gem::Version.new("2.5.0")
49
53
  # RuboCop dependency parallel depends on Ruby > 2.4
50
54
  gem.add_development_dependency "rubocop", "0.50.0"
data/ext/agent.yml CHANGED
@@ -3,92 +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: d573c9b
6
+ version: '06391fb'
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: a9a86594e50f22e7f7fd93a050e334048248a6dc971015e66c26150c4a689345
13
+ checksum: 9bf41c183d94c80e980f57ea2e29d08bae97e8097b5284a2b91a5484bf866f8c
14
14
  filename: appsignal-x86_64-darwin-all-static.tar.gz
15
15
  dynamic:
16
- checksum: 04a69d0b608aa0e834c96c75a3bb226e7ca252fd2c74e439fdd43bf297d6bde2
16
+ checksum: 4d3789e65cf00e446600e883d95d097323ebb3835703c67c8d09f434f09ab496
17
17
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
18
18
  universal-darwin:
19
19
  static:
20
- checksum: a9a86594e50f22e7f7fd93a050e334048248a6dc971015e66c26150c4a689345
20
+ checksum: 9bf41c183d94c80e980f57ea2e29d08bae97e8097b5284a2b91a5484bf866f8c
21
21
  filename: appsignal-x86_64-darwin-all-static.tar.gz
22
22
  dynamic:
23
- checksum: 04a69d0b608aa0e834c96c75a3bb226e7ca252fd2c74e439fdd43bf297d6bde2
23
+ checksum: 4d3789e65cf00e446600e883d95d097323ebb3835703c67c8d09f434f09ab496
24
24
  filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
25
25
  aarch64-darwin:
26
26
  static:
27
- checksum: 92f7f71b685985b310a9f3693a96a5db6b9133b0af807d000b90248e097063c7
27
+ checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
28
28
  filename: appsignal-aarch64-darwin-all-static.tar.gz
29
29
  dynamic:
30
- checksum: ffb54af4c35dd281a4735b57d8e537b8b08e87e08841e5d344caff325948a9e8
30
+ checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
31
31
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
32
32
  arm64-darwin:
33
33
  static:
34
- checksum: 92f7f71b685985b310a9f3693a96a5db6b9133b0af807d000b90248e097063c7
34
+ checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
35
35
  filename: appsignal-aarch64-darwin-all-static.tar.gz
36
36
  dynamic:
37
- checksum: ffb54af4c35dd281a4735b57d8e537b8b08e87e08841e5d344caff325948a9e8
37
+ checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
38
38
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
39
39
  arm-darwin:
40
40
  static:
41
- checksum: 92f7f71b685985b310a9f3693a96a5db6b9133b0af807d000b90248e097063c7
41
+ checksum: 74edd7b97995f3314c10e3d84fc832c1b842c236c331ed4f2f77146ad004d179
42
42
  filename: appsignal-aarch64-darwin-all-static.tar.gz
43
43
  dynamic:
44
- checksum: ffb54af4c35dd281a4735b57d8e537b8b08e87e08841e5d344caff325948a9e8
44
+ checksum: 7165bb164a9cd7a2a5f97897d954390412f7034c667e5826b3307ffbd848bff9
45
45
  filename: appsignal-aarch64-darwin-all-dynamic.tar.gz
46
46
  aarch64-linux:
47
47
  static:
48
- checksum: 79f1e7f9c34ab36c06d5c3d676173ee7c1219af2f51dc77865897598dc01349a
48
+ checksum: 0f2430e637eb77ce2093f021777087e87cb1e7be7c86a53771172696791c4879
49
49
  filename: appsignal-aarch64-linux-all-static.tar.gz
50
50
  dynamic:
51
- checksum: cfd8e98238e2c7cdb10c0e136c47ab8e2dacab0a14d8ccf0e4c6c14946e325f1
51
+ checksum: 0e4f9305aeaaa2d7847e83be04227b865723a0591574108d78040b5921a677a7
52
52
  filename: appsignal-aarch64-linux-all-dynamic.tar.gz
53
53
  i686-linux:
54
54
  static:
55
- checksum: 835c6f823a2c6e9f8fa12704bf0953e3610dc9836355b57d2d6981e6ae412fb4
55
+ checksum: 449ba623aaa1853c2d211bf1e2d3a14e5ae09225a62457cbdbcc0983a5713a52
56
56
  filename: appsignal-i686-linux-all-static.tar.gz
57
57
  dynamic:
58
- checksum: febc5d80a7b0fd9644e2d68d068d28c66359bbef9473f01e9f71fb07fd73bcb8
58
+ checksum: dae994292d602eaf0910bd2ce53f0163e19767a4cbb8e5d0db99c0010d6df486
59
59
  filename: appsignal-i686-linux-all-dynamic.tar.gz
60
60
  x86-linux:
61
61
  static:
62
- checksum: 835c6f823a2c6e9f8fa12704bf0953e3610dc9836355b57d2d6981e6ae412fb4
62
+ checksum: 449ba623aaa1853c2d211bf1e2d3a14e5ae09225a62457cbdbcc0983a5713a52
63
63
  filename: appsignal-i686-linux-all-static.tar.gz
64
64
  dynamic:
65
- checksum: febc5d80a7b0fd9644e2d68d068d28c66359bbef9473f01e9f71fb07fd73bcb8
65
+ checksum: dae994292d602eaf0910bd2ce53f0163e19767a4cbb8e5d0db99c0010d6df486
66
66
  filename: appsignal-i686-linux-all-dynamic.tar.gz
67
67
  x86_64-linux:
68
68
  static:
69
- checksum: 6eb6f0df2f8c62a29769bf7f21cefaec92a24ee0ab363acc5bd4f9c2d1241c53
69
+ checksum: 394796c0ddeb4881c9f2e6ce82f840e66bcb69e027324f6c04f6671067445fbb
70
70
  filename: appsignal-x86_64-linux-all-static.tar.gz
71
71
  dynamic:
72
- checksum: ce710ff2edea2fc7b3b6bafd10af849e95f513abf5d775b9a8361ffed45b70c3
72
+ checksum: 9ca4762c464482b0a5a89898a839388597dd57a17a21527a67f3e3db0e540a03
73
73
  filename: appsignal-x86_64-linux-all-dynamic.tar.gz
74
74
  x86_64-linux-musl:
75
75
  static:
76
- checksum: b16d46074527da5700e10e5a8b176aeb46b7bbb19431653029eda04437bef918
76
+ checksum: 673271c8c5fd55053d8a719bcd307f787db4ca4633baf8cf961c442bf1805614
77
77
  filename: appsignal-x86_64-linux-musl-all-static.tar.gz
78
78
  dynamic:
79
- checksum: 261b79ab790e6a12a748d4649a4389e96d5cf7d1f981c3b56ed331f164d1627b
79
+ checksum: 609d59376d6633652015e838eb649229fe2523d443a5471232b869f48eb99640
80
80
  filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
81
+ aarch64-linux-musl:
82
+ static:
83
+ checksum: e90ca19bf61596be022ba04897e8902b3401add58f351a40a3d3a7af241d0bbb
84
+ filename: appsignal-aarch64-linux-musl-all-static.tar.gz
85
+ dynamic:
86
+ checksum: afb66c65fb82b672887bc6b6e82d82f09d9855a5497a7abb06b438dadea97aca
87
+ filename: appsignal-aarch64-linux-musl-all-dynamic.tar.gz
81
88
  x86_64-freebsd:
82
89
  static:
83
- checksum: e7bfc1dc355ce1237aaee6fdf967c78ecca533db41b09c2b10716e7f8593dbe0
90
+ checksum: cb45da91c51123859e5ef5cea850460c28d6e77dfa08b90375178d9017162ba8
84
91
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
85
92
  dynamic:
86
- checksum: 97af9419cf00e22ea544a2365785a6b5df2a990f17e7735b3bbec1a690b68f0b
93
+ checksum: 6a03e02c2526e05edaa7fa932b2e764318c63ec93d517c6c00f6b7541bfe71f3
87
94
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
88
95
  amd64-freebsd:
89
96
  static:
90
- checksum: e7bfc1dc355ce1237aaee6fdf967c78ecca533db41b09c2b10716e7f8593dbe0
97
+ checksum: cb45da91c51123859e5ef5cea850460c28d6e77dfa08b90375178d9017162ba8
91
98
  filename: appsignal-x86_64-freebsd-all-static.tar.gz
92
99
  dynamic:
93
- checksum: 97af9419cf00e22ea544a2365785a6b5df2a990f17e7735b3bbec1a690b68f0b
100
+ checksum: 6a03e02c2526e05edaa7fa932b2e764318c63ec93d517c6c00f6b7541bfe71f3
94
101
  filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
@@ -325,7 +325,8 @@ module Appsignal
325
325
  "value is not an exception: #{exception.inspect}"
326
326
  return
327
327
  end
328
- return if !active? || Appsignal::Transaction.current.nil?
328
+ return if !active? || !Appsignal::Transaction.current?
329
+
329
330
  transaction = Appsignal::Transaction.current
330
331
  transaction.set_error(exception)
331
332
  transaction.set_tags(tags) if tags
@@ -359,7 +360,7 @@ module Appsignal
359
360
  # @since 2.2.0
360
361
  def set_action(action)
361
362
  return if !active? ||
362
- Appsignal::Transaction.current.nil? ||
363
+ !Appsignal::Transaction.current? ||
363
364
  action.nil?
364
365
  Appsignal::Transaction.current.set_action(action)
365
366
  end
@@ -398,7 +399,7 @@ module Appsignal
398
399
  # @since 2.2.0
399
400
  def set_namespace(namespace)
400
401
  return if !active? ||
401
- Appsignal::Transaction.current.nil? ||
402
+ !Appsignal::Transaction.current? ||
402
403
  namespace.nil?
403
404
  Appsignal::Transaction.current.set_namespace(namespace)
404
405
  end
@@ -438,8 +439,9 @@ module Appsignal
438
439
  # Tagging guide
439
440
  def tag_request(tags = {})
440
441
  return unless active?
442
+ return unless Appsignal::Transaction.current?
443
+
441
444
  transaction = Appsignal::Transaction.current
442
- return false unless transaction
443
445
  transaction.set_tags(tags)
444
446
  end
445
447
  alias :tag_job :tag_request
@@ -471,8 +473,9 @@ module Appsignal
471
473
  # @since 2.12.0
472
474
  def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
473
475
  return unless active?
476
+ return unless Appsignal::Transaction.current?
477
+
474
478
  transaction = Appsignal::Transaction.current
475
- return false unless transaction
476
479
  transaction.add_breadcrumb(category, action, message, metadata, time)
477
480
  end
478
481
 
@@ -20,9 +20,11 @@ module Appsignal
20
20
  module ActiveJobClassInstrumentation
21
21
  def execute(job)
22
22
  job_status = nil
23
- current_transaction = Appsignal::Transaction.current
23
+ has_wrapper_transaction = Appsignal::Transaction.current?
24
24
  transaction =
25
- if current_transaction.nil_transaction?
25
+ if has_wrapper_transaction
26
+ Appsignal::Transaction.current
27
+ else
26
28
  # No standalone integration started before ActiveJob integration.
27
29
  # We don't have a separate integration for this QueueAdapter like
28
30
  # we do for Sidekiq.
@@ -33,8 +35,6 @@ module Appsignal
33
35
  Appsignal::Transaction::BACKGROUND_JOB,
34
36
  Appsignal::Transaction::GenericRequest.new({})
35
37
  )
36
- else
37
- current_transaction
38
38
  end
39
39
 
40
40
  super
@@ -64,7 +64,7 @@ module Appsignal
64
64
  transaction.set_queue_start((Time.parse(enqueued_at).to_f * 1_000).to_i)
65
65
  end
66
66
 
67
- if current_transaction.nil_transaction?
67
+ unless has_wrapper_transaction
68
68
  # Only complete transaction if ActiveJob is not wrapped in
69
69
  # another supported integration, such as Sidekiq.
70
70
  Appsignal::Transaction.complete_current!
@@ -6,8 +6,9 @@ module Appsignal
6
6
  class MongoMonitorSubscriber
7
7
  # Called by Mongo::Monitor when query starts
8
8
  def started(event)
9
+ return unless Appsignal::Transaction.current?
10
+
9
11
  transaction = Appsignal::Transaction.current
10
- return if transaction.nil_transaction?
11
12
  return if transaction.paused?
12
13
 
13
14
  # Format the command
@@ -36,8 +37,9 @@ module Appsignal
36
37
 
37
38
  # Finishes the event in the AppSignal extension
38
39
  def finish(result, event)
40
+ return unless Appsignal::Transaction.current?
41
+
39
42
  transaction = Appsignal::Transaction.current
40
- return if transaction.nil_transaction?
41
43
  return if transaction.paused?
42
44
 
43
45
  # Get the query from the transaction store
@@ -10,21 +10,24 @@ module Appsignal
10
10
  # @api private
11
11
  class SidekiqErrorHandler
12
12
  def call(exception, sidekiq_context)
13
- transaction = Appsignal::Transaction.current
14
-
15
- if transaction.nil_transaction?
16
- # Sidekiq error outside of the middleware scope.
17
- # Can be a job JSON parse error or some other error happening in
18
- # Sidekiq.
19
- transaction = Appsignal::Transaction.create(
20
- SecureRandom.uuid, # Newly generated job id
21
- Appsignal::Transaction::BACKGROUND_JOB,
22
- Appsignal::Transaction::GenericRequest.new({})
23
- )
24
- transaction.set_action_if_nil("SidekiqInternal")
25
- transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
26
- transaction.params = { :jobstr => sidekiq_context[:jobstr] }
27
- end
13
+ transaction =
14
+ if Appsignal::Transaction.current?
15
+ Appsignal::Transaction.current
16
+ else
17
+ # Sidekiq error outside of the middleware scope.
18
+ # Can be a job JSON parse error or some other error happening in
19
+ # Sidekiq.
20
+ transaction =
21
+ Appsignal::Transaction.create(
22
+ SecureRandom.uuid, # Newly generated job id
23
+ Appsignal::Transaction::BACKGROUND_JOB,
24
+ Appsignal::Transaction::GenericRequest.new({})
25
+ )
26
+ transaction.set_action_if_nil("SidekiqInternal")
27
+ transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
28
+ transaction.params = { :jobstr => sidekiq_context[:jobstr] }
29
+ transaction
30
+ end
28
31
 
29
32
  transaction.set_error(exception)
30
33
  Appsignal::Transaction.complete_current!
@@ -7,22 +7,35 @@ module Appsignal
7
7
  @gauge_delta_cache ||= {}
8
8
  end
9
9
 
10
- # Calculate the delta of two values for a gauge metric
10
+ # Calculate the delta of two values for a gauge metric.
11
11
  #
12
- # First call will store the data for the metric in the cache and the
13
- # second call will return the delta of the gauge metric. This is used for
14
- # absolute counter values which we want to track as gauges.
12
+ # When this method is called, the given value is stored in a cache
13
+ # under the given cache key.
14
+ #
15
+ # A block must be passed to this method. The first time the method
16
+ # is called for a given cache key, the block will not be yielded to.
17
+ # In subsequent calls, the delta between the previously stored value
18
+ # in the cache for that key and the value given in this invocation
19
+ # will be yielded to the block.
20
+ #
21
+ # This is used for absolute counter values which we want to track as
22
+ # gauges.
15
23
  #
16
24
  # @example
17
- # gauge_delta :my_cache_key, 10
18
- # gauge_delta :my_cache_key, 15
19
- # # Returns a value of `5`
25
+ # gauge_delta :with_block, 10 do |delta|
26
+ # puts "this block will not be yielded to"
27
+ # end
28
+ # gauge_delta :with_block, 15 do |delta|
29
+ # # `delta` has a value of `5`
30
+ # puts "this block will be yielded to with delta = #{delta}"
31
+ # end
32
+ #
20
33
  def gauge_delta(cache_key, value)
21
34
  previous_value = gauge_delta_cache[cache_key]
22
35
  gauge_delta_cache[cache_key] = value
23
36
  return unless previous_value
24
37
 
25
- value - previous_value
38
+ yield value - previous_value
26
39
  end
27
40
  end
28
41
  end
@@ -8,59 +8,76 @@ module Appsignal
8
8
  defined?(::RubyVM) && ::RubyVM.respond_to?(:stat)
9
9
  end
10
10
 
11
- def self.garbage_collection_profiler
12
- @garbage_collection_profiler ||= Appsignal::GarbageCollectionProfiler.new
13
- end
14
-
15
- def initialize(appsignal = Appsignal)
11
+ def initialize(appsignal: Appsignal, gc_profiler: Appsignal::GarbageCollectionProfiler.new)
16
12
  Appsignal.logger.debug("Initializing VM probe")
17
13
  @appsignal = appsignal
14
+ @gc_profiler = gc_profiler
18
15
  end
19
16
 
20
17
  # @api private
21
18
  def call
22
19
  stat = RubyVM.stat
23
20
 
24
- @appsignal.add_distribution_value(
21
+ set_gauge(
25
22
  "ruby_vm",
26
23
  stat[:class_serial],
27
24
  :metric => :class_serial
28
25
  )
29
26
 
30
- @appsignal.add_distribution_value(
27
+ set_gauge(
31
28
  "ruby_vm",
32
29
  stat[:constant_cache] ? stat[:constant_cache].values.sum : stat[:global_constant_state],
33
30
  :metric => :global_constant_state
34
31
  )
35
32
 
36
- @appsignal.set_gauge("thread_count", Thread.list.size)
37
- @appsignal.set_gauge("gc_total_time", MriProbe.garbage_collection_profiler.total_time)
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
36
+ end
38
37
 
39
38
  gc_stats = GC.stat
40
- allocated_objects =
41
- gauge_delta(
42
- :allocated_objects,
43
- gc_stats[:total_allocated_objects] || gc_stats[:total_allocated_object]
44
- )
45
- if allocated_objects
46
- @appsignal.set_gauge("allocated_objects", allocated_objects)
39
+ gauge_delta(
40
+ :allocated_objects,
41
+ gc_stats[:total_allocated_objects] || gc_stats[:total_allocated_object]
42
+ ) do |allocated_objects|
43
+ set_gauge("allocated_objects", allocated_objects)
47
44
  end
48
45
 
49
- gc_count = gauge_delta(:gc_count, GC.count)
50
- if gc_count
51
- @appsignal.add_distribution_value("gc_count", gc_count, :metric => :gc_count)
46
+ gauge_delta(:gc_count, GC.count) do |gc_count|
47
+ set_gauge("gc_count", gc_count, :metric => :gc_count)
52
48
  end
53
- minor_gc_count = gauge_delta(:minor_gc_count, gc_stats[:minor_gc_count])
54
- if minor_gc_count
55
- @appsignal.add_distribution_value("gc_count", minor_gc_count, :metric => :minor_gc_count)
49
+ gauge_delta(:minor_gc_count, gc_stats[:minor_gc_count]) do |minor_gc_count|
50
+ set_gauge("gc_count", minor_gc_count, :metric => :minor_gc_count)
56
51
  end
57
- major_gc_count = gauge_delta(:major_gc_count, gc_stats[:major_gc_count])
58
- if major_gc_count
59
- @appsignal.add_distribution_value("gc_count", major_gc_count, :metric => :major_gc_count)
52
+ gauge_delta(:major_gc_count, gc_stats[:major_gc_count]) do |major_gc_count|
53
+ set_gauge("gc_count", major_gc_count, :metric => :major_gc_count)
60
54
  end
61
55
 
62
- @appsignal.add_distribution_value("heap_slots", gc_stats[:heap_live_slots] || gc_stats[:heap_live_slot], :metric => :heap_live)
63
- @appsignal.add_distribution_value("heap_slots", gc_stats[:heap_free_slots] || gc_stats[:heap_free_slot], :metric => :heap_free)
56
+ set_gauge("heap_slots", gc_stats[:heap_live_slots] || gc_stats[:heap_live_slot], :metric => :heap_live)
57
+ set_gauge("heap_slots", gc_stats[:heap_free_slots] || gc_stats[:heap_free_slot], :metric => :heap_free)
58
+ end
59
+
60
+ private
61
+
62
+ def set_gauge(metric, value, tags = {})
63
+ @appsignal.set_gauge(metric, value, { :hostname => hostname }.merge(tags))
64
+ end
65
+
66
+ def hostname
67
+ return @hostname if defined?(@hostname)
68
+
69
+ config = @appsignal.config
70
+ @hostname =
71
+ if config[:hostname]
72
+ config[:hostname]
73
+ else
74
+ # Auto detect hostname as fallback. May be inaccurate.
75
+ Socket.gethostname
76
+ end
77
+ Appsignal.logger.debug "MRI probe: Using hostname config " \
78
+ "option '#{@hostname.inspect}' as hostname"
79
+
80
+ @hostname
64
81
  end
65
82
  end
66
83
  end
@@ -44,15 +44,16 @@ module Appsignal
44
44
 
45
45
  gauge "worker_count", stats.workers_size
46
46
  gauge "process_count", stats.processes_size
47
- jobs_processed = gauge_delta :jobs_processed, stats.processed
48
- if jobs_processed
47
+ gauge_delta :jobs_processed, stats.processed do |jobs_processed|
49
48
  gauge "job_count", jobs_processed, :status => :processed
50
49
  end
51
- jobs_failed = gauge_delta :jobs_failed, stats.failed
52
- gauge "job_count", jobs_failed, :status => :failed if jobs_failed
50
+ gauge_delta :jobs_failed, stats.failed do |jobs_failed|
51
+ gauge "job_count", jobs_failed, :status => :failed
52
+ end
53
53
  gauge "job_count", stats.retry_size, :status => :retry_queue
54
- jobs_dead = gauge_delta :jobs_dead, stats.dead_size
55
- gauge "job_count", jobs_dead, :status => :died if jobs_dead
54
+ gauge_delta :jobs_dead, stats.dead_size do |jobs_dead|
55
+ gauge "job_count", jobs_dead, :status => :died
56
+ end
56
57
  gauge "job_count", stats.scheduled_size, :status => :scheduled
57
58
  gauge "job_count", stats.enqueued, :status => :enqueued
58
59
  end
@@ -40,7 +40,11 @@ module Appsignal
40
40
  end
41
41
  transaction.set_http_or_background_queue_start
42
42
  transaction.set_metadata("path", request.path)
43
- transaction.set_metadata("method", request.request_method)
43
+ begin
44
+ transaction.set_metadata("method", request.request_method)
45
+ rescue => error
46
+ Appsignal.logger.error("Unable to report HTTP request method: '#{error}'")
47
+ end
44
48
  Appsignal::Transaction.complete_current!
45
49
  end
46
50
  end
@@ -35,10 +35,24 @@ module Appsignal
35
35
  end
36
36
  end
37
37
 
38
+ # Returns currently active transaction or a {NilTransaction} if none is
39
+ # active.
40
+ #
41
+ # @see .current?
42
+ # @return [Boolean]
38
43
  def current
39
44
  Thread.current[:appsignal_transaction] || NilTransaction.new
40
45
  end
41
46
 
47
+ # Returns if any transaction is currently active or not. A
48
+ # {NilTransaction} is not considered an active transaction.
49
+ #
50
+ # @see .current
51
+ # @return [Boolean]
52
+ def current?
53
+ current && !current.nil_transaction?
54
+ end
55
+
42
56
  def complete_current!
43
57
  current.complete
44
58
  rescue => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.1.0".freeze
4
+ VERSION = "3.1.3".freeze
5
5
  end
@@ -1,13 +1,15 @@
1
1
  class AppsignalMock
2
- attr_reader :distribution_values, :gauges
2
+ attr_reader :gauges
3
3
 
4
- def initialize
5
- @distribution_values = []
4
+ def initialize(hostname: nil)
5
+ @hostname = hostname
6
6
  @gauges = []
7
7
  end
8
8
 
9
- def add_distribution_value(*args)
10
- @distribution_values << args
9
+ def config
10
+ ConfigHelpers.project_fixture_config.tap do |conf|
11
+ conf[:hostname] = @hostname if @hostname
12
+ end
11
13
  end
12
14
 
13
15
  def set_gauge(*args) # rubocop:disable Naming/AccessorMethodName
@@ -16,8 +18,9 @@ class AppsignalMock
16
18
  end
17
19
 
18
20
  describe Appsignal::Probes::MriProbe do
19
- let(:appsignal_mock) { AppsignalMock.new }
20
- let(:probe) { described_class.new(appsignal_mock) }
21
+ let(:appsignal_mock) { AppsignalMock.new(:hostname => hostname) }
22
+ let(:gc_profiler_mock) { instance_double("Appsignal::GarbageCollectionProfiler") }
23
+ let(:probe) { described_class.new(:appsignal => appsignal_mock, :gc_profiler => gc_profiler_mock) }
21
24
 
22
25
  describe ".dependencies_present?" do
23
26
  if DependencyHelper.running_jruby? || DependencyHelper.running_ruby_2_0?
@@ -33,10 +36,15 @@ describe Appsignal::Probes::MriProbe do
33
36
 
34
37
  unless DependencyHelper.running_jruby? || DependencyHelper.running_ruby_2_0?
35
38
  describe "#call" do
39
+ let(:hostname) { nil }
40
+ before do
41
+ allow(gc_profiler_mock).to receive(:total_time)
42
+ end
43
+
36
44
  it "should track vm metrics" do
37
45
  probe.call
38
- expect_distribution_value("ruby_vm", :class_serial)
39
- expect_distribution_value("ruby_vm", :global_constant_state)
46
+ expect_gauge_value("ruby_vm", :tags => { :metric => :class_serial })
47
+ expect_gauge_value("ruby_vm", :tags => { :metric => :global_constant_state })
40
48
  end
41
49
 
42
50
  it "tracks thread counts" do
@@ -44,9 +52,22 @@ describe Appsignal::Probes::MriProbe do
44
52
  expect_gauge_value("thread_count")
45
53
  end
46
54
 
47
- it "tracks GC total time" do
55
+ it "tracks GC time between measurements" do
56
+ expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15)
48
57
  probe.call
49
- expect_gauge_value("gc_total_time")
58
+ probe.call
59
+ expect_gauge_value("gc_time", 5)
60
+ end
61
+
62
+ context "when GC total time overflows" do
63
+ it "skips one report" do
64
+ expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15, 0, 10)
65
+ probe.call # Normal call, create a cache
66
+ probe.call # Report delta value based on cached value
67
+ probe.call # The value overflows and reports no value. Then stores 0 in the cache
68
+ probe.call # Report new value based on cache of 0
69
+ expect_gauges([["gc_time", 5], ["gc_time", 10]])
70
+ end
50
71
  end
51
72
 
52
73
  it "tracks GC run count" do
@@ -57,9 +78,9 @@ describe Appsignal::Probes::MriProbe do
57
78
  )
58
79
  probe.call
59
80
  probe.call
60
- expect_distribution_value("gc_count", :gc_count, 5)
61
- expect_distribution_value("gc_count", :minor_gc_count, 6)
62
- expect_distribution_value("gc_count", :major_gc_count, 7)
81
+ expect_gauge_value("gc_count", 5, :tags => { :metric => :gc_count })
82
+ expect_gauge_value("gc_count", 6, :tags => { :metric => :minor_gc_count })
83
+ expect_gauge_value("gc_count", 7, :tags => { :metric => :major_gc_count })
63
84
  end
64
85
 
65
86
  it "tracks object allocation" do
@@ -75,30 +96,43 @@ describe Appsignal::Probes::MriProbe do
75
96
 
76
97
  it "tracks heap slots" do
77
98
  probe.call
78
- expect_distribution_value("heap_slots", :heap_live)
79
- expect_distribution_value("heap_slots", :heap_free)
99
+ expect_gauge_value("heap_slots", :tags => { :metric => :heap_live })
100
+ expect_gauge_value("heap_slots", :tags => { :metric => :heap_free })
101
+ end
102
+
103
+ context "with custom hostname" do
104
+ let(:hostname) { "my hostname" }
105
+
106
+ it "reports custom hostname tag value" do
107
+ probe.call
108
+ expect_gauge_value("heap_slots", :tags => { :metric => :heap_live, :hostname => hostname })
109
+ end
80
110
  end
81
111
  end
82
112
  end
83
113
 
84
- def expect_distribution_value(expected_key, metric, expected_value = nil)
85
- expect(appsignal_mock.distribution_values).to satisfy do |distribution_values|
86
- distribution_values.any? do |distribution_value|
87
- key, value, metadata = distribution_value
114
+ def expect_gauge_value(expected_key, expected_value = nil, tags: {})
115
+ expected_tags = { :hostname => Socket.gethostname }.merge(tags)
116
+ expect(appsignal_mock.gauges).to satisfy do |gauges|
117
+ gauges.any? do |distribution_value|
118
+ key, value, tags = distribution_value
88
119
  next unless key == expected_key
89
120
  next unless expected_value ? expected_value == value : !value.nil?
90
- next unless metadata == { :metric => metric }
121
+ next unless tags == expected_tags
91
122
 
92
123
  true
93
124
  end
94
125
  end
95
126
  end
96
127
 
97
- def expect_gauge_value(expected_key, expected_value = nil)
98
- expect(appsignal_mock.gauges).to satisfy do |gauges|
99
- gauges.any? do |(key, value)|
100
- expected_key == key && expected_value ? expected_value == value : !value.nil?
101
- end
128
+ def expect_gauges(expected_metrics)
129
+ default_tags = { :hostname => Socket.gethostname }
130
+ keys = expected_metrics.map { |(key)| key }
131
+ metrics = expected_metrics.map do |metric|
132
+ key, value, tags = metric
133
+ [key, value, default_tags.merge(tags || {})]
102
134
  end
135
+ found_gauges = appsignal_mock.gauges.select { |(key)| keys.include? key }
136
+ expect(found_gauges).to eq(metrics)
103
137
  end
104
138
  end
@@ -3,20 +3,37 @@ if DependencyHelper.rails_present?
3
3
  end
4
4
 
5
5
  describe Appsignal::Rack::RailsInstrumentation do
6
- before :context do
6
+ let(:log) { StringIO.new }
7
+ before do
7
8
  start_agent
9
+ Appsignal.logger = test_logger(log)
8
10
  end
9
11
 
12
+ let(:params) do
13
+ {
14
+ "controller" => "blog_posts",
15
+ "action" => "show",
16
+ "id" => "1",
17
+ "my_custom_param" => "my custom secret",
18
+ "password" => "super secret"
19
+ }
20
+ end
21
+ let(:env_extra) { {} }
10
22
  let(:app) { double(:call => true) }
11
23
  let(:env) do
12
- http_request_env_with_data("action_dispatch.request_id" => "1").tap do |request|
13
- request["action_controller.instance"] = double(
24
+ http_request_env_with_data({
25
+ :params => params,
26
+ :with_queue_start => true,
27
+ "action_dispatch.request_id" => "1",
28
+ "action_dispatch.parameter_filter" => [:my_custom_param, :password],
29
+ "action_controller.instance" => double(
14
30
  :class => MockController,
15
31
  :action_name => "index"
16
32
  )
17
- end
33
+ }.merge(env_extra))
18
34
  end
19
35
  let(:middleware) { Appsignal::Rack::RailsInstrumentation.new(app, {}) }
36
+ around { |example| keep_transactions { example.run } }
20
37
 
21
38
  describe "#call" do
22
39
  before do
@@ -46,30 +63,62 @@ if DependencyHelper.rails_present?
46
63
  after { middleware.call(env) }
47
64
  end
48
65
 
49
- describe "#call_with_appsignal_monitoring", :error => false do
50
- it "should create a transaction" do
51
- expect(Appsignal::Transaction).to receive(:create).with(
52
- "1",
53
- Appsignal::Transaction::HTTP_REQUEST,
54
- kind_of(ActionDispatch::Request),
55
- :params_method => :filtered_parameters
56
- ).and_return(
57
- instance_double(
58
- "Appsignal::Transaction",
59
- :set_action => nil,
60
- :set_action_if_nil => nil,
61
- :set_http_or_background_queue_start => nil,
62
- :set_metadata => nil
66
+ describe "#call_with_appsignal_monitoring" do
67
+ def run
68
+ middleware.call(env)
69
+ end
70
+
71
+ it "calls the wrapped app" do
72
+ run
73
+ expect(app).to have_received(:call).with(env)
74
+ end
75
+
76
+ it "creates one transaction with metadata" do
77
+ run
78
+
79
+ expect(created_transactions.length).to eq(1)
80
+ transaction_hash = last_transaction.to_h
81
+ expect(transaction_hash).to include(
82
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST,
83
+ "action" => "MockController#index",
84
+ "metadata" => hash_including(
85
+ "method" => "GET",
86
+ "path" => "/blog"
87
+ )
88
+ )
89
+ expect(last_transaction.ext.queue_start).to eq(
90
+ fixed_time * 1_000.0
91
+ )
92
+ end
93
+
94
+ it "filter parameters in Rails" do
95
+ run
96
+
97
+ transaction_hash = last_transaction.to_h
98
+ expect(transaction_hash).to include(
99
+ "sample_data" => hash_including(
100
+ "params" => params.merge(
101
+ "my_custom_param" => "[FILTERED]",
102
+ "password" => "[FILTERED]"
103
+ )
63
104
  )
64
105
  )
65
106
  end
66
107
 
67
- it "should call the app" do
68
- expect(app).to receive(:call).with(env)
108
+ context "with an invalid HTTP request method" do
109
+ let(:env_extra) { { :request_method => "FOO", "REQUEST_METHOD" => "FOO" } }
110
+
111
+ it "does not store the HTTP request method" do
112
+ run
113
+
114
+ transaction_hash = last_transaction.to_h
115
+ expect(transaction_hash["metadata"]).to_not have_key("method")
116
+ expect(log_contents(log)).to contains_log(:error, "Unable to report HTTP request method: '")
117
+ end
69
118
  end
70
119
 
71
- context "with an exception", :error => true do
72
- let(:error) { ExampleException }
120
+ context "with an exception" do
121
+ let(:error) { ExampleException.new("ExampleException message") }
73
122
  let(:app) do
74
123
  double.tap do |d|
75
124
  allow(d).to receive(:call).and_raise(error)
@@ -77,21 +126,16 @@ if DependencyHelper.rails_present?
77
126
  end
78
127
 
79
128
  it "records the exception" do
80
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
81
- end
82
- end
129
+ expect { run }.to raise_error(error)
83
130
 
84
- it "should set metadata" do
85
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).twice
86
- end
87
-
88
- it "should set the action and queue start" do
89
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil).with("MockController#index")
90
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_http_or_background_queue_start)
131
+ transaction_hash = last_transaction.to_h
132
+ expect(transaction_hash["error"]).to include(
133
+ "name" => "ExampleException",
134
+ "message" => "ExampleException message",
135
+ "backtrace" => kind_of(String)
136
+ )
137
+ end
91
138
  end
92
-
93
- after(:error => false) { middleware.call(env) }
94
- after(:error => true) { expect { middleware.call(env) }.to raise_error(error) }
95
139
  end
96
140
 
97
141
  describe "#request_id" do
@@ -85,7 +85,9 @@ describe Appsignal::Transaction do
85
85
  end
86
86
 
87
87
  describe ".current" do
88
- subject { Appsignal::Transaction.current }
88
+ def current_transaction
89
+ Appsignal::Transaction.current
90
+ end
89
91
 
90
92
  context "when there is a current transaction" do
91
93
  let!(:transaction) do
@@ -93,13 +95,17 @@ describe Appsignal::Transaction do
93
95
  end
94
96
 
95
97
  it "reads :appsignal_transaction from the current Thread" do
96
- expect(subject).to eq Thread.current[:appsignal_transaction]
97
- expect(subject).to eq transaction
98
+ expect(current_transaction).to eq Thread.current[:appsignal_transaction]
99
+ expect(current_transaction).to eq transaction
98
100
  end
99
101
 
100
102
  it "is not a NilTransaction" do
101
- expect(subject.nil_transaction?).to eq false
102
- expect(subject).to be_a Appsignal::Transaction
103
+ expect(current_transaction.nil_transaction?).to eq false
104
+ expect(current_transaction).to be_a Appsignal::Transaction
105
+ end
106
+
107
+ it "returns true for current?" do
108
+ expect(Appsignal::Transaction.current?).to be(true)
103
109
  end
104
110
  end
105
111
 
@@ -109,8 +115,12 @@ describe Appsignal::Transaction do
109
115
  end
110
116
 
111
117
  it "returns a NilTransaction stub" do
112
- expect(subject.nil_transaction?).to eq true
113
- expect(subject).to be_a Appsignal::Transaction::NilTransaction
118
+ expect(current_transaction.nil_transaction?).to eq true
119
+ expect(current_transaction).to be_a Appsignal::Transaction::NilTransaction
120
+ end
121
+
122
+ it "returns false for current?" do
123
+ expect(Appsignal::Transaction.current?).to be(false)
114
124
  end
115
125
  end
116
126
  end
@@ -511,7 +511,14 @@ describe Appsignal do
511
511
  before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
512
512
 
513
513
  context "with transaction" do
514
- let(:transaction) { double }
514
+ let(:transaction) { http_request_transaction }
515
+ around do |example|
516
+ Appsignal.config = project_fixture_config
517
+ set_current_transaction transaction do
518
+ example.run
519
+ end
520
+ end
521
+
515
522
  it "should call add_breadcrumb on transaction" do
516
523
  expect(transaction).to receive(:add_breadcrumb)
517
524
  .with("Network", "http", "User made network request", { :response => 200 }, fixed_time)
@@ -14,6 +14,7 @@ module ConfigHelpers
14
14
  config_file
15
15
  )
16
16
  end
17
+ module_function :project_fixture_config, :project_fixture_path
17
18
 
18
19
  def start_agent(env = "production")
19
20
  Appsignal.config = project_fixture_config(env)
@@ -1,7 +1,8 @@
1
1
  module EnvHelpers
2
2
  def http_request_env_with_data(args = {})
3
+ with_queue_start = args.delete(:with_queue_start)
3
4
  path = args.delete(:path) || "/blog"
4
- Rack::MockRequest.env_for(
5
+ request = Rack::MockRequest.env_for(
5
6
  path,
6
7
  :params => args[:params] || {
7
8
  "controller" => "blog_posts",
@@ -18,6 +19,13 @@ module EnvHelpers
18
19
  :db_runtime => 500,
19
20
  :metadata => { :key => "value" }
20
21
  ).merge(args)
22
+
23
+ # Set default queue value
24
+ if with_queue_start
25
+ request["HTTP_X_QUEUE_START"] = "t=#{(fixed_time * 1_000).to_i}" # in milliseconds
26
+ end
27
+
28
+ request
21
29
  end
22
30
 
23
31
  def background_env_with_data(args = {})
@@ -46,8 +46,14 @@ module TransactionHelpers
46
46
 
47
47
  # Set current transaction manually.
48
48
  # Cleared by {clear_current_transaction!}
49
+ #
50
+ # When a block is given, the current transaction is automatically unset after
51
+ # the block.
49
52
  def set_current_transaction(transaction) # rubocop:disable Naming/AccessorMethodName
50
53
  Thread.current[:appsignal_transaction] = transaction
54
+ yield if block_given?
55
+ ensure
56
+ clear_current_transaction! if block_given?
51
57
  end
52
58
 
53
59
  # Use when {Appsignal::Transaction.clear_current_transaction!} is stubbed to
@@ -50,6 +50,17 @@ module Appsignal
50
50
 
51
51
  class Extension
52
52
  class Transaction
53
+ if Appsignal.extension_loaded?
54
+ attr_reader :queue_start
55
+ alias original_set_queue_start set_queue_start
56
+ # Temporary helper until the extension returns this information
57
+ # https://github.com/appsignal/appsignal-agent/issues/293
58
+ def set_queue_start(start) # rubocop:disable Naming/AccessorMethodName
59
+ @queue_start = start
60
+ original_set_queue_start(start)
61
+ end
62
+ end
63
+
53
64
  alias original_finish finish if method_defined? :finish
54
65
 
55
66
  # Override default {Extension::Transaction#finish} behavior to always
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.0
4
+ version: 3.1.3
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: 2022-07-28 00:00:00.000000000 Z
13
+ date: 2022-08-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -69,35 +69,35 @@ dependencies:
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  - !ruby/object:Gem::Dependency
72
- name: webmock
72
+ name: yard
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - ">="
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: 0.9.20
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
- version: '0'
84
+ version: 0.9.20
85
85
  - !ruby/object:Gem::Dependency
86
- name: yard
86
+ name: pry
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
89
  - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: 0.9.20
91
+ version: '0'
92
92
  type: :development
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
- version: 0.9.20
98
+ version: '0'
99
99
  - !ruby/object:Gem::Dependency
100
- name: pry
100
+ name: webmock
101
101
  requirement: !ruby/object:Gem::Requirement
102
102
  requirements:
103
103
  - - ">="