7

I've been building a library (NuGet package as I'm in the C# world here) & I want to add logging so the consumer of the library can optionally provide some logger to the library. However, there are many logging libraries out there with different interfaces, so I'm not sure the best way to go about it.

I'm leaning towards requiring the ILogger interface from the Microsoft.Extensions namespace which I know some other libraries like Serilogger provide implementations for. But it would still only be a very select few logging libraries that could be used. Is it acceptable to say "You must use an ILogger instance if you want logging from my library"? Do other library makers provide logging from within their library? Or maybe having logging in NuGet libraries isn't standard. Just looking for advice/clarity around the topic, thanks in advance.

4 Answers 4

3

Consider writing your own interface that expresses your softwares real needs. Then write adapters that use interfaces of loggers that can support those needs.

Done this way your interface will be no more complicated than is needed.

Choosing which logger to use is a design decision. Postpone design decisions as long as you can.

1

Is it acceptable to say "You must use an ILogger instance if you want logging from my library"?

You're the developer and in this case product owner; so in the end your library provides what you choose it provides.

If you do end up taking the multiple logging provider route; expose those as separate packages so that your consumers can choose what they want to load. E.g. next to MyLib, also publish a MyLib.Loggging.Nlog, MyLib.Logging.Log4Net, ...

This helps you separate your concerns. MyLib can just make use of some generalized logger interface, and the specific logging libraries can then provide the needed API and implementation to generate such a logger from the specific logging library.

Or maybe having logging in NuGet libraries isn't standard.

Just because it is or isn't standard doesn't mean it is or isn't warranted in your library. This is highly contextual and cannot be blanket answered.

Does the logging provide a meaningful feature? If not, then why would you want to add it?

1

The convention is to use the ILogger interface. The advantage of having a common interface for logging is precisely for this purpose. Your library doesn't really care about the logging implementation, only the fact that it can record debug information as necessary. Your library wouldn't be the only one that has to deal with this. Unsurprisingly there are ways to adapt the different logging libraries to use the Microsoft.Extensions.ILogger interface:

  • Serilog: implementer uses Serilog.Extensions.Logging to wrap a Serilog logger to a Microsoft logger
  • NLog: similar design, implementer uses Nlog.Extensions.Logging
  • Log4Net: couldn't find any already implemented wrappers, but creating one isn't hard

Bottom line, if you are expecting your library to be used in a NetCore environment, use the Microsoft.Extensions.Logging.ILogger interface, with having that be passed in to your classes. All the Dependency Injection implementations have a way to resolve the proper ILogger implementation and provide you an instance to use.

In order for your library to be logging implementation agnostic, the following have to be true:

  • ILogger implementation is provided to your code either through constructor or some other call
  • Use that ILogger implementation in your code, throwing an exception if it is not provided (or provide a dummy implementation as backup)

Now, to play nice with the larger ecosystem, I do recommend you limit yourself to DEBUG and TRACE logging levels (unless you have a real ERROR). My team has been using a number of libraries that treat logging like it was the application, which makes our configuration all the more complicated so we can turn off everything else so the logs really only contain what we built.

-1

If a library does its own logging, that often creates more problems than benefits.

You already addressed one problem, that you can't know the logging framework of your user's application.

The second problem area is configuration. Administrators needs the ability to select different logging levels for different application parts, including your library, meaning that your log messages must fit into the "components" tree of their logging style. And administrators often prefer to globally configure logging levels for an application (over specific levels for application parts, which they often don't know well enough). So, your logging levels need to anticipate the logging-level style of your user application.

So, I'd seriously consider to not do any logging in your library, but to provide a way how a user application can register for various types/levels of messages.

And any event worth an "error" logging should result in the API call returning an error information, preferred in form of an appropriate exception (and then it's the caller's responsibility to do the error logging). So, I see valid use cases for logging only with log levels like DEBUG, INFO, or WARN.

Personal experience anecdote:

One of the most annoying situations I encountered was with the Java CORBA library, doing its own error logging via the rarely-used Java-Utils logging framework, resulting in log messages sent to the console in situations that the application was well designed to handle. This forced us to add special code just to suppress these annoying messages.

4
  • 2
    "So, your logging levels need to anticipate the logging-level style of your user application." Any logging library worth its salt makes it possible to configure this on another level than where the actual log message is generated. The only thing OP really needs to do is label each message with an appropriate level, and whether or not the message gets logged (and how) depends on the configuration that occurs on a much higher level.
    – Flater
    Commented Mar 8, 2022 at 10:00
  • @Flater What I meant was that the log labelling should follow the labelling style of the application (alas, this isn't self-explanatory to many developers). Commented Mar 8, 2022 at 10:41
  • My point remains the same. Log measage formatting (level, timestamp, message, ...) is generally handled via the logging configuration; not the class generating the log message itself.
    – Flater
    Commented Mar 8, 2022 at 12:48
  • I agree that for most libraries raising events is a better choice than calling a logger. Commented Mar 8, 2022 at 14:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.