appsignal 3.4.12-java → 3.4.14-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.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ module DryMonitorIntegration
6
+ def instrument(event_id, payload = {}, &block)
7
+ Appsignal::Transaction.current.start_event
8
+
9
+ super
10
+ ensure
11
+ title, body, body_format = Appsignal::EventFormatter.format("#{event_id}.dry", payload)
12
+
13
+ Appsignal::Transaction.current.finish_event(
14
+ title || event_id.to_s,
15
+ title,
16
+ body,
17
+ body_format
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -36,9 +36,14 @@ module Appsignal
36
36
 
37
37
  Appsignal.start
38
38
 
39
- if Appsignal.config[:enable_rails_error_reporter] && Rails.respond_to?(:error) # rubocop:disable Style/GuardClause
40
- Rails.error.subscribe(Appsignal::Integrations::RailsErrorReporterSubscriber)
41
- end
39
+ initialize_error_reporter
40
+ end
41
+
42
+ def self.initialize_error_reporter
43
+ return unless Appsignal.config[:enable_rails_error_reporter]
44
+ return unless Rails.respond_to?(:error)
45
+
46
+ Rails.error.subscribe(Appsignal::Integrations::RailsErrorReporterSubscriber)
42
47
  end
43
48
  end
44
49
 
@@ -9,7 +9,7 @@ module Appsignal
9
9
  #
10
10
  # @api private
11
11
  class SidekiqErrorHandler
12
- def call(exception, sidekiq_context)
12
+ def call(exception, sidekiq_context, _sidekiq_config = nil)
13
13
  transaction =
14
14
  if Appsignal::Transaction.current?
15
15
  Appsignal::Transaction.current
@@ -78,9 +78,9 @@ module Appsignal
78
78
  redis_info = adapter.redis_info
79
79
  return unless redis_info
80
80
 
81
- gauge "connection_count", redis_info.fetch("connected_clients")
82
- gauge "memory_usage", redis_info.fetch("used_memory")
83
- gauge "memory_usage_rss", redis_info.fetch("used_memory_rss")
81
+ gauge "connection_count", redis_info["connected_clients"]
82
+ gauge "memory_usage", redis_info["used_memory"]
83
+ gauge "memory_usage_rss", redis_info["used_memory_rss"]
84
84
  end
85
85
 
86
86
  def track_stats
@@ -112,6 +112,8 @@ module Appsignal
112
112
 
113
113
  # Track a gauge metric with the `sidekiq_` prefix
114
114
  def gauge(key, value, tags = {})
115
+ return if value.nil?
116
+
115
117
  tags[:hostname] = hostname if hostname
116
118
  Appsignal.set_gauge "sidekiq_#{key}", value, tags
117
119
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.4.12"
4
+ VERSION = "3.4.14"
5
5
  end
@@ -436,7 +436,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
436
436
  "boot" => { "started" => { "result" => true } },
437
437
  "host" => {
438
438
  "uid" => { "result" => Process.uid },
439
- "gid" => { "result" => Process.gid }
439
+ "gid" => { "result" => Process.gid },
440
+ "running_in_container" => { "result" => Appsignal::Extension.running_in_container? }
440
441
  },
441
442
  "config" => { "valid" => { "result" => true } },
442
443
  "logger" => { "started" => { "result" => true } },
@@ -781,6 +782,9 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
781
782
  "file" => {},
782
783
  "env" => {},
783
784
  "override" => { "send_session_data" => true }
785
+ },
786
+ "modifiers" => {
787
+ "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR" => ""
784
788
  }
785
789
  )
786
790
  end
@@ -912,6 +916,28 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
912
916
  end
913
917
  end
914
918
 
919
+ describe "modifiers" do
920
+ before do
921
+ ENV["APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR"] = "1"
922
+ run
923
+ end
924
+
925
+ it "outputs config modifiers" do
926
+ expect(output).to include(
927
+ "Configuration modifiers\n" \
928
+ " APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR: \"1\""
929
+ )
930
+ end
931
+
932
+ it "transmits config modifiers in report" do
933
+ expect(received_report["config"]).to include(
934
+ "modifiers" => {
935
+ "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR" => "1"
936
+ }
937
+ )
938
+ end
939
+ end
940
+
915
941
  it "transmits config in report" do
916
942
  run
917
943
  additional_initial_config = {}
@@ -935,6 +961,9 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
935
961
  "file" => hash_with_string_keys(config.file_config),
936
962
  "env" => {},
937
963
  "override" => { "send_session_data" => true }
964
+ },
965
+ "modifiers" => {
966
+ "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR" => ""
938
967
  }
939
968
  )
940
969
  end
@@ -963,6 +992,9 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
963
992
  "file" => hash_with_string_keys(config.file_config),
964
993
  "env" => {},
965
994
  "override" => { "send_session_data" => true }
995
+ },
996
+ "modifiers" => {
997
+ "APPSIGNAL_INACTIVE_ON_CONFIG_FILE_ERROR" => ""
966
998
  }
967
999
  )
968
1000
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Appsignal::EventFormatter::Rom::SqlFormatter do
4
+ let(:klass) { described_class }
5
+ let(:formatter) { klass.new }
6
+
7
+ it "registers the sql event formatter" do
8
+ expect(Appsignal::EventFormatter.registered?("sql.dry", klass)).to be_truthy
9
+ end
10
+
11
+ describe "#format" do
12
+ let(:payload) do
13
+ {
14
+ :name => "postgres",
15
+ :query => "SELECT * FROM users"
16
+ }
17
+ end
18
+ subject { formatter.format(payload) }
19
+
20
+ it { is_expected.to eq ["query.postgres", "SELECT * FROM users", 1] }
21
+ end
22
+ end
@@ -22,6 +22,12 @@ describe Appsignal::Hooks::ActiveSupportNotificationsHook do
22
22
 
23
23
  it_behaves_like "activesupport instrument override"
24
24
 
25
+ if defined?(::ActiveSupport::Notifications::Fanout::Handle)
26
+ require_relative "./active_support_notifications/start_finish_shared_examples"
27
+
28
+ it_behaves_like "activesupport start finish override"
29
+ end
30
+
25
31
  if ::ActiveSupport::Notifications::Instrumenter.method_defined?(:start)
26
32
  require_relative "./active_support_notifications/start_finish_shared_examples"
27
33
 
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ if DependencyHelper.dry_monitor_present?
4
+ require "dry-monitor"
5
+
6
+ describe Appsignal::Hooks::DryMonitorHook do
7
+ describe "#dependencies_present?" do
8
+ subject { described_class.new.dependencies_present? }
9
+
10
+ context "when Dry::Monitor::Notifications constant is found" do
11
+ before { stub_const "Dry::Monitor::Notifications", Class.new }
12
+
13
+ it { is_expected.to be_truthy }
14
+ end
15
+
16
+ context "when Dry::Monitor::Notifications constant is not found" do
17
+ before { hide_const "Dry::Monitor::Notifications" }
18
+
19
+ it { is_expected.to be_falsy }
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#install" do
25
+ it "installs the dry-monitor hook" do
26
+ start_agent
27
+
28
+ expect(Dry::Monitor::Notifications.included_modules).to include(
29
+ Appsignal::Integrations::DryMonitorIntegration
30
+ )
31
+ end
32
+ end
33
+
34
+ describe "Dry Monitor Integration" do
35
+ before :context do
36
+ start_agent
37
+ end
38
+
39
+ let!(:transaction) do
40
+ Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
41
+ end
42
+
43
+ let(:notifications) { Dry::Monitor::Notifications.new(:test) }
44
+
45
+ context "when is a dry-sql event" do
46
+ let(:event_id) { :sql }
47
+ let(:payload) do
48
+ {
49
+ :name => "postgres",
50
+ :query => "SELECT * FROM users"
51
+ }
52
+ end
53
+
54
+ it "creates an sql event" do
55
+ notifications.instrument(event_id, payload)
56
+ expect(transaction.to_h["events"]).to match([
57
+ {
58
+ "allocation_count" => kind_of(Integer),
59
+ "body" => "SELECT * FROM users",
60
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
61
+ "child_allocation_count" => kind_of(Integer),
62
+ "child_duration" => kind_of(Float),
63
+ "child_gc_duration" => kind_of(Float),
64
+ "count" => 1,
65
+ "duration" => kind_of(Float),
66
+ "gc_duration" => kind_of(Float),
67
+ "name" => "query.postgres",
68
+ "start" => kind_of(Float),
69
+ "title" => "query.postgres"
70
+ }
71
+ ])
72
+ end
73
+ end
74
+
75
+ context "when is an unregistered formatter event" do
76
+ let(:event_id) { :foo }
77
+ let(:payload) do
78
+ {
79
+ :name => "foo"
80
+ }
81
+ end
82
+
83
+ it "creates a generic event" do
84
+ notifications.instrument(event_id, payload)
85
+ expect(transaction.to_h["events"]).to match([
86
+ {
87
+ "allocation_count" => kind_of(Integer),
88
+ "body" => "",
89
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
90
+ "child_allocation_count" => kind_of(Integer),
91
+ "child_duration" => kind_of(Float),
92
+ "child_gc_duration" => kind_of(Float),
93
+ "count" => 1,
94
+ "duration" => kind_of(Float),
95
+ "gc_duration" => kind_of(Float),
96
+ "name" => "foo",
97
+ "start" => kind_of(Float),
98
+ "title" => ""
99
+ }
100
+ ])
101
+ end
102
+ end
103
+ end
104
+ end
@@ -2,6 +2,10 @@ if DependencyHelper.rails_present?
2
2
  require "action_mailer"
3
3
 
4
4
  describe Appsignal::Integrations::Railtie do
5
+ include RailsHelper
6
+
7
+ after { clear_rails_error_reporter! }
8
+
5
9
  context "after initializing the app" do
6
10
  it "should call initialize_appsignal" do
7
11
  expect(Appsignal::Integrations::Railtie).to receive(:initialize_appsignal)
@@ -125,17 +129,16 @@ if DependencyHelper.rails_present?
125
129
 
126
130
  if Rails.respond_to?(:error)
127
131
  describe "Rails error reporter" do
128
- before do
129
- Appsignal::Integrations::Railtie.initialize_appsignal(app)
130
- start_agent
131
- end
132
+ before { start_agent }
132
133
  around { |example| keep_transactions { example.run } }
133
134
 
134
135
  context "when error is not handled (reraises the error)" do
135
136
  it "does nothing" do
136
- expect do
137
- Rails.error.record { raise ExampleStandardError }
138
- end.to raise_error(ExampleStandardError)
137
+ with_rails_error_reporter do
138
+ expect do
139
+ Rails.error.record { raise ExampleStandardError }
140
+ end.to raise_error(ExampleStandardError)
141
+ end
139
142
 
140
143
  expect(created_transactions).to be_empty
141
144
  end
@@ -151,26 +154,28 @@ if DependencyHelper.rails_present?
151
154
  :duplicated_tag => "duplicated value"
152
155
  )
153
156
 
154
- with_current_transaction current_transaction do
155
- Rails.error.handle { raise ExampleStandardError }
156
-
157
- transaction = last_transaction
158
- transaction_hash = transaction.to_h
159
- expect(transaction_hash).to include(
160
- "action" => "CustomAction",
161
- "namespace" => "custom",
162
- "error" => {
163
- "name" => "ExampleStandardError",
164
- "message" => "ExampleStandardError",
165
- "backtrace" => kind_of(String)
166
- },
167
- "sample_data" => hash_including(
168
- "tags" => {
169
- "duplicated_tag" => "duplicated value",
170
- "severity" => "warning"
171
- }
157
+ with_rails_error_reporter do
158
+ with_current_transaction current_transaction do
159
+ Rails.error.handle { raise ExampleStandardError }
160
+
161
+ transaction = last_transaction
162
+ transaction_hash = transaction.to_h
163
+ expect(transaction_hash).to include(
164
+ "action" => "CustomAction",
165
+ "namespace" => "custom",
166
+ "error" => {
167
+ "name" => "ExampleStandardError",
168
+ "message" => "ExampleStandardError",
169
+ "backtrace" => kind_of(String)
170
+ },
171
+ "sample_data" => hash_including(
172
+ "tags" => hash_including(
173
+ "duplicated_tag" => "duplicated value",
174
+ "severity" => "warning"
175
+ )
176
+ )
172
177
  )
173
- )
178
+ end
174
179
  end
175
180
  end
176
181
 
@@ -178,48 +183,52 @@ if DependencyHelper.rails_present?
178
183
  current_transaction = http_request_transaction
179
184
  current_transaction.set_tags(:tag1 => "duplicated value")
180
185
 
181
- with_current_transaction current_transaction do
182
- given_context = { :tag1 => "value1", :tag2 => "value2" }
183
- Rails.error.handle(:context => given_context) { raise ExampleStandardError }
184
-
185
- transaction = last_transaction
186
- transaction_hash = transaction.to_h
187
- expect(transaction_hash).to include(
188
- "sample_data" => hash_including(
189
- "tags" => {
190
- "tag1" => "value1",
191
- "tag2" => "value2",
192
- "severity" => "warning"
193
- }
186
+ with_rails_error_reporter do
187
+ with_current_transaction current_transaction do
188
+ given_context = { :tag1 => "value1", :tag2 => "value2" }
189
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
190
+
191
+ transaction = last_transaction
192
+ transaction_hash = transaction.to_h
193
+ expect(transaction_hash).to include(
194
+ "sample_data" => hash_including(
195
+ "tags" => hash_including(
196
+ "tag1" => "value1",
197
+ "tag2" => "value2",
198
+ "severity" => "warning"
199
+ )
200
+ )
194
201
  )
195
- )
202
+ end
196
203
  end
197
204
  end
198
205
 
199
206
  it "sends tags stored in :appsignal -> :custom_data as custom data" do
200
207
  current_transaction = http_request_transaction
201
208
 
202
- with_current_transaction current_transaction do
203
- given_context = {
204
- :appsignal => {
205
- :custom_data => {
206
- :array => [1, 2],
207
- :hash => { :one => 1, :two => 2 }
209
+ with_rails_error_reporter do
210
+ with_current_transaction current_transaction do
211
+ given_context = {
212
+ :appsignal => {
213
+ :custom_data => {
214
+ :array => [1, 2],
215
+ :hash => { :one => 1, :two => 2 }
216
+ }
208
217
  }
209
218
  }
210
- }
211
- Rails.error.handle(:context => given_context) { raise ExampleStandardError }
212
-
213
- transaction = last_transaction
214
- transaction_hash = transaction.to_h
215
- expect(transaction_hash).to include(
216
- "sample_data" => hash_including(
217
- "custom_data" => {
218
- "array" => [1, 2],
219
- "hash" => { "one" => 1, "two" => 2 }
220
- }
219
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
220
+
221
+ transaction = last_transaction
222
+ transaction_hash = transaction.to_h
223
+ expect(transaction_hash).to include(
224
+ "sample_data" => hash_including(
225
+ "custom_data" => {
226
+ "array" => [1, 2],
227
+ "hash" => { "one" => 1, "two" => 2 }
228
+ }
229
+ )
221
230
  )
222
- )
231
+ end
223
232
  end
224
233
  end
225
234
 
@@ -228,18 +237,20 @@ if DependencyHelper.rails_present?
228
237
  current_transaction.set_namespace "custom"
229
238
  current_transaction.set_action "CustomAction"
230
239
 
231
- with_current_transaction current_transaction do
232
- given_context = {
233
- :appsignal => { :namespace => "context", :action => "ContextAction" }
234
- }
235
- Rails.error.handle(:context => given_context) { raise ExampleStandardError }
240
+ with_rails_error_reporter do
241
+ with_current_transaction current_transaction do
242
+ given_context = {
243
+ :appsignal => { :namespace => "context", :action => "ContextAction" }
244
+ }
245
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
236
246
 
237
- transaction = last_transaction
238
- transaction_hash = transaction.to_h
239
- expect(transaction_hash).to include(
240
- "namespace" => "context",
241
- "action" => "ContextAction"
242
- )
247
+ transaction = last_transaction
248
+ transaction_hash = transaction.to_h
249
+ expect(transaction_hash).to include(
250
+ "namespace" => "context",
251
+ "action" => "ContextAction"
252
+ )
253
+ end
243
254
  end
244
255
  end
245
256
  end
@@ -267,7 +278,9 @@ if DependencyHelper.rails_present?
267
278
  it "fetches the action from the controller in the context" do
268
279
  # The controller key is set by Rails when raised in a controller
269
280
  given_context = { :controller => ExampleRailsControllerMock.new }
270
- Rails.error.handle(:context => given_context) { raise ExampleStandardError }
281
+ with_rails_error_reporter do
282
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
283
+ end
271
284
 
272
285
  transaction = last_transaction
273
286
  transaction_hash = transaction.to_h
@@ -278,7 +291,9 @@ if DependencyHelper.rails_present?
278
291
 
279
292
  it "sets no action if no execution context is present" do
280
293
  # The controller key is set by Rails when raised in a controller
281
- Rails.error.handle { raise ExampleStandardError }
294
+ with_rails_error_reporter do
295
+ Rails.error.handle { raise ExampleStandardError }
296
+ end
282
297
 
283
298
  transaction = last_transaction
284
299
  transaction_hash = transaction.to_h
@@ -296,17 +311,19 @@ if DependencyHelper.rails_present?
296
311
  :tag1 => "value1",
297
312
  :tag2 => "value2"
298
313
  }
299
- Rails.error.handle(:context => given_context) { raise ExampleStandardError }
314
+ with_rails_error_reporter do
315
+ Rails.error.handle(:context => given_context) { raise ExampleStandardError }
316
+ end
300
317
 
301
318
  transaction = last_transaction
302
319
  transaction_hash = transaction.to_h
303
320
  expect(transaction_hash).to include(
304
321
  "sample_data" => hash_including(
305
- "tags" => {
322
+ "tags" => hash_including(
306
323
  "tag1" => "value1",
307
324
  "tag2" => "value2",
308
325
  "severity" => "warning"
309
- }
326
+ )
310
327
  )
311
328
  )
312
329
  end
@@ -266,13 +266,16 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
266
266
 
267
267
  if DependencyHelper.rails7_present?
268
268
  context "with Rails error reporter" do
269
+ include RailsHelper
270
+
269
271
  it "reports the worker name as the action, copies the namespace and tags" do
270
- Appsignal::Integrations::Railtie.initialize_appsignal(MyApp::Application.new)
271
272
  Appsignal.config = project_fixture_config("production")
272
- perform_job do
273
- Appsignal.tag_job("test_tag" => "value")
274
- Rails.error.handle do
275
- raise error, "uh oh"
273
+ with_rails_error_reporter do
274
+ perform_job do
275
+ Appsignal.tag_job("test_tag" => "value")
276
+ Rails.error.handle do
277
+ raise ExampleStandardError, "uh oh"
278
+ end
276
279
  end
277
280
  end
278
281
 
@@ -363,7 +366,9 @@ if DependencyHelper.active_job_present?
363
366
  require "sidekiq/testing"
364
367
 
365
368
  describe "Sidekiq ActiveJob integration" do
369
+ include RailsHelper
366
370
  include ActiveJobHelpers
371
+
367
372
  let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
368
373
  let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
369
374
  let(:log) { StringIO.new }
@@ -416,7 +421,7 @@ if DependencyHelper.active_job_present?
416
421
  ["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"]
417
422
  end
418
423
  end
419
- before do
424
+ around do |example|
420
425
  start_agent
421
426
  Appsignal.logger = test_logger(log)
422
427
  ActiveJob::Base.queue_adapter = :sidekiq
@@ -441,11 +446,11 @@ if DependencyHelper.active_job_present?
441
446
  Sidekiq::Testing.server_middleware do |chain|
442
447
  chain.add Appsignal::Integrations::SidekiqMiddleware
443
448
  end
444
- end
445
- around do |example|
446
- keep_transactions do
447
- Sidekiq::Testing.fake! do
448
- example.run
449
+ with_rails_error_reporter do
450
+ keep_transactions do
451
+ Sidekiq::Testing.fake! do
452
+ example.run
453
+ end
449
454
  end
450
455
  end
451
456
  end
@@ -73,6 +73,20 @@ describe Appsignal::Probes::SidekiqProbe do
73
73
  module Sidekiq7Mock
74
74
  VERSION = "7.0.0".freeze
75
75
 
76
+ def self.redis_info_data=(info)
77
+ @redis_info_data = info
78
+ end
79
+
80
+ def self.redis_info_data
81
+ return @redis_info_data if defined?(@redis_info_data)
82
+
83
+ {
84
+ "connected_clients" => 2,
85
+ "used_memory" => 1024,
86
+ "used_memory_rss" => 512
87
+ }
88
+ end
89
+
76
90
  def self.redis
77
91
  yield Client.new
78
92
  end
@@ -83,11 +97,7 @@ describe Appsignal::Probes::SidekiqProbe do
83
97
  end
84
98
 
85
99
  def info
86
- {
87
- "connected_clients" => 2,
88
- "used_memory" => 1024,
89
- "used_memory_rss" => 512
90
- }
100
+ Sidekiq7Mock.redis_info_data
91
101
  end
92
102
  end
93
103
 
@@ -227,6 +237,19 @@ describe Appsignal::Probes::SidekiqProbe do
227
237
  probe.call
228
238
  probe.call
229
239
  end
240
+
241
+ context "when redis info doesn't contain requested keys" do
242
+ before { Sidekiq7Mock.redis_info_data = {} }
243
+
244
+ it "doesn't create metrics for nil values" do
245
+ expect_gauge("connection_count").never
246
+ expect_gauge("memory_usage").never
247
+ expect_gauge("memory_usage_rss").never
248
+ # Call probe twice so we can calculate the delta for some gauge values
249
+ probe.call
250
+ probe.call
251
+ end
252
+ end
230
253
  end
231
254
 
232
255
  context "with Sidekiq 6" do
@@ -301,7 +324,7 @@ describe Appsignal::Probes::SidekiqProbe do
301
324
  end
302
325
  end
303
326
 
304
- def expect_gauge(key, value, tags = {})
327
+ def expect_gauge(key, value = anything, tags = {})
305
328
  expect(Appsignal).to receive(:set_gauge)
306
329
  .with("sidekiq_#{key}", value, expected_default_tags.merge(tags))
307
330
  .and_call_original
@@ -115,6 +115,10 @@ module DependencyHelper
115
115
  dependency_present? "hanami"
116
116
  end
117
117
 
118
+ def dry_monitor_present?
119
+ dependency_present? "dry-monitor"
120
+ end
121
+
118
122
  def hanami2_present?
119
123
  hanami_present? && Gem.loaded_specs["hanami"].version >= Gem::Version.new("2.0")
120
124
  end