Configuring log4net TextBoxAppender (custom appender) via Xml file

后端 未结 7 2083

This is in followup to my question: Flexible Logging Interface...

I now want to write a custom log4net appender for a multiline TextBox, for my WinForms 2.0 applicat

相关标签:
7条回答
  • 2020-12-03 05:40

    Here is a WPF/XAML version of klodoma's answer

      public class TextBoxAppender : AppenderSkeleton {
        private TextBox AppenderTextBox { get; set; }
        private Window window;
    
        public string WindowName { get; set; }
        public string TextBoxName { get; set; }
    
        private T FindControl<T>(Control root, string textBoxName) where T:class{
            if (root.Name == textBoxName) {
                return root as T;
            }
    
            return root.FindName(textBoxName) as T;
        }
    
        protected override void Append(log4net.Core.LoggingEvent loggingEvent) {
            if (window == null || AppenderTextBox == null) {
                if (string.IsNullOrEmpty(WindowName) ||
                    string.IsNullOrEmpty(TextBoxName))
                    return;
    
                foreach (Window window in Application.Current.Windows) {
                    if (window.Name == WindowName) {
                        this.window = window;
                    }
                }
                if (window == null)
                    return;
    
                AppenderTextBox = FindControl<TextBox>(window, TextBoxName);
                if (AppenderTextBox == null)
                    return;
    
                window.Closing += (s, e) => AppenderTextBox = null;
            }
            window.Dispatcher.BeginInvoke( new Action(delegate {
                AppenderTextBox.AppendText(RenderLoggingEvent(loggingEvent));
            }));
        }
    

    and the log config

     <appender name="textboxAppender" type="Namespace.TextBoxAppender, Namespace">
    <windowName value="Viewer"/>
    <textBoxName value="LogBox"/>
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
    </layout>
    

    Don't forget to give your window a name (must be different to the window type name)

    0 讨论(0)
  • 2020-12-03 05:46

    I would prefer the below approach if you want to do the logging at multiple places in your application. This approach gives the flexibility to change the control instance dynamically through code.

    TextBoxAppender

    public class TextBoxAppender : AppenderSkeleton
        {
            public RichTextBox RichTextBox { get; set; }
    
            protected override void Append(LoggingEvent loggingEvent)
            {
                Action operation = () => { this.RichTextBox.AppendText(RenderLoggingEvent(loggingEvent)); };
                this.RichTextBox.Invoke(operation);
            }
        }
    

    The code to assign the textbox instance. Do this before you start the process that does the logging.

     var appender = LogManager.GetRepository().GetAppenders().Where(a => a.Name == "TextBoxAppender").FirstOrDefault();
     if (appender != null)
           ((TextBoxAppender)appender).RichTextBox = this.richTextBoxLog;
    

    The configuration

    <log4net debug="false">
        <appender name="TextBoxAppender" type="SecurityAudit.UI.TextBoxAppender">
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
          </layout>
        </appender>
        <root>
          <priority value="DEBUG" />
          <appender-ref ref="TextBoxAppender" />
        </root>
      </log4net>
    
    0 讨论(0)
  • 2020-12-03 05:48

    The actual line that appends to the textbox should be...

    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
    

    ...if you want to take advantage of a pattern layout. Otherwise, it just sends the text of the message (the default layout).

    0 讨论(0)
  • 2020-12-03 05:49

    It depends on the way how you configure log4net, but usually there will be no forms created(and thus textBoxes) when log4net reads configuration. So, you need to create properties for form and textbox names. And you should check if form is opened and it has provided textbox just before appending logging event. Also it's better to inherit from AppenderSkeleton than implement IAppender from scratch:

    public class TextBoxAppender : AppenderSkeleton
    {
        private TextBox _textBox;
        public string FormName { get; set; }
        public string TextBoxName { get; set; }
    
        protected override void Append(LoggingEvent loggingEvent)
        {
            if (_textBox == null)
            {
                if (String.IsNullOrEmpty(FormName) || 
                    String.IsNullOrEmpty(TextBoxName))
                    return;
    
                Form form = Application.OpenForms[FormName];
                if (form == null)
                    return;
    
                _textBox = form.Controls[TextBoxName] as TextBox;
                if (_textBox == null)
                    return;
    
                form.FormClosing += (s, e) => _textBox = null;
            }
    
            _textBox.AppendText(loggingEvent.RenderedMessage + Environment.NewLine);
        }
    }
    

    Configuration is simple (log4net will read xml elements and provide values for properties with same names):

    <appender name="textbox" type="Foo.TextBoxAppender, Foo">
      <formName value="Form1"/>
      <textBoxName value="textBox1"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %-5level %logger - %message" />
      </layout>      
    </appender>
    <root>
      <level value="INFO" />
      <appender-ref ref="textbox"/>
    </root>
    

    I didn't provide any error handling code or code related to multi-threading and threads synchronization, because question is about appender configuration.

    0 讨论(0)
  • 2020-12-03 05:50

    here is an updated version of all upper comments: thread safe, doesn't lock the application and uses the conversion pattern:

    namespace MyNamespace
    {
    
        public class TextBoxAppender : AppenderSkeleton
        {
            private TextBox _textBox;
            public TextBox AppenderTextBox
            {
                get
                {
                    return _textBox;
                }
                set
                {
                    _textBox = value;
                }
            }
            public string FormName { get; set; }
            public string TextBoxName { get; set; }
    
            private Control FindControlRecursive(Control root, string textBoxName)
            {
                if (root.Name == textBoxName) return root;
                foreach (Control c in root.Controls)
                {
                    Control t = FindControlRecursive(c, textBoxName);
                    if (t != null) return t;
                }
                return null;
            }
    
            protected override void Append(log4net.Core.LoggingEvent loggingEvent)
            {
                if (_textBox == null)
                {
                    if (String.IsNullOrEmpty(FormName) ||
                        String.IsNullOrEmpty(TextBoxName))
                        return;
    
                    Form form = Application.OpenForms[FormName];
                    if (form == null)
                        return;
    
                    _textBox = (TextBox)FindControlRecursive(form, TextBoxName);
                    if (_textBox == null)
                        return;
    
                    form.FormClosing += (s, e) => _textBox = null;
                }
                _textBox.BeginInvoke((MethodInvoker)delegate
                {
                    _textBox.AppendText(RenderLoggingEvent(loggingEvent));
                });
            }
        }
    
    }
    

    The configuration, place this in app.config:

    <appender name="textboxAppender" type="MyNamespace.TextBoxAppender, MyNamespace">
      <formName value="MainForm"/>
      <textBoxName value="textBoxLog"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="DEBUG" />
      <appender-ref ref="RollingFileAppender" />
      <appender-ref ref="textboxAppender" />
    </root>   
    
    0 讨论(0)
  • 2020-12-03 05:50

    I modified the appender to work with multithreading. Also, I attached the code configuration.

    Regards, Dorin

    Appender:

    public class TextBoxAppender : AppenderSkeleton
    {
        private TextBox _textBox;
        public TextBox AppenderTextBox
        {
            get
            {
                return _textBox;
            }
            set
            {
                _textBox = value;
            }
        }
        public string FormName { get; set; }
        public string TextBoxName { get; set; }
    
        private Control FindControlRecursive(Control root, string textBoxName)
        {
            if (root.Name == textBoxName) return root;
            foreach (Control c in root.Controls)
            {
                Control t = FindControlRecursive(c, textBoxName);
                if (t != null) return t;
            }
            return null;
        }
    
        protected override void Append(log4net.Core.LoggingEvent loggingEvent)
        {
            if (_textBox == null)
            {
                if (String.IsNullOrEmpty(FormName) ||
                    String.IsNullOrEmpty(TextBoxName))
                    return;
    
                Form form = Application.OpenForms[FormName];
                if (form == null)
                    return;
    
                _textBox = (TextBox)FindControlRecursive(form, TextBoxName);
                if (_textBox == null)
                    return;
    
                form.FormClosing += (s, e) => _textBox = null;
            }
            _textBox.Invoke((MethodInvoker)delegate
            {
                _textBox.AppendText(loggingEvent.RenderedMessage + Environment.NewLine);
            });
        }
    }
    

    Configuration:

               var textBoxAppender = new Util.TextBoxAppender();
            textBoxAppender.TextBoxName = "textLog";
            textBoxAppender.FormName = "MainTarget";
            textBoxAppender.Threshold = log4net.Core.Level.All;
            var consoleAppender = new log4net.Appender.ConsoleAppender { Layout = new log4net.Layout.SimpleLayout() };
            var list = new AppenderSkeleton[] { textBoxAppender, consoleAppender };
            log4net.Config.BasicConfigurator.Configure(list);
    
    0 讨论(0)
提交回复
热议问题