appsignal 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73a704494489e09f2d92ffc41f9628aef70e9974
4
- data.tar.gz: 267fe55728850ffe4802ea0761a748c809235de5
3
+ metadata.gz: 9969df5b54cc4d39fa5c1b3e5572715adbe9b912
4
+ data.tar.gz: a6ea0af4047d5cde79e766bdf34180b3600c2f97
5
5
  SHA512:
6
- metadata.gz: cd3aa5683ed5c745a828caa4b4971cc1795056c91d990d114cd8e95737ce9bc02419b5255c91761cb3b5bec4aa1fdee9c59b47c6588f52a3f27679342ca2a14e
7
- data.tar.gz: d2f42c59c32fc1dcfe7be0e64466d0afafc81e4d355b26c5b9618436ed9946b67e8988a960d689ada112798551833c4cc81fe6038a0de9c80487164f00ac3fc5
6
+ metadata.gz: 036c096b3039a580a5d75e472f0a0382e3e0b3f277b77ce8c5af16e4f0de539ee20a379c956c8715d7ee700b33392a3eca474d539b9a5697a24c6ff592a5cbd2
7
+ data.tar.gz: 574d5a9c4a20eb7dd8def062b90ec0978f5f0ff78e977a796e5a14043659405971d31ef4824df4c8be8554924f103f6c0699c5516f05f4bd5aa2ce552f6eb4df
data/.gitignore CHANGED
@@ -25,6 +25,7 @@ bundle_and_spec_all_rvm
25
25
  ext/libappsignal.*
26
26
  ext/appsignal-agent
27
27
  ext/appsignal.h
28
+ ext/appsignal.platform
28
29
  ext/appsignal.version
29
30
  ext/Makefile
30
31
  ext/*.bundle
@@ -13,7 +13,7 @@ Lint/HandleExceptions:
13
13
 
14
14
  # Offense count: 41
15
15
  Metrics/AbcSize:
16
- Max: 59
16
+ Max: 59.92
17
17
 
18
18
  # Offense count: 5
19
19
  # Configuration parameters: CountComments.
@@ -33,6 +33,7 @@ gemfile:
33
33
  - "gemfiles/sinatra.gemfile"
34
34
  - "gemfiles/grape.gemfile"
35
35
  - "gemfiles/webmachine.gemfile"
36
+ - "gemfiles/que.gemfile"
36
37
 
37
38
  matrix:
38
39
  fast_finish: true
@@ -1,3 +1,11 @@
1
+ # 2.4.1
2
+ * Add Que integration. PR #361
3
+ * Support Sidekiq delayed extension job action names better. Now action names
4
+ are reported as their class and class method name (`MyClass.method`), rather
5
+ than `Sidekiq::Extensions::DelayedClass#perform` for all jobs through that
6
+ extension. PR #362
7
+ * Support Sidekiq Enterprise encrypted values. PR #365
8
+
1
9
  # 2.4.0
2
10
  - Add separate GNU linux build. PR #351 and
3
11
  Commit d1763f4dcb685608468a73f3192226f60f66b217
data/Rakefile CHANGED
@@ -17,6 +17,7 @@ GEMFILES = %w(
17
17
  sinatra
18
18
  grape
19
19
  webmachine
20
+ que
20
21
  ).freeze
21
22
 
22
23
  RUBY_VERSIONS = %w(
@@ -6,19 +6,13 @@ require "zlib"
6
6
  require "rubygems/package"
7
7
  require "yaml"
8
8
  require File.expand_path("../../lib/appsignal/version.rb", __FILE__)
9
+ require File.expand_path("../../lib/appsignal/system.rb", __FILE__)
9
10
 
10
11
  EXT_PATH = File.expand_path("..", __FILE__).freeze
11
12
  AGENT_CONFIG = YAML.load(File.read(File.join(EXT_PATH, "agent.yml"))).freeze
12
13
 
13
- local_os = Gem::Platform.local.os
14
- chosen_os =
15
- # Detect musl platforms
16
- # Use `export APPSIGNAL_BUILD_FOR_MUSL=1` if the detection doesn't work.
17
- if ENV["APPSIGNAL_BUILD_FOR_MUSL"] || (local_os =~ /linux/ && `ldd --version 2>&1` =~ /musl/)
18
- "linux-musl"
19
- end
20
- OS = chosen_os || local_os
21
- ARCH = "#{Gem::Platform.local.cpu}-#{OS}".freeze
14
+ PLATFORM = Appsignal::System.agent_platform
15
+ ARCH = "#{Gem::Platform.local.cpu}-#{PLATFORM}".freeze
22
16
  CA_CERT_PATH = File.join(EXT_PATH, "../resources/cacert.pem").freeze
23
17
 
24
18
  def ext_path(path)
@@ -36,8 +30,15 @@ def installation_failed(reason)
36
30
  end
37
31
  end
38
32
 
33
+ def write_agent_platform
34
+ File.open(File.join(EXT_PATH, "appsignal.platform"), "w") do |file|
35
+ file.write PLATFORM
36
+ end
37
+ end
38
+
39
39
  def install
40
40
  logger.info "Installing appsignal agent #{Appsignal::VERSION} for Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
41
+ write_agent_platform
41
42
 
42
43
  if RUBY_PLATFORM =~ /java/
43
44
  installation_failed(
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'que'
4
+
5
+ gemspec :path => '../'
@@ -285,6 +285,8 @@ module Appsignal
285
285
  save :language, "ruby"
286
286
  puts_and_save :package_version, "Gem version", Appsignal::VERSION
287
287
  puts_and_save :agent_version, "Agent version", Appsignal::Extension.agent_version
288
+ puts_and_save :agent_platform, "Agent platform",
289
+ Appsignal::System.installed_agent_platform
288
290
  puts_and_save :package_install_path, "Gem install path", gem_path
289
291
  puts_and_save :extension_loaded, "Extension loaded", Appsignal.extension_loaded
290
292
  end
@@ -297,7 +299,7 @@ module Appsignal
297
299
  puts_and_save :architecture, "Architecture", rbconfig["host_cpu"]
298
300
 
299
301
  os_label = os = rbconfig["host_os"]
300
- os_label = "#{os_label} (Microsoft Windows is not supported.)" if Gem.win_platform?
302
+ os_label = "#{os} (Microsoft Windows is not supported.)" if Gem.win_platform?
301
303
  save :os, os
302
304
  puts_value "Operating System", os_label
303
305
 
@@ -102,3 +102,4 @@ require "appsignal/hooks/unicorn"
102
102
  require "appsignal/hooks/mongo_ruby_driver"
103
103
  require "appsignal/hooks/webmachine"
104
104
  require "appsignal/hooks/data_mapper"
105
+ require "appsignal/hooks/que"
@@ -0,0 +1,21 @@
1
+ module Appsignal
2
+ class Hooks
3
+ # @api private
4
+ class QueHook < Appsignal::Hooks::Hook
5
+ register :que
6
+
7
+ def dependencies_present?
8
+ defined?(::Que::Job)
9
+ end
10
+
11
+ def install
12
+ require "appsignal/integrations/que"
13
+ ::Que::Job.send(:include, Appsignal::Integrations::QuePlugin)
14
+
15
+ ::Que.error_notifier = proc do |error, _job|
16
+ Appsignal::Transaction.current.set_error(error)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,64 +1,144 @@
1
+ require "yaml"
2
+
1
3
  module Appsignal
2
4
  class Hooks
5
+ class SidekiqHook < Appsignal::Hooks::Hook
6
+ register :sidekiq
7
+
8
+ def dependencies_present?
9
+ defined?(::Sidekiq)
10
+ end
11
+
12
+ def install
13
+ ::Sidekiq.configure_server do |config|
14
+ config.server_middleware do |chain|
15
+ chain.add Appsignal::Hooks::SidekiqPlugin
16
+ end
17
+ end
18
+ end
19
+ end
20
+
3
21
  # @api private
4
22
  class SidekiqPlugin
5
23
  include Appsignal::Hooks::Helpers
6
24
 
7
- def job_keys
8
- @job_keys ||= Set.new(%w(
9
- class args retried_at failed_at
10
- error_message error_class backtrace
11
- error_backtrace enqueued_at retry
12
- jid retry created_at wrapped
13
- ))
14
- end
25
+ JOB_KEYS = Set.new(%w(
26
+ args backtrace class created_at enqueued_at error_backtrace error_class
27
+ error_message failed_at jid retried_at retry wrapped
28
+ )).freeze
15
29
 
16
30
  def call(_worker, item, _queue)
17
- args =
18
- if item["class"] == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
19
- item["args"].first["arguments"]
20
- else
21
- item["args"]
22
- end
23
- params = Appsignal::Utils::ParamsSanitizer.sanitize args,
24
- :filter_parameters => Appsignal.config[:filter_parameters]
31
+ transaction = Appsignal::Transaction.create(
32
+ SecureRandom.uuid,
33
+ Appsignal::Transaction::BACKGROUND_JOB,
34
+ Appsignal::Transaction::GenericRequest.new(
35
+ :queue_start => item["enqueued_at"]
36
+ )
37
+ )
25
38
 
26
- Appsignal.monitor_transaction(
27
- "perform_job.sidekiq",
28
- :class => item["wrapped"] || item["class"],
29
- :method => "perform",
30
- :metadata => formatted_metadata(item),
31
- :params => params,
32
- :queue_start => item["enqueued_at"],
33
- :queue_time => (Time.now.to_f - item["enqueued_at"].to_f) * 1000
34
- ) do
35
- yield
39
+ Appsignal.instrument "perform_job.sidekiq" do
40
+ begin
41
+ yield
42
+ rescue Exception => exception # rubocop:disable Lint/RescueException
43
+ transaction.set_error(exception)
44
+ raise exception
45
+ end
46
+ end
47
+ ensure
48
+ if transaction
49
+ transaction.set_action_if_nil(formatted_action_name(item))
50
+ transaction.params = filtered_arguments(item)
51
+ formatted_metadata(item).each do |key, value|
52
+ transaction.set_metadata key, value
53
+ end
54
+ transaction.set_http_or_background_queue_start
55
+ Appsignal::Transaction.complete_current!
36
56
  end
37
57
  end
38
58
 
59
+ private
60
+
61
+ def formatted_action_name(job)
62
+ sidekiq_action_name = parse_action_name(job)
63
+ return sidekiq_action_name if sidekiq_action_name =~ /\.|#/
64
+ "#{sidekiq_action_name}#perform"
65
+ end
66
+
67
+ def filtered_arguments(job)
68
+ Appsignal::Utils::ParamsSanitizer.sanitize(
69
+ parse_arguments(job),
70
+ :filter_parameters => Appsignal.config[:filter_parameters]
71
+ )
72
+ end
73
+
39
74
  def formatted_metadata(item)
40
- {}.tap do |hsh|
41
- item.each do |key, val|
42
- hsh[key] = truncate(string_or_inspect(val)) unless job_keys.include?(key)
75
+ {}.tap do |hash|
76
+ (item || {}).each do |key, value|
77
+ next if JOB_KEYS.include?(key)
78
+ hash[key] = truncate(string_or_inspect(value))
43
79
  end
44
80
  end
45
81
  end
46
- end
47
82
 
48
- class SidekiqHook < Appsignal::Hooks::Hook
49
- register :sidekiq
50
-
51
- def dependencies_present?
52
- defined?(::Sidekiq)
83
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L316-L334
84
+ def parse_action_name(job)
85
+ args = job["args"]
86
+ case job["class"]
87
+ when /\ASidekiq::Extensions::Delayed/
88
+ safe_load(job["args"][0], job["class"]) do |target, method, _|
89
+ "#{target}.#{method}"
90
+ end
91
+ when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
92
+ job_class = job["wrapped"] || args[0]
93
+ if "ActionMailer::DeliveryJob" == job_class
94
+ # MailerClass#mailer_method
95
+ args[0]["arguments"][0..1].join("#")
96
+ else
97
+ job_class
98
+ end
99
+ else
100
+ job["class"]
101
+ end
53
102
  end
54
103
 
55
- def install
56
- ::Sidekiq.configure_server do |config|
57
- config.server_middleware do |chain|
58
- chain.add Appsignal::Hooks::SidekiqPlugin
104
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L336-L358
105
+ def parse_arguments(job)
106
+ args = job["args"]
107
+ case job["class"]
108
+ when /\ASidekiq::Extensions::Delayed/
109
+ safe_load(args[0], args) do |_, _, arg|
110
+ arg
111
+ end
112
+ when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
113
+ is_wrapped = job["wrapped"]
114
+ job_args = is_wrapped ? args[0]["arguments"] : []
115
+ if "ActionMailer::DeliveryJob" == (is_wrapped || args[0])
116
+ # Remove MailerClass, mailer_method and "deliver_now"
117
+ job_args.drop(3)
118
+ else
119
+ job_args
59
120
  end
121
+ else
122
+ # Sidekiq Enterprise argument encryption.
123
+ # More information: https://github.com/mperham/sidekiq/wiki/Ent-Encryption
124
+ if job["encrypt".freeze]
125
+ # No point in showing 150+ bytes of random garbage
126
+ args[-1] = "[encrypted data]".freeze
127
+ end
128
+ args
60
129
  end
61
130
  end
131
+
132
+ # Based on: https://github.com/mperham/sidekiq/blob/63ee43353bd3b753beb0233f64865e658abeb1c3/lib/sidekiq/api.rb#L403-L412
133
+ def safe_load(content, default)
134
+ yield(*YAML.load(content))
135
+ rescue => error
136
+ # Sidekiq issue #1761: in dev mode, it's possible to have jobs enqueued
137
+ # which haven't been loaded into memory yet so the YAML can't be
138
+ # loaded.
139
+ Appsignal.logger.warn "Unable to load YAML: #{error.message}"
140
+ default
141
+ end
62
142
  end
63
143
  end
64
144
  end
@@ -0,0 +1,43 @@
1
+ module Appsignal
2
+ module Integrations
3
+ module QuePlugin
4
+ def self.included(base)
5
+ base.class_eval do
6
+ def _run_with_appsignal
7
+ env = {
8
+ :metadata => {
9
+ :id => attrs[:job_id],
10
+ :queue => attrs[:queue],
11
+ :run_at => attrs[:run_at].to_s,
12
+ :priority => attrs[:priority],
13
+ :attempts => attrs[:error_count].to_i
14
+ },
15
+ :params => attrs[:args]
16
+ }
17
+
18
+ request = Appsignal::Transaction::GenericRequest.new(env)
19
+
20
+ transaction = Appsignal::Transaction.create(
21
+ SecureRandom.uuid,
22
+ Appsignal::Transaction::BACKGROUND_JOB,
23
+ request
24
+ )
25
+
26
+ begin
27
+ Appsignal.instrument("perform_job.que") { _run_without_appsignal }
28
+ rescue Exception => error # rubocop:disable Lint/RescueException
29
+ transaction.set_error(error)
30
+ raise error
31
+ ensure
32
+ transaction.set_action "#{attrs[:job_class]}#run"
33
+ Appsignal::Transaction.complete_current!
34
+ end
35
+ end
36
+
37
+ alias_method :_run_without_appsignal, :_run
38
+ alias_method :_run, :_run_with_appsignal
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,8 +5,62 @@ module Appsignal
5
5
  #
6
6
  # @api private
7
7
  module System
8
+ MUSL_TARGET = "linux-musl".freeze
9
+ GEM_EXT_PATH = File.expand_path("../../../ext", __FILE__).freeze
10
+
8
11
  def self.heroku?
9
12
  ENV.key? "DYNO".freeze
10
13
  end
14
+
15
+ # Returns the platform for which the agent was installed.
16
+ #
17
+ # This value is saved when the gem is installed in `ext/extconf.rb`.
18
+ # We use this value to build the diagnose report with the installed
19
+ # platform, rather than the detected platform in {.agent_platform} during
20
+ # the diagnose run.
21
+ #
22
+ # @api private
23
+ # @return [String]
24
+ def self.installed_agent_platform
25
+ platform_file = File.join(GEM_EXT_PATH, "appsignal.platform")
26
+ return unless File.exist?(platform_file)
27
+ File.read(platform_file)
28
+ end
29
+
30
+ # Detect agent and extension platform build
31
+ #
32
+ # Used by `ext/extconf.rb` to select which build it should download and
33
+ # install.
34
+ #
35
+ # Use `export APPSIGNAL_BUILD_FOR_MUSL=1` if the detection doesn't work
36
+ # and to force selection of the musl build.
37
+ #
38
+ # @api private
39
+ # @return [String]
40
+ def self.agent_platform
41
+ return MUSL_TARGET if ENV["APPSIGNAL_BUILD_FOR_MUSL"]
42
+
43
+ local_os = Gem::Platform.local.os
44
+ if local_os =~ /linux/
45
+ ldd_output = ldd_version_output
46
+ return MUSL_TARGET if ldd_output.include? "musl"
47
+ ldd_version = ldd_output.match(/\d+\.\d+/)
48
+ if ldd_version && versionify(ldd_version[0]) < versionify("2.15")
49
+ return MUSL_TARGET
50
+ end
51
+ end
52
+
53
+ local_os
54
+ end
55
+
56
+ # @api private
57
+ def self.versionify(version)
58
+ Gem::Version.new(version)
59
+ end
60
+
61
+ # @api private
62
+ def self.ldd_version_output
63
+ `ldd --version 2>&1`
64
+ end
11
65
  end
12
66
  end
@@ -54,6 +54,12 @@ module Appsignal
54
54
  rescue => e
55
55
  Appsignal.logger.error("Failed to complete transaction ##{current.transaction_id}. #{e.message}")
56
56
  ensure
57
+ clear_current_transaction!
58
+ end
59
+
60
+ # Remove current transaction from current Thread.
61
+ # @api private
62
+ def clear_current_transaction!
57
63
  Thread.current[:appsignal_transaction] = nil
58
64
  end
59
65
 
@@ -312,6 +318,7 @@ module Appsignal
312
318
  finish_event(name, title, body, body_format)
313
319
  end
314
320
 
321
+ # @api private
315
322
  def to_h
316
323
  JSON.parse(@ext.to_json)
317
324
  end