appsignal 3.1.0-java → 3.1.3-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: 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
  - - ">="