boa-constrictor
boa-constrictor copied to clipboard
Explore Logging Alternatives
Serilog is a popular .NET logging library.
Should we use Serilog as part of Boa Constrictor? Maybe. Let's discuss that possibility here in this issue.
Pros:
- Serilog is very popular and well supported.
- Boa Constrictor wouldn't need to maintain its own logging pattern.
Cons:
- What if projects using Boa Constrictor don't want to use Serilog or already use a different logger?
I found it very easy to get started with Boa Constrictor and I like that there isn't much ceremony required to get started. I also like that I can provide my own implementation of a logger. I see the appeal of using an existing logging library like Serilog so that users can choose from existing sinks but as you noted not everybody may want to use Serilog.
As I see it, there is a balance between supporting different use cases:
- Users who are happy with basic logging (and just want to get started quickly)
- Users want greater flexibility.
With that in mind, some thoughts:
1. Use an action/event for logging Rather than have Boa Constrictor depend on some kind of logger interface or implementation, what if the library just raised log events and users can provide their own implementation for handling those events?
A possible implementation using Actions could be something like:
- Define
LogEvent
type withLogSeverity
andMessage
. - In
IActor
replaceILogger
withAction<LogEvent>
- In
Actor
accept aLoggingAction
of typeAction<LogEvent>
. - Any time you wish to log just invoke the action:
LoggingAction?.Invoke(new LogEvent(severity, message)
For use case 1 Boa Constrictor can provide one or more basic implementations such as a console logger with a fixed message template.
This also meets use case 2 since as a user I can now handle logs as I wish, eg:
Action<LogEvent> loggingAction = (logEvent) => Console.WriteLine($"[{logEvent.Severity}] {message}")
or I could use Serilog, or whatever I want.
An alternative to using Actions could be using actual events, and users create their own event handlers.
2. Use Microsoft.Extensions.Logging.Abstractions
Rather than adding a dependency to a specific logging implementation, what about using Microsoft.Extensions.Logging.Abstractions
which provides a common logging abstraction? Users can then choose from an existing implementation (Serilog, NLog, ...) or provide their own. So this meets use case 2.
This does introduce some effort from the users perspective in terms of wiring up an actual implementation, particularly if they aren't already familiar with logging in .NET. Also, my experience has been that many of search results related to logging in .NET are geared towards ASP.NET (or at least expects the user to be using a DI container).
To meet use case 1:
- an example using one of the popular implementations (such as Serilog) could be included in the tutorial. A basic example of wiring up Serilog would be something like:
// Create Serilog logger
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u4}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
// Create logger factory
var loggerFactory = new LoggerFactory();
loggerFactory.AddSerilog();
// Create logger instance to pass in to actor
_logger = loggerFactory.CreateLogger<MyClass>();
and/or,
- Boa Constrictor could includes its own logger implementation. That example is for ASP.NET Core 3.1, but the only bit which would change is the usage and registration.
Say Boa Constrictor defined
SimpleLogger
,SimpleLoggerProvider
, andSimpleLoggerConfiguration
, using the logger would be:
var simpleLoggerConfig = new SimpleLoggerConfiguration
{
LogLevel = LogLevel.Trace
};
var loggerFactory = LoggerFactory.Create(builder => builder.AddProvider(new SimpleLoggerProvider(simpleLoggerConfig)));
var logger = loggerFactory.CreateLogger<MyClass>();
The above could be wrapped up in a single method. This approach would mean that users don't need to add any additional packages.
*The above examples are using the latest versions of the respective libraries.
Personally I prefer option 1 since I feel it's a lighter touch and doesn't introduce any new dependencies whatsoever, but interested to hear any thoughts.
@shack05, thanks for those awesome ideas! I think I agree with you - I like option 1.