appsignal 1.1.0.beta.6 → 1.1.0.beta.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/ext/agent.yml +7 -7
  4. data/ext/appsignal_extension.c +7 -5
  5. data/ext/extconf.rb +9 -2
  6. data/lib/appsignal/config.rb +5 -2
  7. data/lib/appsignal/event_formatter.rb +2 -0
  8. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -47
  9. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +88 -0
  10. data/lib/appsignal/event_formatter/moped/query_formatter.rb +6 -7
  11. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +13 -0
  12. data/lib/appsignal/hooks/sequel.rb +4 -7
  13. data/lib/appsignal/integrations/capistrano/appsignal.cap +1 -1
  14. data/lib/appsignal/integrations/mongo_ruby_driver.rb +9 -5
  15. data/lib/appsignal/js_exception_transaction.rb +2 -2
  16. data/lib/appsignal/subscriber.rb +3 -2
  17. data/lib/appsignal/transaction.rb +8 -2
  18. data/lib/appsignal/transmitter.rb +1 -1
  19. data/lib/appsignal/utils.rb +38 -4
  20. data/lib/appsignal/version.rb +1 -1
  21. data/spec/lib/appsignal/capistrano3_spec.rb +21 -1
  22. data/spec/lib/appsignal/config_spec.rb +12 -0
  23. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +1 -1
  24. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +14 -186
  25. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +115 -0
  26. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +4 -4
  27. data/spec/lib/appsignal/event_formatter/sequel/sql_formatter_spec.rb +22 -0
  28. data/spec/lib/appsignal/extension_spec.rb +1 -1
  29. data/spec/lib/appsignal/hooks/sequel_spec.rb +1 -1
  30. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +8 -5
  31. data/spec/lib/appsignal/subscriber_spec.rb +23 -5
  32. data/spec/lib/appsignal/transaction_spec.rb +21 -0
  33. data/spec/lib/appsignal/utils_spec.rb +48 -0
  34. data/spec/support/helpers/env_helpers.rb +1 -0
  35. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e48c3733ad27f81562f7088da9751efc8ae6ad5a
4
- data.tar.gz: 169f88717d3a6f64d89c9edda77f7b8e532ee401
3
+ metadata.gz: 1fa63ff273674425ef46e0f345a9952679404e3a
4
+ data.tar.gz: 1a2badc054342ffaa7bf111c839ef8c5b4c0c8d7
5
5
  SHA512:
6
- metadata.gz: 1fcd60d7288e64b0e4d6d4f6bc5c8208c06a391d5a8204a3a6aa2a2523f80a330add31948e69859fb61005835a12dda414f6547e2316838533f0cf09e32d6d26
7
- data.tar.gz: e7d31695310fbe921c385c511ac185a19acd2457f2fa87105e1f1c52a4cbd083687b244f70d7aa96dfc04cb970d32f99f22ef20c8bcdefba263af8450e5dbc77
6
+ metadata.gz: 061ad6a3873066cca81ab131385357dc2811bac4854a51128a7c63a46023ff1f94075e5257514d7bea42fb734b05f3374a114149de3fdf46d781b7eb1063b6ea
7
+ data.tar.gz: 66ff54cd46e64294a8ba13d004c9b297a976b638aece34d98fc174eb7f00d9935caf13678b2843c7d930ed059ebf86600659b2b0061fade58d4c0cb446408196
@@ -4,6 +4,16 @@
4
4
  * Collect perams for Delayed Job and Sidekiq when using ActiveJob
5
5
  * Official Grape support
6
6
 
7
+ # 1.0.5
8
+ * Improved sql sanitization
9
+ * Improved mongoid/mongodb sanitization
10
+ * Minor performance improvements
11
+ * Better handling for non-utf8 convertable strings
12
+ * Make gem installable (but not functional) on jRuby
13
+
14
+ # 1.0.4
15
+ * Make working dir configurable using `APPSIGNAL_WORKING_DIR_PATH` or `:working_dir_path`
16
+
7
17
  # 1.0.3
8
18
  * Fix bug in completing JS transactions
9
19
  * Make Resque integration robust for bigger payloads
@@ -1,15 +1,15 @@
1
1
  ---
2
- version: ee3d28b
2
+ version: d394e75
3
3
  triples:
4
4
  x86_64-linux:
5
- checksum: 709efd9cbf9d8b253d5c55a1a089e99c965d0a5824d11c439db6b539693c3f11
6
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/ee3d28b/appsignal-agent-x86_64-linux-static.tar.gz
5
+ checksum: 20b198a1a8ab5dff24b1e205d98eb7aa90d469e2a9bf8d6c659c3fe4015400c3
6
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d394e75/appsignal-agent-x86_64-linux-static.tar.gz
7
7
  lib_filename: libappsignal.a
8
8
  i686-linux:
9
- checksum: 109ea2cef01c2e71309790f04c409dd2984e257bf7e814b7a37351667fccf2f4
10
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/ee3d28b/appsignal-agent-i686-linux-static.tar.gz
9
+ checksum: c39b3806e36d693a75854a865d7e836b84d9ce35d7522841e7d75aa75a7307e6
10
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d394e75/appsignal-agent-i686-linux-static.tar.gz
11
11
  lib_filename: libappsignal.a
12
12
  x86_64-darwin:
13
- checksum: 2b9a6ae54a5886ec7677da7a99703a3336630e75fe2778782777fa2c45c942b1
14
- download_url: https://appsignal-agent-releases.global.ssl.fastly.net/ee3d28b/appsignal-agent-x86_64-darwin-static.tar.gz
13
+ checksum: 5cf300f552712734b724a6103336353bb809ef246fc62230d6bfcfed7045eed5
14
+ download_url: https://appsignal-agent-releases.global.ssl.fastly.net/d394e75/appsignal-agent-x86_64-darwin-static.tar.gz
15
15
  lib_filename: libappsignal.a
@@ -26,17 +26,19 @@ static VALUE start_event(VALUE self, VALUE transaction_index) {
26
26
  return Qnil;
27
27
  }
28
28
 
29
- static VALUE finish_event(VALUE self, VALUE transaction_index, VALUE name, VALUE title, VALUE body) {
29
+ static VALUE finish_event(VALUE self, VALUE transaction_index, VALUE name, VALUE title, VALUE body, VALUE body_format) {
30
30
  Check_Type(transaction_index, T_FIXNUM);
31
31
  Check_Type(name, T_STRING);
32
32
  Check_Type(title, T_STRING);
33
33
  Check_Type(body, T_STRING);
34
+ Check_Type(body_format, T_FIXNUM);
34
35
 
35
36
  appsignal_finish_event(
36
37
  FIX2INT(transaction_index),
37
38
  StringValueCStr(name),
38
39
  StringValueCStr(title),
39
- StringValueCStr(body)
40
+ StringValueCStr(body),
41
+ FIX2INT(body_format)
40
42
  );
41
43
  return Qnil;
42
44
  }
@@ -187,10 +189,10 @@ static VALUE install_gc_event_hooks() {
187
189
  Qnil
188
190
  );
189
191
  #endif
190
- #if defined(RUBY_INTERNAL_EVENT_GC_END_MARK)
192
+ #if defined(RUBY_INTERNAL_EVENT_GC_END_SWEEP)
191
193
  rb_add_event_hook(
192
194
  track_gc_end,
193
- RUBY_INTERNAL_EVENT_GC_END_MARK,
195
+ RUBY_INTERNAL_EVENT_GC_END_MARK | RUBY_INTERNAL_EVENT_GC_END_SWEEP,
194
196
  Qnil
195
197
  );
196
198
  #endif
@@ -207,7 +209,7 @@ void Init_appsignal_extension(void) {
207
209
  rb_define_singleton_method(Extension, "stop", stop, 0);
208
210
  rb_define_singleton_method(Extension, "start_transaction", start_transaction, 2);
209
211
  rb_define_singleton_method(Extension, "start_event", start_event, 1);
210
- rb_define_singleton_method(Extension, "finish_event", finish_event, 4);
212
+ rb_define_singleton_method(Extension, "finish_event", finish_event, 5);
211
213
  rb_define_singleton_method(Extension, "set_transaction_error", set_transaction_error, 4);
212
214
  rb_define_singleton_method(Extension, "set_transaction_sample_data", set_transaction_sample_data, 3);
213
215
  rb_define_singleton_method(Extension, "set_transaction_action", set_transaction_action, 2);
@@ -1,6 +1,5 @@
1
1
  require 'digest'
2
2
  require 'logger'
3
- require 'mkmf'
4
3
  require 'fileutils'
5
4
  require 'open-uri'
6
5
  require 'zlib'
@@ -30,6 +29,13 @@ end
30
29
  def install
31
30
  logger.info "Installing appsignal agent #{Appsignal::VERSION} for Ruby #{RUBY_VERSION} on #{RUBY_PLATFORM}"
32
31
 
32
+ if RUBY_PLATFORM =~ /java/
33
+ installation_failed(
34
+ "We do not support jRuby at the moment, email support@appsignal.com if you want to join the beta"
35
+ )
36
+ return
37
+ end
38
+
33
39
  unless AGENT_CONFIG['triples'].keys.include?(ARCH)
34
40
  installation_failed(
35
41
  "AppSignal currently does not support your system architecture (#{ARCH})." \
@@ -70,13 +76,14 @@ def install
70
76
  end
71
77
 
72
78
  logger.info "Creating makefile"
79
+ require 'mkmf'
73
80
  if find_library('appsignal', 'appsignal_start', EXT_PATH) &&
74
81
  find_executable('appsignal-agent', EXT_PATH) &&
75
82
  find_header('appsignal_extension.h', EXT_PATH)
76
83
  create_makefile 'appsignal_extension'
77
84
  logger.info 'Successfully installed appsignal extension'
78
85
  else
79
- installation_failed "Aborting installation, extension files were not present"
86
+ installation_failed "Aborting installation, extension files were not present or could not be loaded"
80
87
  end
81
88
  rescue => ex
82
89
  installation_failed "Exception while installing: #{ex}"
@@ -42,7 +42,8 @@ module Appsignal
42
42
  'APPSIGNAL_HTTP_PROXY' => :http_proxy,
43
43
  'APPSIGNAL_ENABLE_ALLOCATION_TRACKING' => :enable_allocation_tracking,
44
44
  'APPSIGNAL_ENABLE_GC_INSTRUMENTATION' => :enable_gc_instrumentation,
45
- 'APPSIGNAL_RUNNING_IN_CONTAINER' => :running_in_container
45
+ 'APPSIGNAL_RUNNING_IN_CONTAINER' => :running_in_container,
46
+ 'APPSIGNAL_WORKING_DIR_PATH' => :working_dir_path
46
47
  }.freeze
47
48
 
48
49
  attr_reader :root_path, :env, :initial_config, :config_hash
@@ -103,6 +104,7 @@ module Appsignal
103
104
  ENV['APPSIGNAL_HTTP_PROXY'] = config_hash[:http_proxy]
104
105
  ENV['APPSIGNAL_IGNORE_ACTIONS'] = config_hash[:ignore_actions].join(',')
105
106
  ENV['APPSIGNAL_RUNNING_IN_CONTAINER'] = config_hash[:running_in_container].to_s
107
+ ENV['APPSIGNAL_WORKING_DIR_PATH'] = config_hash[:working_dir_path] if config_hash[:working_dir_path]
106
108
  end
107
109
 
108
110
  protected
@@ -150,7 +152,8 @@ module Appsignal
150
152
 
151
153
  # Configuration with string type
152
154
  %w(APPSIGNAL_PUSH_API_KEY APPSIGNAL_APP_NAME APPSIGNAL_PUSH_API_ENDPOINT
153
- APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY APPSIGNAL_LOG_PATH).each do |var|
155
+ APPSIGNAL_FRONTEND_ERROR_CATCHING_PATH APPSIGNAL_HTTP_PROXY APPSIGNAL_LOG_PATH
156
+ APPSIGNAL_WORKING_DIR_PATH).each do |var|
154
157
  if env_var = ENV[var]
155
158
  config[ENV_TO_KEY_MAPPING[var]] = env_var
156
159
  end
@@ -60,6 +60,8 @@ module Appsignal
60
60
  end
61
61
  end
62
62
  end
63
+
64
+ SQL_BODY_FORMAT = 1
63
65
  end
64
66
 
65
67
  Dir.glob(File.expand_path('../event_formatter/**/*.rb', __FILE__)).each do |file|
@@ -4,55 +4,9 @@ module Appsignal
4
4
  class SqlFormatter < Appsignal::EventFormatter
5
5
  register 'sql.active_record'
6
6
 
7
- SINGLE_QUOTED_STRING = /'(.?|[^']).*'/.freeze
8
- DOUBLE_QUOTED_STRING = /"(.?|[^"]).*"/.freeze
9
- IN_OPERATOR_CONTENT = /(IN \()[^SELECT][^\)]+(\))/.freeze
10
- NUMERIC = /\d*\.?\d+/.freeze
11
- REPLACEMENT = '?'.freeze
12
- IN_REPLACEMENT = '\1?\2'.freeze
13
- SCHEMA = 'SCHEMA'.freeze
14
-
15
- attr_reader :adapter_uses_double_quoted_table_names
16
-
17
- def initialize
18
- @connection_config = connection_config
19
- @adapter_uses_double_quoted_table_names = adapter_uses_double_quoted_table_names?
20
- rescue ::ActiveRecord::ConnectionNotEstablished
21
- Appsignal::EventFormatter.unregister('sql.active_record', self.class)
22
- Appsignal.logger.error('Error while getting ActiveRecord connection info, unregistering sql.active_record event formatter')
23
- end
24
-
25
7
  def format(payload)
26
- return nil if schema_query?(payload) || !payload[:sql]
27
- sql_string = payload[:sql].dup
28
- unless adapter_uses_double_quoted_table_names
29
- sql_string.gsub!(DOUBLE_QUOTED_STRING, REPLACEMENT)
30
- end
31
- sql_string.gsub!(SINGLE_QUOTED_STRING, REPLACEMENT)
32
- sql_string.gsub!(IN_OPERATOR_CONTENT, IN_REPLACEMENT)
33
- sql_string.gsub!(NUMERIC, REPLACEMENT)
34
- [payload[:name], sql_string]
8
+ [payload[:name], payload[:sql], SQL_BODY_FORMAT]
35
9
  end
36
-
37
- protected
38
-
39
- def schema_query?(payload)
40
- payload[:name] == SCHEMA
41
- end
42
-
43
- def connection_config
44
- # TODO handle ActiveRecord::ConnectionNotEstablished
45
- if ::ActiveRecord::Base.respond_to?(:connection_config)
46
- ::ActiveRecord::Base.connection_config
47
- else
48
- ::ActiveRecord::Base.connection_pool.spec.config
49
- end
50
- end
51
-
52
- def adapter_uses_double_quoted_table_names?
53
- adapter = @connection_config[:adapter]
54
- adapter =~ /postgres/ || adapter =~ /sqlite/
55
- end
56
10
  end
57
11
  end
58
12
  end
@@ -0,0 +1,88 @@
1
+ module Appsignal
2
+ class EventFormatter
3
+ module MongoRubyDriver
4
+ class QueryFormatter
5
+ ALLOWED = {
6
+ "find" => {
7
+ "find" => :allow,
8
+ "filter" => :sanitize_document
9
+ },
10
+ "count" => {
11
+ "count" => :allow,
12
+ "query" => :sanitize_document
13
+ },
14
+ "distinct" => {
15
+ "distinct" => :allow,
16
+ "key" => :allow,
17
+ "query" => :sanitize_document
18
+ },
19
+ "insert" => {
20
+ "insert" => :allow,
21
+ "documents" => :deny_array,
22
+ "ordered" => :allow
23
+ },
24
+ "update" => {
25
+ "update" => :allow,
26
+ "updates" => :sanitize_bulk,
27
+ "ordered" => :allow
28
+ },
29
+ "findandmodify" => {
30
+ "findandmodify" => :allow,
31
+ "query" => :sanitize_document,
32
+ "update" => :deny_array,
33
+ "new" => :allow
34
+ },
35
+ "delete" => {
36
+ "delete" => :allow,
37
+ "deletes" => :sanitize_bulk,
38
+ "ordered" => :allow
39
+ },
40
+ "bulk" => {
41
+ "q" => :sanitize_document,
42
+ "u" => :deny_array,
43
+ "limit" => :allow,
44
+ "multi" => :allow,
45
+ "upsert" => :allow
46
+ }
47
+ }
48
+
49
+ # Format command based on given strategy
50
+ def self.format(strategy, command)
51
+ # Stop processing if command is not a hash
52
+ return {} unless command.is_a?(Hash)
53
+
54
+ # Get the strategy and stop if it's not present
55
+ strategies = ALLOWED[strategy.to_s]
56
+ return {} unless strategies
57
+
58
+ {}.tap do |hsh|
59
+ command.each do |key, val|
60
+ hsh[key] = self.apply_strategy(strategies[key], val)
61
+ end
62
+ end
63
+ end
64
+
65
+ # Applies strategy on hash values based on keys
66
+ def self.apply_strategy(strategy, val)
67
+ case strategy
68
+ when :allow then val
69
+ when :deny then '?'
70
+ when :deny_array then '[?]'
71
+ when :sanitize_document
72
+ Appsignal::Utils.sanitize(val, true, :mongodb)
73
+ when :sanitize_bulk
74
+ if val.length > 1
75
+ [
76
+ self.format(:bulk, val.first),
77
+ "[...]"
78
+ ]
79
+ else
80
+ val.map { |v| self.format(:bulk, v) }
81
+ end
82
+ else '?'
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -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)
14
+ :selector => Appsignal::Utils.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),
19
+ :selector => Appsignal::Utils.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),
28
+ :selector => Appsignal::Utils.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),
34
+ :documents => Appsignal::Utils.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),
42
- :update => Appsignal::Utils.sanitize(op.update, true),
41
+ :selector => Appsignal::Utils.sanitize(op.selector, false, :mongodb),
42
+ :update => Appsignal::Utils.sanitize(op.update, true, :mongodb),
43
43
  :flags => op.flags,
44
44
  }.inspect]
45
45
  when 'Moped::Protocol::KillCursors'
@@ -53,7 +53,6 @@ module Appsignal
53
53
  end
54
54
  end
55
55
  end
56
-
57
56
  end
58
57
  end
59
58
  end
@@ -0,0 +1,13 @@
1
+ module Appsignal
2
+ class EventFormatter
3
+ module Sequel
4
+ class SqlFormatter < Appsignal::EventFormatter
5
+ register 'sql.sequel'
6
+
7
+ def format(payload)
8
+ [nil, payload[:sql], SQL_BODY_FORMAT]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -3,13 +3,10 @@ module Appsignal
3
3
  module SequelExtension
4
4
  # Add query instrumentation
5
5
  def log_yield(sql, args = nil)
6
-
7
- # We'd like to get full sql queries in the payloads as well. To do
8
- # that we need to find out a way to ask Sequel which quoting strategy
9
- # is used by the adapter. We can then do something similar to the AR
10
- # formatter.
11
-
12
- ActiveSupport::Notifications.instrument('sql.sequel') do
6
+ ActiveSupport::Notifications.instrument(
7
+ 'sql.sequel',
8
+ :sql => sql
9
+ ) do
13
10
  yield
14
11
  end
15
12
  end
@@ -1,6 +1,6 @@
1
1
  namespace :appsignal do
2
2
  task :deploy do
3
- env = fetch(:rails_env, fetch(:rack_env, 'production'))
3
+ env = fetch(:stage, 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,16 +1,19 @@
1
1
  module Appsignal
2
2
  class Hooks
3
3
  class MongoMonitorSubscriber
4
-
5
4
  # Called by Mongo::Monitor when query starts
6
5
  def started(event)
7
6
  transaction = Appsignal::Transaction.current
8
7
  return if transaction.nil_transaction?
9
8
  return if transaction.paused?
10
9
 
10
+ # Format the command
11
+ command = Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter
12
+ .format(event.command_name, event.command)
13
+
11
14
  # Store the query on the transaction, we need it when the event finishes
12
15
  store = transaction.store('mongo_driver')
13
- store[event.request_id] = Appsignal::Utils.sanitize(event.command)
16
+ store[event.request_id] = command
14
17
 
15
18
  # Start this event
16
19
  Appsignal::Extension.start_event(transaction.transaction_index)
@@ -36,14 +39,15 @@ module Appsignal
36
39
 
37
40
  # Get the query from the transaction store
38
41
  store = transaction.store('mongo_driver')
39
- command = store[event.request_id].inspect
42
+ command = store.delete(event.request_id) || {}
40
43
 
41
44
  # Finish the event in the extension.
42
45
  Appsignal::Extension.finish_event(
43
46
  transaction.transaction_index,
44
47
  'query.mongodb',
45
- event.command_name.to_s,
46
- %Q(#{event.database_name} | #{result} | #{command})
48
+ "#{event.command_name.to_s} | #{event.database_name} | #{result}",
49
+ Appsignal::Utils.json_generate(command),
50
+ 0
47
51
  )
48
52
  end
49
53
  end