appsignal 2.11.0.alpha.2-java → 2.11.0.beta.5-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 +4 -4
- data/.rubocop.yml +3 -0
- data/.semaphore/semaphore.yml +94 -10
- data/CHANGELOG.md +31 -1
- data/README.md +4 -4
- data/Rakefile +16 -4
- data/appsignal.gemspec +1 -1
- data/build_matrix.yml +7 -3
- data/ext/Rakefile +2 -0
- data/ext/agent.yml +19 -19
- data/ext/base.rb +7 -0
- data/ext/extconf.rb +2 -0
- data/gemfiles/rails-4.2.gemfile +9 -2
- data/gemfiles/rails-5.0.gemfile +1 -0
- data/gemfiles/rails-5.1.gemfile +1 -0
- data/gemfiles/rails-5.2.gemfile +1 -0
- data/gemfiles/rails-6.0.gemfile +1 -0
- data/gemfiles/resque-1.gemfile +7 -0
- data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
- data/lib/appsignal.rb +1 -0
- data/lib/appsignal/auth_check.rb +4 -2
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/config.rb +35 -2
- data/lib/appsignal/extension.rb +6 -5
- data/lib/appsignal/extension/jruby.rb +6 -5
- data/lib/appsignal/hooks.rb +25 -0
- data/lib/appsignal/hooks/active_job.rb +137 -0
- data/lib/appsignal/hooks/puma.rb +0 -1
- data/lib/appsignal/hooks/resque.rb +60 -0
- data/lib/appsignal/hooks/sidekiq.rb +17 -94
- data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +9 -12
- data/lib/appsignal/integrations/resque_active_job.rb +9 -32
- data/lib/appsignal/probes.rb +7 -0
- data/lib/appsignal/probes/puma.rb +1 -1
- data/lib/appsignal/probes/sidekiq.rb +3 -1
- data/lib/appsignal/transaction.rb +10 -0
- data/lib/appsignal/utils/deprecation_message.rb +6 -2
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/auth_check_spec.rb +23 -0
- data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
- data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
- data/spec/lib/appsignal/config_spec.rb +21 -0
- data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
- data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
- data/spec/lib/appsignal/hooks/activejob_spec.rb +591 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +3 -14
- data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +222 -268
- data/spec/lib/appsignal/hooks_spec.rb +57 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -179
- data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
- data/spec/lib/appsignal/marker_spec.rb +1 -1
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +10 -7
- data/spec/lib/appsignal/transaction_spec.rb +5 -7
- data/spec/spec_helper.rb +5 -0
- data/spec/support/helpers/action_mailer_helpers.rb +25 -0
- data/spec/support/helpers/config_helpers.rb +3 -2
- data/spec/support/helpers/dependency_helper.rb +9 -2
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- data/spec/support/stubs/sidekiq/api.rb +1 -1
- data/spec/support/testing.rb +19 -19
- metadata +16 -4
@@ -4,38 +4,15 @@ module Appsignal
|
|
4
4
|
module Integrations
|
5
5
|
# @api private
|
6
6
|
module ResqueActiveJobPlugin
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
queue_start =
|
18
|
-
if job.respond_to?(:enqueued_at) && job.enqueued_at
|
19
|
-
Time.parse(job.enqueued_at).utc
|
20
|
-
end
|
21
|
-
|
22
|
-
Appsignal.monitor_single_transaction(
|
23
|
-
"perform_job.resque",
|
24
|
-
:class => job.class.to_s,
|
25
|
-
:method => "perform",
|
26
|
-
:params => params,
|
27
|
-
:queue_start => queue_start,
|
28
|
-
:metadata => {
|
29
|
-
:id => job.job_id,
|
30
|
-
:queue => job.queue_name
|
31
|
-
}
|
32
|
-
) do
|
33
|
-
block.call
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
Appsignal::Environment.report("ruby_active_job_resque_enabled") { true }
|
7
|
+
def self.included(_)
|
8
|
+
callers = caller
|
9
|
+
Appsignal::Utils::DeprecationMessage.message \
|
10
|
+
"The AppSignal ResqueActiveJobPlugin is deprecated and does " \
|
11
|
+
"nothing on extend. In this version of the AppSignal Ruby gem " \
|
12
|
+
"the integration with Resque is automatic on all Resque workers. " \
|
13
|
+
"Please remove the following line from this file to remove this " \
|
14
|
+
"message: include Appsignal::Integrations::ResqueActiveJobPlugin\n" \
|
15
|
+
"#{callers.first}"
|
39
16
|
end
|
40
17
|
end
|
41
18
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module Appsignal
|
2
2
|
module Probes
|
3
|
-
# @api private
|
4
3
|
class SidekiqProbe
|
4
|
+
# @api private
|
5
5
|
attr_reader :config
|
6
6
|
|
7
|
+
# @api private
|
7
8
|
def self.dependencies_present?
|
8
9
|
Gem::Version.new(::Redis::VERSION) >= Gem::Version.new("3.3.5")
|
9
10
|
end
|
@@ -16,6 +17,7 @@ module Appsignal
|
|
16
17
|
require "sidekiq/api"
|
17
18
|
end
|
18
19
|
|
20
|
+
# @api private
|
19
21
|
def call
|
20
22
|
track_redis_info
|
21
23
|
track_stats
|
@@ -221,6 +221,16 @@ module Appsignal
|
|
221
221
|
set_action_if_nil(group_and_action.compact.join("#"))
|
222
222
|
end
|
223
223
|
|
224
|
+
# Set queue start time for transaction.
|
225
|
+
#
|
226
|
+
# Most commononly called by {set_http_or_background_queue_start}.
|
227
|
+
#
|
228
|
+
# @param start [Integer] Queue start time in milliseconds.
|
229
|
+
# @raise [RangeError] When the queue start time value is too big, this
|
230
|
+
# method raises a RangeError.
|
231
|
+
# @raise [TypeError] Raises a TypeError when the given `start` argument is
|
232
|
+
# not an Integer.
|
233
|
+
# @return [void]
|
224
234
|
def set_queue_start(start)
|
225
235
|
return unless start
|
226
236
|
@ext.set_queue_start(start)
|
@@ -1,10 +1,14 @@
|
|
1
1
|
module Appsignal
|
2
2
|
module Utils
|
3
3
|
module DeprecationMessage
|
4
|
-
def
|
5
|
-
|
4
|
+
def self.message(message, logger = Appsignal.logger)
|
5
|
+
Kernel.warn "appsignal WARNING: #{message}"
|
6
6
|
logger.warn message
|
7
7
|
end
|
8
|
+
|
9
|
+
def deprecation_message(message, logger = Appsignal.logger)
|
10
|
+
Appsignal::Utils::DeprecationMessage.message(message, logger)
|
11
|
+
end
|
8
12
|
end
|
9
13
|
end
|
10
14
|
end
|
data/lib/appsignal/version.rb
CHANGED
@@ -28,6 +28,29 @@ 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
|
+
|
31
54
|
describe "#perform" do
|
32
55
|
subject { auth_check.perform }
|
33
56
|
|
@@ -10,7 +10,7 @@ if DependencyHelper.capistrano2_present?
|
|
10
10
|
let(:capistrano_config) do
|
11
11
|
Capistrano::Configuration.new.tap do |c|
|
12
12
|
c.set(:rails_env, "production")
|
13
|
-
c.set(:repository, "
|
13
|
+
c.set(:repository, "main")
|
14
14
|
c.set(:deploy_to, "/home/username/app")
|
15
15
|
c.set(:current_release, "")
|
16
16
|
c.set(:current_revision, "503ce0923ed177a3ce000005")
|
@@ -15,7 +15,7 @@ if DependencyHelper.capistrano3_present?
|
|
15
15
|
c.set(:log_level, :error)
|
16
16
|
c.set(:logger, logger)
|
17
17
|
c.set(:rails_env, "production")
|
18
|
-
c.set(:repository, "
|
18
|
+
c.set(:repository, "main")
|
19
19
|
c.set(:deploy_to, "/home/username/app")
|
20
20
|
c.set(:current_release, "")
|
21
21
|
c.set(:current_revision, "503ce0923ed177a3ce000005")
|
@@ -290,6 +290,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
290
290
|
jruby = Appsignal::System.jruby?
|
291
291
|
expect(output).to include(
|
292
292
|
"Extension installation report",
|
293
|
+
"Installation result",
|
294
|
+
" Status: success",
|
293
295
|
"Language details",
|
294
296
|
" Implementation: #{jruby ? "jruby" : "ruby"}",
|
295
297
|
" Ruby version: #{"#{rbconfig["ruby_version"]}-p#{rbconfig["PATCHLEVEL"]}"}",
|
@@ -310,6 +312,46 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
|
|
310
312
|
)
|
311
313
|
end
|
312
314
|
|
315
|
+
context "with error in install report" do
|
316
|
+
let(:error) { RuntimeError.new("some error") }
|
317
|
+
before do
|
318
|
+
allow(File).to receive(:read).and_call_original
|
319
|
+
expect(File).to receive(:read)
|
320
|
+
.with(File.expand_path("../../../../../ext/install.report", __FILE__))
|
321
|
+
.and_return(
|
322
|
+
YAML.dump(
|
323
|
+
"result" => {
|
324
|
+
"status" => "error",
|
325
|
+
"error" => "RuntimeError: some error",
|
326
|
+
"backtrace" => error.backtrace
|
327
|
+
}
|
328
|
+
)
|
329
|
+
)
|
330
|
+
end
|
331
|
+
|
332
|
+
it "sends an error" do
|
333
|
+
run
|
334
|
+
expect(received_report["installation"]).to match(
|
335
|
+
"result" => {
|
336
|
+
"status" => "error",
|
337
|
+
"error" => "RuntimeError: some error",
|
338
|
+
"backtrace" => error.backtrace
|
339
|
+
}
|
340
|
+
)
|
341
|
+
end
|
342
|
+
|
343
|
+
it "prints the error" do
|
344
|
+
run
|
345
|
+
|
346
|
+
expect(output).to include(
|
347
|
+
"Extension installation report",
|
348
|
+
"Installation result",
|
349
|
+
"Status: error\n Error: RuntimeError: some error"
|
350
|
+
)
|
351
|
+
expect(output).to_not include("Raw report:")
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
313
355
|
context "without install report" do
|
314
356
|
let(:error) { RuntimeError.new("foo") }
|
315
357
|
before do
|
@@ -244,6 +244,27 @@ describe Appsignal::Config do
|
|
244
244
|
end
|
245
245
|
end
|
246
246
|
|
247
|
+
context "with an overriden config file" do
|
248
|
+
let(:config) do
|
249
|
+
project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "appsignal.yml"))
|
250
|
+
end
|
251
|
+
|
252
|
+
it "is valid and active" do
|
253
|
+
expect(config.valid?).to be_truthy
|
254
|
+
expect(config.active?).to be_truthy
|
255
|
+
end
|
256
|
+
|
257
|
+
context "with an invalid overriden config file" do
|
258
|
+
let(:config) do
|
259
|
+
project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "missing.yml"))
|
260
|
+
end
|
261
|
+
|
262
|
+
it "is not valid" do
|
263
|
+
expect(config.valid?).to be_falsy
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
247
268
|
context "with the config file causing an error" do
|
248
269
|
let(:config_path) do
|
249
270
|
File.expand_path(
|
@@ -1,42 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
describe "JRuby extension", :jruby do
|
2
|
+
let(:extension) { Appsignal::Extension }
|
3
|
+
let(:jruby_module) { Appsignal::Extension::Jruby }
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Tests if the conversions between the conversions without breaking on
|
9
|
-
# NULL terminated strings in C.
|
10
|
-
string = "Merry Christmas! \u0000 🎄"
|
5
|
+
it "creates a JRuby extension module" do
|
6
|
+
expect(Appsignal::Extension::Jruby).to be_kind_of(Module)
|
7
|
+
end
|
11
8
|
|
12
|
-
|
13
|
-
|
9
|
+
describe "string conversions" do
|
10
|
+
it "keeps the same value during string type conversions" do
|
11
|
+
# UTF-8 string with NULL
|
12
|
+
# Tests if the conversions between the conversions without breaking on
|
13
|
+
# NULL terminated strings in C.
|
14
|
+
string = "Merry Christmas! \u0000 🎄"
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
16
|
+
appsignal_string = extension.make_appsignal_string(string)
|
17
|
+
ruby_string = extension.make_ruby_string(appsignal_string)
|
18
18
|
|
19
|
-
|
20
|
-
expect(described_class.ffi_libraries.map(&:name).first).to include "libappsignal"
|
19
|
+
expect(ruby_string).to eq("Merry Christmas! \u0000 🎄")
|
21
20
|
end
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
it "loads libappsignal with FFI" do
|
24
|
+
expect(jruby_module.ffi_libraries.map(&:name).first).to include "libappsignal"
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".lib_extension" do
|
28
|
+
subject { jruby_module.lib_extension }
|
25
29
|
|
26
|
-
|
27
|
-
|
30
|
+
context "when on a darwin system" do
|
31
|
+
before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
33
|
+
it "returns the extension for darwin" do
|
34
|
+
is_expected.to eq "dylib"
|
32
35
|
end
|
36
|
+
end
|
33
37
|
|
34
|
-
|
35
|
-
|
38
|
+
context "when on a linux system" do
|
39
|
+
before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
41
|
+
it "returns the lib extension for linux" do
|
42
|
+
is_expected.to eq "so"
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
describe Appsignal::Extension, :extension_installation_failure do
|
2
|
+
context "when the extension library cannot be loaded" do
|
3
|
+
# This test breaks the installation on purpose and is not run by default.
|
4
|
+
# See `rake test:failure`. If this test was run, run `rake
|
5
|
+
# extension:install` again to fix the extension installation.
|
6
|
+
it "prints and logs an error" do
|
7
|
+
# ENV var to make sure installation fails on purpurse
|
8
|
+
ENV["_TEST_APPSIGNAL_EXTENSION_FAILURE"] = "true"
|
9
|
+
`rake extension:install` # Run installation
|
10
|
+
|
11
|
+
require "open3"
|
12
|
+
_stdout, stderr, _status = Open3.capture3("bin/appsignal --version")
|
13
|
+
expect(stderr).to include("ERROR: AppSignal failed to load extension")
|
14
|
+
error_message =
|
15
|
+
if DependencyHelper.running_jruby?
|
16
|
+
"cannot open shared object file"
|
17
|
+
else
|
18
|
+
"LoadError: cannot load such file"
|
19
|
+
end
|
20
|
+
expect(stderr).to include(error_message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,591 @@
|
|
1
|
+
if DependencyHelper.active_job_present?
|
2
|
+
require "active_job"
|
3
|
+
require "action_mailer"
|
4
|
+
|
5
|
+
describe Appsignal::Hooks::ActiveJobHook do
|
6
|
+
describe "#dependencies_present?" do
|
7
|
+
subject { described_class.new.dependencies_present? }
|
8
|
+
|
9
|
+
context "when ActiveJob constant is found" do
|
10
|
+
before { stub_const "ActiveJob", Class.new }
|
11
|
+
|
12
|
+
it { is_expected.to be_truthy }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when ActiveJob constant is not found" do
|
16
|
+
before { hide_const "ActiveJob" }
|
17
|
+
|
18
|
+
it { is_expected.to be_falsy }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#install" do
|
23
|
+
it "extends ActiveJob::Base with the AppSignal ActiveJob plugin" do
|
24
|
+
start_agent
|
25
|
+
|
26
|
+
path, _line_number = ActiveJob::Base.method(:execute).source_location
|
27
|
+
expect(path).to end_with("/lib/appsignal/hooks/active_job.rb")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Appsignal::Hooks::ActiveJobHook::ActiveJobClassInstrumentation do
|
33
|
+
let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
|
34
|
+
let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
|
35
|
+
let(:queue) { "default" }
|
36
|
+
let(:log) { StringIO.new }
|
37
|
+
let(:parameterized_given_args) do
|
38
|
+
{
|
39
|
+
:foo => "Foo",
|
40
|
+
"bar" => "Bar",
|
41
|
+
"baz" => { "1" => "foo" }
|
42
|
+
}
|
43
|
+
end
|
44
|
+
let(:method_given_args) do
|
45
|
+
[
|
46
|
+
"foo",
|
47
|
+
parameterized_given_args
|
48
|
+
]
|
49
|
+
end
|
50
|
+
let(:parameterized_expected_args) do
|
51
|
+
{
|
52
|
+
"_aj_symbol_keys" => ["foo"],
|
53
|
+
"foo" => "Foo",
|
54
|
+
"bar" => "Bar",
|
55
|
+
"baz" => {
|
56
|
+
"_aj_symbol_keys" => [],
|
57
|
+
"1" => "foo"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
let(:method_expected_args) do
|
62
|
+
[
|
63
|
+
"foo",
|
64
|
+
parameterized_expected_args
|
65
|
+
]
|
66
|
+
end
|
67
|
+
before do
|
68
|
+
ActiveJob::Base.queue_adapter = :inline
|
69
|
+
|
70
|
+
start_agent
|
71
|
+
Appsignal.logger = test_logger(log)
|
72
|
+
class ActiveJobTestJob < ActiveJob::Base
|
73
|
+
def perform(*_args)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class ActiveJobErrorTestJob < ActiveJob::Base
|
78
|
+
def perform
|
79
|
+
raise "uh oh"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class ActiveJobCustomQueueTestJob < ActiveJob::Base
|
84
|
+
queue_as :custom_queue
|
85
|
+
|
86
|
+
def perform(*_args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
around { |example| keep_transactions { example.run } }
|
91
|
+
after do
|
92
|
+
Object.send(:remove_const, :ActiveJobTestJob)
|
93
|
+
Object.send(:remove_const, :ActiveJobErrorTestJob)
|
94
|
+
Object.send(:remove_const, :ActiveJobCustomQueueTestJob)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "reports the name from the ActiveJob integration" do
|
98
|
+
tags = { :queue => queue }
|
99
|
+
expect(Appsignal).to receive(:increment_counter)
|
100
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
|
101
|
+
|
102
|
+
perform_job(ActiveJobTestJob)
|
103
|
+
|
104
|
+
transaction = last_transaction
|
105
|
+
transaction_hash = transaction.to_h
|
106
|
+
expect(transaction_hash).to include(
|
107
|
+
"action" => "ActiveJobTestJob#perform",
|
108
|
+
"error" => nil,
|
109
|
+
"namespace" => namespace,
|
110
|
+
"metadata" => {},
|
111
|
+
"sample_data" => hash_including(
|
112
|
+
"params" => [],
|
113
|
+
"tags" => {
|
114
|
+
"active_job_id" => kind_of(String),
|
115
|
+
"queue" => queue
|
116
|
+
}
|
117
|
+
)
|
118
|
+
)
|
119
|
+
events = transaction_hash["events"]
|
120
|
+
.sort_by { |e| e["start"] }
|
121
|
+
.map { |event| event["name"] }
|
122
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
123
|
+
end
|
124
|
+
|
125
|
+
context "with custom queue" do
|
126
|
+
it "reports the custom queue as tag on the transaction" do
|
127
|
+
tags = { :queue => "custom_queue" }
|
128
|
+
expect(Appsignal).to receive(:increment_counter)
|
129
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
|
130
|
+
perform_job(ActiveJobCustomQueueTestJob)
|
131
|
+
|
132
|
+
transaction = last_transaction
|
133
|
+
transaction_hash = transaction.to_h
|
134
|
+
expect(transaction_hash).to include(
|
135
|
+
"sample_data" => hash_including(
|
136
|
+
"tags" => hash_including("queue" => "custom_queue")
|
137
|
+
)
|
138
|
+
)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
|
143
|
+
context "with priority" do
|
144
|
+
before do
|
145
|
+
class ActiveJobPriorityTestJob < ActiveJob::Base
|
146
|
+
queue_with_priority 10
|
147
|
+
|
148
|
+
def perform(*_args)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
after do
|
153
|
+
Object.send(:remove_const, :ActiveJobPriorityTestJob)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "reports the priority as tag on the transaction" do
|
157
|
+
tags = { :queue => queue }
|
158
|
+
expect(Appsignal).to receive(:increment_counter)
|
159
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
|
160
|
+
expect(Appsignal).to receive(:increment_counter)
|
161
|
+
.with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
|
162
|
+
|
163
|
+
perform_job(ActiveJobPriorityTestJob)
|
164
|
+
|
165
|
+
transaction = last_transaction
|
166
|
+
transaction_hash = transaction.to_h
|
167
|
+
expect(transaction_hash).to include(
|
168
|
+
"sample_data" => hash_including(
|
169
|
+
"tags" => hash_including("queue" => queue, "priority" => 10)
|
170
|
+
)
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "with error" do
|
177
|
+
it "reports the error on the transaction from the ActiveRecord integration" do
|
178
|
+
allow(Appsignal).to receive(:increment_counter) # Other calls we're testing in another test
|
179
|
+
tags = { :queue => queue }
|
180
|
+
expect(Appsignal).to receive(:increment_counter)
|
181
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :failed))
|
182
|
+
expect(Appsignal).to receive(:increment_counter)
|
183
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
|
184
|
+
|
185
|
+
expect do
|
186
|
+
perform_job(ActiveJobErrorTestJob)
|
187
|
+
end.to raise_error(RuntimeError, "uh oh")
|
188
|
+
|
189
|
+
transaction = last_transaction
|
190
|
+
transaction_hash = transaction.to_h
|
191
|
+
expect(transaction_hash).to include(
|
192
|
+
"action" => "ActiveJobErrorTestJob#perform",
|
193
|
+
"error" => {
|
194
|
+
"name" => "RuntimeError",
|
195
|
+
"message" => "uh oh",
|
196
|
+
"backtrace" => kind_of(String)
|
197
|
+
},
|
198
|
+
"namespace" => namespace,
|
199
|
+
"metadata" => {},
|
200
|
+
"sample_data" => hash_including(
|
201
|
+
"params" => [],
|
202
|
+
"tags" => {
|
203
|
+
"active_job_id" => kind_of(String),
|
204
|
+
"queue" => queue
|
205
|
+
}
|
206
|
+
)
|
207
|
+
)
|
208
|
+
events = transaction_hash["events"]
|
209
|
+
.sort_by { |e| e["start"] }
|
210
|
+
.map { |event| event["name"] }
|
211
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
212
|
+
end
|
213
|
+
|
214
|
+
if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
|
215
|
+
context "with priority" do
|
216
|
+
before do
|
217
|
+
class ActiveJobErrorPriorityTestJob < ActiveJob::Base
|
218
|
+
queue_with_priority 10
|
219
|
+
|
220
|
+
def perform(*_args)
|
221
|
+
raise "uh oh"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
after do
|
226
|
+
Object.send(:remove_const, :ActiveJobErrorPriorityTestJob)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "reports the priority as tag on the transaction" do
|
230
|
+
tags = { :queue => queue }
|
231
|
+
expect(Appsignal).to receive(:increment_counter)
|
232
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
|
233
|
+
expect(Appsignal).to receive(:increment_counter)
|
234
|
+
.with("active_job_queue_job_count", 1, tags.merge(:status => :failed))
|
235
|
+
expect(Appsignal).to receive(:increment_counter)
|
236
|
+
.with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :processed))
|
237
|
+
expect(Appsignal).to receive(:increment_counter)
|
238
|
+
.with("active_job_queue_priority_job_count", 1, tags.merge(:priority => 10, :status => :failed))
|
239
|
+
|
240
|
+
expect do
|
241
|
+
perform_job(ActiveJobErrorPriorityTestJob)
|
242
|
+
end.to raise_error(RuntimeError, "uh oh")
|
243
|
+
|
244
|
+
transaction = last_transaction
|
245
|
+
transaction_hash = transaction.to_h
|
246
|
+
expect(transaction_hash).to include(
|
247
|
+
"sample_data" => hash_including(
|
248
|
+
"tags" => hash_including("queue" => queue, "priority" => 10)
|
249
|
+
)
|
250
|
+
)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context "when wrapped in another transaction" do
|
257
|
+
it "does not create a new transaction or close the currently open one" do
|
258
|
+
current_transaction = background_job_transaction
|
259
|
+
allow(current_transaction).to receive(:complete).and_call_original
|
260
|
+
set_current_transaction current_transaction
|
261
|
+
|
262
|
+
perform_job(ActiveJobTestJob)
|
263
|
+
|
264
|
+
expect(created_transactions.count).to eql(1)
|
265
|
+
expect(current_transaction).to_not have_received(:complete)
|
266
|
+
current_transaction.complete
|
267
|
+
|
268
|
+
transaction = current_transaction
|
269
|
+
transaction_hash = transaction.to_h
|
270
|
+
# It does set data on the transaction
|
271
|
+
expect(transaction_hash).to include(
|
272
|
+
"id" => current_transaction.transaction_id,
|
273
|
+
"action" => "ActiveJobTestJob#perform",
|
274
|
+
"error" => nil,
|
275
|
+
"namespace" => namespace,
|
276
|
+
"metadata" => {},
|
277
|
+
"sample_data" => hash_including(
|
278
|
+
"params" => [],
|
279
|
+
"tags" => {
|
280
|
+
"active_job_id" => kind_of(String),
|
281
|
+
"queue" => queue
|
282
|
+
}
|
283
|
+
)
|
284
|
+
)
|
285
|
+
events = transaction_hash["events"]
|
286
|
+
.reject { |e| e["name"] == "enqueue.active_job" }
|
287
|
+
.sort_by { |e| e["start"] }
|
288
|
+
.map { |event| event["name"] }
|
289
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "with params" do
|
294
|
+
it "filters the configured params" do
|
295
|
+
Appsignal.config = project_fixture_config("production")
|
296
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
297
|
+
perform_job(ActiveJobTestJob, method_given_args)
|
298
|
+
|
299
|
+
transaction = last_transaction
|
300
|
+
transaction_hash = transaction.to_h
|
301
|
+
expect(transaction_hash["sample_data"]["params"]).to include(
|
302
|
+
[
|
303
|
+
"foo",
|
304
|
+
{
|
305
|
+
"_aj_symbol_keys" => ["foo"],
|
306
|
+
"foo" => "[FILTERED]",
|
307
|
+
"bar" => "Bar",
|
308
|
+
"baz" => { "_aj_symbol_keys" => [], "1" => "foo" }
|
309
|
+
}
|
310
|
+
]
|
311
|
+
)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
context "with provider_job_id", :skip => DependencyHelper.rails_version < Gem::Version.new("5.0.0") do
|
316
|
+
before do
|
317
|
+
module ActiveJob
|
318
|
+
module QueueAdapters
|
319
|
+
# Adapter used in our test suite to add provider data to the job
|
320
|
+
# data, as is done by Rails provided ActiveJob adapters.
|
321
|
+
#
|
322
|
+
# This implementation is based on the
|
323
|
+
# `ActiveJob::QueueAdapters::InlineAdapter`.
|
324
|
+
class AppsignalTestAdapter < InlineAdapter
|
325
|
+
def enqueue(job)
|
326
|
+
Base.execute(job.serialize.merge("provider_job_id" => "my_provider_job_id"))
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
class ProviderWrappedActiveJobTestJob < ActiveJob::Base
|
333
|
+
self.queue_adapter = :appsignal_test
|
334
|
+
|
335
|
+
def perform(*_args)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
after do
|
340
|
+
ActiveJob::QueueAdapters.send(:remove_const, :AppsignalTestAdapter)
|
341
|
+
Object.send(:remove_const, :ProviderWrappedActiveJobTestJob)
|
342
|
+
end
|
343
|
+
|
344
|
+
it "sets provider_job_id as tag" do
|
345
|
+
perform_job(ProviderWrappedActiveJobTestJob)
|
346
|
+
|
347
|
+
transaction = last_transaction
|
348
|
+
transaction_hash = transaction.to_h
|
349
|
+
expect(transaction_hash["sample_data"]["tags"]).to include(
|
350
|
+
"provider_job_id" => "my_provider_job_id"
|
351
|
+
)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context "with enqueued_at", :skip => DependencyHelper.rails_version < Gem::Version.new("6.0.0") do
|
356
|
+
before do
|
357
|
+
module ActiveJob
|
358
|
+
module QueueAdapters
|
359
|
+
# Adapter used in our test suite to add provider data to the job
|
360
|
+
# data, as is done by Rails provided ActiveJob adapters.
|
361
|
+
#
|
362
|
+
# This implementation is based on the
|
363
|
+
# `ActiveJob::QueueAdapters::InlineAdapter`.
|
364
|
+
class AppsignalTestAdapter < InlineAdapter
|
365
|
+
def enqueue(job)
|
366
|
+
Base.execute(job.serialize.merge("enqueued_at" => "2020-10-10T10:10:10Z"))
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
class ProviderWrappedActiveJobTestJob < ActiveJob::Base
|
373
|
+
self.queue_adapter = :appsignal_test
|
374
|
+
|
375
|
+
def perform(*_args)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
after do
|
380
|
+
ActiveJob::QueueAdapters.send(:remove_const, :AppsignalTestAdapter)
|
381
|
+
Object.send(:remove_const, :ProviderWrappedActiveJobTestJob)
|
382
|
+
end
|
383
|
+
|
384
|
+
it "sets queue time on transaction" do
|
385
|
+
allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
|
386
|
+
perform_job(ProviderWrappedActiveJobTestJob)
|
387
|
+
|
388
|
+
transaction = last_transaction
|
389
|
+
queue_time = Time.parse("2020-10-10T10:10:10Z")
|
390
|
+
expect(transaction).to have_received(:set_queue_start)
|
391
|
+
.with((queue_time.to_f * 1_000).to_i)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
context "with ActionMailer job" do
|
396
|
+
include ActionMailerHelpers
|
397
|
+
|
398
|
+
before do
|
399
|
+
class ActionMailerTestJob < ActionMailer::Base
|
400
|
+
def welcome(_first_arg = nil, _second_arg = nil)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
after do
|
405
|
+
Object.send(:remove_const, :ActionMailerTestJob)
|
406
|
+
end
|
407
|
+
|
408
|
+
context "without params" do
|
409
|
+
it "sets the Action mailer data on the transaction" do
|
410
|
+
perform_mailer(ActionMailerTestJob, :welcome)
|
411
|
+
|
412
|
+
transaction = last_transaction
|
413
|
+
transaction_hash = transaction.to_h
|
414
|
+
expect(transaction_hash).to include(
|
415
|
+
"action" => "ActionMailerTestJob#welcome",
|
416
|
+
"sample_data" => hash_including(
|
417
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now"],
|
418
|
+
"tags" => {
|
419
|
+
"active_job_id" => kind_of(String),
|
420
|
+
"queue" => "mailers"
|
421
|
+
}
|
422
|
+
)
|
423
|
+
)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
context "with multiple arguments" do
|
428
|
+
it "sets the arguments on the transaction" do
|
429
|
+
perform_mailer(ActionMailerTestJob, :welcome, method_given_args)
|
430
|
+
|
431
|
+
transaction = last_transaction
|
432
|
+
transaction_hash = transaction.to_h
|
433
|
+
expect(transaction_hash).to include(
|
434
|
+
"action" => "ActionMailerTestJob#welcome",
|
435
|
+
"sample_data" => hash_including(
|
436
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + method_expected_args,
|
437
|
+
"tags" => {
|
438
|
+
"active_job_id" => kind_of(String),
|
439
|
+
"queue" => "mailers"
|
440
|
+
}
|
441
|
+
)
|
442
|
+
)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
if DependencyHelper.rails_version >= Gem::Version.new("5.2.0")
|
447
|
+
context "with parameterized arguments" do
|
448
|
+
it "sets the arguments on the transaction" do
|
449
|
+
perform_mailer(ActionMailerTestJob, :welcome, parameterized_given_args)
|
450
|
+
|
451
|
+
transaction = last_transaction
|
452
|
+
transaction_hash = transaction.to_h
|
453
|
+
expect(transaction_hash).to include(
|
454
|
+
"action" => "ActionMailerTestJob#welcome",
|
455
|
+
"sample_data" => hash_including(
|
456
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now", parameterized_expected_args],
|
457
|
+
"tags" => {
|
458
|
+
"active_job_id" => kind_of(String),
|
459
|
+
"queue" => "mailers"
|
460
|
+
}
|
461
|
+
)
|
462
|
+
)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
if DependencyHelper.rails_version >= Gem::Version.new("6.0.0")
|
469
|
+
context "with ActionMailer MailDeliveryJob job" do
|
470
|
+
include ActionMailerHelpers
|
471
|
+
|
472
|
+
before do
|
473
|
+
class ActionMailerTestMailDeliveryJob < ActionMailer::Base
|
474
|
+
self.delivery_job = ActionMailer::MailDeliveryJob
|
475
|
+
|
476
|
+
def welcome(*_args)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
after do
|
481
|
+
Object.send(:remove_const, :ActionMailerTestMailDeliveryJob)
|
482
|
+
end
|
483
|
+
|
484
|
+
it "sets the Action mailer data on the transaction" do
|
485
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome)
|
486
|
+
|
487
|
+
transaction = last_transaction
|
488
|
+
transaction_hash = transaction.to_h
|
489
|
+
expect(transaction_hash).to include(
|
490
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
491
|
+
"sample_data" => hash_including(
|
492
|
+
"params" => [
|
493
|
+
"ActionMailerTestMailDeliveryJob",
|
494
|
+
"welcome",
|
495
|
+
"deliver_now",
|
496
|
+
{ active_job_internal_key => ["args"], "args" => [] }
|
497
|
+
],
|
498
|
+
"tags" => {
|
499
|
+
"active_job_id" => kind_of(String),
|
500
|
+
"queue" => "mailers"
|
501
|
+
}
|
502
|
+
)
|
503
|
+
)
|
504
|
+
end
|
505
|
+
|
506
|
+
context "with method arguments" do
|
507
|
+
it "sets the Action mailer data on the transaction" do
|
508
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, method_given_args)
|
509
|
+
|
510
|
+
transaction = last_transaction
|
511
|
+
transaction_hash = transaction.to_h
|
512
|
+
expect(transaction_hash).to include(
|
513
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
514
|
+
"sample_data" => hash_including(
|
515
|
+
"params" => [
|
516
|
+
"ActionMailerTestMailDeliveryJob",
|
517
|
+
"welcome",
|
518
|
+
"deliver_now",
|
519
|
+
{
|
520
|
+
active_job_internal_key => ["args"],
|
521
|
+
"args" => method_expected_args
|
522
|
+
}
|
523
|
+
],
|
524
|
+
"tags" => {
|
525
|
+
"active_job_id" => kind_of(String),
|
526
|
+
"queue" => "mailers"
|
527
|
+
}
|
528
|
+
)
|
529
|
+
)
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
context "with parameterized arguments" do
|
534
|
+
it "sets the Action mailer data on the transaction" do
|
535
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, parameterized_given_args)
|
536
|
+
|
537
|
+
transaction = last_transaction
|
538
|
+
transaction_hash = transaction.to_h
|
539
|
+
expect(transaction_hash).to include(
|
540
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
541
|
+
"sample_data" => hash_including(
|
542
|
+
"params" => [
|
543
|
+
"ActionMailerTestMailDeliveryJob",
|
544
|
+
"welcome",
|
545
|
+
"deliver_now",
|
546
|
+
{
|
547
|
+
active_job_internal_key => ["params", "args"],
|
548
|
+
"args" => [],
|
549
|
+
"params" => parameterized_expected_args
|
550
|
+
}
|
551
|
+
],
|
552
|
+
"tags" => {
|
553
|
+
"active_job_id" => kind_of(String),
|
554
|
+
"queue" => "mailers"
|
555
|
+
}
|
556
|
+
)
|
557
|
+
)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
def perform_active_job
|
564
|
+
Timecop.freeze(time) do
|
565
|
+
yield
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def perform_job(job_class, args = nil)
|
570
|
+
perform_active_job do
|
571
|
+
if args
|
572
|
+
job_class.perform_later(args)
|
573
|
+
else
|
574
|
+
job_class.perform_later
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
def perform_mailer(mailer, method, args = nil)
|
580
|
+
perform_active_job { perform_action_mailer(mailer, method, args) }
|
581
|
+
end
|
582
|
+
|
583
|
+
def active_job_internal_key
|
584
|
+
if DependencyHelper.ruby_version >= Gem::Version.new("2.7.0")
|
585
|
+
"_aj_ruby2_keywords"
|
586
|
+
else
|
587
|
+
"_aj_symbol_keys"
|
588
|
+
end
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|