본문 바로가기

언어/C#

Log4Net ScrollingTextBox

출처: http://www.claassen.net/geek/blog/2005/06/log4net-scrollingtextbox.html


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 log4netMemoryAppender. 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);  
        }  
    }  
}