appsignal 2.4.0 → 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop_todo.yml +1 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Rakefile +1 -0
- data/ext/extconf.rb +10 -9
- data/gemfiles/que.gemfile +5 -0
- data/lib/appsignal/cli/diagnose.rb +3 -1
- data/lib/appsignal/hooks.rb +1 -0
- data/lib/appsignal/hooks/que.rb +21 -0
- data/lib/appsignal/hooks/sidekiq.rb +119 -39
- data/lib/appsignal/integrations/que.rb +43 -0
- data/lib/appsignal/system.rb +54 -0
- data/lib/appsignal/transaction.rb +7 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +2 -0
- data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +336 -176
- data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
- data/spec/lib/appsignal/system_spec.rb +111 -0
- data/spec/support/helpers/dependency_helper.rb +4 -0
- data/spec/support/helpers/log_helpers.rb +11 -6
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9969df5b54cc4d39fa5c1b3e5572715adbe9b912
|
4
|
+
data.tar.gz: a6ea0af4047d5cde79e766bdf34180b3600c2f97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 036c096b3039a580a5d75e472f0a0382e3e0b3f277b77ce8c5af16e4f0de539ee20a379c956c8715d7ee700b33392a3eca474d539b9a5697a24c6ff592a5cbd2
|
7
|
+
data.tar.gz: 574d5a9c4a20eb7dd8def062b90ec0978f5f0ff78e977a796e5a14043659405971d31ef4824df4c8be8554924f103f6c0699c5516f05f4bd5aa2ce552f6eb4df
|
data/.gitignore
CHANGED
data/.rubocop_todo.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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
data/ext/extconf.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
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(
|
@@ -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 = "#{
|
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
|
|
data/lib/appsignal/hooks.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
item["
|
22
|
-
|
23
|
-
|
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.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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 |
|
41
|
-
item.each do |key,
|
42
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
data/lib/appsignal/system.rb
CHANGED
@@ -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
|