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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/circle.yml +4 -0
  4. data/ext/agent.yml +11 -11
  5. data/ext/appsignal_extension.c +105 -40
  6. data/lib/appsignal.rb +18 -6
  7. data/lib/appsignal/cli/install.rb +3 -3
  8. data/lib/appsignal/config.rb +19 -5
  9. data/lib/appsignal/event_formatter.rb +3 -2
  10. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -1
  11. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +1 -1
  12. data/lib/appsignal/event_formatter/moped/query_formatter.rb +13 -6
  13. data/lib/appsignal/hooks/mongo_ruby_driver.rb +0 -1
  14. data/lib/appsignal/hooks/net_http.rb +2 -5
  15. data/lib/appsignal/hooks/redis.rb +1 -1
  16. data/lib/appsignal/hooks/sequel.rb +8 -4
  17. data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -1
  18. data/lib/appsignal/integrations/object.rb +35 -0
  19. data/lib/appsignal/integrations/resque.rb +5 -0
  20. data/lib/appsignal/integrations/sinatra.rb +2 -2
  21. data/lib/appsignal/minutely.rb +41 -0
  22. data/lib/appsignal/params_sanitizer.rb +4 -105
  23. data/lib/appsignal/rack/sinatra_instrumentation.rb +25 -2
  24. data/lib/appsignal/transaction.rb +41 -15
  25. data/lib/appsignal/transmitter.rb +1 -1
  26. data/lib/appsignal/utils.rb +42 -47
  27. data/lib/appsignal/utils/params_sanitizer.rb +58 -0
  28. data/lib/appsignal/utils/query_params_sanitizer.rb +54 -0
  29. data/lib/appsignal/version.rb +1 -1
  30. data/spec/lib/appsignal/config_spec.rb +12 -2
  31. data/spec/lib/appsignal/extension_spec.rb +4 -0
  32. data/spec/lib/appsignal/hooks/net_http_spec.rb +20 -28
  33. data/spec/lib/appsignal/hooks/redis_spec.rb +9 -11
  34. data/spec/lib/appsignal/integrations/object_spec.rb +211 -0
  35. data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
  36. data/spec/lib/appsignal/minutely_spec.rb +54 -0
  37. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +50 -10
  38. data/spec/lib/appsignal/subscriber_spec.rb +5 -6
  39. data/spec/lib/appsignal/transaction_spec.rb +102 -23
  40. data/spec/lib/appsignal/transmitter_spec.rb +1 -1
  41. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +122 -0
  42. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +194 -0
  43. data/spec/lib/appsignal/utils_spec.rb +13 -76
  44. data/spec/lib/appsignal_spec.rb +82 -13
  45. metadata +15 -11
  46. data/lib/appsignal/event_formatter/net_http/request_formatter.rb +0 -13
  47. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +0 -13
  48. data/spec/lib/appsignal/event_formatter/net_http/request_formatter_spec.rb +0 -26
  49. data/spec/lib/appsignal/event_formatter/sequel/sql_formatter_spec.rb +0 -22
  50. data/spec/lib/appsignal/params_sanitizer_spec.rb +0 -200
@@ -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 => false
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).each do |var|
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).each do |var|
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
- SQL_BODY_FORMAT = 1
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|
@@ -18,7 +18,7 @@ module Appsignal
18
18
  if [:index, :type].include?(key)
19
19
  hsh[key] = val
20
20
  else
21
- hsh[key] = Appsignal::Utils.sanitize(val)
21
+ hsh[key] = Appsignal::Utils::QueryParamsSanitizer.sanitize(val)
22
22
  end
23
23
  end
24
24
  end
@@ -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 => Appsignal::Utils.sanitize(op.selector, true, :mongodb)
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 => Appsignal::Utils.sanitize(op.selector, false, :mongodb),
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 => Appsignal::Utils.sanitize(op.selector, false, :mongodb),
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 => Appsignal::Utils.sanitize(op.documents, true, :mongodb),
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 => Appsignal::Utils.sanitize(op.selector, false, :mongodb),
42
- :update => Appsignal::Utils.sanitize(op.update, true, :mongodb),
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
@@ -16,6 +16,5 @@ module Appsignal
16
16
  )
17
17
  end
18
18
  end
19
-
20
19
  end
21
20
  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
- ActiveSupport::Notifications.instrument(
17
+ Appsignal.instrument(
18
18
  'request.net_http',
19
- :protocol => use_ssl? ? 'https' : 'http',
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
@@ -14,7 +14,7 @@ module Appsignal
14
14
  alias process_without_appsignal process
15
15
 
16
16
  def process(commands, &block)
17
- ActiveSupport::Notifications.instrument('query.redis') do
17
+ Appsignal.instrument 'query.redis' do
18
18
  process_without_appsignal(commands, &block)
19
19
  end
20
20
  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
- ActiveSupport::Notifications.instrument(
6
+ Appsignal.instrument(
7
7
  'sql.sequel',
8
- :sql => sql
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
- ActiveSupport::Notifications.instrument(
20
+ Appsignal.instrument(
19
21
  'sql.sequel',
20
- :sql => sql
22
+ nil,
23
+ sql,
24
+ Appsignal::EventFormatter::SQL_BODY_FORMAT
21
25
  ) do
22
26
  yield
23
27
  end
@@ -46,7 +46,7 @@ module Appsignal
46
46
  'query.mongodb',
47
47
  "#{event.command_name.to_s} | #{event.database_name} | #{result}",
48
48
  Appsignal::Utils.json_generate(command),
49
- 0
49
+ Appsignal::EventFormatter::DEFAULT
50
50
  )
51
51
  end
52
52
  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::Application.use(Appsignal::Rack::SinatraInstrumentation)
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
- def sanitize(params)
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 sanitize_array(array)
68
- sanitize_array_with_target(array, [])
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
- def inspected(value)
110
- '?'
111
- end
10
+ deprecate :sanitize, "AppSignal::Utils::ParamsSanitizer.sanitize", 2016, 9
112
11
  end
113
12
  end
114
13
  end