appsignal 1.2.5 → 1.3.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/circle.yml +4 -0
- data/ext/agent.yml +11 -11
- data/ext/appsignal_extension.c +105 -40
- data/lib/appsignal.rb +18 -6
- data/lib/appsignal/cli/install.rb +3 -3
- data/lib/appsignal/config.rb +19 -5
- data/lib/appsignal/event_formatter.rb +3 -2
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +13 -6
- data/lib/appsignal/hooks/mongo_ruby_driver.rb +0 -1
- data/lib/appsignal/hooks/net_http.rb +2 -5
- data/lib/appsignal/hooks/redis.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +8 -4
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -1
- data/lib/appsignal/integrations/object.rb +35 -0
- data/lib/appsignal/integrations/resque.rb +5 -0
- data/lib/appsignal/integrations/sinatra.rb +2 -2
- data/lib/appsignal/minutely.rb +41 -0
- data/lib/appsignal/params_sanitizer.rb +4 -105
- data/lib/appsignal/rack/sinatra_instrumentation.rb +25 -2
- data/lib/appsignal/transaction.rb +41 -15
- data/lib/appsignal/transmitter.rb +1 -1
- data/lib/appsignal/utils.rb +42 -47
- data/lib/appsignal/utils/params_sanitizer.rb +58 -0
- data/lib/appsignal/utils/query_params_sanitizer.rb +54 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/config_spec.rb +12 -2
- data/spec/lib/appsignal/extension_spec.rb +4 -0
- data/spec/lib/appsignal/hooks/net_http_spec.rb +20 -28
- data/spec/lib/appsignal/hooks/redis_spec.rb +9 -11
- data/spec/lib/appsignal/integrations/object_spec.rb +211 -0
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
- data/spec/lib/appsignal/minutely_spec.rb +54 -0
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +50 -10
- data/spec/lib/appsignal/subscriber_spec.rb +5 -6
- data/spec/lib/appsignal/transaction_spec.rb +102 -23
- data/spec/lib/appsignal/transmitter_spec.rb +1 -1
- data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +122 -0
- data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +194 -0
- data/spec/lib/appsignal/utils_spec.rb +13 -76
- data/spec/lib/appsignal_spec.rb +82 -13
- metadata +15 -11
- data/lib/appsignal/event_formatter/net_http/request_formatter.rb +0 -13
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +0 -13
- data/spec/lib/appsignal/event_formatter/net_http/request_formatter_spec.rb +0 -26
- data/spec/lib/appsignal/event_formatter/sequel/sql_formatter_spec.rb +0 -22
- data/spec/lib/appsignal/params_sanitizer_spec.rb +0 -200
data/lib/appsignal/config.rb
CHANGED
@@ -8,6 +8,7 @@ module Appsignal
|
|
8
8
|
:debug => false,
|
9
9
|
:ignore_errors => [],
|
10
10
|
:ignore_actions => [],
|
11
|
+
:filter_parameters => [],
|
11
12
|
:send_params => true,
|
12
13
|
:endpoint => 'https://push.appsignal.com',
|
13
14
|
:instrument_net_http => true,
|
@@ -19,7 +20,9 @@ module Appsignal
|
|
19
20
|
:enable_allocation_tracking => true,
|
20
21
|
:enable_gc_instrumentation => false,
|
21
22
|
:running_in_container => false,
|
22
|
-
:enable_host_metrics =>
|
23
|
+
:enable_host_metrics => true,
|
24
|
+
:enable_minutely_probes => false,
|
25
|
+
:hostname => Socket.gethostname
|
23
26
|
}.freeze
|
24
27
|
|
25
28
|
ENV_TO_KEY_MAPPING = {
|
@@ -37,12 +40,16 @@ module Appsignal
|
|
37
40
|
'APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING' => :enable_frontend_error_catching,
|
38
41
|
'APPSIGNAL_IGNORE_ERRORS' => :ignore_errors,
|
39
42
|
'APPSIGNAL_IGNORE_ACTIONS' => :ignore_actions,
|
43
|
+
'APPSIGNAL_FILTER_PARAMETERS' => :filter_parameters,
|
44
|
+
'APPSIGNAL_SEND_PARAMS' => :send_params,
|
40
45
|
'APPSIGNAL_HTTP_PROXY' => :http_proxy,
|
41
46
|
'APPSIGNAL_ENABLE_ALLOCATION_TRACKING' => :enable_allocation_tracking,
|
42
47
|
'APPSIGNAL_ENABLE_GC_INSTRUMENTATION' => :enable_gc_instrumentation,
|
43
48
|
'APPSIGNAL_RUNNING_IN_CONTAINER' => :running_in_container,
|
44
49
|
'APPSIGNAL_WORKING_DIR_PATH' => :working_dir_path,
|
45
|
-
'APPSIGNAL_ENABLE_HOST_METRICS' => :enable_host_metrics
|
50
|
+
'APPSIGNAL_ENABLE_HOST_METRICS' => :enable_host_metrics,
|
51
|
+
'APPSIGNAL_ENABLE_MINUTELY_PROBES' => :enable_minutely_probes,
|
52
|
+
'APPSIGNAL_HOSTNAME' => :hostname
|
46
53
|
}.freeze
|
47
54
|
|
48
55
|
attr_reader :root_path, :env, :initial_config, :config_hash
|
@@ -111,9 +118,14 @@ module Appsignal
|
|
111
118
|
ENV['APPSIGNAL_APP_NAME'] = config_hash[:name]
|
112
119
|
ENV['APPSIGNAL_HTTP_PROXY'] = config_hash[:http_proxy]
|
113
120
|
ENV['APPSIGNAL_IGNORE_ACTIONS'] = config_hash[:ignore_actions].join(',')
|
121
|
+
ENV['APPSIGNAL_FILTER_PARAMETERS'] = config_hash[:filter_parameters].join(',')
|
122
|
+
ENV['APPSIGNAL_SEND_PARAMS'] = config_hash[:send_params].to_s
|
114
123
|
ENV['APPSIGNAL_RUNNING_IN_CONTAINER'] = config_hash[:running_in_container].to_s
|
115
124
|
ENV['APPSIGNAL_WORKING_DIR_PATH'] = config_hash[:working_dir_path] if config_hash[:working_dir_path]
|
116
125
|
ENV['APPSIGNAL_ENABLE_HOST_METRICS'] = config_hash[:enable_host_metrics].to_s
|
126
|
+
ENV['APPSIGNAL_ENABLE_MINUTELY_PROBES'] = config_hash[:enable_minutely_probes].to_s
|
127
|
+
ENV['APPSIGNAL_HOSTNAME'] = config_hash[:hostname].to_s
|
128
|
+
ENV['APPSIGNAL_PROCESS_NAME'] = $0
|
117
129
|
end
|
118
130
|
|
119
131
|
protected
|
@@ -162,7 +174,7 @@ module Appsignal
|
|
162
174
|
# Configuration with string type
|
163
175
|
%w(APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
|
164
176
|
APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY APPSIGNAL_LOG_PATH
|
165
|
-
APPSIGNAL_WORKING_DIR_PATH).each do |var|
|
177
|
+
APPSIGNAL_WORKING_DIR_PATH APPSIGNAL_HOSTNAME).each do |var|
|
166
178
|
if env_var = ENV[var]
|
167
179
|
config[ENV_TO_KEY_MAPPING[var]] = env_var
|
168
180
|
end
|
@@ -172,14 +184,16 @@ module Appsignal
|
|
172
184
|
%w(APPSIGNAL_ACTIVE APPSIGNAL_DEBUG APPSIGNAL_INSTRUMENT_NET_HTTP
|
173
185
|
APPSIGNAL_SKIP_SESSION_DATA APPSIGNAL_ENABLE_FRONTEND_ERROR_CATCHING
|
174
186
|
APPSIGNAL_ENABLE_ALLOCATION_TRACKING APPSIGNAL_ENABLE_GC_INSTRUMENTATION
|
175
|
-
APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
|
187
|
+
APPSIGNAL_RUNNING_IN_CONTAINER APPSIGNAL_ENABLE_HOST_METRICS
|
188
|
+
APPSIGNAL_SEND_PARAMS APPSIGNAL_ENABLE_MINUTELY_PROBES).each do |var|
|
176
189
|
if env_var = ENV[var]
|
177
190
|
config[ENV_TO_KEY_MAPPING[var]] = env_var == 'true'
|
178
191
|
end
|
179
192
|
end
|
180
193
|
|
181
194
|
# Configuration with array of strings type
|
182
|
-
%w(APPSIGNAL_IGNORE_ERRORS APPSIGNAL_IGNORE_ACTIONS
|
195
|
+
%w(APPSIGNAL_IGNORE_ERRORS APPSIGNAL_IGNORE_ACTIONS
|
196
|
+
APPSIGNAL_FILTER_PARAMETERS).each do |var|
|
183
197
|
if env_var = ENV[var]
|
184
198
|
config[ENV_TO_KEY_MAPPING[var]] = env_var.split(',')
|
185
199
|
end
|
@@ -59,9 +59,10 @@ module Appsignal
|
|
59
59
|
formatter.format(payload) unless formatter.nil?
|
60
60
|
end
|
61
61
|
end
|
62
|
-
end
|
63
62
|
|
64
|
-
|
63
|
+
DEFAULT = 0
|
64
|
+
SQL_BODY_FORMAT = 1
|
65
|
+
end
|
65
66
|
end
|
66
67
|
|
67
68
|
Dir.glob(File.expand_path('../event_formatter/**/*.rb', __FILE__)).each do |file|
|
@@ -69,7 +69,7 @@ module Appsignal
|
|
69
69
|
when :deny then '?'
|
70
70
|
when :deny_array then '[?]'
|
71
71
|
when :sanitize_document
|
72
|
-
Appsignal::Utils.sanitize(val, true, :mongodb)
|
72
|
+
Appsignal::Utils::QueryParamsSanitizer.sanitize(val, true, :mongodb)
|
73
73
|
when :sanitize_bulk
|
74
74
|
if val.length > 1
|
75
75
|
[
|
@@ -11,12 +11,12 @@ module Appsignal
|
|
11
11
|
when 'Moped::Protocol::Command'
|
12
12
|
return ['Command', {
|
13
13
|
:database => op.full_collection_name,
|
14
|
-
:selector =>
|
14
|
+
:selector => sanitize(op.selector, true, :mongodb)
|
15
15
|
}.inspect]
|
16
16
|
when 'Moped::Protocol::Query'
|
17
17
|
return ['Query', {
|
18
18
|
:database => op.full_collection_name,
|
19
|
-
:selector =>
|
19
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
20
20
|
:flags => op.flags,
|
21
21
|
:limit => op.limit,
|
22
22
|
:skip => op.skip,
|
@@ -25,21 +25,21 @@ module Appsignal
|
|
25
25
|
when 'Moped::Protocol::Delete'
|
26
26
|
return ['Delete', {
|
27
27
|
:database => op.full_collection_name,
|
28
|
-
:selector =>
|
28
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
29
29
|
:flags => op.flags,
|
30
30
|
}.inspect]
|
31
31
|
when 'Moped::Protocol::Insert'
|
32
32
|
return ['Insert', {
|
33
33
|
:database => op.full_collection_name,
|
34
|
-
:documents =>
|
34
|
+
:documents => sanitize(op.documents, true, :mongodb),
|
35
35
|
:count => op.documents.count,
|
36
36
|
:flags => op.flags,
|
37
37
|
}.inspect]
|
38
38
|
when 'Moped::Protocol::Update'
|
39
39
|
return ['Update', {
|
40
40
|
:database => op.full_collection_name,
|
41
|
-
:selector =>
|
42
|
-
:update =>
|
41
|
+
:selector => sanitize(op.selector, false, :mongodb),
|
42
|
+
:update => sanitize(op.update, true, :mongodb),
|
43
43
|
:flags => op.flags,
|
44
44
|
}.inspect]
|
45
45
|
when 'Moped::Protocol::KillCursors'
|
@@ -53,6 +53,13 @@ module Appsignal
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def sanitize(params, only_top_level, key_sanitizer)
|
60
|
+
Appsignal::Utils::QueryParamsSanitizer.sanitize \
|
61
|
+
params, only_top_level, key_sanitizer
|
62
|
+
end
|
56
63
|
end
|
57
64
|
end
|
58
65
|
end
|
@@ -14,12 +14,9 @@ module Appsignal
|
|
14
14
|
alias request_without_appsignal request
|
15
15
|
|
16
16
|
def request(request, body=nil, &block)
|
17
|
-
|
17
|
+
Appsignal.instrument(
|
18
18
|
'request.net_http',
|
19
|
-
|
20
|
-
:domain => request['host'] || self.address,
|
21
|
-
:path => request.path,
|
22
|
-
:method => request.method
|
19
|
+
"#{request.method} #{use_ssl? ? 'https' : 'http'}://#{request['host'] || self.address}"
|
23
20
|
) do
|
24
21
|
request_without_appsignal(request, body, &block)
|
25
22
|
end
|
@@ -3,9 +3,11 @@ module Appsignal
|
|
3
3
|
module SequelLogExtension
|
4
4
|
# Add query instrumentation
|
5
5
|
def log_yield(sql, args = nil)
|
6
|
-
|
6
|
+
Appsignal.instrument(
|
7
7
|
'sql.sequel',
|
8
|
-
|
8
|
+
nil,
|
9
|
+
sql,
|
10
|
+
Appsignal::EventFormatter::SQL_BODY_FORMAT
|
9
11
|
) do
|
10
12
|
yield
|
11
13
|
end
|
@@ -15,9 +17,11 @@ module Appsignal
|
|
15
17
|
module SequelLogConnectionExtension
|
16
18
|
# Add query instrumentation
|
17
19
|
def log_connection_yield(sql, conn, args = nil)
|
18
|
-
|
20
|
+
Appsignal.instrument(
|
19
21
|
'sql.sequel',
|
20
|
-
|
22
|
+
nil,
|
23
|
+
sql,
|
24
|
+
Appsignal::EventFormatter::SQL_BODY_FORMAT
|
21
25
|
) do
|
22
26
|
yield
|
23
27
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Object
|
2
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
3
|
+
singleton_class.send \
|
4
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
5
|
+
singleton_class.send(:define_method, method_name) do |*args|
|
6
|
+
name = options.fetch(:name) do
|
7
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
8
|
+
end
|
9
|
+
Appsignal.instrument name do
|
10
|
+
send "appsignal_uninstrumented_#{method_name}", *args
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
16
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
17
|
+
define_method method_name do |*args|
|
18
|
+
name = options.fetch(:name) do
|
19
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
20
|
+
end
|
21
|
+
Appsignal.instrument name do
|
22
|
+
send "appsignal_uninstrumented_#{method_name}", *args
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.appsignal_reverse_class_name
|
28
|
+
return "AnonymousClass" unless name
|
29
|
+
name.split("::").reverse.join(".")
|
30
|
+
end
|
31
|
+
|
32
|
+
def appsignal_reverse_class_name
|
33
|
+
self.class.appsignal_reverse_class_name
|
34
|
+
end
|
35
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
module Appsignal
|
2
2
|
module Integrations
|
3
3
|
module ResquePlugin
|
4
|
+
|
5
|
+
# Do not use this file as a template for your own background processor
|
6
|
+
# Resque is an exception to the rule and the code below causes the
|
7
|
+
# extension to shut itself down after a single job.
|
8
|
+
# see http://docs.appsignal.com/background-monitoring/custom.html
|
4
9
|
def around_perform_resque_plugin(*args)
|
5
10
|
Appsignal.monitor_single_transaction(
|
6
11
|
'perform_job.resque',
|
@@ -5,7 +5,7 @@ Appsignal.logger.info("Loading Sinatra (#{Sinatra::VERSION}) integration")
|
|
5
5
|
|
6
6
|
app_settings = ::Sinatra::Application.settings
|
7
7
|
Appsignal.config = Appsignal::Config.new(
|
8
|
-
app_settings.root,
|
8
|
+
app_settings.root || Dir.pwd,
|
9
9
|
app_settings.environment
|
10
10
|
)
|
11
11
|
|
@@ -13,5 +13,5 @@ Appsignal.start_logger
|
|
13
13
|
Appsignal.start
|
14
14
|
|
15
15
|
if Appsignal.active?
|
16
|
-
::Sinatra::
|
16
|
+
::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation)
|
17
17
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Appsignal
|
2
|
+
class Minutely
|
3
|
+
class << self
|
4
|
+
# List of probes. Probes can be lamdba's or objects that
|
5
|
+
# respond to call.
|
6
|
+
def probes
|
7
|
+
@@probes ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
Thread.new do
|
12
|
+
begin
|
13
|
+
loop do
|
14
|
+
Appsignal.logger.debug("Gathering minutely metrics with #{probes.count} probe(s)")
|
15
|
+
probes.each(&:call)
|
16
|
+
sleep(wait_time)
|
17
|
+
end
|
18
|
+
rescue Exception => ex
|
19
|
+
Appsignal.logger.error("Error in minutely thread: #{ex}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait_time
|
25
|
+
60 - Time.now.sec
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_gc_probe
|
29
|
+
probes << GCProbe.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class GCProbe
|
34
|
+
def call
|
35
|
+
GC.stat.each do |key, value|
|
36
|
+
Appsignal.set_process_gauge("gc.#{key}", value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,114 +1,13 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class ParamsSanitizer
|
3
3
|
class << self
|
4
|
-
|
5
|
-
ParamsSanitizerCopy.sanitize_value(params)
|
6
|
-
end
|
7
|
-
|
8
|
-
def sanitize!(params)
|
9
|
-
ParamsSanitizerDestructive.sanitize_value(params)
|
10
|
-
end
|
11
|
-
|
12
|
-
def scrub(params)
|
13
|
-
ParamsSanitizerCopyScrub.sanitize_value(params)
|
14
|
-
end
|
15
|
-
|
16
|
-
def scrub!(params)
|
17
|
-
ParamsSanitizerDestructiveScrub.sanitize_value(params)
|
18
|
-
end
|
19
|
-
|
20
|
-
protected
|
21
|
-
|
22
|
-
def sanitize_value(value)
|
23
|
-
case value
|
24
|
-
when Hash
|
25
|
-
sanitize_hash(value)
|
26
|
-
when Array
|
27
|
-
sanitize_array(value)
|
28
|
-
when TrueClass, FalseClass, NilClass, Fixnum, String, Symbol, Float
|
29
|
-
unmodified(value)
|
30
|
-
else
|
31
|
-
inspected(value)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def sanitize_hash_with_target(source_hash, target_hash)
|
36
|
-
source_hash.each_pair do |key, value|
|
37
|
-
target_hash[key] = sanitize_value(value)
|
38
|
-
end
|
39
|
-
target_hash
|
40
|
-
end
|
41
|
-
|
42
|
-
def sanitize_array_with_target(source_array, target_array)
|
43
|
-
source_array.each_with_index do |item, index|
|
44
|
-
target_array[index] = sanitize_value(item)
|
45
|
-
end
|
46
|
-
target_array
|
47
|
-
end
|
48
|
-
|
49
|
-
def unmodified(value)
|
50
|
-
value
|
51
|
-
end
|
52
|
-
|
53
|
-
def inspected(value)
|
54
|
-
"#<#{value.class.to_s}>"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class ParamsSanitizerCopy < ParamsSanitizer
|
60
|
-
class << self
|
61
|
-
protected
|
62
|
-
|
63
|
-
def sanitize_hash(hash)
|
64
|
-
sanitize_hash_with_target(hash, {})
|
65
|
-
end
|
4
|
+
extend Gem::Deprecate
|
66
5
|
|
67
|
-
def
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
class ParamsSanitizerDestructive < ParamsSanitizer
|
74
|
-
class << self
|
75
|
-
protected
|
76
|
-
|
77
|
-
def sanitize_hash(hash)
|
78
|
-
sanitize_hash_with_target(hash, hash)
|
79
|
-
end
|
80
|
-
|
81
|
-
def sanitize_array(array)
|
82
|
-
sanitize_array_with_target(array, array)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
class ParamsSanitizerCopyScrub < ParamsSanitizerCopy
|
88
|
-
class << self
|
89
|
-
protected
|
90
|
-
|
91
|
-
def unmodified(value)
|
92
|
-
'?'
|
93
|
-
end
|
94
|
-
|
95
|
-
def inspected(value)
|
96
|
-
'?'
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class ParamsSanitizerDestructiveScrub < ParamsSanitizerDestructive
|
102
|
-
class << self
|
103
|
-
protected
|
104
|
-
|
105
|
-
def unmodified(value)
|
106
|
-
'?'
|
6
|
+
def sanitize(params)
|
7
|
+
Appsignal::Utils::ParamsSanitizer.sanitize(params)
|
107
8
|
end
|
108
9
|
|
109
|
-
|
110
|
-
'?'
|
111
|
-
end
|
10
|
+
deprecate :sanitize, "AppSignal::Utils::ParamsSanitizer.sanitize", 2016, 9
|
112
11
|
end
|
113
12
|
end
|
114
13
|
end
|