Retomando el sqlserver.read_only_route_completeevento extendido mencionado por Kin y Remus, es un buen evento de depuración , pero no lleva mucha información, solo route_port(por ejemplo, 1433) y route_server_name(por ejemplo, sqlserver-0.contoso.com) por defecto . Esto también ayudaría a determinar cuándo una conexión de intención de solo lectura fue exitosa. Hay un read_only_route_failevento pero no pude activarlo, tal vez si hubiera un problema con la URL de enrutamiento, no parece que se active cuando la instancia secundaria no estaba disponible / apagado por lo que pude ver.
Sin embargo, he tenido cierto éxito al unir eso con el sqlserver.loginevento y el seguimiento de causalidad habilitado, junto con algunas acciones (como sqlserver.username) para que sea útil.
Pasos para reproducir
Cree una sesión de eventos extendidos para rastrear eventos relevantes, además de acciones útiles y rastrear causalidad:
CREATE EVENT SESSION [xe_watchLoginIntent] ON SERVER
ADD EVENT sqlserver.login
( ACTION ( sqlserver.username ) ),
ADD EVENT sqlserver.read_only_route_complete
( ACTION (
sqlserver.client_app_name,
sqlserver.client_connection_id,
sqlserver.client_hostname,
sqlserver.client_pid,
sqlserver.context_info,
sqlserver.database_id,
sqlserver.database_name,
sqlserver.username
) ),
ADD EVENT sqlserver.read_only_route_fail
( ACTION (
sqlserver.client_app_name,
sqlserver.client_connection_id,
sqlserver.client_hostname,
sqlserver.client_pid,
sqlserver.context_info,
sqlserver.database_id,
sqlserver.database_name,
sqlserver.username
) )
ADD TARGET package0.event_file( SET filename = N'xe_watchLoginIntent' )
WITH (
MAX_MEMORY = 4096 KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0 KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = ON, --<-- relate events
STARTUP_STATE = ON --<-- ensure sessions starts after failover
)
Ejecute la sesión XE (considere el muestreo ya que este es un evento de depuración) y recopile algunos inicios de sesión:

Tenga en cuenta que sqlserver-0 es mi secundaria legible y sqlserver-1 es la primaria. Aquí estoy usando el -Kinterruptor de sqlcmdpara simular inicios de sesión de intención de aplicación de solo lectura y algunos inicios de sesión SQL. El evento de solo lectura se dispara en un inicio de sesión exitoso de solo lectura.
Al pausar o detener la sesión, puedo consultarla e intentar vincular los dos eventos, por ejemplo:
DROP TABLE IF EXISTS #tmp
SELECT IDENTITY( INT, 1, 1 ) rowId, file_offset, CAST( event_data AS XML ) AS event_data
INTO #tmp
FROM sys.fn_xe_file_target_read_file( 'xe_watchLoginIntent*.xel', NULL, NULL, NULL )
ALTER TABLE #tmp ADD PRIMARY KEY ( rowId );
CREATE PRIMARY XML INDEX _pxmlidx_tmp ON #tmp ( event_data );
-- Pair up the login and read_only_route_complete events via xxx
DROP TABLE IF EXISTS #users
SELECT
rowId,
event_data.value('(event/@timestamp)[1]', 'DATETIME2' ) AS [timestamp],
event_data.value('(event/action[@name="username"]/value/text())[1]', 'VARCHAR(100)' ) AS username,
event_data.value('(event/action[@name="attach_activity_id_xfer"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id_xfer,
event_data.value('(event/action[@name="attach_activity_id"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id
INTO #users
FROM #tmp l
WHERE l.event_data.exist('event[@name="login"]') = 1
AND l.event_data.exist('(event/action[@name="username"]/value/text())[. = "SqlUserShouldBeReadOnly"]') = 1
DROP TABLE IF EXISTS #readonly
SELECT *,
event_data.value('(event/@timestamp)[1]', 'DATETIME2' ) AS [timestamp],
event_data.value('(event/data[@name="route_port"]/value/text())[1]', 'INT' ) AS route_port,
event_data.value('(event/data[@name="route_server_name"]/value/text())[1]', 'VARCHAR(100)' ) AS route_server_name,
event_data.value('(event/action[@name="username"]/value/text())[1]', 'VARCHAR(100)' ) AS username,
event_data.value('(event/action[@name="client_app_name"]/value/text())[1]', 'VARCHAR(100)' ) AS client_app_name,
event_data.value('(event/action[@name="attach_activity_id_xfer"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id_xfer,
event_data.value('(event/action[@name="attach_activity_id"]/value/text())[1]', 'VARCHAR(100)' ) AS attach_activity_id
INTO #readonly
FROM #tmp
WHERE event_data.exist('event[@name="read_only_route_complete"]') = 1
SELECT *
FROM #users u
LEFT JOIN #readonly r ON u.attach_activity_id_xfer = r.attach_activity_id_xfer
SELECT u.username, COUNT(*) AS logins, COUNT( DISTINCT r.rowId ) AS records
FROM #users u
LEFT JOIN #readonly r ON u.attach_activity_id_xfer = r.attach_activity_id_xfer
GROUP BY u.username
La consulta debe mostrar los inicios de sesión con y sin intención de solo lectura de la aplicación:

read_only_route_completees un evento de depuración, así que utilícelo con moderación. Considere el muestreo, por ejemplo.
- Los dos eventos, junto con la causalidad de la pista, ofrecen el potencial para cumplir con sus requisitos: se necesitan más pruebas en este sencillo equipo
- Noté que si el nombre de la base de datos no se especificaba en la conexión, las cosas no parecían funcionar
Traté de conseguir que el pair_matchingobjetivo funcionara pero se me acabó el tiempo. Aquí hay un potencial de desarrollo, algo como:
ALTER EVENT SESSION [xe_watchLoginIntent] ON SERVER
ADD TARGET package0.pair_matching (
SET begin_event = N'sqlserver.login',
begin_matching_actions = N'sqlserver.username',
end_event = N'sqlserver.read_only_route_complete',
end_matching_actions = N'sqlserver.username'
)