219 lines
9.8 KiB
C#
219 lines
9.8 KiB
C#
|
/*============================================================================
|
|||
|
Summary: Contains class implementiong xEvent data logging for Azure Analysis Services
|
|||
|
Copyright (C) Microsoft Corporation.
|
|||
|
|
|||
|
|
|||
|
This source code is intended only as a supplement to Microsoft
|
|||
|
Development Tools and/or on-line documentation.
|
|||
|
|
|||
|
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|||
|
PARTICULAR PURPOSE.
|
|||
|
============================================================================*/
|
|||
|
using System;
|
|||
|
using System.Xml;
|
|||
|
using System.Threading;
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
using Microsoft.AnalysisServices;
|
|||
|
using Microsoft.AnalysisServices.AdomdClient;
|
|||
|
using Microsoft.SqlServer.XEvent.Linq; // Referenced from the GAC version of Microsoft.SqlServer.XEvent.Linq
|
|||
|
|
|||
|
namespace TracingSample
|
|||
|
{
|
|||
|
public class Worker
|
|||
|
{
|
|||
|
private string UserName;
|
|||
|
private string AsServer;
|
|||
|
private string AsDatabase;
|
|||
|
private string eventTmsl;
|
|||
|
private string logFile;
|
|||
|
|
|||
|
public Worker(string user, string server, string db, string events, string log)
|
|||
|
{
|
|||
|
UserName = user;
|
|||
|
AsServer = server;
|
|||
|
AsDatabase = db;
|
|||
|
eventTmsl = events;
|
|||
|
logFile = log;
|
|||
|
}
|
|||
|
|
|||
|
// This method will be called when the thread is started.
|
|||
|
public void DoWork()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (Server server = new Server())
|
|||
|
{
|
|||
|
//Connect and get main objects
|
|||
|
string serverConnectionString;
|
|||
|
|
|||
|
// Assume integratedAuth
|
|||
|
// otherwise serverConnectionString = $"Provider=MSOLAP;Data Source={AsServer};User ID={UserName};Password={Password};Impersonation Level=Impersonate;";
|
|||
|
serverConnectionString = $"Provider=MSOLAP;Data Source={AsServer};Integrated Security=SSPI";
|
|||
|
server.Connect(serverConnectionString);
|
|||
|
|
|||
|
Database database = server.Databases.FindByName(AsDatabase);
|
|||
|
|
|||
|
if (database == null)
|
|||
|
{
|
|||
|
throw new Microsoft.AnalysisServices.ConnectionException($"Could not connect to database {AsDatabase}.");
|
|||
|
}
|
|||
|
|
|||
|
//Register the events you want to trace
|
|||
|
string queryString = System.IO.File.ReadAllText(eventTmsl);
|
|||
|
server.Execute(queryString);
|
|||
|
|
|||
|
// Now you need to subscribe to the xEvent stream and execute a data reader
|
|||
|
// You need to have the same name as in the TMSL file!
|
|||
|
// NOTE calls to the reader will block until new values show up!
|
|||
|
string sessionId = "SampleXEvents";
|
|||
|
AdomdConnection conn = new AdomdConnection(serverConnectionString);
|
|||
|
conn.Open();
|
|||
|
|
|||
|
var cmd = conn.CreateCommand();
|
|||
|
|
|||
|
cmd.CommandType = System.Data.CommandType.Text;
|
|||
|
cmd.CommandText =
|
|||
|
"<Subscribe xmlns=\"http://schemas.microsoft.com/analysisservices/2003/engine\">" +
|
|||
|
"<Object xmlns=\"http://schemas.microsoft.com/analysisservices/2003/engine\">" +
|
|||
|
"<TraceID>" + sessionId + "</TraceID>" +
|
|||
|
"</Object>" +
|
|||
|
"</Subscribe>";
|
|||
|
|
|||
|
XmlReader inputReader = XmlReader.Create(cmd.ExecuteXmlReader(), new XmlReaderSettings() { Async = true });
|
|||
|
|
|||
|
//Connect to this with QueryableXEventData
|
|||
|
using (QueryableXEventData data =
|
|||
|
new QueryableXEventData(inputReader, EventStreamSourceOptions.EventStream, EventStreamCacheOptions.CacheToDisk))
|
|||
|
{
|
|||
|
|
|||
|
using (FileStream fs = new FileStream(logFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
|||
|
{
|
|||
|
//Write out the data in a long format for illustration
|
|||
|
// this could would be adpated for your specific needs
|
|||
|
foreach (PublishedEvent evt in data)
|
|||
|
{
|
|||
|
StringBuilder s = new StringBuilder();
|
|||
|
s.Append($"Event: {evt.Name}\t");
|
|||
|
s.Append(Environment.NewLine);
|
|||
|
s.Append($"Timestamp: {evt.Timestamp}\t");
|
|||
|
s.Append(Environment.NewLine);
|
|||
|
|
|||
|
foreach (PublishedEventField fld in evt.Fields)
|
|||
|
{
|
|||
|
s.Append($"Field: {fld.Name} = {fld.Value}\t");
|
|||
|
s.Append(Environment.NewLine);
|
|||
|
}
|
|||
|
|
|||
|
foreach (PublishedAction act in evt.Actions)
|
|||
|
{
|
|||
|
s.Append($"Action: {act.Name} = {act.Value}\t");
|
|||
|
s.Append(Environment.NewLine);
|
|||
|
}
|
|||
|
s.Append(Environment.NewLine);
|
|||
|
|
|||
|
//Write the data to a log file
|
|||
|
// the format and sink should be changed for your proposes
|
|||
|
byte[] bytes = Encoding.ASCII.GetBytes(s.ToString().ToCharArray());
|
|||
|
fs.Write(bytes, 0, s.Length);
|
|||
|
|
|||
|
if (_shouldStop == true)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
// Writing a . to show progress
|
|||
|
Console.Write(".");
|
|||
|
|
|||
|
//Uncomment this to output to the Console
|
|||
|
//Console.WriteLine(s);
|
|||
|
|
|||
|
}
|
|||
|
//TODO stop the trace !
|
|||
|
fs.Close();
|
|||
|
}
|
|||
|
conn.Close();
|
|||
|
|
|||
|
//clean up the trace on exit -- or you can keep it running
|
|||
|
//var stopCommand = conn.CreateCommand();
|
|||
|
|
|||
|
//stopCommand.CommandType = System.Data.CommandType.Text;
|
|||
|
var stopCommand =
|
|||
|
"<Execute xmlns = \"urn:schemas-microsoft-com:xml-analysis\">" +
|
|||
|
"<Command>" +
|
|||
|
"<Batch …>" +
|
|||
|
"<Delete …>" +
|
|||
|
// You need to have the same name as in the TMSL file!
|
|||
|
"<Object><TraceID>"+sessionId+"</TraceID></Object>" +
|
|||
|
"</Delete>" +
|
|||
|
"<Batch …>" +
|
|||
|
"<Command>" +
|
|||
|
"<Properties></Properties>" +
|
|||
|
"</Execute>";
|
|||
|
|
|||
|
server.Execute(queryString);
|
|||
|
server.Disconnect();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception e)
|
|||
|
{
|
|||
|
//TODO: handle exceptions :-)
|
|||
|
Console.WriteLine(e.ToString());
|
|||
|
Console.WriteLine("There was an error. Verify the command-line parmaters. Press any key to exit.");
|
|||
|
}
|
|||
|
//Worker thread: terminating gracefully.
|
|||
|
|
|||
|
}
|
|||
|
public void RequestStop()
|
|||
|
{
|
|||
|
_shouldStop = true;
|
|||
|
}
|
|||
|
// Volatile is used as hint to the compiler that this data
|
|||
|
// member will be accessed by multiple threads.
|
|||
|
private volatile bool _shouldStop;
|
|||
|
}
|
|||
|
|
|||
|
class Program
|
|||
|
{
|
|||
|
static void Main(string[] args)
|
|||
|
{
|
|||
|
string UserName = "user@contoso.com";
|
|||
|
string AsServer = "asazure://region.asazure.windows.net/myinstance";
|
|||
|
string AsDatabase = "SalesBI";
|
|||
|
string eventTmsl = @"C:\AsXEventSample\eventTmsl.xmla"; // location of the xEvents TMSL file you want to collect, you can create this with SSMS script out
|
|||
|
string logFile = @"C:\AsXEventSample\aslog.txt"; //location of the outputfile
|
|||
|
|
|||
|
// Using a thread here as data comes in asychronously
|
|||
|
// Create the thread object. This does not start the thread.
|
|||
|
Worker workerObject = new Worker(UserName, AsServer, AsDatabase, eventTmsl, logFile);
|
|||
|
Thread workerThread = new Thread(workerObject.DoWork);
|
|||
|
|
|||
|
// Start the worker thread.
|
|||
|
workerThread.Start();
|
|||
|
|
|||
|
// For monitoring, this would be upgraded to a windows service
|
|||
|
Console.WriteLine("Listening for trace events which are sent when there is trace activity.");
|
|||
|
Console.WriteLine("Type the letter q and enter to quit. The process will exit after the next trace event is received.");
|
|||
|
|
|||
|
bool cont = true;
|
|||
|
while (cont)
|
|||
|
{
|
|||
|
Thread.Sleep(1);
|
|||
|
if (ConsoleKey.Q == Console.ReadKey().Key)
|
|||
|
{
|
|||
|
workerObject.RequestStop();
|
|||
|
Console.WriteLine("\nStopping reader on next trace event received...");
|
|||
|
cont = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//wait for the worker to exit
|
|||
|
workerThread.Join();
|
|||
|
Console.WriteLine($"File is stored at: {logFile}");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|