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

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