appsignal 1.4.0.alpha.2 → 1.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -1
  3. data/.travis.yml +3 -1
  4. data/CHANGELOG.md +38 -1
  5. data/Rakefile +29 -12
  6. data/benchmark.rake +3 -7
  7. data/ext/agent.yml +11 -11
  8. data/ext/appsignal_extension.c +364 -72
  9. data/ext/extconf.rb +2 -4
  10. data/gemfiles/resque.gemfile +1 -0
  11. data/lib/appsignal.rb +40 -30
  12. data/lib/appsignal/auth_check.rb +1 -1
  13. data/lib/appsignal/cli/diagnose.rb +4 -3
  14. data/lib/appsignal/cli/install.rb +16 -15
  15. data/lib/appsignal/config.rb +31 -31
  16. data/lib/appsignal/event_formatter.rb +1 -1
  17. data/lib/appsignal/extension.rb +6 -0
  18. data/lib/appsignal/garbage_collection_profiler.rb +47 -0
  19. data/lib/appsignal/hooks.rb +1 -0
  20. data/lib/appsignal/hooks/active_support_notifications.rb +43 -0
  21. data/lib/appsignal/integrations/capistrano/appsignal.cap +1 -1
  22. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +2 -2
  23. data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -1
  24. data/lib/appsignal/integrations/object.rb +4 -4
  25. data/lib/appsignal/integrations/padrino.rb +1 -1
  26. data/lib/appsignal/integrations/sinatra.rb +1 -1
  27. data/lib/appsignal/integrations/webmachine.rb +2 -2
  28. data/lib/appsignal/js_exception_transaction.rb +7 -10
  29. data/lib/appsignal/marker.rb +3 -2
  30. data/lib/appsignal/rack/generic_instrumentation.rb +1 -1
  31. data/lib/appsignal/rack/sinatra_instrumentation.rb +13 -6
  32. data/lib/appsignal/rack/streaming_listener.rb +5 -3
  33. data/lib/appsignal/system.rb +36 -0
  34. data/lib/appsignal/transaction.rb +20 -20
  35. data/lib/appsignal/transmitter.rb +11 -7
  36. data/lib/appsignal/utils.rb +76 -2
  37. data/lib/appsignal/version.rb +1 -1
  38. data/spec/lib/appsignal/auth_check_spec.rb +0 -2
  39. data/spec/lib/appsignal/capistrano2_spec.rb +99 -79
  40. data/spec/lib/appsignal/capistrano3_spec.rb +57 -78
  41. data/spec/lib/appsignal/cli/diagnose_spec.rb +17 -15
  42. data/spec/lib/appsignal/cli/install_spec.rb +38 -20
  43. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +2 -5
  44. data/spec/lib/appsignal/cli_spec.rb +2 -5
  45. data/spec/lib/appsignal/config_spec.rb +385 -158
  46. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +1 -3
  47. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +0 -2
  48. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +0 -2
  49. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +0 -2
  50. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +0 -2
  51. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +0 -2
  52. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +0 -2
  53. data/spec/lib/appsignal/event_formatter_spec.rb +0 -2
  54. data/spec/lib/appsignal/extension_spec.rb +7 -8
  55. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +71 -0
  56. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +42 -0
  57. data/spec/lib/appsignal/hooks/celluloid_spec.rb +0 -2
  58. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +0 -2
  59. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +0 -2
  60. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +0 -2
  61. data/spec/lib/appsignal/hooks/net_http_spec.rb +0 -2
  62. data/spec/lib/appsignal/hooks/passenger_spec.rb +0 -2
  63. data/spec/lib/appsignal/hooks/puma_spec.rb +0 -2
  64. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -2
  65. data/spec/lib/appsignal/hooks/redis_spec.rb +0 -2
  66. data/spec/lib/appsignal/hooks/sequel_spec.rb +19 -21
  67. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +1 -4
  68. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -3
  69. data/spec/lib/appsignal/hooks/unicorn_spec.rb +0 -2
  70. data/spec/lib/appsignal/hooks/webmachine_spec.rb +4 -11
  71. data/spec/lib/appsignal/hooks_spec.rb +0 -2
  72. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +0 -1
  73. data/spec/lib/appsignal/integrations/grape_spec.rb +1 -3
  74. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +1 -2
  75. data/spec/lib/appsignal/integrations/object_spec.rb +32 -3
  76. data/spec/lib/appsignal/integrations/padrino_spec.rb +4 -11
  77. data/spec/lib/appsignal/integrations/railtie_spec.rb +1 -3
  78. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +1 -3
  79. data/spec/lib/appsignal/integrations/resque_spec.rb +2 -4
  80. data/spec/lib/appsignal/integrations/sinatra_spec.rb +33 -8
  81. data/spec/lib/appsignal/integrations/webmachine_spec.rb +6 -15
  82. data/spec/lib/appsignal/js_exception_transaction_spec.rb +3 -5
  83. data/spec/lib/appsignal/marker_spec.rb +35 -48
  84. data/spec/lib/appsignal/minutely_spec.rb +0 -2
  85. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +0 -2
  86. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +0 -2
  87. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +3 -5
  88. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +47 -11
  89. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +6 -7
  90. data/spec/lib/appsignal/system/container_spec.rb +67 -0
  91. data/spec/lib/appsignal/system_spec.rb +49 -0
  92. data/spec/lib/appsignal/transaction_spec.rb +30 -13
  93. data/spec/lib/appsignal/transmitter_spec.rb +53 -20
  94. data/spec/lib/appsignal/utils/gzip_spec.rb +10 -0
  95. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +0 -2
  96. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +0 -2
  97. data/spec/lib/appsignal/utils_spec.rb +59 -3
  98. data/spec/lib/appsignal_spec.rb +132 -58
  99. data/spec/spec_helper.rb +24 -116
  100. data/spec/support/fixtures/containers/cgroups/docker +14 -0
  101. data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
  102. data/spec/support/fixtures/containers/cgroups/lxc +10 -0
  103. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  104. data/spec/support/fixtures/containers/cgroups/none +1 -0
  105. data/spec/support/helpers/api_request_helper.rb +22 -0
  106. data/spec/support/helpers/dependency_helper.rb +61 -0
  107. data/spec/support/helpers/directory_helper.rb +27 -0
  108. data/spec/support/helpers/std_streams_helper.rb +35 -0
  109. data/spec/support/helpers/system_helpers.rb +24 -0
  110. data/spec/support/helpers/transaction_helpers.rb +7 -64
  111. data/spec/support/helpers/very_specific_error.rb +8 -0
  112. data/spec/support/mocks/fake_gc_profiler.rb +19 -0
  113. data/spec/support/project_fixture/config/appsignal.yml +10 -1
  114. metadata +60 -35
  115. data/circle.yml +0 -12
  116. data/lib/appsignal/subscriber.rb +0 -55
  117. data/lib/appsignal/update_active_support.rb +0 -20
  118. data/lib/vendor/active_support/notifications.rb +0 -212
  119. data/lib/vendor/active_support/notifications/fanout.rb +0 -157
  120. data/lib/vendor/active_support/notifications/instrumenter.rb +0 -73
  121. data/lib/vendor/active_support/per_thread_registry.rb +0 -53
  122. data/spec/lib/appsignal/subscriber_spec.rb +0 -160
  123. data/spec/lib/appsignal/update_active_support_spec.rb +0 -17
  124. data/spec/support/helpers/notification_helpers.rb +0 -14
data/circle.yml DELETED
@@ -1,12 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.3.0
4
-
5
- dependencies:
6
- override:
7
- - bundle
8
- - bundle exec rake generate_bundle_and_spec_all
9
-
10
- test:
11
- override:
12
- - ./bundle_and_spec_all_rvm
@@ -1,55 +0,0 @@
1
- module Appsignal
2
- class Subscriber
3
- attr_reader :as_subscriber
4
-
5
- def initialize
6
- subscribe
7
- end
8
-
9
- def subscribe
10
- Appsignal.logger.debug('Subscribing to notifications')
11
- # Subscribe to notifications that don't start with a !
12
- @as_subscriber = ActiveSupport::Notifications.subscribe(/^[^!]/, self)
13
- end
14
-
15
- def unsubscribe
16
- if @as_subscriber
17
- Appsignal.logger.debug('Unsubscribing from notifications')
18
- ActiveSupport::Notifications.unsubscribe(@as_subscriber)
19
- @as_subscriber = nil
20
- end
21
- end
22
-
23
- def resubscribe
24
- Appsignal.logger.debug('Resubscribing to notifications')
25
- unsubscribe
26
- subscribe
27
- end
28
-
29
- def publish(name, *args)
30
- # Not used, it's part of AS notifications but is not used in Rails
31
- # and it seems to be unclear what it's function is. See:
32
- # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/notifications/fanout.rb#L49
33
- end
34
-
35
- def start(name, id, payload)
36
- return unless transaction = Appsignal::Transaction.current
37
- return if transaction.nil_transaction? || transaction.paused?
38
-
39
- transaction.start_event
40
- end
41
-
42
- def finish(name, id, payload)
43
- return unless transaction = Appsignal::Transaction.current
44
- return if transaction.nil_transaction? || transaction.paused?
45
-
46
- title, body, body_format = Appsignal::EventFormatter.format(name, payload)
47
- transaction.finish_event(
48
- name,
49
- title,
50
- body,
51
- body_format
52
- )
53
- end
54
- end
55
- end
@@ -1,20 +0,0 @@
1
- module Appsignal
2
- module UpdateActiveSupport
3
- def self.run
4
- # Get the old subscribers if present
5
- old_notifier = ActiveSupport::Notifications.notifier
6
- subscribers = old_notifier.instance_variable_get('@subscribers') || []
7
-
8
- # Require the newer notifications
9
- require 'vendor/active_support/notifications'
10
-
11
- # Re-subscribe the old subscribers
12
- subscribers.each do |sub|
13
- pattern = sub.instance_variable_get('@pattern')
14
- delegate = sub.instance_variable_get('@delegate')
15
- next unless pattern && delegate
16
- ActiveSupport::Notifications.subscribe(pattern, delegate)
17
- end
18
- end
19
- end
20
- end
@@ -1,212 +0,0 @@
1
- require 'vendor/active_support/notifications/instrumenter'
2
- require 'vendor/active_support/notifications/fanout'
3
- require 'vendor/active_support/per_thread_registry'
4
-
5
- module ActiveSupport
6
- # = Notifications
7
- #
8
- # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
- # Ruby.
10
- #
11
- # == Instrumenters
12
- #
13
- # To instrument an event you just need to do:
14
- #
15
- # ActiveSupport::Notifications.instrument('render', extra: :information) do
16
- # render text: 'Foo'
17
- # end
18
- #
19
- # That executes the block first and notifies all subscribers once done.
20
- #
21
- # In the example above +render+ is the name of the event, and the rest is called
22
- # the _payload_. The payload is a mechanism that allows instrumenters to pass
23
- # extra information to subscribers. Payloads consist of a hash whose contents
24
- # are arbitrary and generally depend on the event.
25
- #
26
- # == Subscribers
27
- #
28
- # You can consume those events and the information they provide by registering
29
- # a subscriber.
30
- #
31
- # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
32
- # name # => String, name of the event (such as 'render' from above)
33
- # start # => Time, when the instrumented block started execution
34
- # finish # => Time, when the instrumented block ended execution
35
- # id # => String, unique ID for this notification
36
- # payload # => Hash, the payload
37
- # end
38
- #
39
- # For instance, let's store all "render" events in an array:
40
- #
41
- # events = []
42
- #
43
- # ActiveSupport::Notifications.subscribe('render') do |*args|
44
- # events << ActiveSupport::Notifications::Event.new(*args)
45
- # end
46
- #
47
- # That code returns right away, you are just subscribing to "render" events.
48
- # The block is saved and will be called whenever someone instruments "render":
49
- #
50
- # ActiveSupport::Notifications.instrument('render', extra: :information) do
51
- # render text: 'Foo'
52
- # end
53
- #
54
- # event = events.first
55
- # event.name # => "render"
56
- # event.duration # => 10 (in milliseconds)
57
- # event.payload # => { extra: :information }
58
- #
59
- # The block in the <tt>subscribe</tt> call gets the name of the event, start
60
- # timestamp, end timestamp, a string with a unique identifier for that event
61
- # (something like "535801666f04d0298cd6"), and a hash with the payload, in
62
- # that order.
63
- #
64
- # If an exception happens during that particular instrumentation the payload will
65
- # have a key <tt>:exception</tt> with an array of two elements as value: a string with
66
- # the name of the exception class, and the exception message.
67
- #
68
- # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
69
- # is able to take the arguments as they come and provide an object-oriented
70
- # interface to that data.
71
- #
72
- # It is also possible to pass an object as the second parameter passed to the
73
- # <tt>subscribe</tt> method instead of a block:
74
- #
75
- # module ActionController
76
- # class PageRequest
77
- # def call(name, started, finished, unique_id, payload)
78
- # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
79
- # end
80
- # end
81
- # end
82
- #
83
- # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
84
- #
85
- # resulting in the following output within the logs including a hash with the payload:
86
- #
87
- # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
88
- # controller: "Devise::SessionsController",
89
- # action: "new",
90
- # params: {"action"=>"new", "controller"=>"devise/sessions"},
91
- # format: :html,
92
- # method: "GET",
93
- # path: "/login/sign_in",
94
- # status: 200,
95
- # view_runtime: 279.3080806732178,
96
- # db_runtime: 40.053
97
- # }
98
- #
99
- # You can also subscribe to all events whose name matches a certain regexp:
100
- #
101
- # ActiveSupport::Notifications.subscribe(/render/) do |*args|
102
- # ...
103
- # end
104
- #
105
- # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
106
- # to all events.
107
- #
108
- # == Temporary Subscriptions
109
- #
110
- # Sometimes you do not want to subscribe to an event for the entire life of
111
- # the application. There are two ways to unsubscribe.
112
- #
113
- # WARNING: The instrumentation framework is designed for long-running subscribers,
114
- # use this feature sparingly because it wipes some internal caches and that has
115
- # a negative impact on performance.
116
- #
117
- # === Subscribe While a Block Runs
118
- #
119
- # You can subscribe to some event temporarily while some block runs. For
120
- # example, in
121
- #
122
- # callback = lambda {|*args| ... }
123
- # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
124
- # ...
125
- # end
126
- #
127
- # the callback will be called for all "sql.active_record" events instrumented
128
- # during the execution of the block. The callback is unsubscribed automatically
129
- # after that.
130
- #
131
- # === Manual Unsubscription
132
- #
133
- # The +subscribe+ method returns a subscriber object:
134
- #
135
- # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
136
- # ...
137
- # end
138
- #
139
- # To prevent that block from being called anymore, just unsubscribe passing
140
- # that reference:
141
- #
142
- # ActiveSupport::Notifications.unsubscribe(subscriber)
143
- #
144
- # You can also unsubscribe by passing the name of the subscriber object. Note
145
- # that this will unsubscribe all subscriptions with the given name:
146
- #
147
- # ActiveSupport::Notifications.unsubscribe("render")
148
- #
149
- # == Default Queue
150
- #
151
- # Notifications ships with a queue implementation that consumes and publishes events
152
- # to all log subscribers. You can use any queue implementation you want.
153
- #
154
- module Notifications
155
- class << self
156
- attr_accessor :notifier
157
-
158
- def publish(name, *args)
159
- notifier.publish(name, *args)
160
- end
161
-
162
- def instrument(name, payload = {})
163
- if notifier.listening?(name)
164
- instrumenter.instrument(name, payload) { yield payload if block_given? }
165
- else
166
- yield payload if block_given?
167
- end
168
- end
169
-
170
- def subscribe(*args, &block)
171
- notifier.subscribe(*args, &block)
172
- end
173
-
174
- def subscribed(callback, *args, &block)
175
- subscriber = subscribe(*args, &callback)
176
- yield
177
- ensure
178
- unsubscribe(subscriber)
179
- end
180
-
181
- def unsubscribe(subscriber_or_name)
182
- notifier.unsubscribe(subscriber_or_name)
183
- end
184
-
185
- def instrumenter
186
- InstrumentationRegistry.instance.instrumenter_for(notifier)
187
- end
188
- end
189
-
190
- # This class is a registry which holds all of the +Instrumenter+ objects
191
- # in a particular thread local. To access the +Instrumenter+ object for a
192
- # particular +notifier+, you can call the following method:
193
- #
194
- # InstrumentationRegistry.instrumenter_for(notifier)
195
- #
196
- # The instrumenters for multiple notifiers are held in a single instance of
197
- # this class.
198
- class InstrumentationRegistry # :nodoc:
199
- extend ActiveSupport::PerThreadRegistry
200
-
201
- def initialize
202
- @registry = {}
203
- end
204
-
205
- def instrumenter_for(notifier)
206
- @registry[notifier] ||= Instrumenter.new(notifier)
207
- end
208
- end
209
-
210
- self.notifier = Fanout.new
211
- end
212
- end
@@ -1,157 +0,0 @@
1
- require 'mutex_m'
2
- require 'thread_safe'
3
-
4
- module ActiveSupport
5
- module Notifications
6
- # This is a default queue implementation that ships with Notifications.
7
- # It just pushes events to all registered log subscribers.
8
- #
9
- # This class is thread safe. All methods are reentrant.
10
- class Fanout
11
- include Mutex_m
12
-
13
- def initialize
14
- @subscribers = []
15
- @listeners_for = ThreadSafe::Cache.new
16
- super
17
- end
18
-
19
- def subscribe(pattern = nil, block = Proc.new)
20
- subscriber = Subscribers.new pattern, block
21
- synchronize do
22
- @subscribers << subscriber
23
- @listeners_for.clear
24
- end
25
- subscriber
26
- end
27
-
28
- def unsubscribe(subscriber_or_name)
29
- synchronize do
30
- case subscriber_or_name
31
- when String
32
- @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
33
- else
34
- @subscribers.delete(subscriber_or_name)
35
- end
36
-
37
- @listeners_for.clear
38
- end
39
- end
40
-
41
- def start(name, id, payload)
42
- listeners_for(name).each { |s| s.start(name, id, payload) }
43
- end
44
-
45
- def finish(name, id, payload)
46
- listeners_for(name).each { |s| s.finish(name, id, payload) }
47
- end
48
-
49
- def publish(name, *args)
50
- listeners_for(name).each { |s| s.publish(name, *args) }
51
- end
52
-
53
- def listeners_for(name)
54
- # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
55
- @listeners_for[name] || synchronize do
56
- # use synchronisation when accessing @subscribers
57
- @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
58
- end
59
- end
60
-
61
- def listening?(name)
62
- listeners_for(name).any?
63
- end
64
-
65
- # This is a sync queue, so there is no waiting.
66
- def wait
67
- end
68
-
69
- module Subscribers # :nodoc:
70
- def self.new(pattern, listener)
71
- if listener.respond_to?(:start) and listener.respond_to?(:finish)
72
- subscriber = Evented.new pattern, listener
73
- else
74
- subscriber = Timed.new pattern, listener
75
- end
76
-
77
- unless pattern
78
- AllMessages.new(subscriber)
79
- else
80
- subscriber
81
- end
82
- end
83
-
84
- class Evented #:nodoc:
85
- def initialize(pattern, delegate)
86
- @pattern = pattern
87
- @delegate = delegate
88
- @can_publish = delegate.respond_to?(:publish)
89
- end
90
-
91
- def publish(name, *args)
92
- if @can_publish
93
- @delegate.publish name, *args
94
- end
95
- end
96
-
97
- def start(name, id, payload)
98
- @delegate.start name, id, payload
99
- end
100
-
101
- def finish(name, id, payload)
102
- @delegate.finish name, id, payload
103
- end
104
-
105
- def subscribed_to?(name)
106
- @pattern === name
107
- end
108
-
109
- def matches?(name)
110
- @pattern && @pattern === name
111
- end
112
- end
113
-
114
- class Timed < Evented
115
- def publish(name, *args)
116
- @delegate.call name, *args
117
- end
118
-
119
- def start(name, id, payload)
120
- timestack = Thread.current[:_timestack] ||= []
121
- timestack.push Time.now
122
- end
123
-
124
- def finish(name, id, payload)
125
- timestack = Thread.current[:_timestack]
126
- started = timestack.pop
127
- @delegate.call(name, started, Time.now, id, payload)
128
- end
129
- end
130
-
131
- class AllMessages # :nodoc:
132
- def initialize(delegate)
133
- @delegate = delegate
134
- end
135
-
136
- def start(name, id, payload)
137
- @delegate.start name, id, payload
138
- end
139
-
140
- def finish(name, id, payload)
141
- @delegate.finish name, id, payload
142
- end
143
-
144
- def publish(name, *args)
145
- @delegate.publish name, *args
146
- end
147
-
148
- def subscribed_to?(name)
149
- true
150
- end
151
-
152
- alias :matches? :===
153
- end
154
- end
155
- end
156
- end
157
- end