Настройка log4net TextBoxAppender (пользовательский appender) через файл Xml
В ответ на мой вопрос: Гибкий интерфейс ведения журнала...
Теперь я хочу написать пользовательское приложение log4net для многострочного TextBox для моего приложения WinForms 2.0. Один из членов StackOverflow devdigital уже указал мне на эту ссылку:
TextBox Appender
Однако статья не описывает, как настроить такой appender через файл Xml. Единственная проблема при настройке этого приложения заключается в том, что нам нужно передать ссылку на объект TextBox этому приложению.
Итак, возможно ли это настроить с помощью файла Xml? Или такие приложения могут быть настроены только программно? Каковы возможности сделать его как настраиваемым или слабо связанным, возможно, можно использовать комбинацию Xml файла и кода?
Спасибо.
Ответы
Ответ 1
Это зависит от способа настройки 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 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>
Я не предоставлял код обработки ошибок или код, связанный с многопоточной и потоковой синхронизацией, потому что вопрос касается конфигурации appender.
Ответ 2
вот обновленная версия всех верхних комментариев: потокобезопасная, не блокирует приложение и использует шаблон преобразования:
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>
Ответ 3
Я изменил приложение для работы с многопоточным процессом.
Кроме того, я прикрепил конфигурацию кода.
С уважением,
Дорин
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);
Ответ 4
Действительная строка, добавляемая в текстовое поле, должна быть...
_textBox.AppendText(RenderLoggingEvent(loggingEvent));
... если вы хотите использовать макет шаблона. В противном случае он просто отправляет текст сообщения (макет по умолчанию).
Ответ 5
Над образцом Клодома неплохо. Если вы измените текстовое поле на 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));
});
Если вы действительно хотите, цвета могут быть сопоставлены с конфигурацией log4net так же, как ColorConsoleAppender, но я оставляю это для следующего кодера, чтобы наткнуться на этот образец...
Ответ 6
Я бы предпочел использовать подход ниже, если вы хотите выполнить регистрацию в нескольких местах в своем
выражение. Такой подход позволяет гибко изменять динамический экземпляр элемента управления
через код.
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>
Ответ 7
Вот WPF/XAML версия ответа клодома
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));
}));
}
и лог конфиг
<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>
Не забудьте дать вашему окну имя (должно отличаться от имени типа окна)