Active Support 提供的 Instrumentation API 允许开发人员提供 Hook,其他开发人员可以连接到这些 Hook。在 Rails 框架中,有 几个这样的 Hook 。使用此 API,开发人员可以选择在应用程序或其他 Ruby 代码中发生某些事件时收到通知。
例如,在 Active Record 中提供了一个 Hook ,它会在 Active Record 对数据库使用 SQL 查询时被调用。这个 Hook 可以被 订阅 ,并用于跟踪特定操作期间的查询次数。还有一个关于处理控制器操作的 Hook 。例如,这可以用来跟踪特定操作所花费的时间。
您甚至可以 在您的应用程序中创建自己的事件 ,您以后可以订阅这些事件。
使用 ActiveSupport::Notifications.subscribe
和一个 block 来监听任何通知。根据 block 接受的参数数量,您将收到不同的数据。
订阅事件的第一种方法是使用一个带单个参数的 block。该参数将是 ActiveSupport::Notifications::Event
的一个实例。
ActiveSupport :: Notifications . subscribe "process_action.action_controller" do | event |
event . name # => "process_action.action_controller"
event . duration # => 10 (in milliseconds)
event . allocations # => 1826
event . payload # => {:extra=>information}
Rails . logger . info " #{ event } Received!"
end
复制
如果您不需要 Event 对象记录的所有数据,您也可以指定一个接受以下五个参数的 block
事件的名称
事件开始的时间
事件结束的时间
触发事件的 Instrumentation 的唯一 ID
事件的有效载荷
ActiveSupport :: Notifications . subscribe "process_action.action_controller" do | name , started , finished , unique_id , payload |
# your own custom stuff
Rails . logger . info " #{ name } Received! (started: #{ started } , finished: #{ finished } )" # process_action.action_controller Received! (started: 2019-05-05 13:43:57 -0800, finished: 2019-05-05 13:43:58 -0800)
end
复制
如果您担心 started
和 finished
的准确性,无法计算出精确的经过时间,那么请使用 ActiveSupport::Notifications.monotonic_subscribe
。给定的 block 将接收与上述相同的参数,但 started
和 finished
将具有准确的单调时间值,而不是挂钟时间。
ActiveSupport :: Notifications . monotonic_subscribe "process_action.action_controller" do | name , started , finished , unique_id , payload |
# your own custom stuff
duration = finished - started # 1560979.429234 - 1560978.425334
Rails . logger . info " #{ name } Received! (duration: #{ duration } )" # process_action.action_controller Received! (duration: 1.0039)
end
复制
您也可以订阅与正则表达式匹配的事件。这使您能够一次订阅多个事件。以下是订阅来自 ActionController
的所有内容的方法
ActiveSupport :: Notifications . subscribe ( /action_controller/ ) do | event |
# inspect all ActionController events
end
复制
在 Ruby on Rails 框架中,提供了一些用于常见事件的 Hook。这些事件及其有效载荷在下面详细介绍。
键
值
:controller
控制器名称
:action
操作
:request
ActionDispatch::Request
对象
:params
请求参数的哈希,不包含任何过滤后的参数
:headers
请求头
:format
html/js/json/xml 等
:method
HTTP 请求动词
:path
请求路径
{
controller: "PostsController" ,
action: "new" ,
params: { "action" => "new" , "controller" => "posts" },
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html ,
method: "GET" ,
path: "/posts/new"
}
复制
键
值
:controller
控制器名称
:action
操作
:params
请求参数的哈希,不包含任何过滤后的参数
:headers
请求头
:format
html/js/json/xml 等
:method
HTTP 请求动词
:path
请求路径
:request
ActionDispatch::Request
对象
:response
ActionDispatch::Response
对象
:status
HTTP 状态码
:view_runtime
在视图中花费的时间(毫秒)
:db_runtime
执行数据库查询所花费的时间(毫秒)
{
controller: "PostsController" ,
action: "index" ,
params: { "action" => "index" , "controller" => "posts" },
headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
format: :html ,
method: "GET" ,
path: "/posts" ,
request: #<ActionDispatch::Request:0x00007ff1cb9bd7b8>,
response: #<ActionDispatch::Response:0x00007f8521841ec8>,
status: 200 ,
view_runtime: 46.848 ,
db_runtime: 0.157
}
复制
调用方可以添加其他键。
ActionController
不会向有效载荷添加任何特定信息。所有选项都将传递到有效载荷。
{
status: 302 ,
location: "https://127.0.0.1:3000/posts/new" ,
request: < ActionDispatch :: Request : 0x00007ff1cb9bd7b8 >
}
复制
{
filter: ":halting_filter"
}
复制
键
值
:keys
未经授权的键
:context
包含以下键的哈希::controller
、:action
、:params
、:request
键
值
:filename
文件名
:type
HTTP 内容类型
:disposition
HTTP 内容处置
{
filename: "subscribers.csv" ,
type: "text/csv" ,
disposition: "attachment"
}
复制
{
key: 'posts/1-dashboard-view'
}
复制
{
key: 'posts/1-dashboard-view'
}
复制
{
key: 'posts/1-dashboard-view'
}
复制
{
key: 'posts/1-dashboard-view'
}
复制
键
值
:identifier
模板的完整路径
:layout
适用的布局
:locals
传递给模板的局部变量
{
identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb" ,
layout: "layouts/application" ,
locals: { foo: "bar" }
}
复制
键
值
:identifier
模板的完整路径
:locals
传递给模板的局部变量
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb" ,
locals: { foo: "bar" }
}
复制
键
值
:identifier
模板的完整路径
:count
集合的大小
:cache_hits
从缓存中获取的局部模板数量
仅当使用 cached: true
渲染集合时,才会包含 :cache_hits
键。
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb" ,
count: 3 ,
cache_hits: 0
}
复制
{
identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}
复制
键
值
:sql
SQL 语句
:name
操作的名称
:connection
连接对象
:transaction
当前事务(如果有)
:binds
绑定参数
:type_casted_binds
类型转换后的绑定参数
:statement_name
SQL 语句名称
:async
true
表示查询是异步加载的
:cached
true
表示使用缓存查询时添加的
:row_count
查询返回的行数
适配器也可以添加它们自己的数据。
{
sql: "SELECT \" posts \" .* FROM \" posts \" " ,
name: "Post Load" ,
connection: < ActiveRecord :: ConnectionAdapters :: SQLite3Adapter : 0x00007f9f7a838850 > ,
transaction: < ActiveRecord :: ConnectionAdapters :: RealTransaction : 0x0000000121b5d3e0 >
binds: [ < ActiveModel :: Attribute :: WithCastValue : 0x00007fe19d15dc00 > ],
type_casted_binds: [ 11 ],
statement_name: nil ,
row_count: 5
}
复制
如果查询不是在事务的上下文中执行的,则 :transaction
为 nil
。
仅当 config.active_record.action_on_strict_loading_violation
设置为 :log
时,才会发出此事件。
键
值
:owner
启用了 strict_loading
的模型
:reflection
尝试加载的关联的反射
键
值
:record_count
实例化的记录数量
:class_name
记录的类
{
record_count: 1 ,
class_name: "User"
}
复制
当事务开始时,会发出此事件。
键
值
:transaction
事务对象
:connection
连接对象
请注意,Active Record 不会在需要之前创建实际的数据库事务
ActiveRecord :: Base . transaction do
# We are inside the block, but no event has been triggered yet.
# The following line makes Active Record start the transaction.
User . count # Event fired here.
end
复制
请记住,普通的嵌套调用不会创建新的事务
ActiveRecord :: Base . transaction do | t1 |
User . count # Fires an event for t1.
ActiveRecord :: Base . transaction do | t2 |
# The next line fires no event for t2, because the only
# real database transaction in this example is t1.
User . first . touch
end
end
复制
但是,如果传递 requires_new: true
,您也会收到嵌套事务的事件。这在幕后可能是保存点
ActiveRecord :: Base . transaction do | t1 |
User . count # Fires an event for t1.
ActiveRecord :: Base . transaction ( requires_new: true ) do | t2 |
User . first . touch # Fires an event for t2.
end
end
复制
当数据库事务结束时,会发出此事件。事务的状态可以在 :outcome
键中找到。
键
值
:transaction
事务对象
:outcome
:commit
、:rollback
、:restart
或 :incomplete
:connection
连接对象
实际上,您无法对事务对象进行太多操作,但它可能仍然有助于跟踪数据库活动。例如,通过跟踪transaction.uuid
。
键
值
:mailer
邮件器类的名称
:message_id
邮件 ID,由 Mail gem 生成
:subject
邮件的主题
:to
邮件的收件人地址
:from
邮件的发送人地址
:bcc
邮件的密件抄送地址
:cc
邮件的抄送地址
:date
邮件的日期
:mail
邮件的编码形式
:perform_deliveries
是否执行此邮件的传递
键
值
:mailer
邮件器类的名称
:action
操作
:args
参数
{
mailer: "Notification" ,
action: "welcome_email" ,
args: []
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
:hit
如果此读取命中
:super_operation
:fetch
如果读取使用 fetch
完成
键
值
:key
存储中使用的键
:store
存储类的名称
:hits
缓存命中的键
:super_operation
:fetch_multi
如果读取使用 fetch_multi
完成
此事件仅在使用 fetch
时调用块时发出。
键
值
:key
存储中使用的键
:store
存储类的名称
传递给fetch
的选项将在写入存储时与有效负载合并。
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
复制
此事件仅在使用 fetch
时调用块时发出。
键
值
:key
存储中使用的键
:store
存储类的名称
传递给fetch
的选项将与有效负载合并。
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
缓存存储也可能添加自己的数据。
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
复制
键
值
:key
写入存储的键和值
:store
存储类的名称
键
值
:key
存储中使用的键
:store
存储类的名称
:amount
增量数量
{
key: "bottles-of-beer" ,
store: "ActiveSupport::Cache::RedisCacheStore" ,
amount: 99
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
:amount
递减数量
{
key: "bottles-of-beer" ,
store: "ActiveSupport::Cache::RedisCacheStore" ,
amount: 1
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
此事件仅在使用 RedisCacheStore
、FileStore
或 MemoryStore
时发出。
键
值
:key
使用的键模式
:store
存储类的名称
{
key: "posts/*" ,
store: "ActiveSupport::Cache::RedisCacheStore"
}
复制
此事件仅在使用 MemoryStore
时发出。
键
值
:store
存储类的名称
:size
清理前缓存中的条目数
{
store: "ActiveSupport::Cache::MemoryStore" ,
size: 9001
}
复制
此事件仅在使用 MemoryStore
时发出。
键
值
:store
存储类的名称
:key
缓存的目标大小(以字节为单位)
:from
修剪前缓存的大小(以字节为单位)
{
store: "ActiveSupport::Cache::MemoryStore" ,
key: 5000 ,
from: 9001
}
复制
键
值
:key
存储中使用的键
:store
存储类的名称
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
复制
键
值
:serializer
主要(预期)序列化器
:fallback
回退(实际)序列化器
:serialized
序列化字符串
:deserialized
反序列化值
{
serializer: :json_allow_marshal ,
fallback: :marshal ,
serialized: " \x04\b { \x06 I \"\n Hello \x06 : \x06 ETI \"\n World \x06 ; \x00 T" ,
deserialized: { "Hello" => "World" },
}
复制
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
键
值
:job
作业对象
:adapter
处理作业的 QueueAdapter 对象
:error
导致重试的错误
:wait
重试的延迟
键
值
:adapter
处理作业的 QueueAdapter 对象
:jobs
作业对象的数组
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
:db_runtime
执行数据库查询所花费的时间(毫秒)
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
:error
导致重试的错误
键
值
:adapter
处理作业的 QueueAdapter 对象
:job
作业对象
:error
导致丢弃的错误
键
值
:channel_class
通道类的名称
:action
操作
:data
数据的哈希表
键
值
:channel_class
通道类的名称
:data
数据的哈希表
:via
通过
键
值
:channel_class
通道类的名称
键
值
:channel_class
通道类的名称
键
值
:broadcasting
命名广播
:message
消息的哈希表
:coder
编码器
键
值
:analyzer
分析器的名称,例如,ffprobe
键
值
:key
安全令牌
:service
服务的名称
:checksum
校验和,以确保完整性
键
值
:key
安全令牌
:service
服务的名称
键
值
:key
安全令牌
:service
服务的名称
:range
尝试读取的字节范围
键
值
:key
安全令牌
:service
服务的名称
键
值
:key
安全令牌
:service
服务的名称
键
值
:prefix
键前缀
:service
服务的名称
键
值
:key
安全令牌
:service
服务的名称
:exist
文件或 Blob 是否存在
键
值
:key
安全令牌
:service
服务的名称
:url
生成的 URL
此事件仅在使用 Google Cloud Storage 服务时发出。
键
值
:key
安全令牌
:service
服务的名称
:content_type
HTTP Content-Type
字段
:disposition
HTTP Content-Disposition
字段
{
mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
inbound_email: {
id: 1 ,
message_id: "[email protected] " ,
status: "processing"
}
}
复制
键
值
:initializer
config/initializers
中加载的初始化程序的路径
键
值
:message
弃用警告
:callstack
弃用来自何处
:gem_name
报告弃用的 gem 的名称
:deprecation_horizon
将删除弃用行为的版本
如果在任何检测期间发生异常,有效负载将包含有关该异常的信息。
键
值
:exception
包含两个元素的数组。异常类名和消息
:exception_object
异常对象
添加您自己的事件也很容易。Active Support 将为您处理所有繁重的工作。只需使用name
、payload
和一个块调用 ActiveSupport::Notifications.instrument
。块返回后将发送通知。Active Support 将生成开始和结束时间,并添加检测器的唯一 ID。传递给instrument
调用的所有数据都将进入有效负载。
以下是一个示例
ActiveSupport :: Notifications . instrument "my.custom.event" , this: :data do
# do your custom stuff here
end
复制
现在,您可以使用以下方法监听此事件
ActiveSupport :: Notifications . subscribe "my.custom.event" do | name , started , finished , unique_id , data |
puts data . inspect # {:this=>:data}
end
复制
您也可以在不传递块的情况下调用instrument
。这使您可以利用检测基础设施来进行其他消息传递用途。
ActiveSupport :: Notifications . instrument "my.custom.event" , this: :data
ActiveSupport :: Notifications . subscribe "my.custom.event" do | name , started , finished , unique_id , data |
puts data . inspect # {:this=>:data}
end
复制
定义自己的事件时,您应该遵循 Rails 约定。格式为:event.library
。如果您的应用程序正在发送推文,您应该创建一个名为tweet.twitter
的事件。