appsignal 2.11.8-java → 3.0.1-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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/.rubocop_todo.yml +1 -1
  4. data/.semaphore/semaphore.yml +88 -111
  5. data/CHANGELOG.md +24 -0
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +11 -15
  8. data/lib/appsignal.rb +2 -29
  9. data/lib/appsignal/auth_check.rb +2 -8
  10. data/lib/appsignal/cli.rb +1 -23
  11. data/lib/appsignal/config.rb +1 -25
  12. data/lib/appsignal/event_formatter.rb +0 -25
  13. data/lib/appsignal/helpers/instrumentation.rb +69 -5
  14. data/lib/appsignal/hooks.rb +6 -13
  15. data/lib/appsignal/hooks/action_cable.rb +3 -34
  16. data/lib/appsignal/hooks/active_support_notifications.rb +7 -86
  17. data/lib/appsignal/hooks/celluloid.rb +5 -9
  18. data/lib/appsignal/hooks/net_http.rb +2 -12
  19. data/lib/appsignal/hooks/puma.rb +3 -5
  20. data/lib/appsignal/hooks/que.rb +1 -1
  21. data/lib/appsignal/hooks/rake.rb +2 -24
  22. data/lib/appsignal/hooks/redis.rb +2 -13
  23. data/lib/appsignal/hooks/resque.rb +2 -43
  24. data/lib/appsignal/hooks/sidekiq.rb +6 -143
  25. data/lib/appsignal/hooks/unicorn.rb +3 -24
  26. data/lib/appsignal/hooks/webmachine.rb +1 -7
  27. data/lib/appsignal/integrations/action_cable.rb +34 -0
  28. data/lib/appsignal/integrations/active_support_notifications.rb +77 -0
  29. data/lib/appsignal/integrations/net_http.rb +16 -0
  30. data/lib/appsignal/integrations/object.rb +39 -4
  31. data/lib/appsignal/integrations/padrino.rb +5 -7
  32. data/lib/appsignal/integrations/que.rb +26 -33
  33. data/lib/appsignal/integrations/railtie.rb +1 -4
  34. data/lib/appsignal/integrations/rake.rb +26 -2
  35. data/lib/appsignal/integrations/redis.rb +17 -0
  36. data/lib/appsignal/integrations/resque.rb +39 -10
  37. data/lib/appsignal/integrations/sidekiq.rb +171 -0
  38. data/lib/appsignal/integrations/unicorn.rb +28 -0
  39. data/lib/appsignal/integrations/webmachine.rb +22 -24
  40. data/lib/appsignal/minutely.rb +0 -12
  41. data/lib/appsignal/version.rb +1 -1
  42. data/spec/lib/appsignal/auth_check_spec.rb +1 -24
  43. data/spec/lib/appsignal/cli_spec.rb +1 -1
  44. data/spec/lib/appsignal/config_spec.rb +2 -66
  45. data/spec/lib/appsignal/event_formatter_spec.rb +0 -37
  46. data/spec/lib/appsignal/hooks/celluloid_spec.rb +6 -1
  47. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -2
  48. data/spec/lib/appsignal/hooks/redis_spec.rb +50 -15
  49. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +12 -464
  50. data/spec/lib/appsignal/hooks/unicorn_spec.rb +14 -3
  51. data/spec/lib/appsignal/hooks/webmachine_spec.rb +2 -13
  52. data/spec/lib/appsignal/hooks_spec.rb +6 -22
  53. data/spec/lib/appsignal/integrations/object_spec.rb +91 -8
  54. data/spec/lib/appsignal/integrations/padrino_spec.rb +2 -3
  55. data/spec/lib/appsignal/integrations/railtie_spec.rb +0 -45
  56. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +524 -0
  57. data/spec/lib/appsignal/integrations/webmachine_spec.rb +26 -8
  58. data/spec/lib/appsignal/minutely_spec.rb +0 -19
  59. data/spec/lib/appsignal/transaction_spec.rb +1 -14
  60. data/spec/lib/appsignal/transmitter_spec.rb +1 -1
  61. data/spec/lib/appsignal_spec.rb +162 -116
  62. data/spec/spec_helper.rb +1 -15
  63. metadata +11 -21
  64. data/lib/appsignal/cli/notify_of_deploy.rb +0 -131
  65. data/lib/appsignal/integrations/object_ruby_19.rb +0 -37
  66. data/lib/appsignal/integrations/object_ruby_modern.rb +0 -64
  67. data/lib/appsignal/integrations/resque_active_job.rb +0 -19
  68. data/lib/appsignal/js_exception_transaction.rb +0 -56
  69. data/lib/appsignal/rack/js_exception_catcher.rb +0 -80
  70. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +0 -180
  71. data/spec/lib/appsignal/integrations/object_19_spec.rb +0 -266
  72. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +0 -28
  73. data/spec/lib/appsignal/integrations/resque_spec.rb +0 -28
  74. data/spec/lib/appsignal/js_exception_transaction_spec.rb +0 -128
  75. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +0 -170
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Appsignal
6
+ module Integrations
7
+ # Error handler for Sidekiq to report errors from jobs and internal Sidekiq
8
+ # errors.
9
+ #
10
+ # @api private
11
+ class SidekiqErrorHandler
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
28
+
29
+ transaction.set_error(exception)
30
+ Appsignal::Transaction.complete_current!
31
+ end
32
+ end
33
+
34
+ # @api private
35
+ class SidekiqMiddleware # rubocop:disable Metrics/ClassLength
36
+ include Appsignal::Hooks::Helpers
37
+
38
+ EXCLUDED_JOB_KEYS = %w[
39
+ args backtrace class created_at enqueued_at error_backtrace error_class
40
+ error_message failed_at jid retried_at retry wrapped
41
+ ].freeze
42
+
43
+ def call(_worker, item, _queue)
44
+ job_status = nil
45
+ transaction = Appsignal::Transaction.create(
46
+ item["jid"],
47
+ Appsignal::Transaction::BACKGROUND_JOB,
48
+ Appsignal::Transaction::GenericRequest.new(
49
+ :queue_start => item["enqueued_at"]
50
+ )
51
+ )
52
+
53
+ Appsignal.instrument "perform_job.sidekiq" do
54
+ yield
55
+ end
56
+ rescue Exception => exception # rubocop:disable Lint/RescueException
57
+ job_status = :failed
58
+ raise exception
59
+ ensure
60
+ if transaction
61
+ transaction.set_action_if_nil(formatted_action_name(item))
62
+
63
+ params = filtered_arguments(item)
64
+ transaction.params = params if params
65
+
66
+ formatted_metadata(item).each do |key, value|
67
+ transaction.set_metadata key, value
68
+ end
69
+ transaction.set_http_or_background_queue_start
70
+ Appsignal::Transaction.complete_current! unless exception
71
+
72
+ queue = item["queue"] || "unknown"
73
+ if job_status
74
+ increment_counter "queue_job_count", 1,
75
+ :queue => queue,
76
+ :status => job_status
77
+ end
78
+ increment_counter "queue_job_count", 1,
79
+ :queue => queue,
80
+ :status => :processed
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def increment_counter(key, value, tags = {})
87
+ Appsignal.increment_counter "sidekiq_#{key}", value, tags
88
+ end
89
+
90
+ def formatted_action_name(job)
91
+ sidekiq_action_name = parse_action_name(job)
92
+ return unless sidekiq_action_name
93
+
94
+ complete_action = sidekiq_action_name =~ /\.|#/
95
+ return sidekiq_action_name if complete_action
96
+
97
+ "#{sidekiq_action_name}#perform"
98
+ end
99
+
100
+ def filtered_arguments(job)
101
+ arguments = parse_arguments(job)
102
+ return unless arguments
103
+
104
+ Appsignal::Utils::HashSanitizer.sanitize(
105
+ arguments,
106
+ Appsignal.config[:filter_parameters]
107
+ )
108
+ end
109
+
110
+ def formatted_metadata(item)
111
+ {}.tap do |hash|
112
+ (item || {}).each do |key, value|
113
+ next if EXCLUDED_JOB_KEYS.include?(key)
114
+
115
+ hash[key] = truncate(string_or_inspect(value))
116
+ end
117
+ end
118
+ end
119
+
120
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L316-L334
121
+ def parse_action_name(job)
122
+ args = job.fetch("args", [])
123
+ job_class = job["class"]
124
+ case job_class
125
+ when "Sidekiq::Extensions::DelayedModel"
126
+ safe_load(args[0], job_class) do |target, method, _|
127
+ "#{target.class}##{method}"
128
+ end
129
+ when /\ASidekiq::Extensions::Delayed/
130
+ safe_load(args[0], job_class) do |target, method, _|
131
+ "#{target}.#{method}"
132
+ end
133
+ else
134
+ job_class
135
+ end
136
+ end
137
+
138
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L336-L358
139
+ def parse_arguments(job)
140
+ args = job.fetch("args", [])
141
+ case job["class"]
142
+ when /\ASidekiq::Extensions::Delayed/
143
+ safe_load(args[0], args) do |_, _, arg|
144
+ arg
145
+ end
146
+ when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
147
+ nil # Set in the ActiveJob integration
148
+ else
149
+ # Sidekiq Enterprise argument encryption.
150
+ # More information: https://github.com/mperham/sidekiq/wiki/Ent-Encryption
151
+ if job["encrypt".freeze]
152
+ # No point in showing 150+ bytes of random garbage
153
+ args[-1] = "[encrypted data]".freeze
154
+ end
155
+ args
156
+ end
157
+ end
158
+
159
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L403-L412
160
+ def safe_load(content, default)
161
+ yield(*YAML.load(content))
162
+ rescue => error
163
+ # Sidekiq issue #1761: in dev mode, it's possible to have jobs enqueued
164
+ # which haven't been loaded into memory yet so the YAML can't be
165
+ # loaded.
166
+ Appsignal.logger.warn "Unable to load YAML: #{error.message}"
167
+ default
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ module UnicornIntegration
6
+ # Make sure that appsignal is started and the last transaction
7
+ # in a worker gets flushed.
8
+ #
9
+ # We'd love to be able to hook this into Unicorn in a less
10
+ # intrusive way, but this is the best we can do given the
11
+ # options we have.
12
+
13
+ module Server
14
+ def worker_loop(worker)
15
+ Appsignal.forked
16
+ super
17
+ end
18
+ end
19
+
20
+ module Worker
21
+ def close
22
+ Appsignal.stop("unicorn")
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -3,35 +3,33 @@
3
3
  module Appsignal
4
4
  module Integrations
5
5
  # @api private
6
- module WebmachinePlugin
7
- module FSM
8
- def run_with_appsignal
9
- transaction = Appsignal::Transaction.create(
10
- SecureRandom.uuid,
11
- Appsignal::Transaction::HTTP_REQUEST,
12
- request,
13
- :params_method => :query
14
- )
6
+ module WebmachineIntegration
7
+ def run
8
+ transaction = Appsignal::Transaction.create(
9
+ SecureRandom.uuid,
10
+ Appsignal::Transaction::HTTP_REQUEST,
11
+ request,
12
+ :params_method => :query
13
+ )
15
14
 
16
- transaction.set_action_if_nil("#{resource.class.name}##{request.method}")
15
+ transaction.set_action_if_nil("#{resource.class.name}##{request.method}")
17
16
 
18
- Appsignal.instrument("process_action.webmachine") do
19
- run_without_appsignal
20
- end
21
-
22
- Appsignal::Transaction.complete_current!
17
+ Appsignal.instrument("process_action.webmachine") do
18
+ super
23
19
  end
24
20
 
25
- private
21
+ Appsignal::Transaction.complete_current!
22
+ end
23
+
24
+ private
26
25
 
27
- def handle_exceptions_with_appsignal
28
- handle_exceptions_without_appsignal do
29
- begin
30
- yield
31
- rescue Exception => e # rubocop:disable Lint/RescueException
32
- Appsignal.set_error(e)
33
- raise e
34
- end
26
+ def handle_exceptions
27
+ super do
28
+ begin
29
+ yield
30
+ rescue Exception => e # rubocop:disable Lint/RescueException
31
+ Appsignal.set_error(e)
32
+ raise e
35
33
  end
36
34
  end
37
35
  end
@@ -3,8 +3,6 @@
3
3
  module Appsignal
4
4
  class Minutely
5
5
  class ProbeCollection
6
- include Appsignal::Utils::DeprecationMessage
7
-
8
6
  def initialize
9
7
  @probes = {}
10
8
  end
@@ -27,16 +25,6 @@ module Appsignal
27
25
  probes[key]
28
26
  end
29
27
 
30
- # @param probe [Object] Any object that listens to the `call` method will
31
- # be used as a probe.
32
- # @deprecated Use {#register} instead.
33
- # @return [void]
34
- def <<(probe)
35
- deprecation_message "Deprecated `Appsignal::Minute.probes <<` " \
36
- "call. Please use `Appsignal::Minutely.probes.register` instead."
37
- register probe.object_id, probe
38
- end
39
-
40
28
  # Register a new minutely probe.
41
29
  #
42
30
  # Supported probe types are:
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.8".freeze
4
+ VERSION = "3.0.1".freeze
5
5
  end
@@ -28,29 +28,6 @@ describe Appsignal::AuthCheck do
28
28
  end.join("&")
29
29
  end
30
30
 
31
- describe ".new" do
32
- describe "with logger argument" do
33
- let(:err_stream) { std_stream }
34
- let(:stderr) { err_stream.read }
35
- let(:log_stream) { std_stream }
36
- let(:log) { log_contents(log_stream) }
37
-
38
- it "logs and prints a deprecation message" do
39
- Appsignal.logger = test_logger(log_stream)
40
-
41
- capture_std_streams(std_stream, err_stream) do
42
- Appsignal::AuthCheck.new(config, Appsignal.logger)
43
- end
44
-
45
- deprecation_message =
46
- "`Appsignal::AuthCheck.new`'s `logger` argument " \
47
- "will be removed in the next major version."
48
- expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
49
- expect(log).to contains_log :warn, deprecation_message
50
- end
51
- end
52
- end
53
-
54
31
  describe "#perform" do
55
32
  subject { auth_check.perform }
56
33
 
@@ -62,7 +39,7 @@ describe Appsignal::AuthCheck do
62
39
  end
63
40
  end
64
41
 
65
- context "when encountering an exception", :not_ruby19 do
42
+ context "when encountering an exception" do
66
43
  before { stubbed_request.to_timeout }
67
44
 
68
45
  it "raises an error" do
@@ -16,7 +16,7 @@ describe Appsignal::CLI do
16
16
 
17
17
  expect(output).to include "appsignal <command> [options]"
18
18
  expect(output).to include \
19
- "Available commands: demo, diagnose, install, notify_of_deploy"
19
+ "Available commands: demo, diagnose, install"
20
20
  end
21
21
  end
22
22
 
@@ -168,8 +168,6 @@ describe Appsignal::Config do
168
168
  :push_api_key => "abc",
169
169
  :name => "TestApp",
170
170
  :active => true,
171
- :enable_frontend_error_catching => false,
172
- :frontend_error_catching_path => "/appsignal_error_catcher",
173
171
  :enable_allocation_tracking => true,
174
172
  :enable_gc_instrumentation => false,
175
173
  :enable_host_metrics => true,
@@ -357,70 +355,6 @@ describe Appsignal::Config do
357
355
  config
358
356
  end
359
357
  end
360
-
361
- describe "support for old config keys" do
362
- let(:err_stream) { std_stream }
363
- let(:stderr) { err_stream.read }
364
- let(:config) { project_fixture_config(env, {}, test_logger(log)) }
365
- let(:log) { StringIO.new }
366
- before { capture_std_streams(std_stream, err_stream) { config } }
367
-
368
- describe ":api_key" do
369
- context "without :push_api_key" do
370
- let(:env) { "old_config" }
371
-
372
- it "sets the :push_api_key with the old :api_key value" do
373
- expect(config[:push_api_key]).to eq "def"
374
- expect(config.config_hash).to_not have_key :api_key
375
-
376
- message = "Old configuration key found. Please update the 'api_key' to 'push_api_key'"
377
- expect(stderr).to include "appsignal WARNING: #{message}"
378
- expect(log_contents(log)).to contains_log :warn, message
379
- end
380
- end
381
-
382
- context "with :push_api_key" do
383
- let(:env) { "old_config_mixed_with_new_config" }
384
-
385
- it "ignores the :api_key config and deletes it" do
386
- expect(config[:push_api_key]).to eq "ghi"
387
- expect(config.config_hash).to_not have_key :api_key
388
-
389
- message = "Old configuration key found. Please update the 'api_key' to 'push_api_key'"
390
- expect(stderr).to include "appsignal WARNING: #{message}"
391
- expect(log_contents(log)).to contains_log :warn, message
392
- end
393
- end
394
- end
395
-
396
- describe ":ignore_exceptions" do
397
- context "without :ignore_errors" do
398
- let(:env) { "old_config" }
399
-
400
- it "sets :ignore_errors with the old :ignore_exceptions value" do
401
- expect(config[:ignore_errors]).to eq ["StandardError"]
402
- expect(config.config_hash).to_not have_key :ignore_exceptions
403
-
404
- message = "Old configuration key found. Please update the 'ignore_exceptions' to 'ignore_errors'"
405
- expect(stderr).to include "appsignal WARNING: #{message}"
406
- expect(log_contents(log)).to contains_log :warn, message
407
- end
408
- end
409
-
410
- context "with :ignore_errors" do
411
- let(:env) { "old_config_mixed_with_new_config" }
412
-
413
- it "ignores the :ignore_exceptions config" do
414
- expect(config[:ignore_errors]).to eq ["NoMethodError"]
415
- expect(config.config_hash).to_not have_key :ignore_exceptions
416
-
417
- message = "Old configuration key found. Please update the 'ignore_exceptions' to 'ignore_errors'"
418
- expect(stderr).to include "appsignal WARNING: #{message}"
419
- expect(log_contents(log)).to contains_log :warn, message
420
- end
421
- end
422
- end
423
- end
424
358
  end
425
359
 
426
360
  context "with config in the environment" do
@@ -440,6 +374,7 @@ describe Appsignal::Config do
440
374
  :active => true,
441
375
  :name => "App name",
442
376
  :debug => true,
377
+ :dns_servers => ["8.8.8.8", "8.8.4.4"],
443
378
  :ignore_actions => %w[action1 action2],
444
379
  :ignore_errors => %w[ExampleStandardError AnotherError],
445
380
  :ignore_namespaces => %w[admin private_namespace],
@@ -459,6 +394,7 @@ describe Appsignal::Config do
459
394
  ENV["APPSIGNAL_ACTIVE"] = "true"
460
395
  ENV["APPSIGNAL_APP_NAME"] = "App name"
461
396
  ENV["APPSIGNAL_DEBUG"] = "true"
397
+ ENV["APPSIGNAL_DNS_SERVERS"] = "8.8.8.8,8.8.4.4"
462
398
  ENV["APPSIGNAL_IGNORE_ACTIONS"] = "action1,action2"
463
399
  ENV["APPSIGNAL_IGNORE_ERRORS"] = "ExampleStandardError,AnotherError"
464
400
  ENV["APPSIGNAL_IGNORE_NAMESPACES"] = "admin,private_namespace"
@@ -114,43 +114,6 @@ describe Appsignal::EventFormatter do
114
114
  end
115
115
  end
116
116
  end
117
-
118
- context "when registering deprecated formatters" do
119
- let(:err_stream) { std_stream }
120
- let(:stderr) { err_stream.read }
121
- let(:deprecated_formatter) do
122
- Class.new(Appsignal::EventFormatter) do
123
- register "mock.deprecated"
124
-
125
- def format(_payload)
126
- end
127
- end
128
- end
129
-
130
- it "registers deprecated formatters and logs & prints a warning" do
131
- message = "Formatter for 'mock.deprecated' is using a deprecated registration method. " \
132
- "This event formatter will not be loaded. " \
133
- "Please update the formatter according to the documentation at: " \
134
- "https://docs.appsignal.com/ruby/instrumentation/event-formatters.html"
135
-
136
- logs = capture_logs do
137
- capture_std_streams(std_stream, err_stream) { deprecated_formatter }
138
- end
139
- expect(logs).to contains_log :warn, message
140
- expect(stderr).to include "appsignal WARNING: #{message}"
141
-
142
- expect(klass.deprecated_formatter_classes.keys).to include("mock.deprecated")
143
- end
144
-
145
- it "initializes deprecated formatters" do
146
- capture_std_streams(std_stream, err_stream) { deprecated_formatter }
147
- klass.initialize_deprecated_formatters
148
-
149
- expect(klass.registered?("mock.deprecated")).to be_truthy
150
- expect(klass.formatters["mock.deprecated"]).to be_instance_of(deprecated_formatter)
151
- expect(klass.deprecated_formatter_classes["mock.deprecated"]).to eq(deprecated_formatter)
152
- end
153
- end
154
117
  end
155
118
 
156
119
  describe ".registered?" do