Olá pessoal
Tudo bem?
Neste post, eu gostaria de mostrar a vocês como ler e gravar eventos no Visualizador de Eventos (Event Viewer) do Windows utilizando o CLR (C#). Para saber mais sobre o CLR, esse poderoso recurso que permite criar e executar códigos escritos utilizando a linguagem de programação C# e o .NET Framework para estender as funcionalidades do SQL Server, acesse o post Introdução ao SQL CLR (Common Language Runtime) no SQL Server.
Em muitas situações, ter essa informação disponÃvel para consultas no banco de dados pode ser muito útil para criar monitoramentos internos ou para criar aplicações web que consomem essas informações e auxiliem a equipe de desenvolvimento a identificar problemas em aplicações e serviços, bem como permitir que se consiga gravar mensagens de evento personalizadas dentro de Stored Procedures do banco de dados SQL Server, de acordo com determinadas situações.
Um outro exemplo legal para esse post, é aqui na empresa em que eu trabalho, onde existe um serviço que fica monitorando o Event Viewer de um servidor, em busca de um evento onde a aplicação seja “XPTO” e o tipo seja “Error”. Quando é gerado um evento utilizando essa aplicação, uma equipe de monitoramento que trabalha no regime 24×7 faz o acionamento telefônico para um determinado número tomar as ações necessárias, especialmente para rotinas altamente crÃticas e que não podem falhar ou parar. Esse evento é gravado pelo CLR, quando uma determinada situação no banco de dados ocorre.
Como gravar eventos no Event Viewer pelo SQL Server
Para gravar eventos no Event Viewer do Windows pelo SQL Server, basta executar a Stored Procedure stpEvent_Viewer_Gravar da sua biblioteca CLR.
Parâmetros:
- @Ds_Servidor: Nome do servidor que você quer gravar o evento
- @Ds_Tipo_Evento: Tipo do evento que você deseja gravar (Error, FailureAudit, Warning, SuccessAudit, Success ou Information)
- @Ds_Fonte: Nome que você quer definir como fonte do evento (Geralmente, o nome do sistema ou aplicação)
- @Ds_Mensagem: Mensagem que você deseja gravar no evento
- @Id_Evento: Código do erro ou mensagem que você quer gerar para este evento (livre escolha sua)
Visualizar código-fonte
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
using System.Diagnostics; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void stpEvent_Viewer_Gravar(string Ds_Servidor, string Ds_Tipo_Evento, string Ds_Fonte, string Ds_Mensagem, int Id_Evento) { var tipoEvento = Ds_Tipo_Evento; var enumTipoEvento = EventLogEntryType.Error; switch (tipoEvento) { case "Error": enumTipoEvento = EventLogEntryType.Error; break; case "FailureAudit": enumTipoEvento = EventLogEntryType.FailureAudit; break; case "Information": enumTipoEvento = EventLogEntryType.Information; break; case "SuccessAudit": case "Success": enumTipoEvento = EventLogEntryType.SuccessAudit; break; case "Warning": enumTipoEvento = EventLogEntryType.Warning; break; default: return; } if (!EventLog.SourceExists(Ds_Fonte)) { EventLog.CreateEventSource(Ds_Fonte, "Application"); } using (var ev = new EventLog("Application", Ds_Servidor)) { ev.Source = Ds_Fonte; ev.WriteEntry(Ds_Mensagem, enumTipoEvento, Id_Evento, 1); } } }; |
Exemplo de utilização
Como gravar eventos no Event Viewer pelo SQL Server (Sem CLR)
É possÃvel gravar eventos no Event Viewer pelo SQL Server sem utilizar o CLR? É sim, embora não seja uma forma tão “elegante”, uma vez que não é possÃvel especificar a origem (Source) do evento.
Uma das formas de se fazer isso, é utilizando a opção “Write to the Windows Application event log” do SQL Agent:
A outra forma é utilizando a procedure não documentada xp_logevent, que eu já até havia publicado sobre ela no post The Undocumented SQL Server Extended Procedures:
1 |
EXEC master..xp_logevent 50001, 'Teste de mensagem no Event Viewer', 'INFORMATIONAL' -- INFORMATIONAL, WARNING, ERROR |
Como ler eventos no Event Viewer pelo SQL Server
Para ler eventos no Event Viewer do Windows pelo SQL Server, vou compartilhar duas formas de se atingir esse objetivo, uma utilizando uma Stored Procedure e outra utilizando uma Table-valued function, que nos dá mais flexibilidade para trabalhar com os dados.
Visualizar código-fonte da stpEvent_Viewer_Listar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
using System; using System.Data; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Diagnostics; public partial class StoredProcedures { [SqlProcedure] public static void stpEvent_Viewer_Listar(string Ds_Servidor, string Ds_Tipo_Log, SqlDateTime Dt_Inicio, SqlDateTime Dt_Fim) { var logType = Ds_Tipo_Log; var dtInicio = Dt_Inicio.Value; var dtFim = Dt_Fim.Value; using (var ev = new EventLog(logType, Ds_Servidor)) { var lastLogToShow = ev.Entries.Count; if (lastLogToShow <= 0) return; var pipe = SqlContext.Pipe; var colunas = new SqlMetaData[5]; colunas[0] = new SqlMetaData("Id_Evento", SqlDbType.BigInt); colunas[1] = new SqlMetaData("Fl_Evento", SqlDbType.NVarChar, 1024); colunas[2] = new SqlMetaData("Ds_Fonte", SqlDbType.NVarChar, 1024); colunas[3] = new SqlMetaData("Ds_Messagem", SqlDbType.NVarChar, 4000); colunas[4] = new SqlMetaData("Dt_Evento", SqlDbType.DateTime); var linhaSql = new SqlDataRecord(colunas); pipe?.SendResultsStart(linhaSql); for (var i = ev.Entries.Count - 1; i >= 0; i--) { try { var currentEntry = ev.Entries[i]; if ((currentEntry.TimeGenerated >= dtInicio) && (currentEntry.TimeGenerated <= dtFim)) { linhaSql.SetSqlInt64(0, new SqlInt64(currentEntry.InstanceId)); linhaSql.SetSqlString(1, new SqlString(currentEntry.EntryType.ToString())); linhaSql.SetSqlString(2, new SqlString(currentEntry.Source)); linhaSql.SetSqlString(3, new SqlString(currentEntry.Message.Substring(0, (currentEntry.Message.Length) > 4000 ? 3999 : currentEntry.Message.Length))); linhaSql.SetSqlDateTime(4, new SqlDateTime(currentEntry.TimeGenerated)); pipe?.SendResultsRow(linhaSql); } else if (currentEntry.TimeGenerated < dtInicio) break; } catch (Exception e) { // ignore } } pipe?.SendResultsEnd(); } } }; |
Visualizar código-fonte da fncEvent_Viewer_Listar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
using System; using System.Collections; using System.Data.SqlTypes; using System.Diagnostics; public partial class UserDefinedFunctions { private class EventViewerListar { public SqlInt64 Id_Evento; public SqlDateTime Dt_Evento; public SqlString Tp_Evento; public SqlString Ds_Fonte; public SqlString Ds_Mensagem; public EventViewerListar(SqlInt64 idEvento, SqlDateTime dtEvento, SqlString tpEvento, SqlString dsFonte, SqlString dsMensagem) { Id_Evento = idEvento; Dt_Evento = dtEvento; Tp_Evento = tpEvento; Ds_Fonte = dsFonte; Ds_Mensagem = dsMensagem; } } [Microsoft.SqlServer.Server.SqlFunction( FillRowMethodName = "FillRowEventViewer", TableDefinition = "Id_Evento BIGINT, Dt_Evento DATETIME, Tp_Evento nvarchar(50), Ds_Fonte nvarchar(50), " + "Ds_Mensagem NVARCHAR(MAX)" )] public static IEnumerable fncEvent_Viewer_Listar(string Ds_Servidor, string Ds_Tipo_Log, DateTime Dt_Inicio, DateTime Dt_Fim) { var eventViewerListarCollection = new ArrayList(); try { var logType = Ds_Tipo_Log; using (var ev = new EventLog(logType, Ds_Servidor)) { var lastLogToShow = ev.Entries.Count; if (lastLogToShow > 0) { for (var i = lastLogToShow - 1; i >= 0; i--) { var currentEntry = ev.Entries[i]; if ((currentEntry.TimeGenerated >= Dt_Inicio) && (currentEntry.TimeGenerated <= Dt_Fim)) { eventViewerListarCollection.Add(new EventViewerListar( currentEntry.InstanceId, currentEntry.TimeGenerated, currentEntry.EntryType.ToString(), currentEntry.Source, currentEntry.Message.Substring(0, (currentEntry.Message.Length) > 4000 ? 3999 : currentEntry.Message.Length) )); } else if (currentEntry.TimeGenerated < Dt_Inicio) break; } } } } catch (Exception e) { // ignored } return eventViewerListarCollection; } protected static void FillRowEventViewer(object objEventViewerListar, out SqlInt64 idEvento, out SqlDateTime dtEvento, out SqlString tpEvento, out SqlString dsFonte, out SqlString dsMensagem) { var eventViewerListar = (EventViewerListar)objEventViewerListar; idEvento = eventViewerListar.Id_Evento; dtEvento = eventViewerListar.Dt_Evento; tpEvento = eventViewerListar.Tp_Evento; dsFonte = eventViewerListar.Ds_Fonte; dsMensagem = eventViewerListar.Ds_Mensagem; } } |
Exemplos de utilização
Listando os eventos utilizando a SP
Listando os eventos utilizando a função
And that's it, folks!
Espero que tenham gostado desse post e por favor, não confundam eventos do Event Viewer com eventos do log do SQL Server.. rs
Um abraço e até o próximo post.