Log4Net ScrollingTextBox

I use log4net for most of my apps. I did similar things in all the languages i've worked with. I just like having a way to verbosely spew information into the ether so that i could use for analysing code when something gest wonky without having to step through the code or go uncommenting loads of Console.Writeline() calls.

On unix, i just always have a terminal open for tailing the log files and while i often do the same under Windows, using a Cygwin bash shell, i've started putting debug windows into my Windows.Forms apps, just because it's nice and convenient. To make this work, you simply use the log4net MemoryAppender. For display purposed, I use a TextBox, but do some legwork to limit the size of the text logged and to make sure it stays scrolled at the bottom.

public class FormX : System.Windows.Forms.Form
{
    #region Static Members ####################################################
    private static readonly ILog log = LogManager.GetLogger(typeof(FormX));
    #endregion

    #region Members Variables #################################################
    private bool logWatching = true;
    private log4net.Appender.MemoryAppender logger;
    private Thread logWatcher;
    /// <summary>
    /// The TextBox for our logging messages
    /// </summary>
    private System.Windows.Forms.TextBox mLog;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;
    #endregion

    #region Constructors #################################################
    public FormX()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        this.Closing += new CancelEventHandler(FormX_Closing);
        logger = new log4net.Appender.MemoryAppender();

        // Could use a fancier Configurator if you don't want to catch every message
        log4net.Config.BasicConfigurator.Configure(logger);

        // Since there are no events to catch on logging, we dedicate
        // a thread to watching for logging events
        logWatcher = new Thread(new ThreadStart(LogWatcher));
        logWatcher.Start();
    }
    #endregion

    // [...]

    private void FormX_Closing(object sender, CancelEventArgs e)
    {
        // Gotta stop our logging thread
        logWatching = false;
        logWatcher.Join();
    }

    private void LogWatcher()
    {
        // we loop until the Form is closed
        while(logWatching)
        {
            LoggingEvent[] events = logger.Events;
            if( events != null && events.Length > 0 )
            {
                // if there are events, we clear them from the logger,
                // since we're done with them
                logger.Clear();
                foreach( LoggingEvent ev in events )
                {
                    StringBuilder builder;
                    // the line we want to log
                    string line = ev.LoggerName + ": " + ev.RenderedMessage+"\\r\\n";
                    // don't want to grow this log indefinetly, so limit to 100 lines
                    if( mLog.Lines.Length > 99 )
                    {
                        builder = new StringBuilder(mLog.Text);
                        // strip out a nice chunk from the beginning
                        builder.Remove(0,mLog.Text.IndexOf('\\r',3000)+2);
                        builder.Append(line);
                        mLog.Clear();
                        // using AppendText since that makes sure the TextBox stays
                        // scrolled at the bottom
                        mLog.AppendText(builder.ToString());
                    }
                    else
                    {
                        mLog.AppendText(line);
                    }
                }
            }
            // nap for a while, don't need the events on the millisecond.
            Thread.Sleep(500);
        }
    }
}