В SQL Server есть интересная расширенная хранимая процедура xp_logevent. Процедура заносит определенное пользователем сообщение в файл журнала SQL Server и в средство просмотра событий Windows Event Viewer. Появилась идея немного расширить возможности этой процедуры, для этого я написал небольшую CLR-сборку, с помощью которой можно работать с журналом ошибок Windows.
Задача новой процедуры, не только заносить событие в журнал, но и просматривать события на сервере. Плюс к этому очень полезным будет возможность очищать журнал.
Записи будут заноситься в отдельный журнал SQLServerLog. Сообщения могут быть одним из 5 типов:
- Error
- FailureAudit
- Information
- SuccessAudit
- Warning
Код сборки:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections;
public class xp_logevent
{
//Выводим записи из журнала в виде таблицы
[SqlFunction(FillRowMethodName = "FillRow", TableDefinition = "Type nvarchar(20), DateTyme nvarchar(20), Source nvarchar(20), Category nvarchar(10), Event nvarchar(10), User nvarchar(50), Computer nvarchar(50), Message nvarchar(max)")]
public static IEnumerable xp_logevent2View()
{
ArrayList rows = new ArrayList();
if (System.Diagnostics.EventLog.SourceExists("SQLSource"))
{
System.Diagnostics.EventLog.DeleteEventSource("SQLSource");
}
System.Diagnostics.EventLog.CreateEventSource("SQLSource", "SQLServerLog");
System.Diagnostics.EventLog SQLServerLog = new System.Diagnostics.EventLog();
SQLServerLog.Log = "SQLServerLog";
SQLServerLog.Source = "SQLSource";
if (SQLServerLog.Entries.Count > 0)
{
foreach (System.Diagnostics.EventLogEntry entry in SQLServerLog.Entries)
{
rows.Add(new object[] { entry.EntryType.ToString()
, entry.TimeWritten.ToString()
, entry.Source.ToString()
, entry.Category.ToString()
, entry.EventID.ToString()
, entry.UserName
, entry.MachineName
, entry.Message
}
);
}
}
else
{
rows.Add(new object[] { "Information" , DateTime.Now.Date.ToLongDateString()
, "SQLSource"
, "None"
,"-"
, "-"
, "-"
, "Журнал пуст" });
}
SQLServerLog.Close();
return rows;
}
public static void FillRow(Object row, out string Type
, out string DateTime
, out string Source
, out string Category
, out string Event
, out string User
, out string Computer
, out string Message
)
{
object[] xrow = (object[])row;
Type = (string)xrow[0];
DateTime = (string)xrow[1];
Source = (string)xrow[2];
Category = (string)xrow[3];
Event = (string)xrow[4];
User = (string)xrow[5];
Computer = (string)xrow[6];
Message = (string)xrow[7];
}
//Заносим сообщение в журнал
[Microsoft.SqlServer.Server.SqlProcedure]
public static void xp_logevent2(int error_number, string message, string severity)
{
if (System.Diagnostics.EventLog.SourceExists("SQLSource"))
{
System.Diagnostics.EventLog.DeleteEventSource("SQLSource");
}
System.Diagnostics.EventLog.CreateEventSource("SQLSource", "SQLServerLog");
System.Diagnostics.EventLog SQLServerLog = new System.Diagnostics.EventLog();
SQLServerLog.Log = "SQLServerLog";
SQLServerLog.Source = "SQLSource";
switch (severity)
{
case "Error":
SQLServerLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Error, error_number);
break;
case "FailureAudit":
SQLServerLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.FailureAudit, error_number);
break;
case "Information":
SQLServerLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Information, error_number);
break;
case "SuccessAudit":
SQLServerLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.SuccessAudit, error_number);
break;
case "Warning":
SQLServerLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Warning, error_number);
break;
default:
SQLServerLog.WriteEntry(message);
break;
}
SQLServerLog.Close();
}
//Очистка журнала
public static void xp_logevent2Clear()
{
if (System.Diagnostics.EventLog.SourceExists("SQLSource"))
{
System.Diagnostics.EventLog.DeleteEventSource("SQLSource");
}
System.Diagnostics.EventLog.CreateEventSource("SQLSource", "SQLServerLog");
System.Diagnostics.EventLog SQLServerLog = new System.Diagnostics.EventLog();
SQLServerLog.Log = "SQLServerLog";
SQLServerLog.Source = "SQLSource";
SQLServerLog.Clear();
SQLServerLog.Close();
}
};
После компиляции получаем нашу библиотеку xp_logevent2.dll
Сборку зарегистрируем в контексте БД master:
alter database [master] set trustworthy on
go
use master
go
create assembly xp_logevent2
from 'C:\xp_logevent2\xp_logevent2.dll'
with permission_set = external_access;
go
Для работы с журналом создадим 2 процедуры:
- xp_logevent2 - заносит запись в журнал
- xp_logevent2Clear - очищает журнал
--Основная процедура, заносит сообщение в журнал
create procedure xp_logevent2
(
@error_number int --Номер пользовательской ошибки
, @message nvarchar(max) --Символьная строка
, @severity nvarchar(20) -- Один из 5 типов сообщения
)
as
external name xp_logevent2.xp_logevent.xp_logevent2
go
--Очистка журнала
create procedure xp_logevent2Clear
as
external name xp_logevent2.xp_logevent.xp_logevent2Clear
go
И функция xp_logevent2View(), для просмотра журнала в виде таблицы:
create function xp_logevent2View()
returns table
(
[Type] nvarchar(20)
, [DateTime] nvarchar(20)
, [Source] nvarchar(20)
, [Category] nvarchar(10)
, [Event] nvarchar(10)
, [User] nvarchar(50)
, [Computer] nvarchar(50)
, [Message] nvarchar(max)
)
external name xp_logevent2.xp_logevent.xp_logevent2View;
А теперь демонстрация работы:
Добавим одно сообщение в журнал
exec xp_logevent2 50001, 'Первое тестовое сообщение', 'Information';
После этого в
Event Viewer`е появится наш журнал:
Очистить журнал можно с помощью 2ой нашей процедуры:
exec xp_logevent2Clear;
Для демонстрации функции, добавим несколько сообщений и запустим нажу функцию
select * from xp_logevent2View()
А так эти сообщения выглядат в журнале Windows
Если кому-то лень компилировать сборку, то выложу
xp_logevent2.dll тут.