There are one way to setup a trace programmatically but 2 ways to acquire and consume the trace data. To setup a trace, you need to to call trace supporting procedures to define events and columns. If you specify the location of the trace target, SQL Server will write trace data to files. By this approach, the SQL Server instance which reads the data files need to have file access permission to them. The another approach is that you don’t specify the target location. By this approach, the trace data will be “streamed” to the client directly.
First, let’s run the query posted previously to find out the events and columns we want to keep track of. For demo purpose, I chose SQL:BatchStarting event with columns DatabaseID, SPID, and Text.
select a.name Category, b.trace_event_id EventID, b.name EventName, d.trace_column_id FieldID, d.name FieldName, d.type_name Type, d.max_size MaxSize from sys.trace_categories a inner join sys.trace_events b on a.category_id = b.category_id inner join sys.trace_event_bindings c on c.trace_event_id = b.trace_event_id inner join sys.trace_columns d on d.trace_column_id = c.trace_column_id where b.name = 'SQL:BatchStarting' and d.trace_column_id in (1,3,12) order by a.name, b.name, d.trace_column_id /* Category EventID EventName FieldID FieldName Type MaxSize ----------------- -------------------------- ---------------------------- TSQL 13 SQL:BatchStarting 1 TextData text 1073741824 TSQL 13 SQL:BatchStarting 3 DatabaseID int 4 TSQL 13 SQL:BatchStarting 12 SPID int 4 (3 row(s) affected) */
Next, you can call sp_trace_create
to create a trace and use sp_trace_setevent
to setup the events and columns as showing below:
set nocount on if OBJECT_ID('tempdb..#1') is not null drop table #1 create table #1(TraceID int) exec xp_cmdshell N'del c:\temp\abc.trc', no_output go declare @TraceID int exec sp_trace_create @traceid = @TraceID output, @options = 2, @tracefile = N'c:\temp\abc' insert into #1 values(@TraceID) -- save the traceid that will be used later exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 1, @on = 1 exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 3, @on = 1 exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 12, @on = 1 exec sp_trace_setstatus @traceid = @TraceID, @status = 1 -- start the trace go exec ('--print') go declare @TraceID int select @TraceID = TraceID from #1 exec sp_trace_setstatus @traceid = @TraceID, @status = 0 -- stop the trace exec sp_trace_setstatus @traceid = @TraceID, @status = 2 -- close the trace go select TextData, DatabaseID, SPID from fn_trace_gettable('c:\temp\abc.trc', default)
TextData DatabaseID SPID ----------------------------------------------------------------------------------------- ----------- NULL NULL NULL NULL NULL NULL exec ('--print') 34 52 declare @TraceID int select @TraceID = TraceID from #1 exec sp_trace_setstatus @traceid = @TraceID, @status = 0 -- stop the trace exec sp_trace_setstatus @traceid = @TraceID, @status = 2 -- close the trace 34 52 NULL NULL NULL
Optionally, you can setup the filter by using sp_trace_setfilter. It will not be demonstrated here. Whenever you need to load the trace data, you will have to call fn_trace_gettable to read the data from the file with sufficient permissions to access the trace files.
You may be very familiar with SQL Profiler. It’s implemented by utilizing the same set of procedures introduced previously. However, it shows the trace data directly on the screen from SQL Server rather than reading it from files. see the code below
if OBJECT_ID('tempdb..#1') is not null drop table #1 go create table #1(ColumnID int, Length int, Data varbinary(max)) go declare @TraceID int exec sp_trace_create @traceid = @TraceID output, @options = 1 select @TraceID exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 1, @on = 1 exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 3, @on = 1 exec sp_trace_setevent @traceid = @TraceID, @eventid = 13, @columnid = 12, @on = 1 exec sp_trace_setstatus @traceid = @TraceID, @status = 1 -- start the trace insert into #1 exec sp_trace_getdata @TraceID, 3 exec sp_trace_setstatus @traceid = @TraceID, @status = 0 -- stop the trace exec sp_trace_setstatus @traceid = @TraceID, @status = 2 -- close the trace select e.*, a.name Category, b.trace_event_id EventID, b.name EventName, d.trace_column_id FieldID, d.name FieldName, d.type_name Type, d.max_size MaxSize from sys.trace_categories a inner join sys.trace_events b on a.category_id = b.category_id and b.name = 'SQL:BatchStarting' inner join sys.trace_event_bindings c on c.trace_event_id = b.trace_event_id inner join sys.trace_columns d on d.trace_column_id = c.trace_column_id right join #1 e on e.ColumnID = d.trace_column_id -- this script will wait until receive a the first trace record
Line 7, you will need to define the trace little bit differently
Line 14, you need to use sp_trace_getdata to receive the trace data generated by SQL Server directly. The first paramenter of this procedure is trace id. The second parameter is number of records you want to receive at a time. If there are not as many records can be received, this procedure will block current caller session.
Line 18, links the captured data in the temp table to metadata. you can use this query to interpret the binary you catched. You will see there are some trace columns does not have meta data. Look like they are all related to composing a trace file. I will do some investigation and post it here.
What if you call sp_trace_getdata in Dot Net by SqlCommand object and open a data reader to receive the trace data, well, you are writing SQL Profiler.