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.
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