appsignal 2.11.0.beta.4-java → 2.11.2-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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +254 -1
  3. data/CHANGELOG.md +27 -0
  4. data/README.md +20 -5
  5. data/Rakefile +27 -9
  6. data/appsignal.gemspec +1 -1
  7. data/build_matrix.yml +15 -2
  8. data/ext/Rakefile +2 -0
  9. data/ext/agent.yml +17 -25
  10. data/ext/appsignal_extension.c +1 -1
  11. data/ext/base.rb +19 -9
  12. data/ext/extconf.rb +2 -0
  13. data/gemfiles/no_dependencies.gemfile +7 -0
  14. data/gemfiles/resque-2.gemfile +0 -1
  15. data/gemfiles/webmachine.gemfile +1 -0
  16. data/lib/appsignal.rb +1 -0
  17. data/lib/appsignal/auth_check.rb +4 -2
  18. data/lib/appsignal/cli/diagnose.rb +1 -1
  19. data/lib/appsignal/cli/diagnose/utils.rb +8 -11
  20. data/lib/appsignal/cli/install.rb +5 -8
  21. data/lib/appsignal/config.rb +82 -17
  22. data/lib/appsignal/extension.rb +6 -5
  23. data/lib/appsignal/extension/jruby.rb +6 -5
  24. data/lib/appsignal/helpers/instrumentation.rb +32 -0
  25. data/lib/appsignal/hooks.rb +24 -0
  26. data/lib/appsignal/hooks/action_mailer.rb +22 -0
  27. data/lib/appsignal/hooks/active_job.rb +32 -9
  28. data/lib/appsignal/hooks/active_support_notifications.rb +72 -0
  29. data/lib/appsignal/hooks/puma.rb +0 -1
  30. data/lib/appsignal/hooks/sidekiq.rb +0 -1
  31. data/lib/appsignal/probes.rb +7 -0
  32. data/lib/appsignal/probes/puma.rb +1 -1
  33. data/lib/appsignal/probes/sidekiq.rb +3 -1
  34. data/lib/appsignal/transaction.rb +30 -2
  35. data/lib/appsignal/utils/deprecation_message.rb +1 -1
  36. data/lib/appsignal/version.rb +1 -1
  37. data/spec/lib/appsignal/auth_check_spec.rb +23 -0
  38. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  39. data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
  40. data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
  41. data/spec/lib/appsignal/config_spec.rb +39 -1
  42. data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
  43. data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
  44. data/spec/lib/appsignal/hooks/action_mailer_spec.rb +54 -0
  45. data/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb +35 -0
  46. data/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb +145 -0
  47. data/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb +69 -0
  48. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +9 -137
  49. data/spec/lib/appsignal/hooks/activejob_spec.rb +44 -1
  50. data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
  51. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +4 -2
  52. data/spec/lib/appsignal/hooks_spec.rb +57 -0
  53. data/spec/lib/appsignal/marker_spec.rb +1 -1
  54. data/spec/lib/appsignal/transaction_spec.rb +55 -0
  55. data/spec/lib/appsignal_spec.rb +30 -0
  56. data/spec/spec_helper.rb +5 -0
  57. data/spec/support/helpers/config_helpers.rb +3 -2
  58. data/spec/support/helpers/dependency_helper.rb +4 -0
  59. data/spec/support/helpers/transaction_helpers.rb +1 -1
  60. data/spec/support/testing.rb +19 -19
  61. metadata +17 -5
@@ -24,7 +24,6 @@ module Appsignal
24
24
  # runs in the Puma main process.
25
25
  # For more information:
26
26
  # https://docs.appsignal.com/ruby/integrations/puma.html
27
- require "appsignal/probes/puma"
28
27
  Appsignal::Minutely.probes.register :puma, ::Appsignal::Probes::PumaProbe
29
28
  end
30
29
 
@@ -12,7 +12,6 @@ module Appsignal
12
12
  end
13
13
 
14
14
  def install
15
- require "appsignal/probes/sidekiq"
16
15
  Appsignal::Minutely.probes.register :sidekiq, Appsignal::Probes::SidekiqProbe
17
16
 
18
17
  ::Sidekiq.configure_server do |config|
@@ -0,0 +1,7 @@
1
+ module Appsignal
2
+ module Probes
3
+ end
4
+ end
5
+
6
+ require "appsignal/probes/puma"
7
+ require "appsignal/probes/sidekiq"
@@ -1,11 +1,11 @@
1
1
  module Appsignal
2
2
  module Probes
3
- # @api private
4
3
  class PumaProbe
5
4
  def initialize
6
5
  @hostname = Appsignal.config[:hostname] || Socket.gethostname
7
6
  end
8
7
 
8
+ # @api private
9
9
  def call
10
10
  puma_stats = fetch_puma_stats
11
11
  return unless puma_stats
@@ -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
@@ -11,6 +11,7 @@ module Appsignal
11
11
  BLANK = "".freeze
12
12
  ALLOWED_TAG_KEY_TYPES = [Symbol, String].freeze
13
13
  ALLOWED_TAG_VALUE_TYPES = [Symbol, String, Integer].freeze
14
+ BREADCRUMB_LIMIT = 20
14
15
 
15
16
  class << self
16
17
  def create(id, namespace, request, options = {})
@@ -58,7 +59,7 @@ module Appsignal
58
59
  end
59
60
  end
60
61
 
61
- attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded
62
+ attr_reader :ext, :transaction_id, :action, :namespace, :request, :paused, :tags, :options, :discarded, :breadcrumbs
62
63
 
63
64
  # @!attribute params
64
65
  # Attribute for parameters of the transaction.
@@ -80,6 +81,7 @@ module Appsignal
80
81
  @paused = false
81
82
  @discarded = false
82
83
  @tags = {}
84
+ @breadcrumbs = []
83
85
  @store = Hash.new({})
84
86
  @options = options
85
87
  @options[:params_method] ||= :params
@@ -156,6 +158,31 @@ module Appsignal
156
158
  @tags.merge!(given_tags)
157
159
  end
158
160
 
161
+ # Add breadcrumbs to the transaction.
162
+ #
163
+ # @param category [String] category of breadcrumb
164
+ # e.g. "UI", "Network", "Navigation", "Console".
165
+ # @param action [String] name of breadcrumb
166
+ # e.g "The user clicked a button", "HTTP 500 from http://blablabla.com"
167
+ # @option message [String] optional message in string format
168
+ # @option metadata [Hash<String,String>] key/value metadata in <string, string> format
169
+ # @option time [Time] time of breadcrumb, should respond to `.to_i` defaults to `Time.now.utc`
170
+ # @return [void]
171
+ #
172
+ # @see Appsignal.add_breadcrumb
173
+ # @see http://docs.appsignal.com/ruby/instrumentation/breadcrumbs.html
174
+ # Breadcrumb reference
175
+ def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
176
+ @breadcrumbs.push(
177
+ :time => time.to_i,
178
+ :category => category,
179
+ :action => action,
180
+ :message => message,
181
+ :metadata => metadata
182
+ )
183
+ @breadcrumbs = @breadcrumbs.last(BREADCRUMB_LIMIT)
184
+ end
185
+
159
186
  # Set an action name for the transaction.
160
187
  #
161
188
  # An action name is used to identify the location of a certain sample;
@@ -287,7 +314,8 @@ module Appsignal
287
314
  :environment => sanitized_environment,
288
315
  :session_data => sanitized_session_data,
289
316
  :metadata => metadata,
290
- :tags => sanitized_tags
317
+ :tags => sanitized_tags,
318
+ :breadcrumbs => breadcrumbs
291
319
  }.each do |key, data|
292
320
  set_sample_data(key, data)
293
321
  end
@@ -2,7 +2,7 @@ module Appsignal
2
2
  module Utils
3
3
  module DeprecationMessage
4
4
  def self.message(message, logger = Appsignal.logger)
5
- $stderr.puts "appsignal WARNING: #{message}"
5
+ Kernel.warn "appsignal WARNING: #{message}"
6
6
  logger.warn message
7
7
  end
8
8
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.0.beta.4".freeze
4
+ VERSION = "2.11.2".freeze
5
5
  end
@@ -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, "master")
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, "master")
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
@@ -1,4 +1,18 @@
1
1
  describe Appsignal::Config do
2
+ describe "config keys" do
3
+ it "all config keys have an environment variable version registered" do
4
+ config = Appsignal::Config
5
+ mapped_env_keys = config::ENV_TO_KEY_MAPPING.keys.sort
6
+ configured_env_keys = (
7
+ config::ENV_STRING_KEYS +
8
+ config::ENV_BOOLEAN_KEYS +
9
+ config::ENV_ARRAY_KEYS
10
+ ).sort
11
+
12
+ expect(mapped_env_keys).to eql(configured_env_keys)
13
+ end
14
+ end
15
+
2
16
  describe "#initialize" do
3
17
  describe "environment" do
4
18
  context "when environment is nil" do
@@ -244,6 +258,27 @@ describe Appsignal::Config do
244
258
  end
245
259
  end
246
260
 
261
+ context "with an overriden config file" do
262
+ let(:config) do
263
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "appsignal.yml"))
264
+ end
265
+
266
+ it "is valid and active" do
267
+ expect(config.valid?).to be_truthy
268
+ expect(config.active?).to be_truthy
269
+ end
270
+
271
+ context "with an invalid overriden config file" do
272
+ let(:config) do
273
+ project_fixture_config("production", {}, Appsignal.logger, File.join(project_fixture_path, "config", "missing.yml"))
274
+ end
275
+
276
+ it "is not valid" do
277
+ expect(config.valid?).to be_falsy
278
+ end
279
+ end
280
+ end
281
+
247
282
  context "with the config file causing an error" do
248
283
  let(:config_path) do
249
284
  File.expand_path(
@@ -397,6 +432,7 @@ describe Appsignal::Config do
397
432
  :debug => true
398
433
  )
399
434
  end
435
+ let(:working_directory_path) { File.join(tmp_dir, "test_working_directory_path") }
400
436
  let(:env_config) do
401
437
  {
402
438
  :running_in_container => true,
@@ -413,7 +449,8 @@ describe Appsignal::Config do
413
449
  :files_world_accessible => false,
414
450
  :request_headers => %w[accept accept-charset],
415
451
  :revision => "v2.5.1",
416
- :send_environment_metadata => false
452
+ :send_environment_metadata => false,
453
+ :working_directory_path => working_directory_path
417
454
  }
418
455
  end
419
456
  before do
@@ -431,6 +468,7 @@ describe Appsignal::Config do
431
468
  ENV["APPSIGNAL_FILES_WORLD_ACCESSIBLE"] = "false"
432
469
  ENV["APPSIGNAL_REQUEST_HEADERS"] = "accept,accept-charset"
433
470
  ENV["APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = "false"
471
+ ENV["APPSIGNAL_WORKING_DIRECTORY_PATH"] = working_directory_path
434
472
  ENV["APP_REVISION"] = "v2.5.1"
435
473
  end
436
474
 
@@ -1,42 +1,45 @@
1
- if Appsignal::System.jruby?
2
- describe Appsignal::Extension::Jruby do
3
- let(:extension) { Appsignal::Extension }
1
+ describe "JRuby extension", :jruby do
2
+ let(:extension) { Appsignal::Extension }
3
+ let(:jruby_module) { Appsignal::Extension::Jruby }
4
4
 
5
- describe "string conversions" do
6
- it "keeps the same value during string type conversions" do
7
- # UTF-8 string with NULL
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
- appsignal_string = extension.make_appsignal_string(string)
13
- ruby_string = extension.make_ruby_string(appsignal_string)
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
- expect(ruby_string).to eq("Merry Christmas! \u0000 🎄")
16
- end
17
- end
16
+ appsignal_string = extension.make_appsignal_string(string)
17
+ ruby_string = extension.make_ruby_string(appsignal_string)
18
18
 
19
- it "loads libappsignal with FFI" do
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
- describe ".lib_extension" do
24
- subject { described_class.lib_extension }
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
- context "when on a darwin system" do
27
- before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
30
+ context "when on a darwin system" do
31
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("darwin") }
28
32
 
29
- it "returns the extension for darwin" do
30
- is_expected.to eq "dylib"
31
- end
33
+ it "returns the extension for darwin" do
34
+ is_expected.to eq "dylib"
32
35
  end
36
+ end
33
37
 
34
- context "when on a linux system" do
35
- before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
38
+ context "when on a linux system" do
39
+ before { expect(Appsignal::System).to receive(:agent_platform).and_return("linux") }
36
40
 
37
- it "returns the lib extension for linux" do
38
- is_expected.to eq "so"
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,54 @@
1
+ describe Appsignal::Hooks::ActionMailerHook do
2
+ if DependencyHelper.action_mailer_present? &&
3
+ DependencyHelper.rails_version >= Gem::Version.new("4.0.0")
4
+ context "with ActionMailer" do
5
+ require "action_mailer"
6
+
7
+ class UserMailer < ActionMailer::Base
8
+ default :from => "test@example.com"
9
+
10
+ def welcome
11
+ mail(:to => "test@example.com", :subject => "ActionMailer test", :content_type => "text/html") do |format|
12
+ format.html { render :html => "This is a test" }
13
+ end
14
+ end
15
+ end
16
+ UserMailer.delivery_method = :test
17
+
18
+ describe ".dependencies_present?" do
19
+ subject { described_class.new.dependencies_present? }
20
+
21
+ it "returns true" do
22
+ is_expected.to be_truthy
23
+ end
24
+ end
25
+
26
+ describe ".install" do
27
+ before do
28
+ start_agent
29
+ expect(Appsignal.active?).to be_truthy
30
+ end
31
+
32
+ it "is subscribed to 'process.action_mailer' and processes instrumentation" do
33
+ expect(Appsignal).to receive(:increment_counter).with(
34
+ :action_mailer_process,
35
+ 1,
36
+ :mailer => "UserMailer", :action => :welcome
37
+ )
38
+
39
+ UserMailer.welcome.deliver_now
40
+ end
41
+ end
42
+ end
43
+ else
44
+ context "without ActionMailer" do
45
+ describe ".dependencies_present?" do
46
+ subject { described_class.new.dependencies_present? }
47
+
48
+ it "returns false" do
49
+ is_expected.to be_falsy
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end