2013-01-01 62 views
16

这是后续对我的问题配置log4net的TextBoxAppender(自定义的appender):Flexible Logging Interface...通过XML文件

我现在想写一个自定义log4net的Appender的一个多行TextBox,我的WinForms 2.0应用程序。其中的StackOverflow成员devdigital已经向我指出这个链接:

TextBox Appender

不过,文章并没有说明如何通过一个XML文件配置这样一个appender。配置这个appender的唯一问题是我们需要将一个对TextBox对象的引用传递给这个appender。

那么是否可以使用Xml文件来配置它?或者这样的appender只能以编程方式配置?有什么选择使它尽可能可配置或松散耦合,可能是使用Xml文件和代码的组合?

谢谢。

+0

在XML配置文件中,有一个名为PARAMS。你不能用这个来获取文本框的名称吗?然后使用:Control [] Items = Controls.Find(“textBoxLog4Net”,false);在运行时获得访问权限? –

+0

但据我所知,Controls是Form的一个属性;那么问题就变成了appender应该引用哪个表单,或者appender如何从Xml文件中获取对Form对象的引用? – AllSolutions

回答

16

这取决于如何配置log4net的方式,但通常在log4net读取配置时将不会创建任何表单(并且因此文本框)。所以,你需要为表单和文本框名称创建属性。你应该检查表单是否被打开,并且在追加记录事件之前它已经提供了文本框。此外,它是更好地从AppenderSkeleton继承不是从头开始实施IAppender

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

配置简单(log4net的将读取XML元素和具有相同名称的属性提供值):

​​

我没有提供与多线程和线程同步有关的任何错误处理代码或代码,因为问题是关于appender配置的。

+0

看起来不错......但FormName如何能够识别Form类的哪个实例,以防万一同一个Form类被多次实例化。我需要将最后几行移植到.Net 2.0 – AllSolutions

+0

@AllSolutions中,如果有几个同名的表单,那么'OpenForms [FormName]'将返回首先打开的表单。你可以像这样搜索文本框'_textBox = form.Controls [TextBoxName] TextBox' –

+0

优秀的答案。但我认为更正确的是,你应该使用'Layout.Format(writer,loggingEvent);'(用适当的StringWriter构造) –

7

我修改了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); 
     }); 
    } 
} 

配置:

  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

如果日志事件来自UI线程之外,它会挂起 –

+0

@AlexeyZimarev:它看起来你必须调用BeginInvoke。有关详细信息,请参阅http://social.msdn.microsoft.com/Forums/vstudio/en-US/a35e5298-33c4-4461-b956-bf265484219e/controlinvoke-hangs-the-application。 –

+0

是的,BeginInvoke解决了悬而未决的问题。查看我发布的完整示例。 – klodoma

2

是追加到文本框应该是实际的行...

_textBox.AppendText(RenderLoggingEvent(loggingEvent)); 

...如果你想利用模式布局。否则,它只是发送消息的文本(默认布局)。

12

这里是所有上评论的更新版本:线程安全的,不会锁定应用程序和使用转换模式:

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

} 

的配置,把这个在的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

你为什么不在这几个小时和几个小时前给我看?比我正在构建的怪物更简单10倍,它挂钩到配置文件中。与RichTextBox一起使用的小修改也是如此。发现。 – WernerCD

+0

只需在VB.Net而不是C#中进行此操作的任何评论:FormName和TextBoxName必须声明为Property才能使用。 – JonS

0

以上由Klodoma样品是相当不错的。如果将文本框更改为richtextbox,则可以使用输出进行更多操作。下面是一级一些代码,以颜色编码的消息:

 System.Drawing.Color text_color; 

     switch (loggingEvent.Level.DisplayName.ToUpper()) 
     { 
      case "FATAL": 
       text_color = System.Drawing.Color.DarkRed; 
       break; 

      case "ERROR": 
       text_color = System.Drawing.Color.Red; 
       break; 

      case "WARN": 
       text_color = System.Drawing.Color.DarkOrange; 
       break; 

      case "INFO": 
       text_color = System.Drawing.Color.Teal; 
       break; 

      case "DEBUG": 
       text_color = System.Drawing.Color.Green; 
       break; 

      default: 
       text_color = System.Drawing.Color.Black; 
       break; 
     } 

     _TextBox.BeginInvoke((MethodInvoker)delegate 
     { 
      _TextBox.SelectionColor = text_color; 
      _TextBox.AppendText(RenderLoggingEvent(loggingEvent)); 
     }); 

如果你真的想要,颜色可以从相同的方式为ColorConsoleAppender log4net的配置可以映射,但是我留到下一个编码器来绊倒这个样本...

0

如果你想在你的 应用程序的多个地方做日志记录,我宁愿采用下面的方法。这种方法可以灵活地通过代码动态地更改控制实例 。

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

的代码分配的文本框实例。在开始执行日志记录的过程之前执行此操作。

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

配置

<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>