Blog Post

Event Log File Paths

,

How does one consistently find the correct path to the Extended Event Log file (XEL file)?

This is a topic that I ventured into some time ago. The previous article can be read here. In that article I covered some of the various trouble spots with capturing the file path for various XE log files. One of the main problems being that there is frequently an inconsistency in where XE logs may actually be stored.

Using what was shown in that previous article, I have some improvements and minor tweaks to fill some gaps I hadn’t completed in the previous script.

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

First, lets just dive straight into the new script.

DECLARE @ErrorPath VARCHAR(256) = CONVERT(VARCHAR(256), SERVERPROPERTY('ErrorLogFileName'));
DECLARE
@SessionName  VARCHAR(128) = NULL --'AlwaysOn_health'
  , @csessionname VARCHAR(128);
CREATE TABLE #SessionPaths
(
SessionName VARCHAR(128)
  , XEFilePathVARCHAR(256)
  , SelectPhase VARCHAR(24)
);
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;
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
INSERT INTO #SessionPaths
(
SessionName
  , XEFilePath
  , SelectPhase
)
SELECT
   ses.name AS SessionName
 , LEFT(CONVERT(VARCHAR(256), sesf.value), LEN(CONVERT(VARCHAR(256), sesf.value))
   - CHARINDEX(
  ''
, REVERSE(CONVERT(
 VARCHAR(256)
   , sesf.value
 )
 )
  ) + 1) AS XEFilePath
 , 'Phase1'
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
INSERT INTO #SessionPaths
(
SessionName
  , XEFilePath
  , SelectPhase
)
SELECTDISTINCT
cte1.SessionName
  , CASE
WHEN RIGHT(LEFT(cte1.targetvalue, LEN(cte1.targetvalue)
  - CHARINDEX('', REVERSE(cte1.targetvalue)) + 1), 1) = ''
 THEN LEFT(cte1.targetvalue, LEN(cte1.targetvalue)
 - CHARINDEX('', REVERSE(cte1.targetvalue)) + 1)
ELSE LEFT(@ErrorPath, LEN(@ErrorPath) - CHARINDEX('', REVERSE(@ErrorPath)) + 1)
END AS XEFilePath
  , CASE
WHEN RIGHT(LEFT(t2.file_name, LEN(t2.file_name)
  - CHARINDEX('', REVERSE(t2.file_name)) + 1), 1) = ''
 THEN 'Phase2'
ELSE 'FailSafe'
END
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
OUTER APPLY sys.fn_xe_file_target_read_file(cte1.targetvalue, NULL, NULL, NULL) t2;
END;
END;
FETCH NEXT FROM xessions
INTO
@csessionname;
END;
CLOSE xessions;
DEALLOCATE xessions;
SELECT
 *
FROM #SessionPaths sp;
DROP TABLE #SessionPaths;

One of the things I wanted to accomplish with this update was to find the correct path for all of the sessions on the server. As mentioned in the previous article, sometimes there are complications with that. Due to the way log files can be specified for an XE session, behaviors can be a bit funky sometimes when trying to parse the correct paths. Due to those problems, I couldn’t quite short-cut the logic in the previous script and had to do the less desirable thing and create a cursor.

In addition to the cursor, I threw in a fix for when a full path is not declared for the session (at the time of creation) and the session was subsequently never started. In these odd cases, the script had been returning an empty result set and thus was not working properly. Now, it is fixed and here is an example of the output.

The third column in this result set is purely for informational purposes so I could determine at which point the file path was being derived. For the 30+ sessions running on my test instance, most paths are resolved via the first select. In the image, that is denoted by the label “Phase1” and circled in red. The system_health session happened to be running, but did not have a full path declared so it fell into the “Phase2” resolution group and is circled in blue. The last group includes those cases where a path could not be resolved for any number of reasons so they fall to the “FailSafe” grouping and an example is circled in green in the image.

Why

Truth be told, there is a method to short cut this script and get the results faster but I felt it would be less accurate. I could obviously just default to the “FailSafe” group automatically if a full path is not defined in the session creation. Would that be accurate though? Most of the time it would be accurate, but then there are the edge cases where occasionally we forget that something has changed. One such case of this is if after the session is created, you decide the SQL Server log files needs to be moved from the default path (this is where the XEL files default to if no path is defined)?

I have run across multiple scenarios where the logs were required (both technical as well as political) to be moved from the default location. Ideally, this move occurs prior to server startup. When the log file path is changed, the logs are not moved automatically to the new location. This, for me, is a case where it is best to be thorough rather than snake bit. I also like to document these things so I can compare them later if necessary.

Alternatively, here is the just good enough to pass muster version of that script.

/* or the "good enough" version */DECLARE @ErrorPath VARCHAR(256) = CONVERT(VARCHAR(256), SERVERPROPERTY('ErrorLogFileName'));
SELECTDISTINCT
cte1.SessionName
  , CASE
WHEN RIGHT(LEFT(cte1.targetvalue, LEN(cte1.targetvalue) - CHARINDEX('', REVERSE(cte1.targetvalue)) + 1), 1) = ''
 THEN LEFT(cte1.targetvalue, LEN(cte1.targetvalue) - CHARINDEX('', REVERSE(cte1.targetvalue)) + 1)
ELSE LEFT(@ErrorPath, LEN(@ErrorPath) - CHARINDEX('', REVERSE(@ErrorPath)) + 1)
END AS XEFilePath
  , CASE
WHEN RIGHT(LEFT(cte1.targetvalue, LEN(cte1.targetvalue) - CHARINDEX('', REVERSE(cte1.targetvalue)) + 1), 1) = ''
 THEN 'Quick'
ELSE 'FailSafe'
END
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'
) cte1;

 

Conclusion

In the article today, I have shown some of the internals to retrieving file paths for Extended Event Sessions. I dove into metadata to pull out the path for the session and discussed some concerns for some of these methods. In the end, you have a few viable options to help retrieve the file path in a more consistent fashion.

To read more about Extended Events, I recommend this series of articles.

Original post (opens in new tab)
View comments in original post (opens in new tab)

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating