Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a RichTextBox as a NLog Target in a WPF application?

I read the following posts but neither helped to just get the same efficient way of printing logs from NLog onto a RichTextBox control target as in Winforms.

How can I use NLog's RichTextBox Target in WPF application?

WPF: Binding RichTextBox to Logger Output

I also browsed the official forum but with no success (except suggestions to read the two above posts).

The idea would be to add the target as:

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

And within the WPF window with MyWPFWindowName as name, to add a RichTextBox control with rtbConsole. Even if I create the target programmatically after the winow has been loaded, it will not use the existing rtbConsole but create a new form.

So, your help is appreciated!

like image 964
Erwin Mayer Avatar asked Jul 07 '11 22:07

Erwin Mayer


3 Answers

I created a custom NLog target and linked it to a text box.

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 ();
        });
    }
}
like image 116
mafu Avatar answered Sep 22 '22 22:09

mafu


While this not really answer your question, but i believe this solution is better. An wpf control used to show NLog logs in listview. https://github.com/erizet/NlogViewer.

like image 38
Syaiful Nizam Yahya Avatar answered Sep 21 '22 22:09

Syaiful Nizam Yahya


As @mafu's answer suggests:

[Create] a custom NLog [memory] Target and [link] it to a TextBox.

This sample will 'link it' via event and Event Handler delegates.

Define a NLog Memory Target as a 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://stackoverflow.com/a/3603571/1366179
        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.
}

Usage in WPF Window Control

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

When you call on DoSomething (or do logger.Trace), your overloaded methods in your memory target will execute - which raises the event OnLog. Since you assigned an event handler, LogText, to OnLog in the construction of MainWindow, it will execute.

like image 42
Brett Caswell Avatar answered Sep 23 '22 22:09

Brett Caswell