appsignal 0.12.rc.7 → 0.12.rc.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/agent.yml +7 -7
- data/lib/appsignal.rb +3 -15
- data/lib/appsignal/hooks.rb +61 -0
- data/lib/appsignal/hooks/celluloid.rb +29 -0
- data/lib/appsignal/hooks/delayed_job.rb +18 -0
- data/lib/appsignal/hooks/net_http.rb +31 -0
- data/lib/appsignal/hooks/passenger.rb +21 -0
- data/lib/appsignal/hooks/puma.rb +20 -0
- data/lib/appsignal/hooks/rake.rb +42 -0
- data/lib/appsignal/hooks/redis.rb +24 -0
- data/lib/appsignal/hooks/resque.rb +28 -0
- data/lib/appsignal/hooks/sequel.rb +38 -0
- data/lib/appsignal/hooks/sidekiq.rb +68 -0
- data/lib/appsignal/hooks/unicorn.rb +39 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +27 -29
- data/lib/appsignal/integrations/delayed_job_plugin.rb +39 -0
- data/lib/appsignal/integrations/railtie.rb +41 -0
- data/lib/appsignal/transaction.rb +9 -4
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/{integrations/capistrano2_spec.rb → capistrano2_spec.rb} +1 -1
- data/spec/lib/appsignal/{integrations/capistrano3_spec.rb → capistrano3_spec.rb} +0 -0
- data/spec/lib/appsignal/{integrations → hooks}/celluloid_spec.rb +8 -11
- data/spec/lib/appsignal/{integrations → hooks}/delayed_job_spec.rb +7 -10
- data/spec/lib/appsignal/hooks/net_http_spec.rb +55 -0
- data/spec/lib/appsignal/hooks/passenger_spec.rb +24 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +50 -0
- data/spec/lib/appsignal/{integrations → hooks}/rake_spec.rb +4 -3
- data/spec/lib/appsignal/hooks/redis_spec.rb +59 -0
- data/spec/lib/appsignal/{integrations → hooks}/resque_spec.rb +5 -11
- data/spec/lib/appsignal/{instrumentations → hooks}/sequel_spec.rb +1 -3
- data/spec/lib/appsignal/{integrations → hooks}/sidekiq_spec.rb +20 -20
- data/spec/lib/appsignal/hooks/unicorn_spec.rb +46 -0
- data/spec/lib/appsignal/hooks_spec.rb +76 -0
- data/spec/lib/appsignal/integrations/{rails_spec.rb → railtie_spec.rb} +0 -0
- data/spec/lib/appsignal/transaction_spec.rb +3 -3
- data/spec/lib/appsignal_spec.rb +0 -33
- metadata +46 -42
- data/lib/appsignal/instrumentations/net_http.rb +0 -17
- data/lib/appsignal/instrumentations/redis.rb +0 -13
- data/lib/appsignal/instrumentations/sequel.rb +0 -31
- data/lib/appsignal/integrations/celluloid.rb +0 -19
- data/lib/appsignal/integrations/delayed_job.rb +0 -44
- data/lib/appsignal/integrations/passenger.rb +0 -11
- data/lib/appsignal/integrations/puma.rb +0 -10
- data/lib/appsignal/integrations/rails.rb +0 -43
- data/lib/appsignal/integrations/rake.rb +0 -30
- data/lib/appsignal/integrations/resque.rb +0 -22
- data/lib/appsignal/integrations/sidekiq.rb +0 -62
- data/lib/appsignal/integrations/unicorn.rb +0 -28
- data/spec/lib/appsignal/instrumentations/net_http_spec.rb +0 -40
- data/spec/lib/appsignal/instrumentations/redis_spec.rb +0 -45
- data/spec/lib/appsignal/integrations/passenger_spec.rb +0 -22
- data/spec/lib/appsignal/integrations/puma_spec.rb +0 -52
- data/spec/lib/appsignal/integrations/unicorn_spec.rb +0 -48
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
|
3
|
-
Net::HTTP.class_eval do
|
4
|
-
alias request_without_appsignal request
|
5
|
-
|
6
|
-
def request(request, body=nil, &block)
|
7
|
-
ActiveSupport::Notifications.instrument(
|
8
|
-
'request.net_http',
|
9
|
-
:protocol => use_ssl? ? 'https' : 'http',
|
10
|
-
:domain => request['host'] || self.address,
|
11
|
-
:path => request.path,
|
12
|
-
:method => request.method
|
13
|
-
) do
|
14
|
-
request_without_appsignal(request, body, &block)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
if defined?(::Redis)
|
2
|
-
Appsignal.logger.info("Loading Redis (#{ Redis::VERSION }) integration")
|
3
|
-
|
4
|
-
::Redis::Client.class_eval do
|
5
|
-
alias process_without_appsignal process
|
6
|
-
|
7
|
-
def process(commands, &block)
|
8
|
-
ActiveSupport::Notifications.instrument('query.redis') do
|
9
|
-
process_without_appsignal(commands, &block)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
if defined?(::Sequel)
|
2
|
-
Appsignal.logger.info("Loading Sequel (#{ Sequel::VERSION }) integration")
|
3
|
-
|
4
|
-
module Appsignal
|
5
|
-
module Integrations
|
6
|
-
module Sequel
|
7
|
-
# Add query instrumentation
|
8
|
-
def log_yield(sql, args = nil)
|
9
|
-
|
10
|
-
# We'd like to get full sql queries in the payloads as well. To do
|
11
|
-
# that we need to find out a way to ask Sequel which quoting strategy
|
12
|
-
# is used by the adapter. We can then do something similar to the AR
|
13
|
-
# formatter.
|
14
|
-
|
15
|
-
ActiveSupport::Notifications.instrument('sql.sequel') do
|
16
|
-
yield
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end # Sequel
|
20
|
-
end # Integrations
|
21
|
-
end # Appsignal
|
22
|
-
|
23
|
-
# Register the extension...
|
24
|
-
Sequel::Database.register_extension(
|
25
|
-
:appsignal_integration,
|
26
|
-
Appsignal::Integrations::Sequel
|
27
|
-
)
|
28
|
-
|
29
|
-
# ... and automatically add it to future instances.
|
30
|
-
Sequel::Database.extension(:appsignal_integration)
|
31
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
if defined?(::Celluloid)
|
2
|
-
Appsignal.logger.info('Loading Celluloid integration')
|
3
|
-
|
4
|
-
# Some versions of Celluloid have race conditions while exiting
|
5
|
-
# that can result in a dead lock. We stop appsignal before shutting
|
6
|
-
# down Celluloid so we're sure our thread does not aggravate this situation.
|
7
|
-
# This way we also make sure any outstanding transactions get flushed.
|
8
|
-
|
9
|
-
::Celluloid.class_eval do
|
10
|
-
class << self
|
11
|
-
alias shutdown_without_appsignal shutdown
|
12
|
-
|
13
|
-
def shutdown
|
14
|
-
Appsignal.stop
|
15
|
-
shutdown_without_appsignal
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
if defined?(::Delayed::Plugin)
|
2
|
-
Appsignal.logger.info('Loading Delayed Job integration')
|
3
|
-
|
4
|
-
module Appsignal
|
5
|
-
module Integrations
|
6
|
-
class DelayedPlugin < ::Delayed::Plugin
|
7
|
-
callbacks do |lifecycle|
|
8
|
-
lifecycle.around(:invoke_job) do |job, &block|
|
9
|
-
invoke_with_instrumentation(job, block)
|
10
|
-
end
|
11
|
-
|
12
|
-
lifecycle.after(:loop) do |loop|
|
13
|
-
Appsignal.stop
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.invoke_with_instrumentation(job, block)
|
18
|
-
class_and_method_name = if job.payload_object.respond_to?(:appsignal_name)
|
19
|
-
job.payload_object.appsignal_name
|
20
|
-
else
|
21
|
-
job.name
|
22
|
-
end
|
23
|
-
class_name, method_name = class_and_method_name.split('#')
|
24
|
-
|
25
|
-
Appsignal.monitor_transaction(
|
26
|
-
'perform_job.delayed_job',
|
27
|
-
:class => class_name,
|
28
|
-
:method => method_name,
|
29
|
-
:metadata => {
|
30
|
-
:id => job.id,
|
31
|
-
:queue => job.queue,
|
32
|
-
:priority => job.priority || 0,
|
33
|
-
:attempts => job.attempts || 0
|
34
|
-
},
|
35
|
-
:queue_start => job.created_at
|
36
|
-
) do
|
37
|
-
block.call(job)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
::Delayed::Worker.plugins << Appsignal::Integrations::DelayedPlugin
|
44
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
if defined?(::PhusionPassenger)
|
2
|
-
Appsignal.logger.info('Loading Passenger integration')
|
3
|
-
|
4
|
-
::PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
5
|
-
Appsignal.forked
|
6
|
-
end
|
7
|
-
|
8
|
-
::PhusionPassenger.on_event(:stopping_worker_process) do
|
9
|
-
Appsignal.stop
|
10
|
-
end
|
11
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
if defined?(::Puma) && ::Puma.respond_to?(:cli_config)
|
2
|
-
Appsignal.logger.info('Loading Puma integration')
|
3
|
-
|
4
|
-
if ::Puma.cli_config
|
5
|
-
::Puma.cli_config.options[:before_worker_shutdown] ||= []
|
6
|
-
::Puma.cli_config.options[:before_worker_shutdown] << Proc.new do |id|
|
7
|
-
Appsignal.stop
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
if defined?(::Rails)
|
2
|
-
Appsignal.logger.info("Loading Rails (#{Rails.version}) integration")
|
3
|
-
|
4
|
-
require 'appsignal/rack/rails_instrumentation'
|
5
|
-
|
6
|
-
module Appsignal
|
7
|
-
module Integrations
|
8
|
-
class Railtie < ::Rails::Railtie
|
9
|
-
initializer 'appsignal.configure_rails_initialization' do |app|
|
10
|
-
Appsignal::Integrations::Railtie.initialize_appsignal(app)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.initialize_appsignal(app)
|
14
|
-
# Load config
|
15
|
-
Appsignal.config = Appsignal::Config.new(
|
16
|
-
Rails.root,
|
17
|
-
ENV.fetch('APPSIGNAL_APP_ENV', Rails.env),
|
18
|
-
:name => Rails.application.class.parent_name,
|
19
|
-
:log_file_path => Rails.root.join('log/appsignal.log')
|
20
|
-
)
|
21
|
-
|
22
|
-
# Start logger
|
23
|
-
Appsignal.start_logger
|
24
|
-
|
25
|
-
app.middleware.insert_before(
|
26
|
-
ActionDispatch::RemoteIp,
|
27
|
-
Appsignal::Rack::RailsInstrumentation
|
28
|
-
)
|
29
|
-
|
30
|
-
if Appsignal.config.active? &&
|
31
|
-
Appsignal.config[:enable_frontend_error_catching] == true
|
32
|
-
app.middleware.insert_before(
|
33
|
-
Appsignal::Rack::RailsInstrumentation,
|
34
|
-
Appsignal::Rack::JSExceptionCatcher,
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
Appsignal.start
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Rake
|
2
|
-
class Task
|
3
|
-
alias_method :invoke_without_appsignal, :invoke
|
4
|
-
|
5
|
-
def invoke(*args)
|
6
|
-
if Appsignal.active?
|
7
|
-
invoke_with_appsignal(*args)
|
8
|
-
else
|
9
|
-
invoke_without_appsignal(*args)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def invoke_with_appsignal(*args)
|
14
|
-
invoke_without_appsignal(*args)
|
15
|
-
rescue => error
|
16
|
-
transaction = Appsignal::Transaction.create(
|
17
|
-
SecureRandom.uuid,
|
18
|
-
Appsignal::Transaction::BACKGROUND_JOB,
|
19
|
-
Appsignal::Transaction::GenericRequest.new(
|
20
|
-
:params => args
|
21
|
-
)
|
22
|
-
)
|
23
|
-
transaction.set_action(name)
|
24
|
-
transaction.set_error(error)
|
25
|
-
transaction.complete!
|
26
|
-
Appsignal.stop
|
27
|
-
raise error
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
if defined?(::Resque)
|
2
|
-
Appsignal.logger.info('Loading Resque integration')
|
3
|
-
|
4
|
-
module Appsignal
|
5
|
-
module Integrations
|
6
|
-
module ResquePlugin
|
7
|
-
def around_perform_resque_plugin(*args)
|
8
|
-
Appsignal.monitor_single_transaction(
|
9
|
-
'perform_job.resque',
|
10
|
-
:class => self.to_s,
|
11
|
-
:method => 'perform'
|
12
|
-
) do
|
13
|
-
yield
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# Extend the default job class with AppSignal instrumentation
|
21
|
-
Resque::Job.send(:extend, Appsignal::Integrations::ResquePlugin)
|
22
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
if defined?(::Sidekiq)
|
2
|
-
Appsignal.logger.info('Loading Sidekiq integration')
|
3
|
-
|
4
|
-
module Appsignal
|
5
|
-
module Integrations
|
6
|
-
class SidekiqPlugin
|
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
|
-
))
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(worker, item, queue)
|
16
|
-
Appsignal.monitor_transaction(
|
17
|
-
'perform_job.sidekiq',
|
18
|
-
:class => item['wrapped'] || item['class'],
|
19
|
-
:method => 'perform',
|
20
|
-
:metadata => formatted_metadata(item),
|
21
|
-
:params => format_args(item['args']),
|
22
|
-
:queue_start => item['enqueued_at']
|
23
|
-
) do
|
24
|
-
yield
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def formatted_metadata(item)
|
29
|
-
{}.tap do |hsh|
|
30
|
-
item.each do |key, val|
|
31
|
-
hsh[key] = truncate(string_or_inspect(val)) unless job_keys.include?(key)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def string_or_inspect(string_or_other)
|
37
|
-
if string_or_other.is_a?(String)
|
38
|
-
string_or_other
|
39
|
-
else
|
40
|
-
string_or_other.inspect
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def format_args(args)
|
45
|
-
args.map do |arg|
|
46
|
-
truncate(string_or_inspect(arg))
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def truncate(text)
|
51
|
-
text.size > 200 ? "#{text[0...197]}..." : text
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
Sidekiq.configure_server do |config|
|
58
|
-
config.server_middleware do |chain|
|
59
|
-
chain.add Appsignal::Integrations::SidekiqPlugin
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
if defined?(::Unicorn)
|
2
|
-
Appsignal.logger.info('Loading Unicorn integration')
|
3
|
-
|
4
|
-
# Make sure the appsignal is started and the last transaction
|
5
|
-
# in a worker gets flushed.
|
6
|
-
#
|
7
|
-
# We'd love to be able to hook this into Unicorn in a less
|
8
|
-
# intrusive way, but this is the best we can do given the
|
9
|
-
# options we have.
|
10
|
-
|
11
|
-
class Unicorn::HttpServer
|
12
|
-
alias_method :original_worker_loop, :worker_loop
|
13
|
-
|
14
|
-
def worker_loop(worker)
|
15
|
-
Appsignal.forked
|
16
|
-
original_worker_loop(worker)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class Unicorn::Worker
|
21
|
-
alias_method :original_close, :close
|
22
|
-
|
23
|
-
def close
|
24
|
-
Appsignal.stop
|
25
|
-
original_close
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require File.expand_path('lib/appsignal/instrumentations/net_http')
|
3
|
-
|
4
|
-
describe "Net::HTTP instrumentation" do
|
5
|
-
let(:events) { [] }
|
6
|
-
before do
|
7
|
-
ActiveSupport::Notifications.subscribe(/^[^!]/) do |*args|
|
8
|
-
events << ActiveSupport::Notifications::Event.new(*args)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should generate an event for a http request" do
|
13
|
-
stub_request(:any, 'http://www.google.com/')
|
14
|
-
|
15
|
-
Net::HTTP.get_response(URI.parse('http://www.google.com'))
|
16
|
-
|
17
|
-
event = events.last
|
18
|
-
event.name.should == 'request.net_http'
|
19
|
-
event.payload[:protocol].should == 'http'
|
20
|
-
event.payload[:domain].should == 'www.google.com'
|
21
|
-
event.payload[:path].should == '/'
|
22
|
-
event.payload[:method].should == 'GET'
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should generate an event for a https request" do
|
26
|
-
stub_request(:any, 'https://www.google.com/')
|
27
|
-
|
28
|
-
uri = URI.parse('https://www.google.com')
|
29
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
30
|
-
http.use_ssl = true
|
31
|
-
http.get(uri.request_uri)
|
32
|
-
|
33
|
-
event = events.last
|
34
|
-
event.name.should == 'request.net_http'
|
35
|
-
event.payload[:protocol].should == 'https'
|
36
|
-
event.payload[:domain].should == 'www.google.com'
|
37
|
-
event.payload[:path].should == '/'
|
38
|
-
event.payload[:method].should == 'GET'
|
39
|
-
end
|
40
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Net::HTTP instrumentation" do
|
4
|
-
let(:file) { File.expand_path('lib/appsignal/instrumentations/redis.rb') }
|
5
|
-
|
6
|
-
let(:events) { [] }
|
7
|
-
before do
|
8
|
-
ActiveSupport::Notifications.subscribe(/^[^!]/) do |*args|
|
9
|
-
events << ActiveSupport::Notifications::Event.new(*args)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
context "with redis" do
|
14
|
-
before :all do
|
15
|
-
module Redis
|
16
|
-
class Client
|
17
|
-
def process(commands, &block)
|
18
|
-
1
|
19
|
-
end
|
20
|
-
end
|
21
|
-
VERSION = '1.0'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
before do
|
26
|
-
load file
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should generate an event for a redis call" do
|
30
|
-
client = Redis::Client.new
|
31
|
-
|
32
|
-
client.process([]).should == 1
|
33
|
-
|
34
|
-
event = events.last
|
35
|
-
event.name.should == 'query.redis'
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context "without redis" do
|
40
|
-
before(:all) { Object.send(:remove_const, :Redis) }
|
41
|
-
|
42
|
-
specify { expect { ::Redis }.to raise_error(NameError) }
|
43
|
-
specify { expect { load file }.to_not raise_error }
|
44
|
-
end
|
45
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Passenger integration" do
|
4
|
-
let(:file) { File.expand_path('lib/appsignal/integrations/passenger.rb') }
|
5
|
-
before(:all) do
|
6
|
-
module PhusionPassenger
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
it "adds behavior to stopping_worker_process and starting_worker_process" do
|
11
|
-
PhusionPassenger.should_receive(:on_event).with(:starting_worker_process)
|
12
|
-
PhusionPassenger.should_receive(:on_event).with(:stopping_worker_process)
|
13
|
-
load file
|
14
|
-
end
|
15
|
-
|
16
|
-
context "without passenger" do
|
17
|
-
before(:all) { Object.send(:remove_const, :PhusionPassenger) }
|
18
|
-
|
19
|
-
specify { expect { PhusionPassenger }.to raise_error(NameError) }
|
20
|
-
specify { expect { load file }.to_not raise_error }
|
21
|
-
end
|
22
|
-
end
|