appsignal 0.9.6 → 0.10.0.beta.0
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.
- 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
|