Best logging library that supports working within "fire and forget" Task code

Does anyone have a recommendation for a logging library that they have personally used and like? Here is what I am looking for: - Needs to work with WPF desktop apps - Needs to be able to log to the filesystem - Needs to work with both typical application code AND code that is running as a Task in a "fire and forget" scenario. In other words, I have typical application code that needs to write to a "log.txt" file and then I have several places in that application code that "fire and forget" Tasks and those Tasks also need to write to that same "log.txt" file. By a "fire and forget" Task I mean that I am initiating several different Tasks throughout the code but not "awaiting" their completion. In other words, the Tasks fire asynchronously, there may be several of them running at the same time, and I don't wait for them to complete. - Easy to use My current situation: I have written my application but there are some final nagging bugs. Unfortunately those bugs manifest and cause the application to silently crash only after several days of running. This makes it very inconvenient to debug and I don't want to have an instance of VS running for days at a time just to debug this app. I currently have my own simple bespoke logging code but I think it doesn't handle the scenario where multiple threads/Tasks/etc are trying to log something at the same time even though I have tried to handle this scenario using a lock as follows:
29 Replies
PurplePeopleEater
private static readonly Object LogFileLockObj = new Object(); public static void PhysicalLogFile_WriteLine(string message, string logFileName = "_log.txt") { Task.Run(() => { try { lock (LogFileLockObj) { using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine(message); } } } catch { } }); } I also tried the above code without wrapping the body of the method in a Task. Both versions of the code did not fix my issue. Thank you in advance for any help!
PurplePeopleEater
Hi! I am thinking to use the MS logger so that I don't have to add another dependency. Giving those docs a read now...
Mayka
Mayka5mo ago
I cannot speak for the MS logger, but I have been extremely happy with Serilog thus far. I’m using it with the ILogger implementation so that I have the flexibility to switch out to a different logger in the future. Serilog has an async wrapper you can use to write to the file sink on a background worker thread, and the file sink itself supports multi-process shared log files. I was able to customize the log output to include the thread number and thread name for each line logged as well, which has been helpful for debugging. It may be possible for the MS logger to do all that as well, I just have not personally used it.
PurplePeopleEater
Thank you Mayka. I have been looking at the MS logger and it seems like it does not have a provider for writing to the disk out of the box - you seem to have to supply your own. From what you said it seems like Serilog will handle my Task/multi-process stuff. Also, if Serilog has a file sink like you said - which I assume means it will write to the disk for logging out of the box - then all the better.
Mayka
Mayka5mo ago
Serilog takes the approach of having a main package with the absolute minimum core functionality, and then any additional functionality is in separate nuget packages. It's actually kind of nice, because instead of needing to pull in one huge dependency, you can just add what you need. These are the packages I'm currently using based on my own requirements, which is smaller than if there was one mega package that included a bunch of extra stuff I don't need. - Serilog.Sinks.File - sink to log to a file - Serilog.Sinks.Console - sink to log to the console - Serilog.Sinks.Async - a wrapper sink to add async support to sinks that don't natively support it - Serilog.Extensions.Logging - adds the abiliy to use Microsoft's ILogger - Serilog.Settings.Configuration - adds support to configure logger using appsettings.json - Serilog.Expressions - adds greater ability to customize the log output format - Serilog.Enrichers.Thread - adds ability to add properties from the current thread to the log output, such as the thread ID and thread name
PurplePeopleEater
That sounds really nice
Mayka
Mayka5mo ago
I definitely suggest giving it a try! If you use the MEL ILogger interface, you can easily switch to a different logging provider in the future without having to change any of your logging code, so there’s really no worry of being locked into using it.
PurplePeopleEater
That's cool. For my usage where I want to: - Log to disk - Log from my normal application code - Log from Task code that is being run as "fire and forget" - Have all logging (from regular application code AND from Task code) end up in the same "log.txt" file I want to avoid race conditions from the various sources of logging information causing logging to fail. ... it sounds like I would only need to use the following: - Serilog.Sinks.File - sink to log to a file and none of the the other packages. Does that sound right to you?
Mayka
Mayka5mo ago
If you don’t need anything fancy, that should do you just fine. You might also want to use the async wrapper though, so the logging could happen on a background thread. Since you’ll have the Task code running at the same time and getting intermingled with logs from the regular application code, you may also want to include extra details in your logs like the source context if you’re not using structured logging That shouldn’t require extra packages unless you’re getting real fancy with the output, it’d just be tweaking the log output format
PurplePeopleEater
Ok, and you would not anticipate any race conditions from the various sources of logging information (regular application code and Tasks) causing logging to fail if I start out by only using Serilog.Sinks.File to get my feet wet?
Mayka
Mayka5mo ago
I don’t have a massive amount of experience with logging from multiple places at once, but I did get the impression from the file sink that it’s possible.
Shared log files To enable multi-process shared log files, set shared to true: .WriteTo.File("log.txt", shared: true)
I assumed that enabled it to check if the file is locked and if so, retry once the lock is released. But I’m not 100% sure.
PurplePeopleEater
Ok great, thank you
Mayka
Mayka5mo ago
If for some reason that’s not the case, hopefully there is a different logger that will do the job for you!
PurplePeopleEater
Yeah! I can't be the only person logging from Tasks so one of these loggers has got to work 🙂
Sir Rufo
Sir Rufo5mo ago
I use Wpf.Extensions.Hosting and with that you have all the nice goodies (DI, Logging, etc.).
Wpf.Extensions.Hosting 1.2.0
Package Description
PurplePeopleEater
Thank you. As per the link you sent it sounds like it uses Serilog internally for the logging
Sir Rufo
Sir Rufo5mo ago
Nope, it does not … but adding Serilog.Extensions.Hosting will do the job (with some configuration)
Serilog.Extensions.Hosting 8.0.0
Serilog support for .NET Core logging in hosted services
Sir Rufo
Sir Rufo5mo ago
builder.UseSeriLog(); needs to be added in Program.cs It is the abstract logging framework from Microsoft and nearly any other logger can be plugged in. So you write your logging code once and you can choose what logger you attach
PurplePeopleEater
On https://www.nuget.org/packages/Wpf.Extensions.Hosting it says the following as part of the first example: // Configure logging. // Using the diagnostic logging library Serilog. builder.Host.UseSerilog((hostingContext, services, loggerConfiguration) => loggerConfiguration .ReadFrom.Configuration(hostingContext.Configuration) .Enrich.FromLogContext() .WriteTo.Debug() .WriteTo.File( @"Logs\log.txt", rollingInterval: RollingInterval.Day));
Doesn't that mean it uses Serilog internally?
Wpf.Extensions.Hosting 1.2.0
Package Description
Sir Rufo
Sir Rufo5mo ago
Nope, you have to add the Serilog nuget package and then you can configure Serilog as I already described. And you can attach many other loggers that supports the Microsoft logging framework - as you like The concrete logger is an external dependency not an internal one 😉
Mayka
Mayka5mo ago
Hm, I haven’t used a third party hosting extension yet. I will say if you don’t need anything extra, Serilog can be configured with just the standard dependency injection container.
using ServiceProvider serviceProvider = new ServiceCollection()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))
// add other services here
.BuildServiceProvider();
using ServiceProvider serviceProvider = new ServiceCollection()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true))
// add other services here
.BuildServiceProvider();
It’s also common to use it with the microsoft host builder as well.
PurplePeopleEater
Sorry Mayka, I meant that comment for Sir Rufo. But thank you for the response none the less
Mayka
Mayka5mo ago
Haha whoops I’m gonna blame it on the time :catlaugh:
PurplePeopleEater
Ah, ok. I guess I am not familiar with "Generic Hosting" and why I would want to use " Wpf.Extensions.Hosting". Does "Wpf.Extensions.Hosting" provide a way to log to the file system out of the box and avoid race conditions stemming from using Tasks?
Sir Rufo
Sir Rufo5mo ago
Wpf.Extensions.Hosting did nothing provide any logging itself but references the Microsoft logging framework. Anything you want to know about it you have to look at Microsoft.Extensions.Logging. There are nuget packages for Console and Debug output (from Microsoft). There are many loggers that can log to a file and attached to that framework (as Serilog). That MS logging framework is heavily used by ASP.Net Core where it is very common to have log messages from Tasks all the way. So yes it should handle this. Why I want to use it: Automatic DependencyInjection (ViewModel into View, Services into ViewModel, etc.). Register the types in Program.cs, add the dependencies to the constructor and thats it.
PurplePeopleEater
Ok, thanks for the explanation
Sir Rufo
Sir Rufo5mo ago
GitHub
GitHub - SirRufo/so-77577469-InitChildViewModel: Example code
Example code. Contribute to SirRufo/so-77577469-InitChildViewModel development by creating an account on GitHub.
PurplePeopleEater
Cool