Migrating Extended Event Sessions from one server to another should be a simple task. So simple, one would think there was no need to give it a second thought, right?
Well, I have previously written about this topic, you are welcome to read it here. The article discusses quite a bit about scripting out your XE Sessions. One thing lacking in that article is the ability to script out every session on a server.
If you are still not using Extended Events, I recommend checking out this library of articles that will cover just about all of the basics concerning Extended Events.
New and Improved
What about scripting out all of the sessions in SSMS? Surely there is an easy way to do that, right? Well, you might think that. Let me step through the problem that I have seen in SSMS (and unfortunately it is not consistent).
First, from Object Explorer Details (or F5), let’s try to script a single session.
When scripting a single session from the “Object Explorer Details”, I have several sub-menus that allow me to script the session to a “New Query Editor Window”. Now, let’s see what happens when trying to script multiple sessions.
With several sessions selected, I try yet again to script the sessions and I get an unwanted result. Notice that the “Script Session as” option is grayed out and unusable. However, if I try it again (several times or maybe just once, your mileage may vary and it seems to not be relevant to version of SSMS), I may see something like this.
Tada! Luck was with me and it finally worked that time. So, what should I do to be able to consistently script all of sessions? Well, that comes with an enhancement to the script I presented in the prior article here.
Lets just dive straight into the new script.
SET NOCOUNT ON;
DECLARE
@statement VARCHAR(MAX)
, @statement1 VARCHAR(MAX)
, @statement2 VARCHAR(MAX)
, @sessionname VARCHAR(128) --= 'AlwaysOn_health' -- 'system_health'
, @csessionname VARCHAR(128)
, @XeFilePath VARCHAR(256)
, @IsDefaultPath TINYINT
/* order of events scripted is different
default order of creation is different than if scripted from the gui
the gui scripts by alpha order of package then event
while original creation has order by event_id only
*/ , @eventorder VARCHAR(64) = 'original'; --'original' for order based on original script, --'ssms' for order based on ssms guy scripter
/* todo:
add option to exclude default/system sessions
add ability to do this for azure db
*/DECLARE xessions CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT
ses.name
FROM sys.server_event_sessions ses
WHERE ses.name = ISNULL(@sessionname, ses.name)
ORDER BY ses.name;
OPEN xessions;
FETCH NEXT FROM xessions
INTO
@csessionname;
DECLARE @ErrorLog VARCHAR(256) = CONVERT(VARCHAR(256), SERVERPROPERTY('ErrorLogFileName'));
DECLARE @ErrorPath VARCHAR(256) = LEFT(@ErrorLog, LEN(@ErrorLog) - CHARINDEX('', REVERSE(@ErrorLog)) + 1);
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS
(
SELECT
1
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_fields sesf
ON ses.event_session_id = sesf.event_session_id
WHERE sesf.name = 'filename'
AND ses.name = @csessionname
)
BEGIN
IF
(
SELECT
CHARINDEX('', CONVERT(VARCHAR(256), sesf.value))
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_fields sesf
ON ses.event_session_id = sesf.event_session_id
WHERE sesf.name = 'filename'
AND ses.name = @csessionname
) > 0
BEGIN
SELECT
@XeFilePath
= LEFT(CONVERT(VARCHAR(256), sesf.value), LEN(CONVERT(VARCHAR(256), sesf.value))
- CHARINDEX(
''
, REVERSE(CONVERT(
VARCHAR(256)
, sesf.value
)
)
) + 1)
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_fields sesf
ON ses.event_session_id = sesf.event_session_id
WHERE sesf.name = 'filename'
AND ses.name = @csessionname;
END;
ELSE
BEGIN
IF
(
SELECTTOP 1
LEFT(t2.file_name, LEN(t2.file_name) - CHARINDEX('', REVERSE(t2.file_name)) + 1) AS XEFilePath
FROM
(
SELECT
REPLACE(CONVERT(NVARCHAR(128), sesf.value), '.xel', '*.xel') AS targetvalue
, ses.nameAS SessionName
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_fields sesf
ON ses.event_session_id = sesf.event_session_id
WHERE sesf.name = 'filename'
AND ses.name = @csessionname
)cte1
CROSS APPLY sys.fn_xe_file_target_read_file(cte1.targetvalue, NULL, NULL, NULL) t2
) IS NOT NULL
BEGIN
SELECT
@XeFilePath
=
(
SELECTTOP 1
LEFT(t2.file_name, LEN(t2.file_name)
- CHARINDEX('', REVERSE(t2.file_name)) + 1) AS XEFilePath
FROM
(
SELECT
REPLACE(CONVERT(NVARCHAR(128), sesf.value), '.xel', '*.xel') AS targetvalue
, ses.nameAS SessionName
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_fields sesf
ON ses.event_session_id = sesf.event_session_id
WHERE sesf.name = 'filename'
AND ses.name = @csessionname
) cte1
CROSS APPLY sys.fn_xe_file_target_read_file(
cte1.targetvalue
, NULL
, NULL
, NULL
) t2
);
END;
ELSE
BEGIN
SELECT
@XeFilePath = @ErrorPath;
SELECT
@XeFilePath
, @ErrorPath;
END;
END;
END;
IF @ErrorPath = @XeFilePath
BEGIN
SET @IsDefaultPath = 1;
END;
ELSE
BEGIN
SET @IsDefaultPath = 0;
END;
SELECT
@statement
= 'USE master;
GO
-- Create the Event Session
IF EXISTS ( SELECT *
FROM sys.server_event_sessions
WHERE name = ' + ses.name + ' )
DROP EVENT SESSION ' + ses.name + '
ON SERVER;
GO
'
FROM sys.server_event_sessions ses
WHERE ses.name = @csessionname;
IF @IsDefaultPath = 1
BEGIN
SET @statement = @statement + '';
END;
ELSE
BEGIN
SET @statement = @statement + '
EXECUTE xp_create_subdir ''' + @XeFilePath + ''';
GO
';
END;
SELECT
@statement = @statement + '
CREATE EVENT SESSION [' + ses.name + '] ON SERVER
'
FROM sys.server_event_sessions ses
WHERE ses.name = @csessionname;
WITH presel AS
(SELECT
ses.name AS SessionName
, sese.event_id
, sese.package
, 'ADD EVENT ' + sese.package + '.' + sese.name + ' (' AS EventName
, CASE
WHEN ISNULL(sese.predicate, '') = ''
THEN ')'
ELSE 'WHERE ' + sese.predicate + ' )'
END AS SQLPredicate
, CASE
WHEN ISNULL(sesf.name, '') = ''
THEN ''
ELSE 'SET ' + sesf.name + ' = ( ' + CAST(sesf.value AS VARCHAR(10)) + ' )'
END AS SETOperation
FROM sys.server_event_sessions ses
INNER JOIN sys.server_event_session_events sese
ON ses.event_session_id = sese.event_session_id
LEFT OUTER JOIN sys.server_event_session_fields sesf
ON sese.event_session_id = sesf.event_session_id
AND sese.event_id = sesf.object_id
WHERE ses.name = @csessionname)
, actpresel AS
(SELECT
ses.name AS SessionName
, sesa.event_session_id
, sesa.event_id
, sesa.package + '.' + sesa.name AS ActPack
FROMsys.server_event_session_events sese
INNER JOIN sys.server_event_session_actions sesa
ON sese.event_session_id = sesa.event_session_id
AND sese.event_id = sesa.event_id
INNER JOIN sys.server_event_sessions ses
ON sesa.event_session_id = ses.event_session_id
WHEREses.name = @csessionname)
, pstuff AS
(SELECT
p.SessionName
, p.event_id
, 'ACTION (
' +STUFF((
SELECT
', ' + pin.ActPack + '
'
FROMactpresel pin
WHERE pin.SessionName = p.SessionName
AND pin.event_id = p.event_id
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
, 1
, 2
, ''
) + '
)'AS ActionPack
FROM actpresel p
GROUP BY p.SessionName
, p.event_id)
SELECT
@statement1 = @statement + STUFF((
SELECT
', ' + CAST(p.EventName + '
' + p.SETOperation + '
' + CASE
WHEN ISNULL(ps.ActionPack, '') = ''
THEN ''
ELSE ps.ActionPack
END + '
' + p.SQLPredicate + '
' AS VARCHAR(MAX)) AS EventStatement
FROM presel p
LEFT OUTER JOIN pstuff ps
ON p.SessionName = ps.SessionName
AND p.event_id = ps.event_id
ORDER BY p.event_id
--ORDER BY p.package, p.EventName
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
, 1
, 2
, ''
)
, @statement2 = @statement + STUFF((
SELECT
', ' + CAST(p.EventName + '
' + p.SETOperation + '
' + CASE
WHEN ISNULL(ps.ActionPack, '') = ''
THEN ''
ELSE ps.ActionPack
END + '
' + p.SQLPredicate + '
' AS VARCHAR(MAX)) AS EventStatement
FROM presel p
LEFT OUTER JOIN pstuff ps
ON p.SessionName = ps.SessionName
AND p.event_id = ps.event_id
ORDER BY p.package
, p.EventName
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
, 1
, 2
, ''
);
IF @eventorder = 'original'
BEGIN
SET @statement = @statement1;
END;
IF @eventorder = 'ssms'
BEGIN
SET @statement = @statement2;
END;
SELECT
@statement
= @statement
+ STUFF(
(
SELECT
', ' + '
ADD TARGET '+ sest.package + '.' + sest.name
+ CASE
WHEN ISNULL(ca.setop, '') = ''
THEN '
'
ELSE '
(SET ' +ca.setop + ' )
'
END
FROM sys.server_event_sessionsses
INNER JOIN sys.server_event_session_targets sest
ON ses.event_session_id = sest.event_session_id
CROSS APPLY
(
SELECT
STUFF(
(
SELECT
', ' + CAST(sesf.name AS VARCHAR(128)) + ' = '
+ CASE
WHEN ISNUMERIC(CAST(sesf.value AS VARCHAR(128))) = 1
THEN '( ' + CAST(sesf.value AS VARCHAR(128)) + ' )
'
ELSE 'N''' + CAST(sesf.value AS VARCHAR(128)) + '''
'
END AS setop
FROM sys.server_event_session_fieldssesf
INNER JOIN sys.server_event_sessionssesi
ON sesi.event_session_id = sesf.event_session_id
INNER JOIN sys.server_event_session_targets sesti
ON sesf.object_id = sesti.target_id
AND sesi.event_session_id = sesti.event_session_id
WHERE sest.target_id = sesti.target_id
AND sest.event_session_id = sesti.event_session_id
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
, 1
, 2
, ''
)
)AS ca(setop)
WHERE ses.name = @csessionname
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
, 1
, 2
, ''
);
SELECT
@statement
= @statement
+ CAST('WITH ( MAX_MEMORY = ' + CAST(ses.max_memory AS VARCHAR(20)) + ' KB
,EVENT_RETENTION_MODE = ' + ses.event_retention_mode_desc + '
, MAX_DISPATCH_LATENCY = ' + CAST(ses.max_dispatch_latency / 1000 AS VARCHAR(20))
+ ' SECONDS
, MAX_EVENT_SIZE = ' + CAST(ses.max_event_size AS VARCHAR(20))
+ ' KB
, MEMORY_PARTITION_MODE = ' + ses.memory_partition_mode_desc + '
, TRACK_CAUSALITY = ' + CASE ses.track_causality
WHEN 1
THEN 'ON'
ELSE 'OFF'
END + '
, STARTUP_STATE = ' + CASE ses.startup_state
WHEN 1
THEN 'ON'
ELSE 'OFF'
END + '
);'AS VARCHAR(1024))
FROM sys.server_event_sessions ses
WHERE ses.name = @csessionname;
PRINT @statement;
FETCH NEXT FROM xessions
INTO
@csessionname;
END;
CLOSE xessions;
DEALLOCATE xessions;This is a rather lengthy script, so I won’t explain the entire thing. That said, this script will produce the exact XE Session as it was written when you deployed it to the server. In addition, the script will ensure the destination directory for the event_file target is created as a part of the script.
I can definitely hear the gears of thought churning as you ponder about this whole scenario. Surely, you have all of your XE Sessions stored in source control so there is no need whatsoever for this little script. Then again, that would be in an ideal environment. Sadly, source control is seldom considered for XE Sessions. Thus, it is always good to have a backup plan.
Why
Sadly, I had the very need of migrating a ton of sessions from one server to another recently and the methods in SSMS just wouldn’t work. There was no source control in the environment. Building out this little script saved me tons of time in migrating all of the sessions for this server and also provided me with a good script to place in source control.
Conclusion
In the article today, I have provided an excellent tool for backing up all of your XE sessions on the server. This script will help create the necessary scripts for all of your XE Sessions (or even just a single session if you like) in order to migrate the sessions to a new server or place them in source control.
To read more about Extended Events, I recommend this series of articles.

