Application Block de Loggin

 


 


A mi me encanta Enterprise Library, creo que es una de las librerias de Microsoft a las que se puede sacar jugo de una manera muy sencilla. En este post voy a intentar explicar como realizar logging en nuestras aplicaciones apoyandonos en Enterprise Library 2.0

¿A que necesidades nos solemos enfrentar cuando desarrollamos una aplicación y queremos implementar un sistema de Logging?. Respondiendo a esta pregunta tendríamos las respuestas:


  • Poder generar eventos de log en diferentes repositorios(bases de datos, log de eventos de Windows, archivos de texto), sin modificar código
  • Dar soporte a “Debugging” en producción
  • Permitir cambiar las reglas del logging sin tener que cambiar código
  • Permitir incluir información de contexto dentro de los mensajes de log
  • Por ultimo contar con un marco de trabajo organizado para toda la organización

Vamos a definir unos cuantos conceptos básicos:


  • Log Entry: Representa un evento que se desea almacenar en Bitácora. Un Log Entry tiene propiedades como el mensaje, si es información o error y también tiene un conjunto de propiedad extendidas al cual se le puede agregar información propia de la aplicación como un IdCliente
  • Trace Listener: Componentes que se encargan de persistir los eventos en cada uno de los destinos (Ej:EventLogTraceListener almacena eventos en el EventLog de Windows)
  • Trace: Representa una actividad a monitorizar, en un sector de código digo que quiero iniciar el trace y todo lo que haga mientras no cierre el trace aunque baje en el código (llamadas a funciones) va a mantener un contexto con un id del trace

Generar Un Log de Eventos


Primero debemos agregar la referencia a EnterPrise Library


Agremamos el Imports o using en el código

Introducimos nuestra primer código de logging

            ‘Declaramos una instancia de LogEntry
Dim entry As New LogEntry
‘Le asociamos un menaje a la entrada
entry.Message = «Se calculo el valor de pi»
‘Indicamos la severidad de la entrada, es decir,
el tipo de entrada en este caso es una información
entry.Severity = TraceEventType.Information
‘Le indicamos la categoria nos sirve para ordenar
como queremos que se persistan estos
elementos en bitacora
entry.Categories.Add(Category.General)
‘Pr ultimo escribimos la entrada
Logger.Write(entry)

 


En este código no le hemos dicho donde lo tiene que escribir esa entrada que hemos creado, eso lo vamos a indicar en configuración, para ello debemos abrir el app.config con EntLibConfig.exe que es el configurador de Enterprise, como hablamos en el bloque de acceso a datos.


Una vez abierto vamos a introducir la entrada para configurar el Looging


Vemos que genera una serie de secciones



  • Formatters: Indica como se formatean los mensajes que envio al EventLog, por defecto tengo un formateador de texto que tiene un template que sigue el estandar que se muestra


Nos muestra el mensaje, categoría y nos permite insertar Tokens, pero también nos permite crear nuestros formateadores, por ejemplo podríamos crear un formateador que generase archivos XML


  • Trace Listeners: Es el encargado de persistir la información en los repositorios, por defecto tenemos el de Event Log, pero también esta el de Base de Datos, e-mail, MSMQ, WMI…

Nota:

En Trace Listener en el Event Log podemos indicar en el campo Source la aplicación que estamos loggeado y en MachineName un nombre de maquina a la cual tengamos permisos y se grabarán centralizados todo el Logging


  • Special Sources: Hay un source para todos los eventos, si el Application Block recibe un evento que no esta asociado a ninguna categoría el va a entrar en este nodo. Dentro de este nodo tenemos



    • Logging Errors & Warnings: Cuando se intenta grabar un evento y se da un error, por ejemplo intento grabar el Log en una base de datos y esta caída, lo que va ha hacer el Application Block de Logging es generar un Log de ese error y va a utilizar el Trace Listeners, obviamente si estamos intentando logear en una base de datos y esta caida, no hay que utilizar un Trace Listener de EventLog en vez de la base de datos y eso lo indicamos en FormatedEventLog Trace Listener que viene debajo de Logging Errors & Warnings, por defecto viene el Event Log


  • Category Sources: Se puede definir n categorias y para cada categoría se puede poner uno o mas listeners



Una vez configurada la aplicación, si yo la ejecuto y voy al visor de sucesos, veo que se ha grabado la entrada que había programado



Si quisieramos agregar propiedades extendidas a esta entrada, como por ejemplo el valor calculado de pi.

‘Declaramos una instancia de LogEntry
Dim entry As New LogEntry
‘Le asociamos un menaje a la entrada
entry.Message = «Se calculo el valor de pi»
‘Indicamos la severidad de la entrada, es decir,
el tipo de entrada en este caso es una información
entry.Severity = TraceEventType.Information
‘Le indicamos la categoria nos sirve para ordenar
como queremos que se persistan estos elementos en bitacora
entry.Categories.Add(Category.General)
‘Creo una propiedad Extendida, para visualizar
el valor de Pi en el logging
entry.ExtendedProperties.AddValor de Pi:«, args.Pi)
‘Pr ultimo escribimos la entrada
Logger.Write(entry)

Si yo ejecuto otra vez la aplicación, vemos que se genera una nueva entrada en el visor de sucesos con la propiedad extendida que hemos creado


De esta forma a un LogEntry le podemos incluir información de nuestra lógica de negocio, esta información esta lejos de poder guardarse en una base de datos porque esta tratada con el formateador de texto, deberíamos crear un formateador de base de datos para poder guardar esta información en una tabla con sus correspondientes columnas.

Trace


Si queremos definir una actividad (trace) y queremos guardar información como cuando se inicia, termina, información particular de los métodos que componen esa actividad.

Para declara un trace utilizaremos la sentencia using.

Using New Tracer(Category.Trace)

End Using

Todo el código y métodos que sean llamados en el ámbito del using, utilizaran el mismo identificador de Trace.

Vamos a ver como configuraria el Trace para que lo guarde en un archivo de Texto, generaria una nueva entrada en el Trace Listener de tipo Flat


Donde le pudo indicar el nombre del archivo donde quiero que lo guarde…

En la Category Sources añadimos la categoría de Trace y en la category Trace en el nodo SourceLevels le indicamos que solo vamos a mostrar información de Trace.


En la categoría le indicamos cual es el Trace Listener, en este caso el Flat Listeners


Ahora en el código cuando calculamos Pi, introducimos el trace

Public Function Calculate(ByVal digits As Integer) As String
Dim pi As New StringBuilder(«3«, digits + 2)
Dim result As String = Nothing

Try
If digits > 0 Then

Using New Tracer(Category.Trace)

pi.Append(«.«)
Dim i As Integer = 0
While (i < digits)
Dim args As CalculatingEventArgs
args = New CalculatingEventArgs(pi.ToString(), i + 1)
OnCalculating(args)

‘ Break out if cancelled
If args.Cancel = True Then
Exit While
End If

‘ Calculate next 9 digits
Dim nineDigits As Integer = NineDigitsOfPi.StartingAt((i + 1))
Dim digitCount As Integer = Math.Min(digits – i, 9)
Dim ds As String = String.Format(«{0:D9}«, nineDigits)
pi.Append(ds.Substring(0, digitCount))

i += 9
End While
End Using
End If

result = pi.ToString()

‘ Tell the world I’ve finished!
OnCalculated(New CalculatedEventArgs(result))
Catch ex As Exception
‘ Tell the world I’ve crashed!
OnCalculatorException(New CalculatorExceptionEventArgs(ex))
End Try

Return result
End Function


Si lo ejecutamos vemos que nos crea el archivo trace.log que habiamos indicado en el Trace listener


Si lo abrimos vemos el siguiente texto

—————————————-

Trace Start: 1 : Timestamp: 13/06/2006 9:02:44

Message: Start Trace: Activity ‘27465ceb-89f1-4d25-b13d-b368ed8c0257’ in method ‘Calculate’ at 213569458568 ticks

Category: Trace

Priority: 5

EventId: 1

Severity: Start

Title:TracerEnter

Machine: NOMBRE-FDEED211

App Domain: EnoughPI.vshost.exe

ProcessId: 2568

Process Name: C:Archivos de programaMicrosoft Enterprise Library January 2006labsvbLoggingexercisesex01beginEnoughPIbinEnoughPI.vshost.exe

Thread Name:

Win32 ThreadId:3684

Extended Properties:

—————————————-

—————————————-

Trace Stop: 1 : Timestamp: 13/06/2006 9:02:44

Message: End Trace: Activity ‘27465ceb-89f1-4d25-b13d-b368ed8c0257’ in method ‘Calculate’ at 213570572775 ticks (elapsed time: 0,311 seconds)

Category: Trace

Priority: 5

EventId: 1

Severity: Stop

Title:TracerExit

Machine: NOMBRE-FDEED211

App Domain: EnoughPI.vshost.exe

ProcessId: 2568

Process Name: C:Archivos de programaMicrosoft Enterprise Library January 2006labsvbLoggingexercisesex01beginEnoughPIbinEnoughPI.vshost.exe

Thread Name:

Win32 ThreadId:3684

Extended Properties:

—————————————-

Vemos la hora de comienzo y fin, el tiempo que ha tardado (0,311 seconds), además podemos ver que por cada actividad nos genera un identificador de esa actividad, esto nos será útil para mantener la correlación entre diferentes eventos. Por ejemplo vamos a crear un método Dummy y una llamada a este metodo dentro del using y en este metodo realizamos una entrada en el logging con el id de la actividad

Private Sub Dummy()

Logger.Write(string.Format(«El Id {0} es:», trace.CorrelationManager.ActivityId)

End Sub

Si ejecutamos ahora el código vemos que me genera una nueva entrada en el fichero de trace con el Id de la actividad: 04ae6dcb-d803-4c11-90c3-e849608b44cc y si vamos al visor de sucesos vemos que tengo una entrada con esta información


Si quisieramos que los eventos se generasen en el fichero de trace debemos de ir a configuración y en la categoría de Trace indicarle en SourceLevels que quiero guardar todo


Si lo ejecutamos de nuevo la aplicación y vemos el fichero de trace tenemos la entrada que he introducido, con lo que podemos generar un contexto que genere muchos eventos ligados a una actividad y poder ver el orden de secuencia.

Trace Start: 1 : Timestamp: 13/06/2006 10:05:22

Message: Start Trace: Activity ‘9bfe5aaa-9ac4-411c-a982-24998aeaf5d0’ in method ‘Calculate’ at 227020326631 ticks

Category: Trace

Priority: 5

EventId: 1

Severity: Start

Title:TracerEnter

Machine: NOMBRE-FDEED211

App Domain: EnoughPI.vshost.exe

ProcessId: 1808

Process Name: C:Archivos de programaMicrosoft Enterprise Library January 2006labsvbLoggingexercisesex01beginEnoughPIbinEnoughPI.vshost.exe

Thread Name:

Win32 ThreadId:5180

Extended Properties:

—————————————-

—————————————-

Trace Information: 1 : Timestamp: 13/06/2006 10:05:22

Message: El Id 9bfe5aaa-9ac4-411c-a982-24998aeaf5d0 es:

Category: General, Trace

Priority: -1

EventId: 1

Severity: Information

Title:

Machine: NOMBRE-FDEED211

App Domain: EnoughPI.vshost.exe

ProcessId: 1808

Process Name: C:Archivos de programaMicrosoft Enterprise Library January 2006labsvbLoggingexercisesex01beginEnoughPIbinEnoughPI.vshost.exe

Thread Name:

Win32 ThreadId:5180

Extended Properties:

—————————————-

—————————————-

Trace Stop: 1 : Timestamp: 13/06/2006 10:05:22

Message: End Trace: Activity ‘9bfe5aaa-9ac4-411c-a982-24998aeaf5d0’ in method ‘Calculate’ at 227021301350 ticks (elapsed time: 0,272 seconds)

Category: Trace

Priority: 5

EventId: 1

Severity: Stop

Title:TracerExit

Machine: NOMBRE-FDEED211

App Domain: EnoughPI.vshost.exe

ProcessId: 1808

Process Name: C:Archivos de programaMicrosoft Enterprise Library January 2006labsvbLoggingexercisesex01beginEnoughPIbinEnoughPI.vshost.exe

Thread Name:

Win32 ThreadId:5180

Extended Properties:

Configurar Login Para Realizar el Logging en Base de Datos


Para ello recordamos que debemos crear un Trace Listener que se configure para la base de datos


Tendré que configurar la Base de Datos


Automáticamente me va a crear una entrada en DataAccessApplicationBlock, y tendremos que configurar el Connection String, para luego en la entrada DataBaseInstance asignarl el ConnectionString


Luego hay que decir como se llama el procedimiento almacenado que queremos que ejecute para añadir entradas a la base de datos, por defecto se llama WriteLog y estan los scripts de este procedimiento almacenados y de las tablas necesarios junto con EnterPrise Librery en el fichero LoggingDatabase.sql si yo quisiera por ejemplo añadir una columna a esa tabla deberiamos de utilizar un formateador diferente que nos permita pasar esa información a esa columna.

Vamos a ver como generamos un nuevo formateador, pero en este caso sobre ficheros XML

Lo primero es realizar las importaciones

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;

Debemos de crear una clase que derive de LogFormatter y deberemos sobrescribir el metodo Format(LogEntry log) que devuelve un string


Por ejemplo la clase de XML seria


[ConfigurationElementType(typeof(CustomFormatterData))]
public class XmlFormatter : LogFormatter
{
    private NameValueCollection Attributes = null;

    public XmlFormatter(NameValueCollection attributes)
    {
        this.Attributes = attributes;
    }

    public override string Format(LogEntry log)
    {
        string prefix = this.Attributes[«prefix»];
        string ns = this.Attributes[«namespace»];

        using (StringWriter s = new StringWriter())
        {
            XmlTextWriter w = new XmlTextWriter(s);
            w.Formatting = Formatting.Indented;
            w.Indentation = 2;

            w.WriteStartDocument(true);
            w.WriteStartElement(prefix, «logEntry», ns);
            w.WriteAttributeString(«Priority», ns,
                log.Priority.ToString(CultureInfo.InvariantCulture));
            w.WriteElementString(«Timestamp», ns, log.TimeStampString);
            w.WriteElementString(«Message», ns, log.Message);
            w.WriteElementString(«EventId», ns,
                log.EventId.ToString(CultureInfo.InvariantCulture));
            w.WriteElementString(«Severity», ns, log.Severity.ToString());
            w.WriteElementString(«Title», ns, log.Title);
            w.WriteElementString(«Machine», ns, log.MachineName);
            w.WriteElementString(«AppDomain», ns, log.AppDomainName);
            w.WriteElementString(«ProcessId», ns, log.ProcessId);
            w.WriteElementString(«ProcessName», ns, log.ProcessName);
            w.WriteElementString(«Win32ThreadId», ns, log.Win32ThreadId);
            w.WriteElementString(«ThreadName», ns, log.ManagedThreadName);
            w.WriteEndElement();
            w.WriteEndDocument();

return s.ToString();
        }
    }
}

Una vez compilada nuestra clase, añadimos nuestro formateador en la configuración


Seleccionamos la propiedad Type y pulsamos el botón asociado, mostrando



Seleccionamos nuestro formateador y seleccionamos Atributtes y pulsamos el Botón


Añadimos los pares de clave/valor



    • Key = prefix, Value = x, and
    • Key = namespace, Value = EnoughPI/2.0


Ahora deberemos elegir en Trace Listeners nuestro formateador


Si lo ejecutamos vemos que nos muestra


Despliegue en Producción



Un aspecto importante es que a la hora de desplegar en servidores de producción si no tengo creado en el Visor de Sucesos el Source que he designado para la aplicación


6 comentarios sobre “Application Block de Loggin”

  1. tu sabes como puedo resolver el problema que no me escribe cuado al flat file le asigno un archivo para guardar, al principio escribia todos los eventos pero no se que paso que ahora no me logea nada.

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *