Hola, chicos
¿Todo está bien?

En esta publicación, me gustaría mostrarle cómo leer y escribir eventos en el Visor de eventos de Windows usando CLR (C#). Para conocer más sobre el CLR, este poderoso recurso que permite crear y ejecutar código escrito usando el lenguaje de programación C# y .NET Framework para extender las funcionalidades de SQL Server, acceda al post Introducción a SQL CLR (Common Language Runtime) en SQL Server.

En muchas situaciones, tener esta información disponible para consultas en la base de datos puede ser de gran utilidad para crear monitoreo interno o para crear aplicaciones web que consuman esta información y ayuden al equipo de desarrollo a identificar problemas en aplicaciones y servicios, además de permitir la posibilidad de registrar mensajes de eventos personalizados dentro de los Procedimientos Almacenados de la base de datos SQL Server, según ciertas situaciones.

Otro buen ejemplo para esta publicación es aquí en la empresa donde trabajo, donde hay un servicio que monitorea el Visor de Eventos de un servidor, buscando un evento donde la aplicación es “XPTO” y el tipo es “Error”. Cuando se genera un evento usando esta aplicación, un equipo de monitoreo que trabaja 24x7 llama a un número determinado para tomar las acciones necesarias, especialmente para rutinas altamente críticas que no pueden fallar ni detenerse. El CLR registra este evento cuando ocurre una determinada situación en la base de datos.

Cómo registrar eventos en el Visor de eventos desde SQL Server

Para registrar eventos en el Visor de eventos de Windows a través de SQL Server, simplemente ejecute el procedimiento almacenado stpEvent_Viewer_Record desde su biblioteca CLR.

Parámetros:

  • @Ds_Servidor: Nombre del servidor donde desea grabar el evento
  • @Ds_Tipo_Evento: Tipo de evento que desea registrar (Error, FailureAudit, Advertencia, SuccessAudit, Success o Información)
  • @Ds_Fonte: nombre que desea definir como origen del evento (generalmente, el nombre del sistema o aplicación)
  • @Ds_Mensagem: Mensaje que deseas grabar en el evento
  • @Id_Evento: Código de error o mensaje que deseas generar para este evento (libre elección)

Ver código fuente

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);
        }
        
    }

};

Ejemplo de uso

Cómo registrar eventos en el Visor de eventos usando SQL Server (sin CLR)

¿Es posible registrar eventos en el Visor de eventos a través de SQL Server sin utilizar CLR? Sí, lo es, aunque no es una forma tan “elegante”, ya que no es posible precisar el origen del suceso.

Una de las formas de hacerlo es utilizando la opción "Escribir en el registro de eventos de la aplicación de Windows" del Agente SQL:

La otra forma es utilizar el procedimiento indocumentado xp_logevent, sobre el cual ya había publicado en la publicación. Procedimientos extendidos no documentados de SQL Server:

EXEC master..xp_logevent 50001, 'Teste de mensagem no Event Viewer', 'INFORMATIONAL' -- INFORMATIONAL, WARNING, ERROR

Ejemplo de uso:

Cómo leer eventos en el Visor de eventos usando SQL Server

Para leer eventos en el Visor de eventos de Windows a través de SQL Server, compartiré dos formas de lograr este objetivo, una usando un procedimiento almacenado y la otra usando una función con valores de tabla, lo que nos brinda más flexibilidad para trabajar con los datos.

Ver el código fuente de stpEvent_Viewer_Listar

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();
            
        }
        
    }

};

Ver el código fuente de fncEvent_Viewer_Listar

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;

    }

}

Ejemplos de uso

Listado de eventos usando SP

Listado de eventos usando la función

¡Eso es todo, amigos!
Espero que hayas disfrutado de esta publicación y no confundas los eventos del Visor de eventos con los eventos de registro de SQL Server... jajaja
Un abrazo y nos vemos en el próximo post.