Click here to Skip to main content
Click here to Skip to main content

Limited Length Logging

, 18 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
Preventing a log file from growing forever

Introduction 

This example is a simple class that provides a method to write to a log file and control its size. It lets you set the maximum number of lines that will be maintained in the log. Once the max number of lines have been reached then the oldest entry is taken off the top each time a new line is added.

Background

Several years ago I worked with a team that was dealing with a serious customer issue where errors being logged over several years had actually filled the systems hard drive completely. Lesson learned!

For me it is important that programs elegantly log any errors - especially services that have no UI where there is no place to display errors for the users.  It's also important to make sure the logging doesn't just go on forever or you, at the very least, will be wasting a lot of disc space; a worst you could wind up using up all of the available disc space and cause serious problems for the end user. This class, written in VB.NET, lets you define the output log file name and the maximum number of lines to maintain. The default is 99 lines.

Using the code  

To use this class create a new class in your project and then cut and past the class code below into the new class (replacing everything).

You can then create the ErrorLogger object as follows:

Dim MyLogger as New ErrorLogger(MyLogfilename, 150)

There are two methods in this class: Logit and MakeEventLogEntry.    MakeEventLogEntry is for adding an entry to the system Event Viewer Application log.   You can either make a separate call to MakeEventLogEntry, or whenever you call the Logit method you can set the AddToSystemApplicationLog flag to true and any message you place into your own log file will be added to the System Application log.   The flag is optional and defaults to False.   This uses the optional parameter feature offered in VB.NET (I know many of you think that this is just syntactacal candy - but it requires less code to do this than to overload the method).

There are two properties: LogFilename and MaxLogLines. Both have defaults set (explained in the Intellisense).  You can set these when instantiating an object from the class using the New() keyword, or you can set them separately.

Here is the class - including Intellisense.

Imports System.IO

Public Class ErrorLogger

    ''' <summary>
    ''' Creates a new instance of the ErrorLogger
    ''' </summary>
    ''' <param name="MaxLogLines">The maximum number of lines to maintain in the log file</param>
    ''' <param name="LogFileName">The fully qualified path and name of the log file</param>
    ''' <remarks></remarks>
    Sub New(ByVal LogFileName As String, ByVal MaxLogLines As Integer)
        Me.LogFileName = New FileInfo(LogFileName)
        Me.MaxLogLines = MaxLogLines
    End Sub

    ''' <summary>
    ''' Creates a new instance of the ErrorLogger
    ''' </summary>
    ''' <param name="LogFileName">The fully qualified path and name of the log file</param>
    ''' <remarks></remarks>
    Sub New(ByVal LogFileName As String)
        Me.LogFileName = New FileInfo(LogFileName)
    End Sub

    ''' <summary>
    ''' Creates a new instance of the ErrorLogger with the default settings
    ''' of MaxLogLines (99) and LogFileName ([directorypath\assmblyname].log).
    ''' </summary>
    ''' <remarks></remarks>
    Sub New()
        'accept a New without paramters and use the defaults for LogFileName and MaxLogLines
    End Sub

    ''' <summary>
    ''' Gets/Sets the maximum number of lines the log will contain.  This is circular so once the
    ''' maximum number of lines is reached (specified with MaxLogLines property) a line will be removed from the top of the file.
    ''' </summary>
    Public Property MaxLogLines As Integer = 99

    Private _LogFileName As FileInfo = New FileInfo(Path.Combine(My.Application.Info.DirectoryPath, _
      System.Reflection.Assembly.GetEntryAssembly().GetName().Name & ".log"))

    ''' <summary>
    ''' Gets/Sets the fully qualified path for the log file.  If the path to the log
    ''' file specified does not exist it will throw an exception.
    ''' </summary>
    Public Property LogFileName As FileInfo
        Get
            Return _LogFileName
        End Get
        Set(value As FileInfo)
            If Not value.Directory.Exists Then
                Throw New ArgumentException("Folder/Path for specified log file name does not exist!")
            End If
            _LogFileName = value
        End Set
    End Property

    ''' <summary>
    '''Places a message into the log file (defined with the property LogFileName).  
    ''' </summary>
    ''' <param name="Message">The message you wish to log</param>
    ''' <param name="AddToSystemApplicationLog">If True then the message
    ''' will also be placed into the Windows system Application Eventlog</param>
    ''' <remarks></remarks>
    Public Sub Logit(ByVal Message As String, Optional ByVal AddToSystemApplicationLog As Boolean = False)
        Static LogLines As New List(Of String)
        If LogLines.Count = 0 Then
            If Me.LogFileName.Exists Then
                Try
                    LogLines = IO.File.ReadAllLines(Me.LogFileName.FullName).ToList
                Catch ex As Exception
                    Try
                        MakeEventlogEntry("Cannot read log file!")
                    Catch exx As Exception
                        Exit Sub 'There's no place to log anything
                    End Try
                    Exit Sub  'there's nothing we can do really...
                End Try
            End If
        End If

        'clean up the message
        If Message.EndsWith(Environment.NewLine) Then
            Message = Message.Substring(0, Message.Length - 2)
        End If
        Message = System.DateTime.Now & "     " & Message
        LogLines.Add(Message)

        'make sure we keep only MaxEntries entries
        Do While LogLines.Count > Me.MaxLogLines
            LogLines.RemoveAt(0)
        Loop

        Try
            IO.File.WriteAllLines(Me.LogFileName.FullName, LogLines)
            If AddToSystemApplicationLog Then
                MakeEventlogEntry(Message)
            End If
        Catch ex As Exception
            Try
                MakeEventlogEntry("Cannot write to log file!")
            Catch exx As Exception
                Exit Sub  'There's no place to log anything
            End Try
            Exit Sub 'There's no where to log it so just ignore error
        End Try

    End Sub

    ''' <summary>
    ''' Places the message into the Windows Eventlog Application section
    ''' </summary>
    ''' <param name="Message"></param>
    ''' <remarks></remarks>
    Public Sub MakeEventlogEntry(Message As String)
        Dim sSource As String = My.Application.Info.ProductName
        Dim sLog As String = "Application"
        Dim sMachine As String = "."
        Dim ESCD As New EventSourceCreationData(sSource, sLog)

        If Not EventLog.SourceExists(sSource, sMachine) Then
            System.Diagnostics.EventLog.CreateEventSource(ESCD)
            EventLog.WriteEntry(sSource, Message, EventLogEntryType.Error)
        End If
    End Sub

End Class

You will notice there are three New() overloads.   Since the LogFilename and MaxLogLines properties both have defaults these overloaded New() methods let you accept either, none or both of them.

Points of Interest 

Adding the MakeEventlogEntry method may be overkill, but I use this class when I create services - and I think most sophisticated users expect service errors to be logged in the Event Log.

The number of entries in the log file should be set only once at run-time and never changed. If it is the program will take care of it, but its more likely you may confuse your user if they visit the log file often.

Scaling

As another Code Project contributor has aptly pointed out; this method of logging does not scale up very much. If your logging needs exceed about 5 to 10k (gut numbers here...) of logging then you should consider alternate methods. This class essentially rewrites the entire log file each time, so there would be a performance and memory cost associated with it for large logs.  In the example above I have limited the log to 150 lines, and the default in the class is 99.   These numbers will have little impact on most any application that will benefit from this smaller logging requirement. If, however, your application needs 5-thousand lines - this is not the method for you.   

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

dirigo
Product Manager Weather Central, LP
United States United States
Product Manager/Senior Technology Manager at Weather Central, LP for 9+ years. Experienced VB.NET programmer and television broadcast infrastructure specialist.

Comments and Discussions

 
SuggestionEfficiency PinmemberJohn Brett8-Feb-13 1:14 
GeneralRe: Efficiency Pinmemberdirigo18-Feb-13 8:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411022.1 | Last Updated 18 Feb 2013
Article Copyright 2013 by dirigo
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid