appsignal 1.2.5 → 1.3.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/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
|