在线版的内容可能落后于电子书,如果想及时获得更新,请购买电子书

第 28 章 Active Support 监测程序

Active Support 是 Rails 核心的一部分,提供 Ruby 语言扩展、实用方法等。其中包括一份监测 API,在应用中可以用它测度 Ruby 代码(如 Rails 应用或框架自身)中的特定操作。不过,这个 API 不限于只能在 Rails 中使用,如果愿意,也可以在其他 Ruby 脚本中使用。

本文教你如何使用 Active Support 中的监测 API 测度 Rails 和其他 Ruby 代码中的事件。

读完本文后,您将学到:

  • 使用监测程序能做什么;

  • Rails 框架为监测提供的钩子;

  • 订阅钩子;

  • 自定义监测点。

注意

本文原文尚未完工!

28.1 监测程序简介

Active Support 提供的监测 API 允许开发者提供钩子,供其他开发者订阅。在 Rails 框架中,有很多。通过这个 API,开发者可以选择在应用或其他 Ruby 代码中发生特定事件时接收通知。

例如,Active Record 中有一个钩子,在每次使用 SQL 查询数据库时调用。开发者可以订阅这个钩子,记录特定操作执行的查询次数。还有一个钩子在控制器的动作执行前后调用,记录动作的执行时间。

在应用中甚至还可以自己创建事件,然后订阅。

28.2 Rails 框架中的钩子

Ruby on Rails 框架为很多常见的事件提供了钩子。下面详述。

28.3 Action Controller

28.3.1 write_fragment.action_controller

:key

完整的键

{
  key: 'posts/1-dashboard-view'
}

28.3.2 read_fragment.action_controller

:key

完整的键

{
  key: 'posts/1-dashboard-view'
}

28.3.3 expire_fragment.action_controller

:key

完整的键

{
  key: 'posts/1-dashboard-view'
}

28.3.4 exist_fragment?.action_controller

:key

完整的键

{
  key: 'posts/1-dashboard-view'
}

28.3.5 write_page.action_controller

:path

完整的路径

{
  path: '/users/1'
}

28.3.6 expire_page.action_controller

:path

完整的路径

{
  path: '/users/1'
}

28.3.7 start_processing.action_controller

:controller

控制器名

:action

动作名

: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"
}

28.3.8 process_action.action_controller

:controller

控制器名

:action

动作名

:params

请求参数散列,不过滤

:headers

请求首部

:format

html、js、json、xml 等

:method

HTTP 请求方法

:path

请求路径

:status

HTTP 状态码

:view_runtime

花在视图上的时间量(ms)

:db_runtime

执行数据库查询的时间量(ms)

{
  controller: "PostsController",
  action: "index",
  params: {"action" => "index", "controller" => "posts"},
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts",
  status: 200,
  view_runtime: 46.848,
  db_runtime: 0.157
}

28.3.9 send_file.action_controller

:path

文件的完整路径

提示

调用方可以添加额外的键。

28.3.10 send_data.action_controller

ActionController 在载荷(payload)中没有任何特定的信息。所有选项都传到载荷中。

28.3.11 redirect_to.action_controller

:status

HTTP 响应码

:location

重定向的 URL

{
  status: 302,
  location: "http://localhost:3000/posts/new"
}

28.3.12 halted_callback.action_controller

:filter

过滤暂停的动作

{
  filter: ":halting_filter"
}

28.4 Action View

28.4.1 render_template.action_view

:identifier

模板的完整路径

:layout

使用的布局

{
  identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
  layout: "layouts/application"
}

28.4.2 render-partial-action-view

:identifier

模板的完整路径

{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb"
}

28.4.3 render_collection.action_view

:identifier

模板的完整路径

:count

集合的大小

:cache_hits

从缓存中获取的局部视图数量

仅当渲染集合时设定了 cached: true 选项,才有 :cache_hits 键。

{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
  count: 3,
  cache_hits: 0
}

28.5 Active Record

28.5.1 sql.active_record

:sql

SQL 语句

:name

操作的名称

:connection_id

self.object_id

:binds

绑定的参数

:cached

使用缓存的查询时为 true

提示

适配器也会添加数据。

{
  sql: "SELECT \"posts\".* FROM \"posts\" ",
  name: "Post Load",
  connection_id: 70307250813140,
  binds: []
}

28.5.2 instantiation.active_record

:record_count

实例化记录的数量

:class_name

记录所属的类

{
  record_count: 1,
  class_name: "User"
}

28.6 Action Mailer

28.6.1 receive.action_mailer

:mailer

邮件程序类的名称

:message_id

邮件的 ID,由 Mail gem 生成

:subject

邮件的主题

:to

邮件的收件地址

:from

邮件的发件地址

:bcc

邮件的密送地址

:cc

邮件的抄送地址

:date

发送邮件的日期

:mail

邮件的编码形式

{
  mailer: "Notification",
  message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
  subject: "Rails Guides",
  to: ["users@rails.com", "ddh@rails.com"],
  from: ["me@rails.com"],
  date: Sat, 10 Mar 2012 14:18:09 +0100,
  mail: "..." # 为了节省空间,省略
}

28.6.2 deliver.action_mailer

:mailer

邮件程序类的名称

:message_id

邮件的 ID,由 Mail gem 生成

:subject

邮件的主题

:to

邮件的收件地址

:from

邮件的发件地址

:bcc

邮件的密送地址

:cc

邮件的抄送地址

:date

发送邮件的日期

:mail

邮件的编码形式

{
  mailer: "Notification",
  message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
  subject: "Rails Guides",
  to: ["users@rails.com", "ddh@rails.com"],
  from: ["me@rails.com"],
  date: Sat, 10 Mar 2012 14:18:09 +0100,
  mail: "..." # 为了节省空间,省略
}

28.7 Active Support

28.7.1 cache_read.active_support

:key

存储器中使用的键

:hit

是否读取了缓存

:super_operation

如果使用 #fetch 读取了,添加 :fetch

28.7.2 cache_generate.active_support

仅当使用块调用 #fetch 时使用这个事件。

:key

存储器中使用的键

提示

写入存储器时,传给 fetch 的选项会合并到载荷中。

{
  key: 'name-of-complicated-computation'
}

28.7.3 cache_fetch_hit.active_support

仅当使用块调用 #fetch 时使用这个事件。

:key

存储器中使用的键

提示

传给 fetch 的选项会合并到载荷中。

{
  key: 'name-of-complicated-computation'
}

28.7.4 cache_write.active_support

:key

存储器中使用的键

提示

缓存存储器可能会添加其他键。

{
  key: 'name-of-complicated-computation'
}

28.7.5 cache_delete.active_support

:key

存储器中使用的键

{
  key: 'name-of-complicated-computation'
}

28.7.6 cache_exist?.active_support

:key

存储器中使用的键

{
  key: 'name-of-complicated-computation'
}

28.8 Active Job

28.8.1 enqueue_at.active_job

:adapter

处理作业的 QueueAdapter 对象

:job

作业对象

28.8.2 enqueue.active_job

:adapter

处理作业的 QueueAdapter 对象

:job

作业对象

28.8.3 perform_start.active_job

:adapter

处理作业的 QueueAdapter 对象

:job

作业对象

28.8.4 perform.active_job

:adapter

处理作业的 QueueAdapter 对象

:job

作业对象

28.9 Railties

28.9.1 load_config_initializer.railties

:initializer

config/initializers 中加载的初始化脚本的路径

28.10 Rails

28.10.1 deprecation.rails

:message

弃用提醒

:callstack

弃用的位置

28.11 订阅事件

订阅事件是件简单的事,在 ActiveSupport::Notifications.subscribe 的块中监听通知即可。

这个块接收下述参数:

  • 事件的名称

  • 开始时间

  • 结束时间

  • 事件的唯一 ID

  • 载荷(参见前述各节)

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, data|
  # 自己编写的其他代码
  Rails.logger.info "#{name} Received!"
end

每次都定义这些块参数很麻烦,我们可以使用 ActiveSupport::Notifications::Event 创建块参数,如下:

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
  event = ActiveSupport::Notifications::Event.new *args

  event.name      # => "process_action.action_controller"
  event.duration  # => 10 (in milliseconds)
  event.payload   # => {:extra=>information}

  Rails.logger.info "#{event} Received!"
end

多数时候,我们只关注数据本身。下面是只获取数据的简洁方式:

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args|
  data = args.extract_options!
  data # { extra: :information }
end

此外,还可以订阅匹配正则表达式的事件。这样可以一次订阅多个事件。下面是订阅 ActionController 中所有事件的方式:

ActiveSupport::Notifications.subscribe /action_controller/ do |*args|
  # 审查所有 ActionController 事件
end

28.12 自定义事件

自己添加事件也很简单,繁重的工作都由 ActiveSupport::Notifications 代劳,我们只需调用 instrument,并传入 namepayload 和一个块。通知在块返回后发送。ActiveSupport 会生成起始时间和唯一的 ID。传给 instrument 调用的所有数据都会放入载荷中。

下面举个例子:

ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
  # 自己编写的其他代码
end

然后可以使用下述代码监听这个事件:

ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
  puts data.inspect # {:this=>:data}
end

自己定义事件时,应该遵守 Rails 的约定。事件名称的格式是 event.library。如果应用发送推文,应该把事件命名为 tweet.twitter