/*============================================================================ 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 = "" + "" + "" + sessionId + "" + "" + ""; 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 = "" + "" + "" + "" + // You need to have the same name as in the TMSL file! ""+sessionId+"" + "" + "" + "" + "" + ""; 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}"); } } }