Login


A Convenient LogFile Class

By Jonathan Wood on 12/21/2010
Language: C#
Technology: WinForms
Platform: Windows
License: CPOL
Views: 7,482
Desktop Development » Files & Directories » General » A Convenient LogFile Class

Demo Project Screenshot

Sample Log File

Download Demo Project Download Demo Project

Introduction

A common task is to write information to a log file so that it becomes possible to later review exactly what your program did. For example, if a program processes orders and then, later, there is a question about one of those orders, a log file might contains clues as to what the program did if it had logged the information to disk while the program was doing it.

LogFile Class

Listing 1 shows my LogFile class. This is by no means a complex class. Writing text to a file in .NET is a trivial task. But wrapping logging operations up in a class like this is a great convenience, and I was also able to add a couple of extras to make it even nicer.

The LogFile constructor takes the name of the file where log data will be written. The constructor is overloaded to also allow an append and keepOpen argument.

The append argument specifies if any existing data will be overwritten. And the keepOpen argument specifies if the file is kept open or if the file is opened and closed each time data is written to it. Keeping the file open can make logging data faster because you do not have the overhead of opening and closing the file for each line written. But opening and closing the file for each write reduces the resources used by your program between writes. Opening and closing the file for each write also makes it less likely that a program error will leave the file opened after your operation is complete.

The Write() method takes a string and writes the string to the log file. The string is prepended with the current date and time. In addition, the Write() method is overloaded to accept additional arguments. The string argument can contain placeholders such as "{0}" and "{1}" (just like String.Format() does) and the remaining arguments will specify the values for those placeholders. This makes it very easy to log a string that includes the values of variables or other conditions.

The public Filename and KeepOpen properties expose the current filename (read-only) and the current keep-open setting. In addition, there is a public IsLogging property. This property provides an easy way to turn logging on and off without change the rest of your code. If Write() is called when IsLogging is false, the method returns without doing anything.

The class inherits from IDisposable, making it easy to use the class in a using block, if appropriate.

Listing 1: The LogFile Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SoftCircuits
{
    // Class to log results
    public class LogFile : IDisposable
    {
        protected string _filename;
        protected StreamWriter _writer;
        protected bool _disposed;

        /// <summary>
        /// Gets the current filename
        /// </summary>
        public string Filename { get { return _filename; } }

        /// <summary>
        /// Gets or sets if the file is closed between each call to Write()
        /// </summary>
        public bool KeepOpen { get; set; }

        /// <summary>
        /// Gets or sets if logging is active or suppressed
        /// </summary>
        public bool IsLogging { get; set; }

        /// <summary>
        /// Constructs a new LogFile object. Data will be appended to
        /// any existing data. File will be closed between writes.
        /// </summary>
        /// <param name="filename">Name of log file to write.</param>
        public LogFile(string filename)
            : this(filename, true, false)
        {
        }

        /// <summary>
        /// Constructs a new LogFile object. File will be closed between
        /// writes.
        /// </summary>
        /// <param name="filename">Name of log file to write.</param>
        /// <param name="append">If true, data is written to the end of any
        /// existing data; otherwise, existing data is overwritten.</param>
        public LogFile(string filename, bool append)
            : this(filename, append, false)
        {
        }

        /// <summary>
        /// Constructs a new LogFile object.
        /// </summary>
        /// <param name="filename">Name of log file to write.</param>
        /// <param name="append">If true, data is written to the end of any
        /// existing data; otherwise, existing data is overwritten.</param>
        /// <param name="keepOpen">If true, performance is improved by
        /// keeping the file open between writes; otherwise, the file
        /// is opened and closed for each write.</param>
        public LogFile(string filename, bool append, bool keepOpen)
        {
            _filename = filename;
            KeepOpen = keepOpen;
            IsLogging = true;

            _writer = null;
            _disposed = false;

            // Delete existing file if !append (ignore exceptions)
            if (!append)
            {
                try
                {
                    File.Delete(filename);
                }
                catch { }
            }
        }

        /// <summary>
        /// Closes the current log file
        /// </summary>
        public void Close()
        {
            if (_writer != null)
            {
                _writer.Dispose();
                _writer = null;
            }
        }

        /// <summary>
        /// Writes a formatted string to the current log file
        /// </summary>
        /// <param name="fmt"></param>
        /// <param name="args"></param>
        public void Write(string fmt, params object[] args)
        {
            Write(String.Format(fmt, args));
        }

        /// <summary>
        /// Writes a string to the current log file
        /// </summary>
        /// <param name="s"></param>
        public void Write(string s)
        {
            if (IsLogging)
            {
                // Establish file stream if needed
                if (_writer == null)
                    _writer = new StreamWriter(_filename, true);

                // Write string with date/time stamp
                _writer.WriteLine(String.Format("{0:d} {0:T} : {1}", DateTime.Now, s));

                // Close file if not keeping open
                if (!KeepOpen)
                    Close();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                // Need to dispose managed resources if being called manually
                if (disposing)
                    Close();
                _disposed = true;
            }
        }

        #endregion
    }
}

Demo Project

The demo project I've included demonstrates using the LogFile class.

This simple project contains two buttons: a Start and a Stop button. When you click the Start button, a timer is enabled. With each timer tick, an entry is written to the log file (LogTest.log). Finally, when the Stop button is clicked, the timer is halted and the current log file is displayed.

Each time the Start button is clicked, any previous log data is overwritten.

Conclusion

That's about it for this simple yet convenient class. Perhaps you will find it useful.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.

I hike each week with my dogs Suki and Sasha. You can see my hiking blog at Hiking Salt Lake.