La posibilidad de tener logs en las aplicaciones nos pueden ayudar bastante en el día a día, sobretodo cuando no podemos depurar nuestro código. Por ello, hoy os propongo un código sencillo para llevar un registro de los eventos de nuestra aplicación en VB.NET.

El código

Imports System.IO

Public Class Logger
  Public Enum TipoMensaje
    DEBUG
    INFO
    WARNING
    LERROR
  End Enum

  Private Const TAM_FICHERO As Integer = 52428800
  Private Const EXTENSION As String = ".log"

  Private Shared eNivel As TipoMensaje = TipoMensaje.DEBUG

  Public Shared Property Nivel As TipoMensaje
    Get
      Return eNivel
    End Get
    Set(eActual As TipoMensaje)
      eNivel = eActual
    End Set
  End Property

  Private Shared Sub add(ByVal eTipo As TipoMensaje, _
                         ByVal sMensaje As String, _
                         Optional ByVal objTraza As StackFrame = Nothing)
    If eTipo >= eNivel Then
      Dim sDestino As String = getFileFecha()

      verificar(sDestino)

      Dim objFichero As New FileStream(sDestino, FileMode.Append, _
                                       FileAccess.Write)

      If Not objFichero Is Nothing And objFichero.CanWrite Then
        Dim sDeb As String = String.Empty
        Dim objFecha As Date = Date.Now

        sDeb = objFecha.ToString() + " [" + eTipo.ToString + "] "
        sDeb &= getTraza(objTraza) & sMensaje

        Dim objStream As New StreamWriter(objFichero)
        objStream.WriteLine(sDeb)
        objStream.Close()
        objFichero.Close()
      End If
    End If
  End Sub

  Private Shared Function getFileFecha() As String
    Dim objFecha As Date = Date.Now
    Dim sCadena As String = String.Empty

    sCadena &= objFecha.Year
    If objFecha.Month < 10 Then
      sCadena &= "0"
    End If
    sCadena &= objFecha.Month
    If objFecha.Day < 10 Then
      sCadena &= "0"
    End If
    sCadena &= objFecha.Day
    sCadena &= EXTENSION

    Return sCadena
  End Function

  Private Shared Sub verificar(sFichero As String)
    Try
      If My.Computer.FileSystem.GetFileInfo(sFichero).Length >= _
         TAM_FICHERO Then
        My.Computer.FileSystem.MoveFile(sFichero, sFichero & ".old")
      End If
    Catch ex As IOException
    End Try
  End Sub

  Public Shared Sub e(sMensaje As String, objExcepcion As Exception, _
                      Optional ByVal objTraza As StackFrame = Nothing)
    add(TipoMensaje.LERROR, sMensaje & vbNewLine & _
        vbTab & objExcepcion.Message & vbNewLine & _
        vbTab & objExcepcion.ToString, objTraza)
  End Sub

  Public Shared Sub e(sMensaje As String, _
                      Optional ByVal objTraza As StackFrame = Nothing)
    add(TipoMensaje.LERROR, sMensaje, objTraza)
  End Sub

  Public Shared Sub w(sMensaje As String, _
                      Optional ByVal objTraza As StackFrame = Nothing)
    add(TipoMensaje.WARNING, sMensaje, objTraza)
  End Sub

  Public Shared Sub i(sMensaje As String, _
                      Optional ByVal objTraza As StackFrame = Nothing)
    add(TipoMensaje.INFO, sMensaje, objTraza)
  End Sub

  Public Shared Sub d(sMensaje As String, _
                      Optional ByVal objTraza As StackFrame = Nothing)
    add(TipoMensaje.DEBUG, sMensaje, objTraza)
  End Sub

  Public Shared Function getTraza(objTraza As StackFrame) As String
    Dim sTraza As String = String.Empty

    If Not objTraza Is Nothing Then
      Dim iPosicion As Integer = objTraza.GetFileName.LastIndexOf("\")

      If iPosicion < 0 Then
        sTraza = objTraza.GetFileName()
      Else
        sTraza = objTraza.GetFileName().Substring(iPosicion + 1)
      End If

      sTraza &= "::" & objTraza.GetFileLineNumber & " - "
    End If

    Return sTraza
  End Function
End Class

Explicación

Lo primero que vemos es la enumeración TipoMensaje que contiene los posibles tipos de registro que tendremos. A la hora de guardar nuestro registro se mostrará el nivel de cada evento.

La función getFileFecha() obtiene el nombre del fichero que vamos a utilizar. Por ejemplo, en el día de escritura de este artículo el nombre obtenido sería: 20120724.log.

El método verificar() lo que hace es analizar el tamaño del fichero de registro. Si este excede el límite que viene dado por la constante TAM_FICHERO se realiza una copia de seguridad y se comienza un nuevo fichero. Esta parte la podéis obviar si no os importa el tamaño de los archivos.

El método add() es el que contiene la parte principal del código. Recibe el tipo de registro, así como el mensaje a almacenar y de forma opcional la traza y se encarga de almacenarlos en el fichero de registro con la fecha y hora actuales.

Los métodos d(), i(), w() y e() hacen de interfaz para simplificar la escritura en el registro y son los métodos que utilizará el usuario o usuaria. El método e() puede recibir un parámetro extra que será un objeto de tipo Exception, de esta forma podremos añadir a nuestro log el contenido de la excepción.

Resultado

El resultado de utilizar nuestra clase Logger sería parecido al siguiente:

24/07/2012 16:37:34 [LERROR] - Este no tiene traza
24/07/2012 16:37:35 [DEBUG] FrmPrueba.vb::7 - Este si tiene traza
24/07/2012 16:37:36 [INFO] FrmPrueba.vb::9 - Mensaje de información.
24/07/2012 16:38:02 [LERROR] FrmPrueba.vb::19 - Ocurrió un error
  Valor demasiado grande o demasiado pequeño para un decimal.
  System.OverflowException: Valor demasiado grande o demasiado pequeño para un decimal.
    en System.Decimal..ctor(Double value)
    en Logger.Form1.btnEnviar_Click(Object sender, EventArgs e) en FrmPrueba.vb:línea 19

Finalmente, veamos como utilizar nuestro sistema de log. Lo primero es indicar el nivel de registro. Por defecto, es DEBUG, pero podemos ponerle un nivel, por ejemplo, de WARNING, esto se haría de la forma siguiente:

Logger.Nivel = Logger.TipoMensaje.WARNING

En este caso sólo se mostrarán los mensajes de WARNING y de ERROR, siendo omitidos los demás.

Ejemplos de uso:

Logger.e("Error con excepción", ex)
Logger.d("Debug con traza", New StackFrame(True))
Logger.i("Info sin traza", New StackFrame(True))
Logger.e("Error con excepción y traza", ex, New StackFrame(True))
Comparte este artículo con quien quieras
InfoBat 0.2
AVM 0.5.5.5 - Nuevo diseño y corrección de fallo

Discussion

  • Commenter's Avatar
    jose — 1 agosto, 2012 at 11:20

    Tengo puesto el código en Github también, de forma que si hay cambios, en dicha plataforma podréis seguirlos: https://gist.github.com/3170403

  • Commenter's Avatar
    David — 10 julio, 2013 at 19:14

    Excelente Me salvaste!! muy bueno y sencillo!! Gracias!

  • Commenter's Avatar
    Sonyx — 31 enero, 2014 at 17:32

    Muy bueno el codigo, pero debo ser franco y comentarte que estoy comenzo a programar en vb.net, por lo cual me gustaria usar y entender en profundidad tu codigo, ahora el problema es que cree la clase logger.vb pero no encuentro donde queda el archivo fisico, ademas como hago la inicializacion del archivo? o como se dispara y comienza a registrar informacion?. Se podria mediante un SQL conexion realizar el seguimiento a una Base de Datos (TRACE) con este codigo?

    Espero puedas ayudarme y contestar a mis preguntas (novatas). Y Gracias por la información.

    Saludos,

    Sonyx.

    • Commenter's Avatar
      jose — 2 febrero, 2014 at 19:56

      Buenas, por un lado, en la versión de la clase que se indica en este código, el fichero de log se crea en el directorio de trabajo de la aplicación, es decir, si tu aplicación tiene como directorio de trabajo (C:/mi_ruta/), en dicho directorio se almacenaran una serie de ficheros del tipo AAAAMMDD.log (año/mes/día). Dicho archivo es creado automáticamente por la clase en caso de ser necesario, ahorrádonos su gestión.

      Por otro, todos los métodos de la clase son estáticos, por lo que no es necesario instanciarla para comenzar a ejecutarla, tan sólo hacer llamadas como las que se indican en Ejemplos de uso al final del artículo.

      Finalmente, el método add() es donde se añade la información de registro al fichero, en dicho método si sería posible introducir código que enviará la traza a un servidor de base de datos. Simplemente habría que cambiar los objStream y objFichero por objetos para poder realizar un INSERT sobre una base de datos.

      Espero que te haya servido de ayuda.

  • Commenter's Avatar
    Fran — 26 febrero, 2014 at 22:45

    Saludos:
    Muy buen código,pero…¿dónde se guarda el log?
    Es que tengo un programa hecho en VB.NET(con un hook de teclado,el cual reproduce un sonido al pulsar una tecla) que al depurarlo funciona bien,pero cuando lo publico,instalo….e inicio; y cuando pulso la tecla me sale un cartel que dice “Application1 dejo de funcionar; Windows está buscando una solución….”pero no dice nada más.
    He añadido tu log pero no me aparece,¿qué he hecho mal?
    Saludos y muchas gracias

    • Commenter's Avatar
      jose — 27 febrero, 2014 at 9:44

      Buenas, en el código expuesto el fichero se guardaría en la ruta de trabajo de la aplicación, es algo que en el futuro debería mejorar, porque si tenemos por ejemplo un acceso directo se almacenaría en el directorio de “Iniciar en”.

      Si quieres puedes añadirle una ruta al método getFileFecha() para así tener controlado donde se almacena. Por ejemplo algo del estilo:

      Private Shared Function getFileFecha() As String
         ...
      
        Dim path As String = My.Application.Info.DirectoryPath & "/log/"
        If Not My.Computer.FileSystem.DirectoryExists(path) Then
          My.Computer.FileSystem.CreateDirectory(path)
        End If
      
        Return path & sCadena
      End Function
      

      Con lo anterior se almacenaría en el directorio donde está la aplicación sin importar el directorio de ejecución.

      Si sigue sin aparecerte el log no dudes en avisarme.

      Un saludo

  • Commenter's Avatar
    Marcelo — 14 abril, 2014 at 18:49

    disculpa la ignorancia, pero…como lo hago funcionar?

    • Commenter's Avatar
      jose — 15 abril, 2014 at 16:03

      Hola, sin problema para eso estamos 😉

      Pues bien, debes añadir la clase que se indica en el código a tu aplicación, es decir, crear un fichero vb y copiar y pegar el código de la clase que se expone arriba.

      Una vez hecho esto, tan sólo deberás importarla en el código que quieras usarla y llamar a sus métodos d(), i(), w() y e(), lo cuales recibirán una cadena que mostrarán como DEBUG, INFO, WARNING o ERROR respectivamente. Además, si los llamas con un objeto StackFrame se incluirá en la traza el fichero y línea desde donde se insertó.

      Finalmente, si quieres que no se muestren los mensajes inferiores a un nivel determinado, deberás establecer la propiedad nivel. Por ejemplo, si la estableces a WARNING, los mensajes de tipo DEBUG e INFO serán descartados.

  • Commenter's Avatar
    Patxi — 29 mayo, 2014 at 15:10

    ¡¡ Muy bueno !! ¡ Muchas gracias ! Te debo unas horas… ¡¡ Gracias !!

  • Commenter's Avatar
    Alejandro — 11 marzo, 2015 at 19:19

    Buenas! Muchas gracias por el código!
    Podrías indicarme un ejemplo de cómo utilizar el objeto StackFrame para debuggear por favor?

  • Commenter's Avatar
    Fernando — 12 agosto, 2016 at 15:34

    Hola, buenas tardes Jose, ¿podrias explicarme porque hay dos Public Shared Sub “e”?

    • Commenter's Avatar
      jose — 13 agosto, 2016 at 0:23

      Buenas, el tener dos métodos e, es porque a uno le pasamos una excepción para que la imprima y en el otro método no se recibe dicho parámetro.

  • Commenter's Avatar
    Fernando — 25 agosto, 2016 at 20:36

    Hola Jose, cuando ejecuto la aplicación donde he incorporado tu log, en un equipo que no tiene instalado microsoft visual studio me arroja el siguiente error: Referencia a objeto no establecida como instancia de un objeto.

    • Commenter's Avatar
      jose — 25 agosto, 2016 at 22:13

      Buenas, revisa que esté instalado .NET Framework en la misma versión con la que has compilado la aplicación. En caso de que si, si me puedes pasar más información de la traza para analizar el por qué…

  • Commenter's Avatar
    JIMMY — 24 febrero, 2017 at 1:19

    Estimado, de acuerdo con Fernando me sale este error ya revise la version en la pc cliente y es la misma

    Consulte el final de este mensaje para obtener más detalles sobre cómo invocar a la depuración
    Just-In-Time (JIT) en lugar de a este cuadro de diálogo.

    ************** Texto de la excepción **************
    System.NullReferenceException: Referencia a objeto no establecida como instancia de un objeto.
    en SistemaOrdenesServicio.Logger.getTraza(StackFrame objTraza)
    en SistemaOrdenesServicio.Logger.add(TipoMensaje eTipo, String sMensaje, StackFrame objTraza)
    en System.Windows.Forms.Form.CheckCloseDialog(Boolean closingOnly)

    gracias desde ya por tu pronta respuesta.

  • Commenter's Avatar
    angel rodriguez ruiz — 17 mayo, 2020 at 0:41

    Hola,

    A mi me da el mismo problema, uso VS 2019

Leave a Comment

Your email address will not be published. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.