3

I am trying to learn using Log4Net to be able to improve my logging technique in my program. I have read the documents provided in the Log4Net website, however I have these questions in mind.

This is the most simple output shown in the example program on the Log4Net website produced by BasicConfigurator.Configure().

0    [main] INFO  MyApp        - Entering application.
36   [main] DEBUG Com.Foo.Bar  - Did it again!
51   [main] INFO  MyApp        - Exiting application.

My program already has a pane (in debug mode) to show the flow of the program which cycles through numerous asynchronous attempts. So when I run it I can see the flow of the program (typical mass debugging method).

In this level, as far as I know by using Log4Net I can collect logs in those moments and these logs can be appended into so many different output methods such as console, external file, db and etc. This leads to double ups in the code. One line to output the logs to the UI and the other to log for Log4Net, in each test point.

Is there any method that I can output the contents of one of the appenders, such as the one shown above, to a variable of a type string or similar to be able to show it at runtime?

I am not sure how you can see the output of the console in the WPF application. And it seems quite funny to output the Log4Net into a file and then print the contests of it back to a string.


Update

The output of the Console Appender can be seen in the Output window of Visual Studio when Show output from: drop-down is set to "Debug". Many thanks to @Aron for pointing it out.

Mehrad
  • 4,093
  • 4
  • 43
  • 61

2 Answers2

5

Log4net comes with the UDPAppender, which is a low profile way of transmitting the content of your logs to any listener on the network. I would recommend using it as a way to push your logs to your UI.

<appender name="UdpAppender" type="log4net.Appender.UdpAppender">
  <remoteAddress value="127.0.0.1" /> <!-- local for the demo on my machine --> 
  <remotePort value="10000" />
  <layout type="log4net.Layout.PatternLayout" value="%-5level %logger [%ndc] - %message%newline" />
</appender>

In the UI, hooking a listener in a background thread of your WPF app to these messages lets you display them however you like, for example as a sliding window on the x latest logs, or pushing all the logs to the pane you mentioned in your question.

// launch this in a background thread
private static void UDPListen()
{
    IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
    var udpClient = new UdpClient(10000);

    while (true)
    {
        var buffer = udpClient.Receive(ref remoteEndPoint);
        var loggingEvent = System.Text.Encoding.Unicode.GetString(buffer);
        // write the log to your pane
    }
}

Interestingly, this is an approach many "dashboards" for log4net use in order to monitor applications, so if you want you can remove the pane in a release build of your application and look at your app from another machine on the network.


EDIT: Writing to the console in any application writes to the standard output stream of the program, which you can pipe into other processes; for example launching your wpf app that writes to the console with the following syntax would log the messages to the log.txt file

myApp.exe > log.txt

(kudos to @Aron for the following example) As a much more useful example Visual studio pipes the stdout of the program you are attached to into its Output window, so anything sent to the ConsoleAppender will be present in the Output window.

So a console appender can be useful if you reuse the default output of the program, but nothing forces you to use it.

samy
  • 14,832
  • 2
  • 54
  • 82
  • Thanks for the detailed response pal. I'll give it a go. Looks very possible to me. However, I still want to know what is the purpose of having a `console` appender in the WPF application? I mean if I developed for a `CLI` I could reason it but in general will I be needing to have a `console` appender in my WPF? – Mehrad May 27 '14 at 23:53
  • 1
    @Mehrad I added a short bit about the console logging in any kind of apps – samy May 28 '14 at 07:23
  • 2
    @Mehrad Console appender is awesome with WPF applications. Visual Studio will be able to display the output on your "console" when attached, but during normal operation, its not doing anything. – Aron Jun 05 '14 at 06:06
  • @Aron I am not sure what do you exactly mean by "Displaying the logs on the console when attached". As far as I know if you haven't created a console for your WPF manually you won't see any console and can even output for it either using console.write(). !! – Mehrad Jun 05 '14 at 06:17
  • 1
    @Aron NOW I KNOWW... you mean the "Output" window in visual studio. Mine is normally set to `Build` instead of `Debug` and as soon as I changed it I can see the *Log4Net* `console appender` results in it coming through. Quite an interesting point you mentioned. Thanks :) – Mehrad Jun 05 '14 at 06:21
  • Thanks for clarifying info, @Aron, I didn't think of using the Output window as an example of stdout. – samy Jun 05 '14 at 15:30
  • @samy Might I suggest you add that to your answer? – Aron Jun 06 '14 at 05:20
  • @Aron Mehrad and I did so – samy Jun 06 '14 at 08:03
  • @samy I attempted to implement this UDPlistener you recommended, however I had this problem because of the changes I made beforehand. I removed the `BackgroundWorker` from my code in the favor of using newer methods implemented in .Net 4+ and it worked perfectly fine. Meanwhile it made implementing your suggestion a little bit complicated and I had problems doing it. I posted a question in this regards which hasn't received a reply yet. Would you be able to have a look and give me your input? http://stackoverflow.com/questions/24074252/how-to-implement-a-listener-in-the-background – Mehrad Jun 10 '14 at 00:02
  • @Mehrad I see that you managed to do it, let me +1 your answer on your own question – samy Jun 10 '14 at 08:49
  • @samy For some reason lots of people didn't like my question at all. I must have not knowing what I was talking about cuz in one point it was getting down-voted like crazy. I really appreciate the support pal. after all we all need to learn and I think my electrical engineering degree gap in between my programing experience was big enough that now that I am back, everything is new and it's like starting over again. I am glad the answer was the right approach that deserved your +1 at least. – Mehrad Jun 10 '14 at 22:37
  • Mehrad, how do you implement the last bit: // write the log to your pane I have a textbox, but when I append the UDP message to its .Text property, I do not see anything in the UI – Daniel Williams May 27 '16 at 19:22
0

Maybe offtopic a little, but I found this topic when tried to find a way to write logs both to file and to message bar in the app. I managed to find a more elegant solution.

So, we're in 2020, and there is .Net Core and Microsoft.Extensions.Logging with its universal logger factory and interfaces:

        var messageBar = new MessageBar();

        using var loggerFactory = LoggerFactory.Create(builder => builder
            .AddLog4Net()
            .AddProvider(new MessageBarLoggerProvider(messageBar))
            .SetMinimumLevel(LogLevel.Debug));

        ILogger programLogger = loggerFactory.CreateLogger(nameof(App));
        programLogger.LogDebug("hello world!");

The "hello world!" text appeared both in configured in the log4net.config log file and message bar. It's not that difficult to implement ILoggerProvider and ILogger, here's the concept:

internal sealed class MessageBarLoggerProvider : ILoggerProvider
{
    private readonly MessageBar _messageBar;
    public MessageBarLoggerProvider(MessageBar messageBar) => _messageBar = messageBar;
    public ILogger CreateLogger(string categoryName) => new MessageBarLogger(_messageBar);
    public void Dispose() { }
}


internal class MessageBarLogger : ILogger
{
    private readonly MessageBar _messageBar;
    public MessageBarLogger(MessageBar messageBar) => _messageBar = messageBar;
    public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
    public bool IsEnabled(LogLevel logLevel) => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
        Func<TState, Exception, string> formatter)
    {
        _messageBar.MessagesList.Add(new Message(formatter(state, exception)));
    }
}
Lev
  • 811
  • 12
  • 13