9

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!

Community
  • 1
  • 1
Erwin Mayer
  • 18,076
  • 9
  • 88
  • 126
  • what did you try so far and what happens? Please show some code instead of simply saying does not work. – Davide Piras Jul 07 '11 at 23:19
  • What specific issues are you experiencing with logging to RichTextBox? "It's not as efficient" is pretty vague. What are you expecting to happen? – Adam Lear Jul 07 '11 at 23:20
  • Please see my edit above. The issue is quite straightforward to replicate if you are using NLog and WPF. – Erwin Mayer Jul 08 '11 at 10:26
  • [`Logs text to Windows.Forms.Control.Text property control of specified Name.`](http://nlog-project.org/doc/2.0/netfx20/html/T_NLog_Targets_FormControlTarget.htm) – Jake Berger Dec 23 '11 at 15:37
  • For those questioning whether the question is valid or not, nlog richtextbox output to winforms richtextbox ONLY and this question asked how can you output nlog ot wpf richtextbox. ive been meaning to do the same thing without success. – Syaiful Nizam Yahya Mar 17 '13 at 05:17

4 Answers4

9

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 ();
        });
    }
}
Risky Pathak
  • 598
  • 5
  • 16
mafu
  • 31,798
  • 42
  • 154
  • 247
  • how do you actually add log? – Syaiful Nizam Yahya Mar 16 '13 at 05:50
  • 1
    @publicENEMY fixed. The log is sent by delegate, to a text box in this case. – mafu Mar 17 '13 at 00:44
  • im sorry for being a noob. could you add a single line example of how to add logs? is it LogText("my log message");? – Syaiful Nizam Yahya Mar 17 '13 at 05:06
  • 1
    @publicENEMY No, that would circumvent the whole NLog. Instead, perform logging as usual in NLog: `private static _L = LogManager.GetCurrentClassLogger(); [...] _L.Info ("hello world");`. You have to configure _L to log to the MemoryTarget that was created in the answer code, see here: http://stackoverflow.com/q/3516242/39590 – mafu Mar 17 '13 at 11:35
  • 1
    Now that I look at it, this seems like a whole lot of work for a simple job. I guess that's because the target does not exist in the default set provided by NLog ;( – mafu Mar 17 '13 at 11:39
  • did you get my edit or you dislike my edit? thanks. just askin. – Syaiful Nizam Yahya Mar 19 '13 at 21:26
  • @publicENEMY It was a pretty extensive change, I think it had some debug text in it that was not required? I'm trying to find it but I don't see it in my history. – mafu Mar 20 '13 at 10:24
  • 1
    @mafu `SimpleConfigurator.ConfigureForTargetLogging (this, level);` overwrites all logging configurations. If you have some logging already configured, you should use `LogManager.Configuration.Reload();` instead. Also don't forget `LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", level, this));` in order to work with existing rules. – S. Dixon Nov 13 '14 at 20:29
  • Editing @mafu's answer to have correct order of statements in case you need to work with existing rules. `public MemoryTargetEx(string name, LogLevel level) { LogManager.Configuration.AddTarget(name, this); LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", level, this)); LogManager.Configuration.Reload(); }` – Risky Pathak Apr 30 '16 at 07:50
  • @RiskyPathak Feel free to edit that into the answer (I think you get +2 rep from that, too) – mafu Apr 30 '16 at 08:45
  • Would you mind to improve your code so we can see colorized messages at least based on log types like trace, debug, info and etc. Thank you! – NoWar May 09 '16 at 13:37
  • @mafu I added a separate answer to reflect on some important changes and considerations, especially in the LogManager.Configuration scope. Your code snippet in it's current form wasn't usable for me. I was originally going to modify your answer to be the answer I provided, but it was quite a few changes. If you update your answer to resemble mine, let me know - I will remove my answer. – Brett Caswell Nov 26 '19 at 16:11
  • `LogManager.Configuration.Reload()` doesn't help, see @BrettCaswell 's [answer](https://stackoverflow.com/a/59055005/1844247). – Serg Dec 08 '19 at 19:03
6

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.

Syaiful Nizam Yahya
  • 4,196
  • 11
  • 51
  • 71
3

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.

Brett Caswell
  • 1,486
  • 1
  • 13
  • 25
0

I agree that the 2 referenced links in the question are not optimal. (I also would NOT use those solutions.)

Here's what I'd try:

Write a custom (WPF) control target using an algorithm similar to NLog's FormControlTarget.

Ensure to register your new target.
Also, NLog's FormHelper may be helpful.

Most of the WinForms code should be easily convertible to WPF.

Jake Berger
  • 5,237
  • 1
  • 28
  • 22