Analysis-Services/AsTrace/ASTrace CS - 2014/ASTrace/Service1.cs
2017-01-11 18:27:38 -08:00

443 lines
19 KiB
C#

/*============================================================================
File: Service1.cs
Summary: Contains class implementiong ASTrace service
Part of ASTrace
Date: January 2007
------------------------------------------------------------------------------
This file is part of the Microsoft SQL Server Code Samples.
Copyright (C) Microsoft Corporation. All rights reserved.
This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation. See these other
materials for detailed information regarding Microsoft code samples.
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.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.IO;
using Microsoft.SqlServer.Management.Trace;
using Microsoft.SqlServer.Management.Common;
namespace ASTrace
{
public partial class Trace : ServiceBase
{
List<Thread> workers = new List<Thread>();
string localPath;
List<string> _ServernamesList;
int _NumInstance;
TextWriter writer;
List<TraceServer> traceServers = new List<TraceServer>();
public Trace()
{
//Have seen errors here, normally when configuring for first time
//Note, the eventlog will need to create a source the very first time it runs and that needs admin privs.
try
{
InitializeComponent();
// Read registry to find out where the service executable is installed
Microsoft.Win32.RegistryKey software, microsoft, astrace;
software = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE");
microsoft = software.OpenSubKey("Microsoft");
astrace = microsoft.OpenSubKey("ASTrace");
localPath = (string)astrace.GetValue("path");
writer = new StreamWriter(localPath + "\\ASTraceService.log", true);
WriteLog(DateTime.Now.ToString() + ": Service Started in '" + localPath + "'");
}
catch (Exception ex)
{
StringBuilder messageText = new StringBuilder();
messageText.Append("Failed to write to log file ").AppendLine();
messageText.Append("Error: " + ex.Message).AppendLine();
while (ex.InnerException != null)
{
messageText.Append("INNER EXCEPTION: ");
messageText.Append(ex.InnerException.Message).AppendLine();
messageText.Append(ex.InnerException.StackTrace).AppendLine();
ex = ex.InnerException;
}
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Error);
}
}
protected override void OnStart(string[] args)
{
if (Properties.Settings.Default.AppendDateToSQLTable && Properties.Settings.Default.PreserveHistory)
{
StringBuilder messageText = new StringBuilder();
messageText.Append(DateTime.Now.ToString() + ": Both AppendDateToSQLTable and PreserveHistory cannot be true!");
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Error);
WriteLog(messageText.ToString());
Stop();
return;
}
//Initial startup so get the server list
string sServerNames = Properties.Settings.Default.AnalysisServerName + "," + Properties.Settings.Default.AnalysisServerNames;
_ServernamesList = new List<string>(sServerNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
//Get the number of servers so we can maintain legacy behaviour
_NumInstance = _ServernamesList.Count;
foreach (string SSASserver in _ServernamesList)
{
//Spin up a thread to write the trace to SQL
Thread worker = new Thread(DoWork);
workers.Add(worker);
//Start the tracing and pass SSAS Server name so we can handle retries
worker.Start(SSASserver);
}
}
bool ConnectOlap(out TraceServer traceServer, string SSASserver)
{
OlapConnectionInfo ci = new OlapConnectionInfo();
traceServer = new TraceServer();
StringBuilder messageText = new StringBuilder();
try
{
ci.UseIntegratedSecurity = true;
ci.ServerName = SSASserver;
string tracetemplate = localPath + "\\" + Properties.Settings.Default.TraceDefinition;
traceServer.InitializeAsReader(ci, tracetemplate);
lock (traceServers)
{
traceServers.Add(traceServer);
}
messageText.Append(
DateTime.Now.ToString()
+ ": Created trace for Analysis Server : '"
+ SSASserver
+ "'");
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Information);
return true;
}
catch (Exception e)
{
messageText.Append(DateTime.Now.ToString() + ": Cannot start Analysis Server trace: ").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": Analysis Server name: '" + SSASserver + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": Trace definition : '" + localPath + "\\" + Properties.Settings.Default.TraceDefinition + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": Error: " + e.Message).AppendLine();
messageText.Append(DateTime.Now.ToString() + ": Stack Trace: " + e.StackTrace).AppendLine();
while (e.InnerException != null)
{
messageText.Append("INNER EXCEPTION: ");
messageText.Append(e.InnerException.Message).AppendLine();
messageText.Append(e.InnerException.StackTrace).AppendLine();
e = e.InnerException;
}
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Error);
return false;
}
}
bool ConnectSQL(ref TraceServer traceServer, out TraceTable tableWriter, string SSASserver)
{
SqlConnectionInfo connInfo = new SqlConnectionInfo(Properties.Settings.Default.SQLServer);
StringBuilder messageText = new StringBuilder();
string _AppendInst = "_" + SSASserver;
string _SQLTable;
string _AppendDate;
//Maintains legacy logic where by a server with single instance does not have any inst names appended.
if (_NumInstance == 1)
_AppendInst = "";
//Append data to end of SQL table. Useful as an alternative to preserver SQL but data only survives 1 restart a day and you need cleanup logic in SQL Server
if (Properties.Settings.Default.AppendDateToSQLTable)
_AppendDate = "_" + DateTime.Now.ToString("yyyyMMdd");
else
_AppendDate = "";
_SQLTable =
Properties.Settings.Default.TraceTableName
+ _AppendInst
+ _AppendDate;
if (Properties.Settings.Default.PreserveHistory)
PreserveSQLHistory(ref _SQLTable);
tableWriter = new TraceTable();
try
{
connInfo.DatabaseName = Properties.Settings.Default.SQLServerDatabase;
tableWriter.InitializeAsWriter(traceServer, connInfo, _SQLTable);
messageText.Append(DateTime.Now.ToString() + ": Created Analysis Server trace table: '" + _SQLTable + "' on SQL Server: '" + Properties.Settings.Default.SQLServer
+ "' in database: " + Properties.Settings.Default.SQLServerDatabase + "'");
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Information);
return true;
}
catch (Exception e)
{
messageText.Append(DateTime.Now.ToString() + ": Cannot create Analysis Server trace table: '" + SSASserver + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": SQL Server Name: '" + Properties.Settings.Default.SQLServer + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": SQL Server Database : '" + Properties.Settings.Default.SQLServerDatabase + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": SQL Server Table : '" + _SQLTable + "'").AppendLine();
messageText.Append(DateTime.Now.ToString() + ": Error: " + e.Message).AppendLine();
while (e.InnerException != null)
{
messageText.Append("INNER EXCEPTION: ");
messageText.Append(e.InnerException.Message).AppendLine();
messageText.Append(e.InnerException.StackTrace.ToString()).AppendLine();
e = e.InnerException;
}
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Error);
return false;
}
}
void PreserveSQLHistory(ref string _SQLTable)
{
string sHistorySuffix = "History";
if (_NumInstance > 1)
{
sHistorySuffix = "_History";
}
StringBuilder messageText = new StringBuilder();
try
{
SqlConnectionInfo connInfo = new SqlConnectionInfo(Properties.Settings.Default.SQLServer);
IDbConnection conn = connInfo.CreateConnectionObject();
conn.Open();
conn.ChangeDatabase(Properties.Settings.Default.SQLServerDatabase);
string sSQL = @"if object_id('[" + _SQLTable.Replace("'", "''") + @"]') is not null
select top 1 * from [" + _SQLTable.Replace("]", "]]") + "]";
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(sSQL, (System.Data.SqlClient.SqlConnection)conn);
cmd.CommandTimeout = 0;
System.Data.SqlClient.SqlDataReader datareader = cmd.ExecuteReader();
string sColumns = "";
for (int i = 0; i < datareader.FieldCount; i++)
{
if (!string.IsNullOrEmpty(sColumns)) sColumns += ", ";
sColumns += "[" + datareader.GetName(i) + "]";
}
datareader.Close();
if (sColumns != "")
{
sSQL = @"
if object_id('[" + _SQLTable.Replace("'", "''") + @"]') is not null
begin
if object_id('[" + _SQLTable.Replace("'", "''") + sHistorySuffix + @"]') is null
begin
select * into [" + _SQLTable.Replace("]", "]]") + sHistorySuffix + "] from [" + _SQLTable.Replace("]", "]]") + @"]
end
else
begin
SET IDENTITY_INSERT [" + _SQLTable.Replace("]", "]]") + sHistorySuffix + @"] ON
insert into [" + _SQLTable.Replace("]", "]]") + sHistorySuffix + "] (" + sColumns + @")
select * from [" + _SQLTable.Replace("]", "]]") + @"]
SET IDENTITY_INSERT [" + _SQLTable.Replace("]", "]]") + sHistorySuffix + @"] OFF
end
end
";
cmd.CommandText = sSQL;
int iRowsPreserved = cmd.ExecuteNonQuery();
messageText.Append(DateTime.Now.ToString() + ": Successfully preserved " + iRowsPreserved + " rows of history to table: " + _SQLTable + sHistorySuffix);
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Information);
}
conn.Close();
conn.Dispose();
}
catch (Exception ex)
{
messageText.Append(DateTime.Now.ToString() + ": Cannot preserve history of trace table. ").AppendLine();
messageText.Append("Error: " + ex.Message).AppendLine();
messageText.Append(ex.StackTrace).AppendLine();
while (ex.InnerException != null)
{
messageText.Append("INNER EXCEPTION: ");
messageText.Append(ex.InnerException.Message).AppendLine();
messageText.Append(ex.InnerException.StackTrace).AppendLine();
ex = ex.InnerException;
}
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Warning);
}
}
void DoWork(object SSAS)
{
string SSASserver = (string)SSAS;
TraceTable _SQLDestTableWriter = null;
TraceServer _SSASSourceTraceServer = null;
int _RetryCounter = 0;
bool bFirstLoop = true;
while (bFirstLoop || _RetryCounter < Properties.Settings.Default.RestartRetries)
{
bFirstLoop = false;
try
{
//Grab connection to SSAS
bool bSuccess = ConnectOlap(out _SSASSourceTraceServer, SSASserver);
if (bSuccess)
{
//Grab connection to SQL and connect it with the SSAS trace
bSuccess = ConnectSQL(ref _SSASSourceTraceServer, out _SQLDestTableWriter, SSASserver);
if (bSuccess)
{
_RetryCounter = 0;
while (_SQLDestTableWriter.Write())
{
if (_SQLDestTableWriter.IsClosed)
throw new Exception("SQL connection closed unexpectedly.");
if (_SSASSourceTraceServer.IsClosed)
throw new Exception("SSAS connection closed unexpectedly.");
}
}
}
}
catch (Exception ex)
{
StringBuilder messageText = new StringBuilder();
messageText.Append(DateTime.Now.ToString() + ": Error reading trace: " + ex.Message).AppendLine();
messageText.Append(ex.StackTrace).AppendLine();
while (ex.InnerException != null)
{
messageText.Append("INNER EXCEPTION: ");
messageText.Append(ex.InnerException.Message).AppendLine();
messageText.Append(ex.InnerException.StackTrace).AppendLine();
ex = ex.InnerException;
}
WriteLog(messageText.ToString());
EventLog.WriteEntry(this.ServiceName, messageText.ToString(), EventLogEntryType.Warning);
}
try
{
_SSASSourceTraceServer.Stop();
_SSASSourceTraceServer.Close();
}
catch { }
try
{
_SQLDestTableWriter.Close();
}
catch { }
_RetryCounter++;
if (_RetryCounter < Properties.Settings.Default.RestartRetries)
{
StringBuilder messageText2 = new StringBuilder();
messageText2.Append(DateTime.Now.ToString() + ": Exception caught tracing server: " + SSASserver + ", retry " + _RetryCounter + " of " + Properties.Settings.Default.RestartRetries
+ ". Pausing for " + Properties.Settings.Default.RestartDelayMinutes + " minute(s) then restarting automatically"
).AppendLine();
WriteLog(messageText2.ToString());
EventLog.WriteEntry(this.ServiceName, messageText2.ToString(), EventLogEntryType.Warning);
System.Threading.Thread.Sleep(new TimeSpan(0, Properties.Settings.Default.RestartDelayMinutes, 0));
}
else
{
WriteLog(DateTime.Now.ToString() + ": Exceeded the number of allowed retries for server: " + SSASserver);
}
}
//if this one trace exceeded the number of retries so stop the service and stop all traces
Stop();
}
protected override void OnStop()
{
try
{
WriteLog(DateTime.Now.ToString() + ": Stopping");
writer.Close();
writer.Dispose();
}
catch { }
try
{
foreach (TraceServer ts in traceServers)
{
try
{
ts.Stop();
ts.Close();
ts.Dispose();
}
catch { }
}
}
catch { }
}
private void WriteLog(string sMessage)
{
lock (writer)
{
try
{
writer.WriteLine(sMessage);
writer.Flush();
}
catch { }
}
}
}
}