appsignal 0.11.18 → 0.12.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGELOG.md +4 -38
  3. data/Rakefile +14 -6
  4. data/appsignal.gemspec +3 -1
  5. data/benchmark.rake +12 -16
  6. data/ext/appsignal_extension.c +183 -0
  7. data/ext/extconf.rb +39 -0
  8. data/gemfiles/capistrano2.gemfile +0 -1
  9. data/gemfiles/capistrano3.gemfile +0 -1
  10. data/gemfiles/rails-4.2.gemfile +1 -1
  11. data/lib/appsignal.rb +23 -61
  12. data/lib/appsignal/capistrano.rb +1 -2
  13. data/lib/appsignal/config.rb +13 -1
  14. data/lib/appsignal/event_formatter.rb +67 -0
  15. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +23 -0
  16. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +74 -0
  17. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  18. data/lib/appsignal/event_formatter/net_http/request_formatter.rb +13 -0
  19. data/lib/appsignal/instrumentations/net_http.rb +6 -4
  20. data/lib/appsignal/integrations/resque.rb +2 -10
  21. data/lib/appsignal/integrations/sidekiq.rb +2 -2
  22. data/lib/appsignal/integrations/sinatra.rb +1 -0
  23. data/lib/appsignal/js_exception_transaction.rb +44 -28
  24. data/lib/appsignal/marker.rb +11 -13
  25. data/lib/appsignal/params_sanitizer.rb +5 -8
  26. data/lib/appsignal/rack/instrumentation.rb +2 -0
  27. data/lib/appsignal/rack/js_exception_catcher.rb +1 -0
  28. data/lib/appsignal/rack/listener.rb +1 -1
  29. data/lib/appsignal/rack/sinatra_instrumentation.rb +2 -12
  30. data/lib/appsignal/subscriber.rb +59 -0
  31. data/lib/appsignal/transaction.rb +117 -174
  32. data/lib/appsignal/transmitter.rb +8 -37
  33. data/lib/appsignal/version.rb +2 -1
  34. data/spec/lib/appsignal/config_spec.rb +25 -4
  35. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +42 -0
  36. data/spec/lib/appsignal/{aggregator/middleware/active_record_sanitizer_spec.rb → event_formatter/active_record/sql_formatter_spec.rb} +61 -61
  37. data/spec/lib/appsignal/{event/moped_event_spec.rb → event_formatter/moped/query_formatter_spec.rb} +32 -78
  38. data/spec/lib/appsignal/event_formatter/net_http/request_formatter_spec.rb +26 -0
  39. data/spec/lib/appsignal/event_formatter_spec.rb +102 -0
  40. data/spec/lib/appsignal/extension_spec.rb +75 -0
  41. data/spec/lib/appsignal/instrumentations/net_http_spec.rb +20 -4
  42. data/spec/lib/appsignal/integrations/delayed_job_spec.rb +3 -2
  43. data/spec/lib/appsignal/integrations/rails_spec.rb +0 -7
  44. data/spec/lib/appsignal/integrations/resque_spec.rb +51 -55
  45. data/spec/lib/appsignal/integrations/sequel_spec.rb +8 -3
  46. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -21
  47. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -6
  48. data/spec/lib/appsignal/js_exception_transaction_spec.rb +57 -60
  49. data/spec/lib/appsignal/params_sanitizer_spec.rb +11 -27
  50. data/spec/lib/appsignal/rack/listener_spec.rb +6 -6
  51. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +2 -43
  52. data/spec/lib/appsignal/subscriber_spec.rb +162 -0
  53. data/spec/lib/appsignal/transaction_spec.rb +283 -615
  54. data/spec/lib/appsignal/transmitter_spec.rb +3 -32
  55. data/spec/lib/appsignal_spec.rb +41 -90
  56. data/spec/lib/generators/appsignal/appsignal_generator_spec.rb +0 -17
  57. data/spec/spec_helper.rb +18 -22
  58. data/spec/support/helpers/notification_helpers.rb +1 -1
  59. data/spec/support/helpers/time_helpers.rb +11 -0
  60. data/spec/support/helpers/transaction_helpers.rb +6 -18
  61. data/spec/support/project_fixture/config/appsignal.yml +1 -2
  62. metadata +68 -78
  63. checksums.yaml +0 -7
  64. data/gemfiles/padrino-0.13.gemfile +0 -7
  65. data/gemfiles/resque.gemfile +0 -5
  66. data/lib/appsignal/agent.rb +0 -217
  67. data/lib/appsignal/aggregator.rb +0 -67
  68. data/lib/appsignal/aggregator/middleware.rb +0 -4
  69. data/lib/appsignal/aggregator/middleware/action_view_sanitizer.rb +0 -23
  70. data/lib/appsignal/aggregator/middleware/active_record_sanitizer.rb +0 -65
  71. data/lib/appsignal/aggregator/middleware/chain.rb +0 -101
  72. data/lib/appsignal/aggregator/middleware/delete_blanks.rb +0 -16
  73. data/lib/appsignal/aggregator/post_processor.rb +0 -32
  74. data/lib/appsignal/event.rb +0 -20
  75. data/lib/appsignal/event/moped_event.rb +0 -90
  76. data/lib/appsignal/integrations/padrino.rb +0 -64
  77. data/lib/appsignal/integrations/passenger.rb +0 -13
  78. data/lib/appsignal/integrations/rake.rb +0 -29
  79. data/lib/appsignal/integrations/unicorn.rb +0 -25
  80. data/lib/appsignal/ipc.rb +0 -68
  81. data/lib/appsignal/transaction/formatter.rb +0 -85
  82. data/lib/appsignal/transaction/params_sanitizer.rb +0 -4
  83. data/lib/appsignal/zipped_payload.rb +0 -37
  84. data/spec/lib/appsignal/agent_spec.rb +0 -592
  85. data/spec/lib/appsignal/aggregator/middleware/action_view_sanitizer_spec.rb +0 -44
  86. data/spec/lib/appsignal/aggregator/middleware/chain_spec.rb +0 -168
  87. data/spec/lib/appsignal/aggregator/middleware/delete_blanks_spec.rb +0 -37
  88. data/spec/lib/appsignal/aggregator/post_processor_spec.rb +0 -99
  89. data/spec/lib/appsignal/aggregator_spec.rb +0 -186
  90. data/spec/lib/appsignal/event_spec.rb +0 -48
  91. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -171
  92. data/spec/lib/appsignal/integrations/passenger_spec.rb +0 -22
  93. data/spec/lib/appsignal/integrations/rake_spec.rb +0 -92
  94. data/spec/lib/appsignal/integrations/unicorn_spec.rb +0 -48
  95. data/spec/lib/appsignal/ipc_spec.rb +0 -128
  96. data/spec/lib/appsignal/transaction/formatter_spec.rb +0 -247
  97. data/spec/lib/appsignal/zipped_payload_spec.rb +0 -42
@@ -0,0 +1,13 @@
1
+ module Appsignal
2
+ class EventFormatter
3
+ module NetHttp
4
+ class RequestFormatter < Appsignal::EventFormatter
5
+ register 'request.net_http'
6
+
7
+ def format(payload)
8
+ ["#{payload[:method]} #{payload[:protocol]}://#{payload[:domain]}", nil]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,15 @@
1
+ require 'net/http'
2
+
1
3
  Net::HTTP.class_eval do
2
4
  alias request_without_appsignal request
3
5
 
4
6
  def request(request, body=nil, &block)
5
7
  ActiveSupport::Notifications.instrument(
6
8
  'request.net_http',
7
- :host => request['host'] || self.address,
8
- :scheme => use_ssl? ? 'https' : 'http',
9
- :path => request.path,
10
- :method => request.method
9
+ :protocol => use_ssl? ? 'https' : 'http',
10
+ :domain => request['host'] || self.address,
11
+ :path => request.path,
12
+ :method => request.method
11
13
  ) do
12
14
  request_without_appsignal(request, body, &block)
13
15
  end
@@ -17,14 +17,6 @@ if defined?(::Resque)
17
17
  end
18
18
  end
19
19
 
20
- # Set up IPC
21
- Resque.before_first_fork do
22
- Appsignal::IPC::Server.start if Appsignal.active?
23
- end
24
-
25
- # In the fork, stop the normal agent startup
26
- # and stop listening to the server
27
- Resque.after_fork do |job|
28
- Appsignal::IPC.forked! if Appsignal.active?
29
- end
20
+ # Extend the default job class with AppSignal instrumentation
21
+ Resque::Job.send(:extend, Appsignal::Integrations::ResquePlugin)
30
22
  end
@@ -15,7 +15,7 @@ if defined?(::Sidekiq)
15
15
  def call(worker, item, queue)
16
16
  Appsignal.monitor_transaction(
17
17
  'perform_job.sidekiq',
18
- :class => item['wrapped'] || item['class'],
18
+ :class => item['class'],
19
19
  :method => 'perform',
20
20
  :metadata => formatted_metadata(item),
21
21
  :params => format_args(item['args']),
@@ -48,7 +48,7 @@ if defined?(::Sidekiq)
48
48
  end
49
49
 
50
50
  def truncate(text)
51
- text.size > 200 ? "#{text[0...197]}..." : text
51
+ text.size > 100 ? "#{text[0...97]}..." : text
52
52
  end
53
53
  end
54
54
  end
@@ -1,4 +1,5 @@
1
1
  require 'appsignal'
2
+ require 'appsignal/rack/sinatra_instrumentation'
2
3
 
3
4
  Appsignal.logger.info("Loading Sinatra (#{Sinatra::VERSION}) integration")
4
5
 
@@ -3,45 +3,61 @@ module Appsignal
3
3
 
4
4
  def initialize(data)
5
5
  @data = data
6
- @time = Time.now.to_i
6
+ @uuid = SecureRandom.uuid
7
+
8
+ Appsignal::Extension.start_transaction(@uuid)
9
+
10
+ set_base_data
11
+ set_metadata
12
+ set_error
13
+ set_error_data
7
14
  end
8
15
 
9
- def type
10
- :exception
16
+ def set_base_data
17
+ Appsignal::Extension.set_transaction_basedata(
18
+ @uuid,
19
+ 'frontend',
20
+ @data['action'],
21
+ 0
22
+ )
11
23
  end
12
24
 
13
- def action
14
- @data['action']
25
+ def set_metadata
26
+ Appsignal::Extension.set_transaction_metadata(
27
+ @uuid, 'path', @data['path']
28
+ ) if @data['path']
15
29
  end
16
30
 
17
- def clear_events!; end
18
- def convert_values_to_primitives!; end
19
- def events; []; end
31
+ def set_error
32
+ Appsignal::Extension.set_transaction_error(
33
+ @uuid,
34
+ @data['name'],
35
+ @data['message']
36
+ )
37
+ end
20
38
 
21
- def to_hash
39
+ def set_error_data
22
40
  {
23
- :request_id => SecureRandom.uuid,
24
- :log_entry => {
25
- :action => action,
26
- :path => @data['path'],
27
- :kind => 'frontend',
28
- :time => @time,
29
- :environment => @data['environment'],
30
- :tags => @data['tags'],
31
- :revision => Appsignal.agent.revision
32
- },
33
- :exception => {
34
- :exception => @data['name'],
35
- :message => @data['message'],
36
- :backtrace => @data['backtrace']
37
- },
38
- :failed => true
39
- }
41
+ :params => @data['params'],
42
+ :environment => @data['environment'],
43
+ :backtrace => @data['backtrace'],
44
+ :tags => @data['tags']
45
+ }.each do |key, data|
46
+ next unless data.is_a?(Array) || data.is_a?(Hash)
47
+ begin
48
+ Appsignal::Extension.set_transaction_error_data(
49
+ @uuid,
50
+ key.to_s,
51
+ JSON.generate(data)
52
+ )
53
+ rescue JSON::GeneratorError=>e
54
+ Appsignal.logger.error("JSON generate error (#{e.message}) for '#{data.inspect}'")
55
+ end
56
+ end
40
57
  end
41
58
 
42
59
  def complete!
43
- Appsignal.enqueue(self)
60
+ Appsignal::Extension.finish_transaction(@uuid)
44
61
  end
45
-
46
62
  end
47
63
  end
@@ -14,20 +14,18 @@ module Appsignal
14
14
  end
15
15
 
16
16
  def transmit
17
- begin
18
- transmitter = Transmitter.new(ACTION, config)
19
- logger.info("Notifying Appsignal of deploy with: revision: #{marker_data[:revision]}, user: #{marker_data[:user]}")
20
- result = transmitter.transmit(marker_data)
21
- if result == '200'
22
- logger.info('Appsignal has been notified of this deploy!')
23
- else
24
- raise "#{result} at #{transmitter.uri}"
25
- end
26
- rescue Exception => e
27
- carefully_log_error(
28
- "Something went wrong while trying to notify Appsignal: #{e}"
29
- )
17
+ transmitter = Transmitter.new(ACTION, config)
18
+ logger.info("Notifying Appsignal of deploy with: revision: #{marker_data[:revision]}, user: #{marker_data[:user]}")
19
+ result = transmitter.transmit(marker_data)
20
+ if result == '200'
21
+ logger.info('Appsignal has been notified of this deploy!')
22
+ else
23
+ raise "#{result} at #{transmitter.uri}"
30
24
  end
25
+ rescue Exception => e
26
+ carefully_log_error(
27
+ "Something went wrong while trying to notify Appsignal: #{e}"
28
+ )
31
29
  end
32
30
  end
33
31
  end
@@ -25,10 +25,8 @@ module Appsignal
25
25
  sanitize_hash(value)
26
26
  when Array
27
27
  sanitize_array(value)
28
- when Fixnum, String, Symbol, Float
28
+ when Fixnum, String, Symbol
29
29
  unmodified(value)
30
- when TrueClass, FalseClass
31
- stringified(value)
32
30
  else
33
31
  inspected(value)
34
32
  end
@@ -48,16 +46,15 @@ module Appsignal
48
46
  target_array
49
47
  end
50
48
 
51
- def stringified(value)
52
- value.to_s
53
- end
54
-
55
49
  def unmodified(value)
56
50
  value
57
51
  end
58
52
 
59
53
  def inspected(value)
60
- "#<#{value.class.to_s}>"
54
+ value.inspect
55
+ rescue
56
+ # It turns out that sometimes inspect can fail
57
+ "#<#{value.class.to_s}/>"
61
58
  end
62
59
  end
63
60
  end
@@ -1,3 +1,5 @@
1
+ require 'rack'
2
+
1
3
  module Appsignal
2
4
  module Rack
3
5
  class Instrumentation
@@ -11,6 +11,7 @@ module Appsignal
11
11
  body = JSON.parse(env['rack.input'].read)
12
12
  transaction = JSExceptionTransaction.new(body)
13
13
  transaction.complete!
14
+
14
15
  return [ 200, {}, []]
15
16
  else
16
17
  @app.call(env)
@@ -18,7 +18,7 @@ module Appsignal
18
18
  Appsignal::Transaction.create(request_id(env), env)
19
19
  @app.call(env)
20
20
  rescue Exception => exception
21
- Appsignal.add_exception(exception)
21
+ Appsignal.set_exception(exception)
22
22
  raise exception
23
23
  ensure
24
24
  Appsignal::Transaction.complete_current!
@@ -1,3 +1,5 @@
1
+ require 'rack'
2
+
1
3
  module Appsignal
2
4
  module Rack
3
5
  class SinatraInstrumentation
@@ -7,14 +9,6 @@ module Appsignal
7
9
  end
8
10
 
9
11
  def call(env)
10
- if Appsignal.active?
11
- call_with_appsignal_monitoring(env)
12
- else
13
- @app.call(env)
14
- end
15
- end
16
-
17
- def call_with_appsignal_monitoring(env)
18
12
  ActiveSupport::Notifications.instrument(
19
13
  'process_action.sinatra',
20
14
  raw_payload(env)
@@ -27,10 +21,6 @@ module Appsignal
27
21
  payload[:action] = env['sinatra.route']
28
22
  end
29
23
  end
30
- ensure
31
- # In production newer versions of Sinatra don't raise errors, but store
32
- # them in the sinatra.error env var.
33
- Appsignal.add_exception(env['sinatra.error']) if env['sinatra.error']
34
24
  end
35
25
 
36
26
  def raw_payload(env)
@@ -0,0 +1,59 @@
1
+ module Appsignal
2
+ class Subscriber
3
+ PROCESS_ACTION_PREFIX = 'process_action'.freeze
4
+ PERFORM_JOB_PREFIX = 'perform_job'.freeze
5
+ BLANK = ''.freeze
6
+
7
+ def initialize
8
+ subscribe
9
+ end
10
+
11
+ def subscribe
12
+ Appsignal.logger.debug('Subscribing to notifications')
13
+ # Subscribe to notifications that don't start with a !
14
+ ActiveSupport::Notifications.subscribe(/^[^!]/, self)
15
+ end
16
+
17
+ def unsubscribe
18
+ Appsignal.logger.debug('Unsubscribing from notifications')
19
+ ActiveSupport::Notifications.unsubscribe(self)
20
+ end
21
+
22
+ def resubscribe
23
+ Appsignal.logger.debug('Resubscribing to notifications')
24
+ unsubscribe
25
+ subscribe
26
+ end
27
+
28
+ def publish(name, *args)
29
+ # Not used, it's part of AS notifications but is not used in Rails
30
+ # and it seems to be unclear what it's function is. See:
31
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/notifications/fanout.rb#L49
32
+ end
33
+
34
+ def start(name, id, payload)
35
+ return unless transaction = Appsignal::Transaction.current
36
+
37
+ return if transaction.paused?
38
+ Appsignal::Extension.start_event(transaction.request_id)
39
+ end
40
+
41
+ def finish(name, id, payload)
42
+ return unless transaction = Appsignal::Transaction.current
43
+
44
+ if name.start_with?(PROCESS_ACTION_PREFIX, PERFORM_JOB_PREFIX)
45
+ transaction.set_root_event(name, payload)
46
+ end
47
+
48
+ return if transaction.paused?
49
+
50
+ title, body = Appsignal::EventFormatter.format(name, payload)
51
+ Appsignal::Extension.finish_event(
52
+ transaction.request_id,
53
+ name,
54
+ title || BLANK,
55
+ body || BLANK
56
+ )
57
+ end
58
+ end
59
+ end
@@ -1,5 +1,8 @@
1
1
  module Appsignal
2
2
  class Transaction
3
+ HTTP_REQUEST = 'http_request'.freeze
4
+ BACKGROUND_JOB = 'background_job'.freeze
5
+
3
6
  # Based on what Rails uses + some variables we'd like to show
4
7
  ENV_METHODS = %w(CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
5
8
  PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
@@ -11,40 +14,39 @@ module Appsignal
11
14
  HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM HTTP_NEGOTIATE
12
15
  HTTP_PRAGMA HTTP_REFERER HTTP_X_FORWARDED_FOR HTTP_CLIENT_IP).freeze
13
16
 
14
- def self.create(request_id, env, defaults={})
15
- Appsignal.logger.debug("Creating transaction: #{request_id}")
16
- Thread.current[:appsignal_transaction_id] = request_id
17
- Appsignal::Transaction.new(request_id, env, defaults)
18
- end
17
+ class << self
18
+ def create(request_id, env)
19
+ Appsignal.logger.debug("Creating transaction: #{request_id}")
20
+ Appsignal::Extension.start_transaction(request_id)
21
+ Thread.current[:appsignal_transaction] = Appsignal::Transaction.new(request_id, env)
22
+ end
19
23
 
20
- def self.current
21
- Appsignal.transactions[Thread.current[:appsignal_transaction_id]]
22
- end
24
+ def current
25
+ Thread.current[:appsignal_transaction]
26
+ end
23
27
 
24
- def self.complete_current!
25
- if current
26
- current.complete!
27
- Thread.current[:appsignal_transaction_id] = nil
28
- else
29
- Appsignal.logger.error('Trying to complete current, but no transaction present')
28
+ def complete_current!
29
+ if current
30
+ Appsignal::Extension.finish_transaction(current.request_id)
31
+ Thread.current[:appsignal_transaction] = nil
32
+ else
33
+ Appsignal.logger.error('Trying to complete current, but no transaction present')
34
+ end
30
35
  end
31
36
  end
32
37
 
33
- attr_reader :request_id, :events, :process_action_event, :action, :exception,
34
- :env, :fullpath, :time, :tags, :kind, :queue_start, :paused, :params
38
+ attr_reader :request_id, :process_action_event, :action, :exception,
39
+ :env, :fullpath, :time, :tags, :kind, :queue_start, :paused, :root_event_payload
35
40
 
36
- def initialize(request_id, env, defaults={})
37
- Appsignal.transactions[request_id] = self
38
- @request_id = request_id
39
- @events = []
41
+ def initialize(request_id, env)
42
+ @root_event_payload = nil
43
+ @request_id = request_id
40
44
  @process_action_event = nil
41
- @exception = {}
42
- @env = env
43
- @params = defaults[:params] || {}
44
- @tags = defaults[:tags] || {}
45
- @kind = defaults[:kind] || 'http_request'
46
- @action = defaults[:action]
47
- @paused = false
45
+ @exception = nil
46
+ @env = env
47
+ @tags = {}
48
+ @paused = false
49
+ @queue_start = -1
48
50
  end
49
51
 
50
52
  def sanitized_environment
@@ -55,148 +57,96 @@ module Appsignal
55
57
  @sanitized_session_data ||= {}
56
58
  end
57
59
 
58
- def sanitized_params
59
- @sanitized_params ||= {}
60
- end
61
-
62
60
  def request
63
- ::Rack::Request.new(@env) if @env
61
+ @request ||= ::Rack::Request.new(env)
64
62
  end
65
63
 
66
64
  def set_tags(given_tags={})
67
65
  @tags.merge!(given_tags)
68
66
  end
69
67
 
70
- def pause!
71
- @paused = true
72
- end
73
-
74
- def resume!
75
- @paused = false
76
- end
77
-
78
- def set_process_action_event(event)
79
- return unless event && event.payload
80
- @process_action_event = event.dup
81
- if @process_action_event.payload[:controller]
82
- @action = "#{@process_action_event.payload[:controller]}##{@process_action_event.payload[:action]}"
83
- else
84
- @action = @process_action_event.payload[:action]
68
+ def set_root_event(name, payload)
69
+ @root_event_payload = payload
70
+ if name.start_with?(Subscriber::PROCESS_ACTION_PREFIX)
71
+ @action = "#{@root_event_payload[:controller]}##{@root_event_payload[:action]}"
72
+ @kind = HTTP_REQUEST
73
+ set_http_queue_start
74
+ set_metadata('path', payload[:path])
75
+ set_metadata('request_format', payload[:request_format])
76
+ set_metadata('request_method', payload[:request_method])
77
+ set_metadata('status', payload[:status].to_s)
78
+ elsif name.start_with?(Subscriber::PERFORM_JOB_PREFIX)
79
+ @action = "#{@root_event_payload[:class]}##{@root_event_payload[:method]}"
80
+ @kind = BACKGROUND_JOB
81
+ set_background_queue_start
85
82
  end
86
- @kind = 'http_request'
87
- set_http_queue_start
88
- end
89
-
90
- def set_perform_job_event(event)
91
- return unless event && event.payload
92
- @process_action_event = event.dup
93
- @action = "#{@process_action_event.payload[:class]}##{@process_action_event.payload[:method]}"
94
- @kind = 'background_job'
95
- set_background_queue_start
96
- end
97
-
98
- def add_event(event)
99
- @events << event unless @paused == true
100
- end
101
-
102
- def add_exception(ex=nil)
103
- return unless ex
104
- Appsignal.logger.debug("Adding #{ex.class.name} to transaction: #{request_id}")
105
- @time = Time.now.utc.to_f
106
- @exception = {
107
- :exception => ex.class.name,
108
- :message => ex.message,
109
- :backtrace => clean_backtrace(ex)
110
- }
111
- end
112
-
113
- def exception?
114
- exception.any?
115
- end
116
-
117
- def clean_backtrace(exception)
118
- return [] unless exception.backtrace.is_a?(Array)
119
- if defined?(::Rails)
120
- ::Rails.backtrace_cleaner.clean(exception.backtrace, nil)
121
- else
122
- exception.backtrace
83
+ Appsignal::Extension.set_transaction_basedata(
84
+ request_id,
85
+ kind,
86
+ action,
87
+ queue_start
88
+ )
89
+ end
90
+
91
+ def set_metadata(key, value)
92
+ return unless value
93
+ Appsignal::Extension.set_transaction_metadata(request_id, key, value)
94
+ end
95
+
96
+ def set_error(error)
97
+ return unless error
98
+ Appsignal::Extension.set_transaction_error(
99
+ request_id,
100
+ error.class.name,
101
+ error.message
102
+ )
103
+
104
+ {
105
+ :params => sanitized_params,
106
+ :environment => sanitized_environment,
107
+ :session_data => sanitized_session_data,
108
+ :backtrace => cleaned_backtrace(error.backtrace),
109
+ :tags => sanitized_tags
110
+ }.each do |key, data|
111
+ next unless data.is_a?(Array) || data.is_a?(Hash)
112
+ begin
113
+ Appsignal::Extension.set_transaction_error_data(
114
+ request_id,
115
+ key.to_s,
116
+ JSON.generate(data)
117
+ )
118
+ rescue JSON::GeneratorError=>e
119
+ Appsignal.logger.error("JSON generate error (#{e.message}) for '#{data.inspect}'")
120
+ end
123
121
  end
124
122
  end
123
+ alias_method :add_exception, :set_error
125
124
 
126
- def slow_request?
127
- return false unless process_action_event && process_action_event.payload
128
- Appsignal.config[:slow_request_threshold] <= process_action_event.duration
129
- end
130
-
131
- def slower?(transaction)
132
- process_action_event.duration > transaction.process_action_event.duration
133
- end
134
-
135
- def clear_events!
136
- events.clear
137
- end
138
-
139
- def truncate!
140
- return if truncated?
141
- process_action_event.truncate!
142
- events.clear
143
- tags.clear
144
- sanitized_environment.clear
145
- sanitized_session_data.clear
146
- sanitized_params.clear
147
- @env = nil
148
- @truncated = true
149
- end
150
-
151
- def truncated?
152
- !! @truncated
153
- end
154
-
155
- def convert_values_to_primitives!
156
- return if have_values_been_converted_to_primitives?
157
- @process_action_event.sanitize! if @process_action_event
158
- @events.each { |event| event.sanitize! }
159
- add_sanitized_context!
160
- @have_values_been_converted_to_primitives = true
161
- end
162
-
163
- def have_values_been_converted_to_primitives?
164
- !! @have_values_been_converted_to_primitives
125
+ def pause!
126
+ @paused = true
165
127
  end
166
128
 
167
- def type
168
- return :exception if exception?
169
- return :slow_request if slow_request?
170
- :regular_request
129
+ def resume!
130
+ @paused = false
171
131
  end
172
132
 
173
- def to_hash
174
- Formatter.new(self).to_hash
133
+ def paused?
134
+ @paused == true
175
135
  end
176
136
 
177
- def complete!
178
- Thread.current[:appsignal_transaction_id] = nil
179
- Appsignal.transactions.delete(@request_id)
180
- if process_action_event || exception?
181
- if Appsignal::IPC::Client.active?
182
- convert_values_to_primitives!
183
- Appsignal::IPC::Client.enqueue(self)
184
- else
185
- Appsignal.logger.debug("Enqueueing transaction: #{@request_id}")
186
- Appsignal.enqueue(self)
187
- end
188
- else
189
- Appsignal.logger.debug("Not processing transaction: #{@request_id} (#{events.length} events recorded)")
190
- end
191
- ensure
192
- Appsignal.transactions.delete(@request_id)
193
- end
137
+ protected
194
138
 
195
139
  def set_background_queue_start
196
- queue_start = process_action_event.payload[:queue_start]
140
+ return unless root_event_payload
141
+ queue_start = root_event_payload[:queue_start]
197
142
  return unless queue_start
198
143
  Appsignal.logger.debug("Setting background queue start: #{queue_start}")
199
- @queue_start = queue_start.to_f
144
+ @queue_start = (queue_start.to_f * 1000.0).to_i
145
+ end
146
+
147
+ def sanitized_params
148
+ return unless root_event_payload
149
+ Appsignal::ParamsSanitizer.sanitize(root_event_payload[:params])
200
150
  end
201
151
 
202
152
  def set_http_queue_start
@@ -208,52 +158,45 @@ module Appsignal
208
158
  unless cleaned_value.empty?
209
159
  value = cleaned_value.to_i
210
160
  [1_000_000.0, 1_000.0].each do |factor|
211
- @queue_start = value / factor
161
+ @queue_start = (value / factor).to_i
212
162
  break if @queue_start > 946_681_200.0 # Ok if it's later than 2000
213
163
  end
214
164
  end
215
165
  end
216
166
  end
217
167
 
218
- protected
168
+ def sanitized_environment
169
+ return unless env
170
+ {}.tap do |out|
171
+ ENV_METHODS.each do |key|
172
+ out[key] = env[key] if env[key]
173
+ end
174
+ end
175
+ end
219
176
 
220
- def add_sanitized_context!
221
- sanitize_environment!
222
- sanitize_session_data! if kind == 'http_request'
223
- sanitize_tags!
224
- sanitize_params!
225
- @env = nil
177
+ def sanitized_session_data
178
+ return if Appsignal.config[:skip_session_data] || !env
179
+ Appsignal::ParamsSanitizer.sanitize(request.session.to_hash)
226
180
  end
227
181
 
228
182
  # Only keep tags if they meet the following criteria:
229
183
  # * Key is a symbol or string with less then 100 chars
230
184
  # * Value is a symbol or string with less then 100 chars
231
185
  # * Value is an integer
232
- def sanitize_tags!
233
- @tags.keep_if do |k,v|
186
+ def sanitized_tags
187
+ @tags.select do |k, v|
234
188
  (k.is_a?(Symbol) || k.is_a?(String) && k.length <= 100) &&
235
189
  (((v.is_a?(Symbol) || v.is_a?(String)) && v.length <= 100) || (v.is_a?(Integer)))
236
190
  end
237
191
  end
238
192
 
239
- def sanitize_environment!
240
- return unless env && env.keys.any?
241
- ENV_METHODS.each do |key|
242
- sanitized_environment[key] = env[key]
193
+ def cleaned_backtrace(backtrace)
194
+ if defined?(::Rails)
195
+ ::Rails.backtrace_cleaner.clean(backtrace, nil)
196
+ else
197
+ backtrace
243
198
  end
244
199
  end
245
200
 
246
- def sanitize_session_data!
247
- return unless request
248
- @sanitized_session_data = Appsignal::ParamsSanitizer.sanitize(
249
- request.session.to_hash
250
- ) if Appsignal.config[:skip_session_data] == false
251
- @fullpath = request.fullpath
252
- end
253
-
254
- def sanitize_params!
255
- return unless Appsignal.config[:send_params]
256
- @sanitized_params = Appsignal::ParamsSanitizer.sanitize(@params)
257
- end
258
201
  end
259
202
  end