2013-01-01 10 views
16

Questo è in follow-up alla mia domanda: Flexible Logging Interface...Configurazione log4net TextBoxAppender (personalizzato appender) tramite file XML

Ora voglio scrivere un personalizzato log4net appender per un controllo TextBox su più righe, per i miei WinForms applicazione 2.0. Uno dei membri StackOverflow devdigital mi ha già segnalato a questo link:

TextBox Appender

Tuttavia, l'articolo non descrive come configurare un tale appender tramite un file XML. Il problema unico nella configurazione di questo appender è che dobbiamo passare un riferimento a un oggetto TextBox a questo appender.

Quindi è possibile configurarlo utilizzando un file Xml? Oppure tali appendici possono essere configurate solo programmaticamente? Quali sono le opzioni per renderlo configurabile o liberamente accoppiato possibile, potrebbe utilizzare una combinazione di file Xml e codice?

Grazie.

+0

nel file di configurazione XML, ci sono chiamati params. Non potresti usare questo per ottenere il nome della casella di testo? Quindi utilizzare: Control [] Items = Controls.Find ("textBoxLog4Net", false); ottenere l'accesso al runtime? –

+0

Ma per quanto ne so, Controls è una proprietà del Form; allora la domanda diventa a quale forma dovrebbe riferirsi l'appender, o in che modo l'appender otterrà un riferimento all'oggetto Form dal file Xml? – AllSolutions

risposta

16

Dipende dal modo in cui si configura log4net, ma di solito non ci saranno moduli creati (e quindi textBox) quando log4net legge la configurazione. Pertanto, è necessario creare proprietà per i nomi di moduli e caselle di testo. E dovresti controllare se il modulo è aperto e ha fornito la casella di testo prima di aggiungere l'evento di registrazione. Inoltre è meglio ereditare da AppenderSkeleton di implementare IAppender da zero:

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

La configurazione è semplice (log4net leggerà elementi XML e fornire i valori per le proprietà con gli stessi nomi):

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

non l'ho fatto fornire qualsiasi codice di gestione degli errori o codice relativo alla sincronizzazione multi-thread e ai thread, poiché la domanda riguarda la configurazione dell'appender.

+0

Sembra buono ... ma in che modo FormName sarà in grado di identificare quale istanza di una classe Form, nel caso in cui la stessa classe Form venga istanziata più volte. E avrò bisogno di portare le ultime poche righe a .Net 2.0 – AllSolutions

+0

@AllSolutions se ci sono diverse forme con lo stesso nome, quindi 'OpenForms [NomeFormato]' restituirà il modulo che è stato aperto per primo. Puoi cercare una casella di testo come questo '_textBox = form.Controls [TextBoxName] come TextBox' –

+0

Ottima risposta. Ma penso di essere più corretto, dovresti usare 'Layout.Format (writer, loggingEvent);' (con StringWriter appropriato costruito) –

7

Ho modificato l'appender per lavorare con il multithreading. Inoltre, ho allegato la configurazione del codice.

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

Configurazione:

  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

Se l'evento di log proviene da fuori del thread dell'interfaccia utente, si blocca –

+0

@AlexeyZimarev: it sembra che devi chiamare BeginInvoke invece. Vedi http://social.msdn.microsoft.com/Forums/vstudio/en-US/a35e5298-33c4-4461-b956-bf265484219e/controlinvoke-hangs-the-application per i dettagli. –

+0

Sì, BeginInvoke risolve il problema sospeso. Guarda l'esempio completo che ho pubblicato. – klodoma

2

La linea attuale che aggiunge alla casella di testo dovrebbe essere ...

_textBox.AppendText(RenderLoggingEvent(loggingEvent)); 

... se vuoi approfittare di un layout di pattern. Altrimenti, invia solo il testo del messaggio (il layout predefinito).

12

qui è una versione aggiornata di tutti i commenti superiori: thread-safe, non bloccare l'applicazione e utilizza il modello di conversione:

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

} 

La configurazione, collocare questo 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

Perché non mi hai mostrato questo orario e ORE fa? 10 volte più semplice della mostruosità che stavo costruendo E si aggancia al file di configurazione. Modifiche minori per funzionare anche con RichTextBox. Spot on. – WernerCD

+0

Solo un commento per chiunque lo faccia in VB.Net invece che in C#: FormName e TextBoxName devono essere dichiarati come proprietà affinché funzioni. – JonS

0

Il campione sopra riportato di Klodoma è abbastanza buono. Se cambi la casella di testo in un richtextbox, puoi fare di più con l'output.Ecco il codice per messaggi in codice colore per livello:

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

Se davvero si vuole, i colori potrebbero essere mappati dal config log4net nello stesso modo come il ColorConsoleAppender, ma lascio che per la prossima coder a imbattersi in questo esempio ...

0

Preferirei l'approccio seguente se si desidera eseguire la registrazione in più punti nell'applicazione . Questo approccio offre la flessibilità di cambiare dinamicamente l'istanza di controllo tramite codice.

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

Il codice per assegnare l'istanza di testo. Fatelo prima di iniziare il processo che esegue la registrazione.

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

La configurazione

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