监控
驱动程序允许在发生某些事件时通知应用程序。 这些事件分为以下几类:
命令监控
拓扑结构生命周期
服务器生命周期
服务器心跳
连接池和连接
拓扑结构和服务器事件是服务器发现和监控 (SDAM) 的一部分。
命令监控
发送到服务器的所有用户启动的命令都会发布事件,可以订阅这些事件以获取细粒度信息。 监控 API 为每个命令发布一个有保证的启动事件,然后发布一个成功或失败的事件。 订阅者必须实现 3 个方法: started
、 succeeded
和failed
,每个方法都有一个事件参数。 以下是基于驱动程序内部使用的日志订阅服务器的示例日志订阅服务器:
class CommandLogSubscriber include Mongo::Loggable def started(event) # The default inspection of a command which is a BSON document gets # truncated in the middle. To get the full rendering of the command, the # ``to_json`` method can be called on the document. log_debug("#{prefix(event)} | STARTED | #{format_command(event.command.to_json)}") end def succeeded(event) log_debug("#{prefix(event)} | SUCCEEDED | #{event.duration}s") end def failed(event) log_debug("#{prefix(event)} | FAILED | #{event.message} | #{event.duration}s") end private def logger Mongo::Logger.logger end def format_command(args) begin args.inspect rescue Exception '<Unable to inspect arguments>' end end def format_message(message) format("COMMAND | %s".freeze, message) end def prefix(event) "#{event.address.to_s} | #{event.database_name}.#{event.command_name}" end end
要注册自定义订阅者,您可以为所有客户端全局注册,也可以逐个客户端注册:
subscriber = CommandLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, subscriber) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) client.subscribe( Mongo::Monitoring::COMMAND, subscriber )
示例输出:
D, [2018-09-23T13:47:31.258020 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | STARTED | {"hello"=>1, "$readPreference"=>{"mode"=>"primary"}, "lsid"=>{"id"=><BSON::Binary:0x47111693353080 type=uuid data=0x730341e880dc40a2...>}} D, [2018-09-23T13:47:31.259145 #4692] DEBUG -- : COMMAND | 127.0.0.1:27027 | test.hello | SUCCEEDED | 0.000791175s
服务器发现和监控
Ruby驱动程序实施MongoDB Server 发现和监控 (SDAM) 规范 。并使以下事件可供应用程序使用:
拓扑结构开放
服务器开放
服务器描述已更改
拓扑结构已更改
服务器已关闭
拓扑结构闭合
心跳事件(在下面的单独部分中介绍)
对于心跳事件以外的所有事件,将在每个事件订阅者上调用succeeded
方法,并将事件作为唯一参数。 事件的可用数据各不相同,因此要记录事件,每种事件类型都需要一个单独的类。 简单的 SDAM 日志订阅服务器可能如下所示:
class SDAMLogSubscriber include Mongo::Loggable def succeeded(event) log_debug(format_event(event)) end private def logger Mongo::Logger.logger end def format_message(message) format("SDAM | %s".freeze, message) end end class TopologyOpeningLogSubscriber < SDAMLogSubscriber private def format_event(event) "Topology type '#{event.topology.display_name}' initializing." end end class ServerOpeningLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server #{event.address} initializing." end end class ServerDescriptionChangedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server description for #{event.address} changed from " + "'#{event.previous_description.server_type}' to '#{event.new_description.server_type}'." end end class TopologyChangedLogSubscriber < SDAMLogSubscriber private def format_event(event) if event.previous_topology != event.new_topology "Topology type '#{event.previous_topology.display_name}' changed to " + "type '#{event.new_topology.display_name}'." else "There was a change in the members of the '#{event.new_topology.display_name}' " + "topology." end end end class ServerClosedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Server #{event.address} connection closed." end end class TopologyClosedLogSubscriber < SDAMLogSubscriber private def format_event(event) "Topology type '#{event.topology.display_name}' closed." end end
要订阅全局 SDAM 事件,请执行以下操作:
topology_opening_subscriber = TopologyOpeningLogSubscriber.new server_opening_subscriber = ServerOpeningLogSubscriber.new server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new topology_changed_subscriber = TopologyChangedLogSubscriber.new server_closed_subscriber = ServerClosedLogSubscriber.new topology_closed_subscriber = TopologyClosedLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, topology_opening_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_OPENING, server_opening_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, server_description_changed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, topology_changed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_CLOSED, server_closed_subscriber) Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, topology_closed_subscriber)
为单个客户端订阅 SDAM 事件稍微复杂一些,因为事件可能会在客户端构建期间发布:
topology_opening_subscriber = TopologyOpeningLogSubscriber.new server_opening_subscriber = ServerOpeningLogSubscriber.new server_description_changed_subscriber = ServerDescriptionChangedLogSubscriber.new topology_changed_subscriber = TopologyChangedLogSubscriber.new server_closed_subscriber = ServerClosedLogSubscriber.new topology_closed_subscriber = TopologyClosedLogSubscriber.new sdam_proc = Proc.new do |client| client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, topology_opening_subscriber) client.subscribe(Mongo::Monitoring::SERVER_OPENING, server_opening_subscriber) client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, server_description_changed_subscriber) client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, topology_changed_subscriber) client.subscribe(Mongo::Monitoring::SERVER_CLOSED, server_closed_subscriber) client.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, topology_closed_subscriber) end client = Mongo::Client.new(['127.0.0.1:27017'], database: 'test', sdam_proc: sdam_proc)
示例输出:
D, [2018-10-09T13:58:03.489461 #22079] DEBUG -- : SDAM | Topology type 'Unknown' initializing. D, [2018-10-09T13:58:03.489699 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 initializing. D, [2018-10-09T13:58:03.491384 #22079] DEBUG -- : SDAM | Server description for 127.0.0.1:27100 changed from 'unknown' to 'unknown'. D, [2018-10-09T13:58:03.491642 #22079] DEBUG -- : SDAM | Server localhost:27100 initializing. D, [2018-10-09T13:58:03.493199 #22079] DEBUG -- : SDAM | Server description for localhost:27100 changed from 'unknown' to 'primary'. D, [2018-10-09T13:58:03.493473 #22079] DEBUG -- : SDAM | Server localhost:27101 initializing. D, [2018-10-09T13:58:03.494874 #22079] DEBUG -- : SDAM | Server description for localhost:27101 changed from 'unknown' to 'secondary'. D, [2018-10-09T13:58:03.495139 #22079] DEBUG -- : SDAM | Server localhost:27102 initializing. D, [2018-10-09T13:58:03.496504 #22079] DEBUG -- : SDAM | Server description for localhost:27102 changed from 'unknown' to 'secondary'. D, [2018-10-09T13:58:03.496777 #22079] DEBUG -- : SDAM | Topology type 'Unknown' changed to type 'ReplicaSetNoPrimary'. D, [2018-10-09T13:58:03.497306 #22079] DEBUG -- : SDAM | Server 127.0.0.1:27100 connection closed. D, [2018-10-09T13:58:03.497606 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetNoPrimary' changed to type 'ReplicaSetWithPrimary'. # client.close D, [2018-10-09T13:58:05.342057 #22079] DEBUG -- : SDAM | Server localhost:27100 connection closed. D, [2018-10-09T13:58:05.342299 #22079] DEBUG -- : SDAM | Server localhost:27101 connection closed. D, [2018-10-09T13:58:05.342565 #22079] DEBUG -- : SDAM | Server localhost:27102 connection closed. D, [2018-10-09T13:58:05.342693 #22079] DEBUG -- : SDAM | Topology type 'ReplicaSetWithPrimary' closed.
注意
:sdam_proc
客户端选项仅适用于在构造过程中给出客户端。 当通过Client#with
调用更改某些客户端选项时,驱动程序可能会使用一组默认的事件订阅者创建一个新集群。 如果发生这种情况,则不会调用所提供的:sdam_proc
,并且应用程序可能会错过事件。
服务器心跳
通过订阅 SERVER_HEARTBEAT 主题,可以向应用程序通知每个服务器心跳。 服务器心跳监听器必须实现三个方法: started
、 succeeded
和failed
。 每次心跳都会调用侦听器上的started
方法,然后根据心跳的结果调用succeeded
或failed
方法。
所有心跳事件都包含心跳发送到的服务器的地址。 成功和失败的事件包含 hello 或旧版 hello 命令的往返时间。失败事件还包含在执行 hello 或旧版 hello 命令期间引发的实例。请查看 ServerHeartbeatStarted、ServerHeartbeatSucceeded 和 ServerHeartbeatFailed 的 API 文档,了解事件属性的详细信息。
以下是记录心跳事件订阅者的示例:
class HeartbeatLogSubscriber include Mongo::Loggable def started(event) log_debug("#{event.address} | STARTED") end def succeeded(event) log_debug("#{event.address} | SUCCEEDED | #{event.duration}s") end def failed(event) log_debug("#{event.address} | FAILED | #{event.error.class}: #{event.error.message} | #{event.duration}s") end private def logger Mongo::Logger.logger end def format_message(message) format("HEARTBEAT | %s".freeze, message) end end
与命令事件类似,应用程序可以全局订阅心跳事件,也可以订阅特定客户端的心跳事件:
subscriber = HeartbeatLogSubscriber.new Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::SERVER_HEARTBEAT, subscriber) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) client.subscribe( Mongo::Monitoring::SERVER_HEARTBEAT, subscriber )
示例输出:
D, [2018-09-23T13:44:10.707018 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | STARTED D, [2018-09-23T13:44:10.707778 #1739] DEBUG -- : HEARTBEAT | 127.0.0.1:27027 | SUCCEEDED | 0.000772381s
心跳事件间隔
当连接到 MongoDB 4.2 及更早版本的服务器时,Ruby 驱动程序默认每:heartbeat_frequency
(Ruby 客户端选项)秒发出一次心跳,并且心跳不重叠(保证心跳的成功事件在心跳的启动事件之前发布)。已发布下一个心跳)。 当连接到 MongoDB 4.4 及更高版本的服务器时,该驱动程序使用多个监控线程和更复杂的心跳协议,旨在更快地检测服务器状态的变化;因此,心跳事件间隔可能会更加不规则,并且心跳事件可能会重叠。 具体来说,等待的心跳可以在非等待的心跳正在进行时开始或结束,反之亦然。 使用ServerHeartbeatStarted#awaited?
、 ServerHeartbeatSucceeded#awaited?
和ServerHeartbeatFailed#awaited?
方法区分未等待和等待的心跳。
当客户端尝试执行操作但没有合适的服务器时,会更频繁地扫描部署 — 每台服务器最多每 500 毫秒轮询一次。 该应用程序还可以请求对特定服务器进行手动扫描;驱动程序在扫描之间强制执行 500 毫秒的最小间隔。
连接池和连接监控
每个客户端都为它知道的部署中的每个服务器维护一个连接池,并发布连接池和单个连接的事件。 要订阅这些事件,请定义一个订阅者类,该类实施pubished
方法,该方法为正在发布的事件采用单个参数。 请注意,驱动程序的未来版本可能会引入通过此机制发布的其他事件。
以下事件当前由驱动程序按照 CMAP 规范 实施 :
PoolCreated
PoolCleared
PoolClosed
connectionCreated
connectionReady
connectionClosed
connectionCheckOutStarted
connectionCheckOutFailed
ConnectionCheckOutSucceeded
connectionCheckedIn
驱动程序提供了一个日志订阅器,可用于记录所有连接池和与连接相关的事件。 该订阅者默认未启用,因为它将为应用程序执行的每个操作创建日志条目。 要全局或按客户端启用此订户:
Mongo::Monitoring::Global.subscribe( Mongo::Monitoring::CONNECTION_POOL, Mongo::Monitoring::CmapLogSubscriber.new) client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test' ) subscriber = Mongo::Monitoring::CmapLogSubscriber.new client.subscribe( Mongo::Monitoring::CONNECTION_POOL, subscriber )
示例输出:
D, [2019-05-06T17:23:21.595412 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCreated address=127.0.0.1:27741 options={...}> D, [2019-05-06T17:23:21.595584 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCleared address=127.0.0.1:27741> D, [2019-05-06T17:23:21.603549 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCreated address=localhost:27741 options={...}> D, [2019-05-06T17:23:21.603616 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckOutStarted address=localhost:27741> D, [2019-05-06T17:23:21.603684 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCreated address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.604079 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckedOut address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605759 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionReady address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605784 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionCheckedIn address=localhost:27741 connection_id=1> D, [2019-05-06T17:23:21.605817 #8576] DEBUG -- : MONGODB | EVENT: #<PoolCleared address=localhost:27741> D, [2019-05-06T17:23:21.605852 #8576] DEBUG -- : MONGODB | EVENT: #<ConnectionClosed address=localhost:27741 connection_id=1 reason=stale>
禁用监控
要关闭监控,请将客户端监控选项设置为false
:
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'test', :monitoring => false )
已排除和编辑的事件
Ruby 驱动程序不会通过命令监控机制发布(有时也会编辑)某些事件:
如果该命令属于日志校订命令的特定子集,或包含trigger有效负载日志校订的密钥,则出于安全原因,将提供空有效负载。可以通过将
MONGO_RUBY_DRIVER_UNREDACT_EVENTS
环境变量设置为1
、true
或yes
来访问完整的有效负载。 以下命令已编辑:authenticate
saslStart
saslContinue
getnonce
createUser
updateUser
copydbgetnonce
copydbsaslstart
copydb
如果该命令是握手命令(在非监控连接上为
ismaster
或hello
),则根本不会发布任何事件。通过监控连接发送的命令(例如 ismaster 和 hello)不会发布命令监控事件。 相反,每次检查服务器时都会发布服务器心跳事件。 服务器心跳事件不包括命令或回复有效负载。
如果该命令是握手命令,并且
speculativeAuthenticate
选项为true
,则该命令将被编辑,并提供空有效负载。