如何在WPF应用程序中将RichTextBox作为NLog目标使用?

9

我阅读了以下帖子,但都没有帮助我找到与Winforms中从NLog打印日志到RichTextBox控件目标的相同高效方法。

如何在WPF应用程序中使用NLog的RichTextBox目标?

WPF:将RichTextBox绑定到记录器输出

我还浏览了官方论坛,但没有成功(除了建议阅读上述两篇文章之外)。

想法是将目标添加为:

<target xsi:type="RichTextBox" name="console"
     layout="${longdate:useUTC=true}|${level:uppercase=true}|${logger}::${message}"
     autoScroll="true"
     maxLines="1000000"
     controlName="rtbConsole"
     formName="MyWPFWindowName"
     useDefaultRowColoringRules="true">
</target>

在名为MyWPFWindowName的WPF窗口中,添加一个名为rtbConsole的RichTextBox控件。即使我在窗口加载后以编程方式创建目标,它也不会使用现有的rtbConsole而是创建一个新的窗体。
因此,非常感谢您的帮助!

你尝试了什么?发生了什么事情?请展示一些代码,而不是简单地说“不起作用”。 - Davide Piras
您在使用RichTextBox日志记录时遇到了哪些具体问题?“不够高效”这个描述有点模糊。您期望发生什么? - Adam Lear
请看我上面的编辑。如果您正在使用NLog和WPF,则问题非常容易复制。 - Erwin Mayer
将文本记录到指定名称的 Windows.Forms.Control.Text 属性控件中。 - Jake Berger
对于那些质疑问题是否有效的人,nlog richtextbox输出到winforms richtextbox ONLY,而这个问题问如何将nlog输出到wpf richtextbox。我一直想做同样的事情,但没有成功。 - Syaiful Nizam Yahya
4个回答

9

我创建了一个自定义的 NLog 目标,并将其链接到一个文本框。

public class NlogMemoryTarget : Target
{
    public Action<string> Log = delegate { };

    public NlogMemoryTarget (string name, LogLevel level)
    {
        LogManager.Configuration.AddTarget (name, this);

        LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", level, this));//This will ensure that exsiting rules are not overwritten
        LogManager.Configuration.Reload(); //This is important statement to reload all applied settings

        //SimpleConfigurator.ConfigureForTargetLogging (this, level); //use this if you are intending to use only NlogMemoryTarget  rule
    }

    protected override void Write (AsyncLogEventInfo[] logEvents)
    {
        foreach (var logEvent in logEvents) {
            Write (logEvent);
        }
    }

    protected override void Write (AsyncLogEventInfo logEvent)
    {
        Write (logEvent.LogEvent);
    }

    protected override void Write (LogEventInfo logEvent)
    {
        Log (logEvent.FormattedMessage);
    }
}


public partial class MainWindow
{
    private NlogMemoryTarget _Target;

    public MainWindow ()
    {
        InitializeComponent ();

        this.Loaded += (s, e) => {
            _Target = new NlogMemoryTarget ("text box output", LogLevel.Trace);
            _Target.Log += log => LogText (log);
        };
    }

    private void LogText (string message)
    {
        this.Dispatcher.Invoke ((Action) delegate () {
            this.MessageView.AppendText (message + "\n");
            this.MessageView.ScrollToEnd ();
        });
    }
}

1
@publicENEMY已修复。日志由委托发送,此处为文本框。 - mafu
1
@publicENEMY 不,那样会规避整个 NLog。相反,在 NLog 中像往常一样执行日志记录:private static _L = LogManager.GetCurrentClassLogger(); [...] _L.Info ("hello world");。您必须配置 _L 以将日志记录到答案代码中创建的 MemoryTarget,请参见此处:https://dev59.com/83A75IYBdhLWcg3wBkK1。 - mafu
1
现在看起来,这对于一个简单的工作来说似乎是很多的工作量。我想这是因为目标不存在于NLog提供的默认集中。;( - mafu
@publicENEMY 这是一个相当大的更改,我认为其中有一些不必要的调试文本?我正在尝试找到它,但在我的历史记录中没有看到它。 - mafu
1
@mafu SimpleConfigurator.ConfigureForTargetLogging (this, level); 会覆盖所有日志配置。如果您已经配置了一些日志记录,请改用 LogManager.Configuration.Reload();。此外,不要忘记添加 LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", level, this)); 以便与现有规则配合使用。 - stephen
显示剩余8条评论

6

虽然这并不是直接回答你的问题,但我认为这个解决方案更好。这是一个用于在列表视图中显示NLog日志的WPF控件。https://github.com/erizet/NlogViewer


3

正如@mafu的回答所建议的:

创建一个自定义的NLog内存Target并将其链接到一个TextBox

这个示例将通过事件和事件处理程序委托来'链接'它。

将NLog Memory Target定义为Type

public class NlogMemoryTarget : Target
{
    public event EventHandler<string> OnLog;

    public NlogMemoryTarget(string name, LogLevel level) : this(name, level, level) {}
    public NlogMemoryTarget(string name, LogLevel minLevel, LogLevel maxLevel)
    {
        // important: we want LogManager.Configuration property assign behaviors \ magic to occur
        //   see: https://dev59.com/83A75IYBdhLWcg3wBkK1#3603571
        var config = LogManager.Configuration;

        // Add Target and Rule to their respective collections
        config.AddTarget(name, this);
        config.LoggingRules.Add(new LoggingRule("*", minLevel, maxLevel, this));

        LogManager.Configuration = config;
    }

    [Obsolete]
    protected override void Write(AsyncLogEventInfo[] logEvents)
    {
        foreach (var logEvent in logEvents) {
            Write(logEvent.LogEvent);
        }
    }

    protected override void Write(AsyncLogEventInfo logEvent)
    {
        Write(logEvent.LogEvent);
    }

    protected override void Write(LogEventInfo logEvent)
    {
        OnLog(this, logEvent.FormattedMessage);
    }

    // consider overriding WriteAsyncThreadSafe methods as well.
}

在 WPF 窗口控件中的使用

public partial class MainWindow
{
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private NlogMemoryTarget _nlogMemoryTarget;

    public MainWindow()
    {
        InitializeComponent();

        _nlogMemoryTarget = new NlogMemoryTarget("TextBoxOutput", LogLevel.Trace);
        _nlogMemoryTarget.OnLog += LogText;
    }

    private void LogText(object sender, string message)
    {
        this.MessageView.AppendText($"{message}\n");
        this.MessageView.ScrollToEnd();
    }

    private void DoSomething() {
       logger.Trace("DoSomething called!");
    }
}

当您调用DoSomething(或执行logger.Trace)时,您的内存目标中的重载方法将被执行,从而引发事件OnLog。由于您在MainWindow的构造中将事件处理程序LogText分配给了OnLog,因此它将被执行。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接