Configurazione di log4net TextBoxAppender (appender personalizzato) tramite file Xml

Questo è in seguito alla mia domanda: interfaccia di registrazione flessibile …

Ora voglio scrivere un appender log4net personalizzato per un TextBox multilinea, per la mia applicazione WinForms 2.0. Uno dei membri di StackOverflow devdigital mi ha già indirizzato 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 object TextBox a questo appender.

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

Grazie.

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 che 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à gli elementi xml e fornirà i valori per le proprietà con gli stessi nomi):

            

Non ho fornito alcun codice di gestione degli errori o codice relativo alla sincronizzazione multi-thread e dei thread, perché la domanda riguarda la configurazione dell’appender.

ecco una versione aggiornata di tutti i commenti in alto: thread safe, non blocca l’applicazione e usa 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, posto questo in app.config:

             

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

La linea effettiva che si 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).

Il campione sopra di Klodoma è abbastanza buono. Se cambi la casella di testo in un richtextbox, puoi fare di più con l’output. Ecco un codice per i messaggi con 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 lo desideri, i colors potrebbero essere mappati dalla configurazione di log4net allo stesso modo di ColorConsoleAppender, ma lascio che il prossimo codificatore inciampi su questo campione …

Preferirei l’approccio seguente se si desidera eseguire la registrazione in più punti della propria applicazione. Questo approccio offre la flessibilità di modificare dynamicmente l’istanza di controllo tramite il 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 textbox. 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