appsignal 1.0.7 → 1.1.0.beta.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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +5 -21
- data/Rakefile +2 -0
- data/circle.yml +2 -1
- data/ext/agent.yml +7 -7
- data/ext/appsignal_extension.c +3 -5
- data/ext/extconf.rb +6 -15
- data/gemfiles/grape.gemfile +7 -0
- data/lib/appsignal/config.rb +2 -5
- data/lib/appsignal/event_formatter.rb +0 -2
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +47 -1
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +29 -0
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +7 -6
- data/lib/appsignal/hooks.rb +33 -0
- data/lib/appsignal/hooks/net_http.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +7 -4
- data/lib/appsignal/hooks/sidekiq.rb +10 -19
- data/lib/appsignal/integrations/capistrano/appsignal.cap +1 -1
- data/lib/appsignal/integrations/delayed_job_plugin.rb +20 -11
- data/lib/appsignal/integrations/grape.rb +44 -0
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +5 -9
- data/lib/appsignal/integrations/railtie.rb +4 -0
- data/lib/appsignal/integrations/resque.rb +1 -1
- data/lib/appsignal/js_exception_transaction.rb +2 -3
- data/lib/appsignal/subscriber.rb +2 -3
- data/lib/appsignal/transaction.rb +2 -8
- data/lib/appsignal/transmitter.rb +1 -1
- data/lib/appsignal/utils.rb +7 -43
- data/lib/appsignal/version.rb +1 -1
- data/lib/tasks/diag.rake +75 -0
- data/spec/lib/appsignal/capistrano3_spec.rb +1 -21
- data/spec/lib/appsignal/config_spec.rb +0 -12
- data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +1 -1
- data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +186 -14
- data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +54 -0
- data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +4 -4
- data/spec/lib/appsignal/extension_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +49 -14
- data/spec/lib/appsignal/hooks/sequel_spec.rb +1 -1
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +29 -62
- data/spec/lib/appsignal/hooks_spec.rb +115 -0
- data/spec/lib/appsignal/integrations/grape_spec.rb +94 -0
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +5 -8
- data/spec/lib/appsignal/integrations/resque_spec.rb +0 -1
- data/spec/lib/appsignal/js_exception_transaction_spec.rb +0 -1
- data/spec/lib/appsignal/subscriber_spec.rb +5 -23
- data/spec/lib/appsignal/transaction_spec.rb +0 -21
- data/spec/lib/appsignal/utils_spec.rb +1 -68
- data/spec/spec_helper.rb +16 -0
- data/spec/support/helpers/env_helpers.rb +0 -1
- data/spec/support/stubs/delayed_job.rb +0 -0
- metadata +15 -11
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +0 -88
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +0 -13
- data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +0 -115
- data/spec/lib/appsignal/event_formatter/sequel/sql_formatter_spec.rb +0 -22
@@ -1,6 +1,6 @@
|
|
1
1
|
namespace :appsignal do
|
2
2
|
task :deploy do
|
3
|
-
env = fetch(:
|
3
|
+
env = fetch(:rails_env, fetch(:rack_env, 'production'))
|
4
4
|
user = ENV['USER'] || ENV['USERNAME']
|
5
5
|
revision = fetch(:appsignal_revision, fetch(:current_revision))
|
6
6
|
logger = fetch(:logger, Logger.new($stdout))
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class Hooks
|
3
3
|
class DelayedJobPlugin < ::Delayed::Plugin
|
4
|
+
extend Appsignal::Hooks::Helpers
|
5
|
+
|
4
6
|
callbacks do |lifecycle|
|
5
7
|
lifecycle.around(:invoke_job) do |job, &block|
|
6
8
|
invoke_with_instrumentation(job, block)
|
@@ -12,24 +14,31 @@ module Appsignal
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.invoke_with_instrumentation(job, block)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
if job.respond_to?(:payload_object)
|
18
|
+
# Direct Delayed Job
|
19
|
+
class_and_method_name = extract_value(job.payload_object, :appsignal_name, job.name)
|
20
|
+
class_name, method_name = class_and_method_name.split('#')
|
21
|
+
args = extract_value(job.payload_object, :args, {})
|
22
|
+
job_data = job
|
23
|
+
elsif job.respond_to?(:job_data)
|
24
|
+
# Via ActiveJob
|
25
|
+
class_name, method_name = job.job_data[:name].split('#')
|
26
|
+
args = job.job_data[:args] || {}
|
27
|
+
job_data = job.job_data
|
28
|
+
end
|
21
29
|
|
22
30
|
Appsignal.monitor_transaction(
|
23
31
|
'perform_job.delayed_job',
|
24
32
|
:class => class_name,
|
25
33
|
:method => method_name,
|
26
34
|
:metadata => {
|
27
|
-
:id =>
|
28
|
-
:queue =>
|
29
|
-
:priority =>
|
30
|
-
:attempts =>
|
35
|
+
:id => extract_value(job_data, :id, nil, true),
|
36
|
+
:queue => extract_value(job_data, :queue),
|
37
|
+
:priority => extract_value(job_data, :priority, 0),
|
38
|
+
:attempts => extract_value(job_data, :attempts, 0)
|
31
39
|
},
|
32
|
-
:
|
40
|
+
:params => format_args(args),
|
41
|
+
:queue_start => extract_value(job_data, :created_at)
|
33
42
|
) do
|
34
43
|
block.call(job)
|
35
44
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Appsignal
|
2
|
+
module Grape
|
3
|
+
class Middleware < ::Grape::Middleware::Base
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
if Appsignal.active?
|
10
|
+
call_with_appsignal_monitoring(env)
|
11
|
+
else
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def call_with_appsignal_monitoring(env)
|
17
|
+
request = ::Rack::Request.new(env)
|
18
|
+
transaction = Appsignal::Transaction.create(
|
19
|
+
SecureRandom.uuid,
|
20
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
21
|
+
request
|
22
|
+
)
|
23
|
+
begin
|
24
|
+
@app.call(env)
|
25
|
+
rescue => error
|
26
|
+
transaction.set_error(error)
|
27
|
+
raise error
|
28
|
+
ensure
|
29
|
+
api_endpoint = env['api.endpoint']
|
30
|
+
if api_endpoint && options = api_endpoint.options
|
31
|
+
method = options[:method].first
|
32
|
+
klass = options[:for]
|
33
|
+
action = options[:path].first
|
34
|
+
transaction.set_action("#{method}::#{klass}##{action}")
|
35
|
+
end
|
36
|
+
transaction.set_http_or_background_queue_start
|
37
|
+
transaction.set_metadata('path', request.path)
|
38
|
+
transaction.set_metadata('method', env['REQUEST_METHOD'])
|
39
|
+
Appsignal::Transaction.complete_current!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,19 +1,16 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class Hooks
|
3
3
|
class MongoMonitorSubscriber
|
4
|
+
|
4
5
|
# Called by Mongo::Monitor when query starts
|
5
6
|
def started(event)
|
6
7
|
transaction = Appsignal::Transaction.current
|
7
8
|
return if transaction.nil_transaction?
|
8
9
|
return if transaction.paused?
|
9
10
|
|
10
|
-
# Format the command
|
11
|
-
command = Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter
|
12
|
-
.format(event.command_name, event.command)
|
13
|
-
|
14
11
|
# Store the query on the transaction, we need it when the event finishes
|
15
12
|
store = transaction.store('mongo_driver')
|
16
|
-
store[event.request_id] = command
|
13
|
+
store[event.request_id] = Appsignal::Utils.sanitize(event.command)
|
17
14
|
|
18
15
|
# Start this event
|
19
16
|
Appsignal::Extension.start_event(transaction.transaction_index)
|
@@ -39,15 +36,14 @@ module Appsignal
|
|
39
36
|
|
40
37
|
# Get the query from the transaction store
|
41
38
|
store = transaction.store('mongo_driver')
|
42
|
-
command = store
|
39
|
+
command = store[event.request_id].inspect
|
43
40
|
|
44
41
|
# Finish the event in the extension.
|
45
42
|
Appsignal::Extension.finish_event(
|
46
43
|
transaction.transaction_index,
|
47
44
|
'query.mongodb',
|
48
|
-
|
49
|
-
|
50
|
-
0
|
45
|
+
event.command_name.to_s,
|
46
|
+
%Q(#{event.database_name} | #{result} | #{command})
|
51
47
|
)
|
52
48
|
end
|
53
49
|
end
|
@@ -28,7 +28,7 @@ module Appsignal
|
|
28
28
|
@transaction_index,
|
29
29
|
@data['name'],
|
30
30
|
@data['message'],
|
31
|
-
|
31
|
+
JSON.generate(@data['backtrace'])
|
32
32
|
)
|
33
33
|
end
|
34
34
|
|
@@ -43,7 +43,7 @@ module Appsignal
|
|
43
43
|
Appsignal::Extension.set_transaction_sample_data(
|
44
44
|
@transaction_index,
|
45
45
|
key.to_s,
|
46
|
-
|
46
|
+
JSON.generate(data)
|
47
47
|
)
|
48
48
|
rescue JSON::GeneratorError=>e
|
49
49
|
Appsignal.logger.error("JSON generate error (#{e.message}) for '#{data.inspect}'")
|
@@ -53,7 +53,6 @@ module Appsignal
|
|
53
53
|
|
54
54
|
def complete!
|
55
55
|
Appsignal::Extension.finish_transaction(@transaction_index)
|
56
|
-
Appsignal::Extension.complete_transaction(@transaction_index)
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
data/lib/appsignal/subscriber.rb
CHANGED
@@ -45,13 +45,12 @@ module Appsignal
|
|
45
45
|
return unless transaction = Appsignal::Transaction.current
|
46
46
|
return if transaction.nil_transaction? || transaction.paused?
|
47
47
|
|
48
|
-
title, body
|
48
|
+
title, body = Appsignal::EventFormatter.format(name, payload)
|
49
49
|
Appsignal::Extension.finish_event(
|
50
50
|
transaction.transaction_index,
|
51
51
|
name,
|
52
52
|
title || BLANK,
|
53
|
-
body || BLANK
|
54
|
-
body_format || 0
|
53
|
+
body || BLANK
|
55
54
|
)
|
56
55
|
end
|
57
56
|
end
|
@@ -113,7 +113,7 @@ module Appsignal
|
|
113
113
|
Appsignal::Extension.set_transaction_sample_data(
|
114
114
|
transaction_index,
|
115
115
|
key.to_s,
|
116
|
-
|
116
|
+
JSON.generate(data)
|
117
117
|
)
|
118
118
|
rescue JSON::GeneratorError=>e
|
119
119
|
Appsignal.logger.error("JSON generate error (#{e.message}) for '#{data.inspect}'")
|
@@ -124,7 +124,6 @@ module Appsignal
|
|
124
124
|
:params => sanitized_params,
|
125
125
|
:environment => sanitized_environment,
|
126
126
|
:session_data => sanitized_session_data,
|
127
|
-
:metadata => metadata,
|
128
127
|
:tags => sanitized_tags
|
129
128
|
}.each do |key, data|
|
130
129
|
set_sample_data(key, data)
|
@@ -142,7 +141,7 @@ module Appsignal
|
|
142
141
|
transaction_index,
|
143
142
|
error.class.name,
|
144
143
|
error.message,
|
145
|
-
backtrace ?
|
144
|
+
backtrace ? JSON.generate(backtrace) : ''
|
146
145
|
)
|
147
146
|
rescue JSON::GeneratorError=>e
|
148
147
|
Appsignal.logger.error("JSON generate error (#{e.message}) for '#{backtrace.inspect}'")
|
@@ -213,11 +212,6 @@ module Appsignal
|
|
213
212
|
Appsignal::ParamsSanitizer.sanitize(session.to_hash)
|
214
213
|
end
|
215
214
|
|
216
|
-
def metadata
|
217
|
-
return unless request.env
|
218
|
-
request.env[:metadata]
|
219
|
-
end
|
220
|
-
|
221
215
|
# Only keep tags if they meet the following criteria:
|
222
216
|
# * Key is a symbol or string with less then 100 chars
|
223
217
|
# * Value is a symbol or string with less then 100 chars
|
@@ -53,7 +53,7 @@ module Appsignal
|
|
53
53
|
request['Content-Type'] = CONTENT_TYPE
|
54
54
|
request['Content-Encoding'] = CONTENT_ENCODING
|
55
55
|
request.body = Zlib::Deflate.deflate(
|
56
|
-
|
56
|
+
JSON.generate(payload, :quirks_mode => true),
|
57
57
|
Zlib::BEST_SPEED
|
58
58
|
)
|
59
59
|
end
|
data/lib/appsignal/utils.rb
CHANGED
@@ -1,61 +1,25 @@
|
|
1
1
|
module Appsignal
|
2
2
|
module Utils
|
3
|
-
def self.sanitize(params, only_top_level=false
|
3
|
+
def self.sanitize(params, only_top_level=false)
|
4
4
|
if params.is_a?(Hash)
|
5
5
|
{}.tap do |hsh|
|
6
6
|
params.each do |key, val|
|
7
|
-
hsh[
|
8
|
-
'?'
|
9
|
-
else
|
10
|
-
sanitize(val, only_top_level, key_sanitizer=nil)
|
11
|
-
end
|
7
|
+
hsh[key] = only_top_level ? '?' : sanitize(val, only_top_level)
|
12
8
|
end
|
13
9
|
end
|
14
10
|
elsif params.is_a?(Array)
|
15
11
|
if only_top_level
|
16
|
-
sanitize(params[0], only_top_level
|
12
|
+
sanitize(params[0], only_top_level)
|
13
|
+
elsif params.first.is_a?(String)
|
14
|
+
['?']
|
17
15
|
else
|
18
16
|
params.map do |item|
|
19
|
-
sanitize(item, only_top_level
|
20
|
-
end
|
17
|
+
sanitize(item, only_top_level)
|
18
|
+
end
|
21
19
|
end
|
22
20
|
else
|
23
21
|
'?'
|
24
22
|
end
|
25
23
|
end
|
26
|
-
|
27
|
-
def self.sanitize_key(key, sanitizer)
|
28
|
-
case sanitizer
|
29
|
-
when :mongodb then key.to_s.gsub(/(\..+)/, '.?')
|
30
|
-
else key
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.json_generate(body)
|
35
|
-
JSON.generate(jsonify(body))
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.jsonify(value)
|
39
|
-
case value
|
40
|
-
when String
|
41
|
-
encode_utf8(value)
|
42
|
-
when Numeric, NilClass, TrueClass, FalseClass
|
43
|
-
value
|
44
|
-
when Hash
|
45
|
-
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
|
46
|
-
when Array
|
47
|
-
value.map { |v| jsonify(v) }
|
48
|
-
else
|
49
|
-
jsonify(value.to_s)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.encode_utf8(value)
|
54
|
-
value.encode(
|
55
|
-
'utf-8'.freeze,
|
56
|
-
:invalid => :replace,
|
57
|
-
:undef => :replace
|
58
|
-
)
|
59
|
-
end
|
60
24
|
end
|
61
25
|
end
|
data/lib/appsignal/version.rb
CHANGED
data/lib/tasks/diag.rake
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'appsignal'
|
2
|
+
|
3
|
+
namespace :appsignal do
|
4
|
+
namespace :diag do
|
5
|
+
|
6
|
+
desc "Shows the AppSignal gem version"
|
7
|
+
task :gem_version do
|
8
|
+
puts "Gem version: #{Appsignal::VERSION}"
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Shows the agent version"
|
12
|
+
task :agent_version do
|
13
|
+
puts "Agent version: #{Appsignal::Extension.agent_version}"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Attempt to start appsignal"
|
17
|
+
task :start_appsignal do
|
18
|
+
Appsignal.start
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Checks if config is present and shows the values"
|
22
|
+
task :config => :start_appsignal do
|
23
|
+
Appsignal.config.config_hash.each do |key, val|
|
24
|
+
puts "Config #{key}: #{val}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Checks if required paths are writeable"
|
29
|
+
task :paths_writable => :start_appsignal do
|
30
|
+
possible_paths = [
|
31
|
+
Appsignal.config.root_path,
|
32
|
+
Appsignal.config.log_file_path
|
33
|
+
]
|
34
|
+
|
35
|
+
puts "Checking if required paths are writable:"
|
36
|
+
possible_paths.each do |path|
|
37
|
+
result = File.writable?(path) ? 'Ok' : 'Failed'
|
38
|
+
puts "#{path} ...#{result}"
|
39
|
+
end
|
40
|
+
puts "\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Check if API key is valid"
|
44
|
+
task :check_api_key => :start_appsignal do
|
45
|
+
auth_check = ::Appsignal::AuthCheck.new(Appsignal.config, Appsignal.logger)
|
46
|
+
status, result = auth_check.perform_with_result
|
47
|
+
if status == '200'
|
48
|
+
puts "Checking API key: Ok"
|
49
|
+
else
|
50
|
+
puts "Checking API key: Failed"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Check the ext installation log"
|
55
|
+
task :check_ext_install do
|
56
|
+
require 'bundler/cli'
|
57
|
+
require "bundler/cli/common"
|
58
|
+
path = Bundler::CLI::Common.select_spec('appsignal').full_gem_path
|
59
|
+
log_path = "#{path.strip}/ext/install.log"
|
60
|
+
puts "Showing last lines of extension install log: #{log_path}"
|
61
|
+
puts File.read(log_path)
|
62
|
+
puts "\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
task :run => [
|
66
|
+
"diag:gem_version",
|
67
|
+
"diag:agent_version",
|
68
|
+
"diag:start_appsignal",
|
69
|
+
"diag:config",
|
70
|
+
"diag:check_api_key",
|
71
|
+
"diag:paths_writable",
|
72
|
+
"diag:check_ext_install"
|
73
|
+
]
|
74
|
+
end
|
75
|
+
end
|
@@ -80,29 +80,9 @@ if capistrano3_present?
|
|
80
80
|
)
|
81
81
|
end
|
82
82
|
end
|
83
|
-
|
84
|
-
context "when stage is used instead of rack_env / rails_env" do
|
85
|
-
before do
|
86
|
-
@capistrano_config.delete(:rails_env)
|
87
|
-
@capistrano_config.set(:stage, 'stage_production')
|
88
|
-
end
|
89
|
-
|
90
|
-
it "should be instantiated with the right params" do
|
91
|
-
Appsignal::Config.should_receive(:new).with(
|
92
|
-
project_fixture_path,
|
93
|
-
'stage_production',
|
94
|
-
{:name => 'AppName'},
|
95
|
-
kind_of(Logger)
|
96
|
-
)
|
97
|
-
end
|
98
|
-
end
|
99
83
|
end
|
100
84
|
|
101
|
-
after
|
102
|
-
invoke('appsignal:deploy')
|
103
|
-
@capistrano_config.delete(:stage)
|
104
|
-
@capistrano_config.delete(:rack_env)
|
105
|
-
end
|
85
|
+
after { invoke('appsignal:deploy') }
|
106
86
|
end
|
107
87
|
|
108
88
|
context "send marker" do
|
@@ -98,18 +98,6 @@ describe Appsignal::Config do
|
|
98
98
|
ENV['APPSIGNAL_HTTP_PROXY'].should == 'http://localhost'
|
99
99
|
ENV['APPSIGNAL_IGNORE_ACTIONS'].should == 'action1,action2'
|
100
100
|
ENV['APPSIGNAL_RUNNING_IN_CONTAINER'].should == 'false'
|
101
|
-
ENV['APPSIGNAL_WORKING_DIR_PATH'].should be_nil
|
102
|
-
end
|
103
|
-
|
104
|
-
context "if working_dir_path is set" do
|
105
|
-
before do
|
106
|
-
subject.config_hash[:working_dir_path] = '/tmp/appsignal2'
|
107
|
-
subject.write_to_environment
|
108
|
-
end
|
109
|
-
|
110
|
-
it "should write the current config to env vars" do
|
111
|
-
ENV['APPSIGNAL_WORKING_DIR_PATH'].should == '/tmp/appsignal2'
|
112
|
-
end
|
113
101
|
end
|
114
102
|
end
|
115
103
|
|