appsignal 0.9.6 → 0.10.0.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -3
- data/appsignal.gemspec +0 -1
- data/lib/appsignal.rb +29 -6
- data/lib/appsignal/agent.rb +11 -6
- data/lib/appsignal/cli.rb +2 -1
- data/lib/appsignal/config.rb +1 -0
- data/lib/appsignal/integrations/delayed_job.rb +11 -19
- data/lib/appsignal/integrations/resque.rb +1 -9
- data/lib/appsignal/integrations/sidekiq.rb +1 -7
- data/lib/appsignal/transaction.rb +1 -14
- data/lib/appsignal/transaction/formatter.rb +1 -0
- data/lib/appsignal/transmitter.rb +2 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/generators/appsignal/templates/appsignal.yml +4 -0
- data/lib/vendor/active_support/notifications.rb +212 -0
- data/lib/vendor/active_support/notifications/fanout.rb +157 -0
- data/lib/vendor/active_support/notifications/instrumenter.rb +73 -0
- data/lib/vendor/active_support/per_thread_registry.rb +53 -0
- data/resources/cacert.pem +502 -485
- data/spec/lib/appsignal/agent_spec.rb +19 -4
- data/spec/lib/appsignal/config_spec.rb +2 -0
- data/spec/lib/appsignal/integrations/delayed_job_spec.rb +2 -3
- data/spec/lib/appsignal/integrations/resque_spec.rb +2 -2
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +3 -6
- data/spec/lib/appsignal/transaction/formatter_spec.rb +18 -0
- data/spec/lib/appsignal/transaction_spec.rb +1 -26
- data/spec/lib/appsignal/transmitter_spec.rb +17 -10
- data/spec/lib/appsignal_spec.rb +103 -12
- data/spec/spec_helper.rb +3 -1
- data/spec/support/fixtures/generated_config.yml +4 -0
- metadata +8 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eaaf7d6c75bf0cacbef11b40c1931a2cca2c9121
|
4
|
+
data.tar.gz: 1b7b1151ffbde430bc17267d93c7272a224c105d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 050ba5e12ac07e9f160e6be7711395f18424e4ba7a9ebb4a28cff887db5483d3762a9e70dc693d3bd7be8501663024f5e1f7e01fb67caf338976300d44acd95b
|
7
|
+
data.tar.gz: 05cd41d9e8f821244902433f910be5c42be51174f3d7e42572d84524beefee4e9e2a40bf85a28d876bbe338c89daa0a87e809d18b44a6e17abff406c3585c2b4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
-
# 0.
|
2
|
-
*
|
1
|
+
# 0.10.0
|
2
|
+
* Remove ActiveSupport dependency
|
3
|
+
* Use vendored notifications if ActiveSupport is not present
|
4
|
+
* Update bundled CA certificates
|
5
|
+
* Fix issue where backtrace can be nil
|
6
|
+
* Use Appsignal.monitor_transaction to instrument and log errors for
|
7
|
+
custom actions
|
8
|
+
* Add option to ignore a specific action
|
9
|
+
* Use a Mutex in agent instead of Thread.exclusive
|
3
10
|
|
4
11
|
# 0.9.4
|
5
12
|
* Log Rails and Sinatra version
|
@@ -59,7 +66,7 @@ Yanked
|
|
59
66
|
* Alias tag_request to tag_job, for background jobs
|
60
67
|
* Skip sanitization of env if env is nil
|
61
68
|
* Small bugfix in forking logic
|
62
|
-
* Don't send params if send_params is off in config
|
69
|
+
* Don't send params if send_params is off in config
|
63
70
|
* Remove --repository option in CLI
|
64
71
|
* Name option in appsignal notify_of_deploy CLI
|
65
72
|
* Don't call to_hash on ENV
|
data/appsignal.gemspec
CHANGED
data/lib/appsignal.rb
CHANGED
@@ -2,8 +2,12 @@ require 'logger'
|
|
2
2
|
require 'rack'
|
3
3
|
require 'thread_safe'
|
4
4
|
require 'securerandom'
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'active_support/notifications'
|
8
|
+
rescue LoadError
|
9
|
+
require 'vendor/active_support/notifications'
|
10
|
+
end
|
7
11
|
|
8
12
|
module Appsignal
|
9
13
|
class << self
|
@@ -69,6 +73,25 @@ module Appsignal
|
|
69
73
|
agent.enqueue(transaction)
|
70
74
|
end
|
71
75
|
|
76
|
+
def monitor_transaction(name, payload={})
|
77
|
+
unless active?
|
78
|
+
yield
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
begin
|
83
|
+
Appsignal::Transaction.create(SecureRandom.uuid, ENV)
|
84
|
+
ActiveSupport::Notifications.instrument(name, payload) do
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
rescue Exception => exception
|
88
|
+
Appsignal.add_exception(exception)
|
89
|
+
raise exception
|
90
|
+
ensure
|
91
|
+
Appsignal::Transaction.complete_current!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
72
95
|
def listen_for_exception(&block)
|
73
96
|
yield
|
74
97
|
rescue Exception => exception
|
@@ -129,10 +152,6 @@ module Appsignal
|
|
129
152
|
@logger << @in_memory_log.string if @in_memory_log
|
130
153
|
end
|
131
154
|
|
132
|
-
def json
|
133
|
-
ActiveSupport::JSON
|
134
|
-
end
|
135
|
-
|
136
155
|
def post_processing_middleware
|
137
156
|
@post_processing_chain ||= Appsignal::Aggregator::PostProcessor.default_middleware
|
138
157
|
yield @post_processing_chain if block_given?
|
@@ -147,6 +166,10 @@ module Appsignal
|
|
147
166
|
Appsignal.config[:ignore_exceptions].include?(exception.class.name)
|
148
167
|
end
|
149
168
|
|
169
|
+
def is_ignored_action?(action)
|
170
|
+
Appsignal.config[:ignore_actions].include?(action)
|
171
|
+
end
|
172
|
+
|
150
173
|
# Convenience method for skipping instrumentations around a block of code.
|
151
174
|
#
|
152
175
|
# @since 0.8.7
|
data/lib/appsignal/agent.rb
CHANGED
@@ -2,8 +2,8 @@ module Appsignal
|
|
2
2
|
class Agent
|
3
3
|
ACTION = 'log_entries'.freeze
|
4
4
|
|
5
|
-
attr_accessor :aggregator, :thread, :master_pid, :pid, :active,
|
6
|
-
:transmitter, :subscriber, :paused
|
5
|
+
attr_accessor :aggregator, :mutex, :thread, :master_pid, :pid, :active,
|
6
|
+
:sleep_time, :transmitter, :subscriber, :paused
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
return unless Appsignal.active?
|
@@ -16,6 +16,7 @@ module Appsignal
|
|
16
16
|
@pid = @master_pid
|
17
17
|
@aggregator = Aggregator.new
|
18
18
|
@transmitter = Transmitter.new(ACTION)
|
19
|
+
@mutex = Mutex.new
|
19
20
|
subscribe
|
20
21
|
start_thread
|
21
22
|
Appsignal.logger.info('Started Appsignal agent')
|
@@ -81,6 +82,10 @@ module Appsignal
|
|
81
82
|
|
82
83
|
def enqueue(transaction)
|
83
84
|
forked! if @pid != Process.pid
|
85
|
+
if Appsignal.is_ignored_action?(transaction.action)
|
86
|
+
Appsignal.logger.debug("Ignoring transaction: #{transaction.request_id} (#{transaction.action})")
|
87
|
+
return
|
88
|
+
end
|
84
89
|
aggregator.add(transaction)
|
85
90
|
end
|
86
91
|
|
@@ -89,7 +94,7 @@ module Appsignal
|
|
89
94
|
# Replace aggregator while making sure no thread
|
90
95
|
# is adding to it's queue
|
91
96
|
aggregator_to_be_sent = nil
|
92
|
-
|
97
|
+
mutex.synchronize do
|
93
98
|
aggregator_to_be_sent = aggregator
|
94
99
|
@aggregator = Aggregator.new
|
95
100
|
end
|
@@ -108,7 +113,7 @@ module Appsignal
|
|
108
113
|
Appsignal.logger.debug('Clearing queue')
|
109
114
|
# Replace aggregator while making sure no thread
|
110
115
|
# is adding to it's queue
|
111
|
-
|
116
|
+
mutex.synchronize do
|
112
117
|
@aggregator = Aggregator.new
|
113
118
|
end
|
114
119
|
end
|
@@ -116,7 +121,7 @@ module Appsignal
|
|
116
121
|
def forked!
|
117
122
|
Appsignal.logger.info('Forked worker process')
|
118
123
|
@pid = Process.pid
|
119
|
-
|
124
|
+
mutex.synchronize do
|
120
125
|
@aggregator = Aggregator.new
|
121
126
|
end
|
122
127
|
resubscribe
|
@@ -126,7 +131,7 @@ module Appsignal
|
|
126
131
|
def shutdown(send_current_queue=false, reason=nil)
|
127
132
|
Appsignal.logger.info("Shutting down agent (#{reason})")
|
128
133
|
unsubscribe
|
129
|
-
|
134
|
+
Thread.kill(thread) if thread
|
130
135
|
send_queue if send_current_queue && @aggregator.has_transactions?
|
131
136
|
end
|
132
137
|
|
data/lib/appsignal/cli.rb
CHANGED
@@ -110,7 +110,8 @@ module Appsignal
|
|
110
110
|
|
111
111
|
def validate_required_options(required_options)
|
112
112
|
missing = required_options.select do |required_option|
|
113
|
-
options[required_option]
|
113
|
+
val = options[required_option]
|
114
|
+
val.nil? || (val.respond_to?(:empty?) && val.empty?)
|
114
115
|
end
|
115
116
|
if missing.any?
|
116
117
|
puts "Missing options: #{missing.join(', ')}"
|
data/lib/appsignal/config.rb
CHANGED
@@ -11,25 +11,17 @@ if defined?(::Delayed::Plugin)
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.invoke_with_instrumentation(job, block)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
) do
|
26
|
-
block.call(job)
|
27
|
-
end
|
28
|
-
rescue Exception => exception
|
29
|
-
Appsignal.add_exception(exception)
|
30
|
-
raise exception
|
31
|
-
ensure
|
32
|
-
Appsignal::Transaction.complete_current!
|
14
|
+
class_name, method_name = job.name.split('#')
|
15
|
+
Appsignal.monitor_transaction(
|
16
|
+
'perform_job.delayed_job',
|
17
|
+
:class => class_name,
|
18
|
+
:method => method_name,
|
19
|
+
:priority => job.priority,
|
20
|
+
:attempts => job.attempts,
|
21
|
+
:queue => job.queue,
|
22
|
+
:queue_start => job.created_at
|
23
|
+
) do
|
24
|
+
block.call(job)
|
33
25
|
end
|
34
26
|
end
|
35
27
|
end
|
@@ -4,23 +4,15 @@ if defined?(::Resque)
|
|
4
4
|
module Appsignal
|
5
5
|
module Integrations
|
6
6
|
module ResquePlugin
|
7
|
-
|
8
7
|
def around_perform_resque_plugin(*args)
|
9
|
-
Appsignal
|
10
|
-
ActiveSupport::Notifications.instrument(
|
8
|
+
Appsignal.monitor_transaction(
|
11
9
|
'perform_job.resque',
|
12
10
|
:class => self.to_s,
|
13
11
|
:method => 'perform'
|
14
12
|
) do
|
15
13
|
yield
|
16
14
|
end
|
17
|
-
rescue Exception => exception
|
18
|
-
Appsignal.add_exception(exception)
|
19
|
-
raise exception
|
20
|
-
ensure
|
21
|
-
Appsignal::Transaction.complete_current!
|
22
15
|
end
|
23
|
-
|
24
16
|
end
|
25
17
|
end
|
26
18
|
end
|
@@ -5,8 +5,7 @@ if defined?(::Sidekiq)
|
|
5
5
|
module Integrations
|
6
6
|
class SidekiqPlugin
|
7
7
|
def call(worker, item, queue)
|
8
|
-
Appsignal
|
9
|
-
ActiveSupport::Notifications.instrument(
|
8
|
+
Appsignal.monitor_transaction(
|
10
9
|
'perform_job.sidekiq',
|
11
10
|
:class => item['class'],
|
12
11
|
:method => 'perform',
|
@@ -16,11 +15,6 @@ if defined?(::Sidekiq)
|
|
16
15
|
) do
|
17
16
|
yield
|
18
17
|
end
|
19
|
-
rescue Exception => exception
|
20
|
-
Appsignal.add_exception(exception)
|
21
|
-
raise exception
|
22
|
-
ensure
|
23
|
-
Appsignal::Transaction.complete_current!
|
24
18
|
end
|
25
19
|
end
|
26
20
|
end
|
@@ -85,7 +85,7 @@
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def exception?
|
88
|
-
!!
|
88
|
+
!!exception
|
89
89
|
end
|
90
90
|
|
91
91
|
def slow_request?
|
@@ -98,32 +98,20 @@
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def truncate!
|
101
|
-
return if truncated?
|
102
101
|
process_action_event.payload.clear
|
103
102
|
events.clear
|
104
103
|
tags.clear
|
105
104
|
sanitized_environment.clear
|
106
105
|
sanitized_session_data.clear
|
107
106
|
@env = nil
|
108
|
-
@truncated = true
|
109
|
-
end
|
110
|
-
|
111
|
-
def truncated?
|
112
|
-
!! @truncated
|
113
107
|
end
|
114
108
|
|
115
109
|
def convert_values_to_primitives!
|
116
|
-
return if have_values_been_converted_to_primitives?
|
117
110
|
Appsignal::Transaction::ParamsSanitizer.sanitize!(@process_action_event.payload) if @process_action_event
|
118
111
|
@events.map do |o|
|
119
112
|
Appsignal::Transaction::ParamsSanitizer.sanitize(o.payload)
|
120
113
|
end
|
121
114
|
add_sanitized_context!
|
122
|
-
@have_values_been_converted_to_primitives = true
|
123
|
-
end
|
124
|
-
|
125
|
-
def have_values_been_converted_to_primitives?
|
126
|
-
!! @have_values_been_converted_to_primitives
|
127
115
|
end
|
128
116
|
|
129
117
|
def type
|
@@ -141,7 +129,6 @@
|
|
141
129
|
Appsignal.transactions.delete(@request_id)
|
142
130
|
if process_action_event || exception?
|
143
131
|
if Appsignal::Pipe.current
|
144
|
-
convert_values_to_primitives!
|
145
132
|
Appsignal.logger.debug("Writing transaction to pipe: #{@request_id}")
|
146
133
|
Appsignal::Pipe.current.write(self)
|
147
134
|
else
|
@@ -2,6 +2,7 @@ require 'net/http'
|
|
2
2
|
require 'net/https'
|
3
3
|
require 'uri'
|
4
4
|
require 'rack/utils'
|
5
|
+
require 'json'
|
5
6
|
|
6
7
|
module Appsignal
|
7
8
|
class Transmitter
|
@@ -40,7 +41,7 @@ module Appsignal
|
|
40
41
|
request['Content-Type'] = CONTENT_TYPE
|
41
42
|
request['Content-Encoding'] = CONTENT_ENCODING
|
42
43
|
request.body = Zlib::Deflate.deflate(
|
43
|
-
|
44
|
+
JSON.generate(payload, :quirks_mode => true),
|
44
45
|
Zlib::BEST_SPEED
|
45
46
|
)
|
46
47
|
end
|
data/lib/appsignal/version.rb
CHANGED
@@ -9,6 +9,10 @@ default: &defaults
|
|
9
9
|
# The cuttoff point in ms above which a request is considered slow, default is 200
|
10
10
|
# slow_request_threshold: 200
|
11
11
|
|
12
|
+
# Actions that should not be monitored by AppSignal
|
13
|
+
# ignore_actions:
|
14
|
+
# - ApplicationController#isup
|
15
|
+
|
12
16
|
# Configuration per environment, leave out an environment or set active
|
13
17
|
# to false to not push metrics for that environment.
|
14
18
|
<% environments.each do |environment| -%>
|
@@ -0,0 +1,212 @@
|
|
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
|