24

With the logging system Serilog is it possible to display the log in a text box, or a list view or some other GUI control; what is the mechanism to get it there?

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
TheColonel26
  • 2,618
  • 7
  • 25
  • 50
  • how are you writing to the log, what key word search are you performing.. perhaps you should use the EventLog since you can write something to easily read from the log.. or store the log entries as xml in a database.. https://logentries.com/doc/video-tutorials/ – MethodMan Feb 23 '16 at 03:06
  • 1
    The question and answer were very specifically helpful to me, and I'd like to expand on Nick's work with a more "complete" solution. Further, at no point in the question or the answer is there a request for an external ("off-site") resource. How to petition to reopen? – Marc L. Jul 11 '18 at 21:22
  • @MarcL.This is my question. I have no idea why it was closed as Off-Topic. It seems very on topic to me. While it was a little vague at the time. I didn't know enough about the standard features of most loggers to make the question more specific. If I had known more about the standard features of most loggers, I wouldn't have needed to ask the question in the first place. I voted to reopen it. There is a link right next to the edit link. – TheColonel26 Aug 09 '18 at 11:27
  • There is now a dedicated sink for logging to WPF textboxes: [Serilog.Sinks.WPF](https://www.nuget.org/packages/Serilog.Sinks.WPF) – Gavin Ward Jun 25 '21 at 12:33

4 Answers4

27

Serilog provides the ILogEventSink extension point for this. You can use it to add log events to a list, or render them onto a TextWriter somewhere.

There are varying degrees of sophistication possible, depending on your needs. This one (based on ConsoleSink) should get you going:

class InMemorySink : ILogEventSink
{
    readonly ITextFormatter _textFormatter = new MessageTemplateTextFormatter("{Timestamp} [{Level}] {Message}{Exception}");

    public ConcurrentQueue<string> Events { get; } = new ConcurrentQueue<string>();

    public void Emit(LogEvent logEvent)
    {
        if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
        var renderSpace = new StringWriter();
        _textFormatter.Format(logEvent, renderSpace);
        Events.Enqueue(renderSpace.ToString());
    }
}

Then hooking it up means creating an instance:

var sink = new InMemorySink();
Log.Logger = new LoggerConfiguration()
    .WriteTo.Sink(sink)
    .CreateLogger();

Then, it's up to your app to figure out how to load sink.Events into the text box when it is shown.

Adding some code to limit the size of the queue in Emit() is a good idea.

Nicholas Blumhardt
  • 30,271
  • 4
  • 90
  • 101
  • 1
    **Nicholas Blumhardt:** It would be nice if you could give some guidance on "how to load sink.Events into the text box" – Musa Biralo Nov 06 '22 at 23:02
12

You might want to check out Serilog.Sinks.RichTextBox sink which can write logs to a RichTextBox control with colors, formatting, etc. similar to what you'd expect in a Console application (you have full control of the colors through themes).

Serilog.Sinks.RichTextBox

The sink was originally built for WPF applications, but can easily be used with Windows Forms application as well and there's an example in the repository that shows how to use it with Windows Forms.

Disclaimer: I'm the author of the Serilog.Sinks.RichTextBox sink.

C. Augusto Proiete
  • 24,684
  • 2
  • 63
  • 91
  • I am using your code (thanks), but do get `System.InvalidOperationException: 'Cross-thread operation not valid: Control 'rtb_Log' accessed from a thread other than the thread it was created on.'` Clear what the problem is, but why do I get it. Do I need to consider something when "adding" the logger? Any idea? – Horst Walter May 11 '23 at 16:15
  • @HorstWalter The best way to go about this is for you to open an issue in the repo with details on how to reproduce this error you see - https://github.com/serilog-contrib/serilog-sinks-richtextbox/issues – C. Augusto Proiete Jun 09 '23 at 02:37
6

You can use Serilog.Sinks.TextWriter

You can display the logs on the multi-line textbox like below;

namespace SerilogSample
{
    public partial class Form1 : Form
    {
        private StringWriter _messages;
        ILogger _logger;
        public Form1()
        {
            InitializeComponent();

            _messages = new StringWriter();
            _logger = new LoggerConfiguration()
                .WriteTo.TextWriter(_messages)
                .CreateLogger();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            _logger.Information("Clicked!");
            textBox1.Text = _messages.ToString();
        }
    }
}

The result;

enter image description here

Tolga Cakir
  • 725
  • 1
  • 7
  • 13
0

I haven't tried it, but this package looks promising for Windows Forms:

I also started with the accepted answer and came up with my own bare-bones Windows Forms TextBox sink:

Custom sink

    public class TextBoxSink : ILogEventSink
{
    private readonly TextBox textBox;
    private readonly LogEventLevel minLevel;
    private readonly ITextFormatter formatter;

    public TextBoxSink(
        TextBox textBox,
        LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,
        string outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u5}: {Message:lj}{NewLine}")
    {
        this.textBox = textBox;
        minLevel = restrictedToMinimumLevel;
        formatter = new MessageTemplateTextFormatter(outputTemplate);
    }

    public void Emit(LogEvent logEvent)
    {
        if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
        if (logEvent.Level < minLevel) return;

        var sw = new StringWriter();
        formatter.Format(logEvent, sw);

        textBox.InvokeIfRequired(() => textBox.AppendText(sw.ToString()));
    }
}

Extension method (used above to manipulate the text box from any thread; source)

public static class WinFormsControlExtensions
{
    public static void InvokeIfRequired(this Control c, MethodInvoker action)
    {
        if (c.InvokeRequired)
        {
            c.Invoke(action);
        }
        else
        {
            action();
        }
    }
}

Usage

public partial class Form1 : Form
{
    public System.Windows.Forms.TextBox LogTextBox;

    private void Form1_Shown(object sender, EventArgs e)
    {
        Logger.Log = new LoggerConfiguration()
            .WriteTo.Sink(new TextBoxSink(LogTextBox))
            .CreateLogger();
    }
}
MarredCheese
  • 17,541
  • 8
  • 92
  • 91