ActiveSupport::Notifications
は、レールベースの通知システムです。 Railsで特定の通知をサブスクライブし、送信時にコードを呼び出すことができます。 これは
ActiveSupport::Callbacks
多少似ていますが、プロジェクト全体で機能し、イベントを事前に通知する必要はありません。
たとえば、通知
'render'
サブスクライブでき
'render'
。
ActiveSupport::Notifications.subscribe("render") do |*args| # render end
正規表現を通知の名前として使用し、その表現に一致するすべての通知をサブスクライブできます。 すべての通知をサブスクライブする場合は、
subscribe
メソッドに何も渡さないでください。
subscribe
メソッドは、サブスクライバーへのリンクを返します;通知のサブスクリプションを解除する必要がある場合があります。
ActiveSupport::Notifications.publish
メソッドを使用して通知を送信できます。
ActiveSupport::Notifications.publish('render', 'arg1', 'arg2')
subscribe
ブロックでは、
'render'
、
'arg1'
、および
'arg2'
が渡されます。
これらの機能を直接使用するRailsのコードは見つかりませんでした。おそらく、この機能はフレームワークのユーザーがアプリケーションタスクに使用するはずです。 しかし、
ActiveSupport::Notifications
には非常に便利な
instrument
メソッドがあります。 ブロックを受信すると、ブロック実行の開始と終了のタイムスタンプと、追加データを含むハッシュを含む通知を送信します。 Railsはこのメカニズムを使用し、間接的に、
subscribe
および
publish
メソッドを使用して開発環境で詳細なログを作成します(本番環境では同じ「メーター」が使用されますが、すべてがログに書き込まれるわけではありません)
同じ目的で、これらの「メーター」をアプリケーションで使用できます。 このメソッドでチェックインするコードをラップし、実行結果をログに書き込むことができます。 たとえば、追跡したい実行速度のメソッドがあります。
def do_something # end
ActiveSupport::Notifications.instrument
ブロックでラップするだけです
def do_something ActiveSupport::Notifications.instrument('benchmark.do_something', desc: 'Some description') do # end end
config/initializers/benchmarking.rb
などの場所では、
'benchmark.'
という行ですべての通知
config/initializers/benchmarking.rb
サブスクライブし
'benchmark.'
。
logger = Logger.new(File.join(Rails.root, 'log', "benchmarks.log")) ActiveSupport::Notifications.subscribe(%r/benchmark\.*/) do |name, start, ending, transaction_id, payload| method = name.split(?.).last duration = (1000.0 * (ending - start)) message = if payload[:exception].present? payload[:exception].join(' ') else payload[:desc].to_s end logger.info("Benchmark:%s: %.0fms - %s" % method, duration, message) end
次の変数がブロックに転送されます:
name, start, ending, transaction_id, payload
。
- name-キャッチされた通知の名前
-
start
ブロックのstart
時間 -
ending
-ブロック実行終了時間 -
transaction_id
一意のID、通常は同じスレッド内で一意 -
payload
-「メーター」に送信される追加データ
次に、実行時間をログに書き込みます。 例外が発生した場合、
payload[:exception]
は、例外の名前とエラーメッセージを含む配列を記録します。 これも考慮に入れる必要があります。
ActiveSupport::Notifications
Railsロギングの
ActiveSupport::Notifications
役割の詳細については、
ActiveSupport::LogSubscriber
、
ActiveRecord::LogSubscriber
、
ActionController::LogSubscriber
および
ActionMailer::LogSubscriber
。
一時的に通知をサブスクライブする別のメソッドがありますが、それに渡されたブロックが実行されている間のみです。
callback = lambda do|*args| # # "event.name" end ActiveSupport::Notifications.subscribed(callback, "event.name") do # end
イベントのサブスクリプ
unsubscribe
するには、
unsubscribe
メソッドを呼び出して、サブスクライバーリンクを渡します。
ActiveSupport::Notifications.unsubscribe(subscriber)
通知配信メカニズム自体は、
ActiveSupport::Notifications::Fanout
に隠されています
ActiveSupport::Notifications::Instrumenter
クラスを見ると興味深いでしょう。このクラスは、
instrument
メソッドでブロックの実行時間を測定する役割を果たします。
例として、
ActiveSupport::Notifications
を使用してメソッドの実行時間をリアルタイムで計算するための
ActiveSupport::Notifications
:
class Module def benchmark_it *names options, names = benchmark_options_and_names(*names) names.each do |name| target, punctuation = name.to_s.sub(/([?!=])$/, ''), $1 define_method "#{target}_with_benchmark#{punctuation}" do |*args| ActiveSupport::Notifications.instrument("benchmark.#{self.name}.#{name}", options) do send("#{target}_without_benchmark#{punctuation}", *args) end end alias_method_chain name, :benchmark end end protected def benchmark_options_and_names *args options = args.last.is_a?(Hash) ? args.pop : {} [{desc: ''}.merge(options), args] end end ActiveSupport::Notifications.subscribe(%r/benchmark\.*/) do |name, start, ending, transaction_id, payload| _, classname, method = name.split(?.) duration = (1000.0 * (ending - start)) message = if payload[:exception].present? payload[:exception].join(' ') else payload[:desc].to_s end Rails.logger.info("Benchmark: %s.%s: %.0fms - %s" % classname, method, duration, message) end
このように使用します:
class MyClass def my_method # - end # benchmark_it :my_method, desc: ' ' end
これは何のためですか? 同じRailsの例を使用すると、コードが異なる疎結合コンポーネントで構成されている場合、そのような通知を使用してそれらとの対話を確立できることがわかります。 したがって、ORMまたは他のライブラリ(
MyORM
など)を
MyORM
して、
MyORM::LogSubscriber
から継承した
MyORM::LogSubscriber
クラスにロギングタスクを
ActiveSupport::LogSubscriber
ます。 ログの表示を担当するメッセージコードはアプリケーションによって塗りつぶされませんが、1か所にあります。 もちろん、ライブラリ全体にセンサーを配置する必要があります。 ちなみに、これらの同じセンサーは、ロギング以外のあらゆるものに使用できます。
一方で、私のコードはRailsに結び付けられておらず、一方で、Railsはライブラリについても何も知りませんが、それにもかかわらず、私のgemは共通のロギングシステムに接続されています。
当然、ロギングは通知のアプリケーションの唯一の領域ではありませんが、この例では通知が必要な理由を簡単に示すことができます。