Should I take ILogger, ILoggerT, ILoggerFactory or ILoggerProvider for a library?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
When building a .NET library that needs logging, accept ILogger<T> through constructor injection. This is the recommended approach because it follows the standard dependency injection pattern, produces correctly categorized log output, and works seamlessly with the host application's logging configuration. ILoggerFactory is the second choice when your library needs to create loggers for multiple internal classes. Avoid depending on ILoggerProvider directly — it is an implementation detail of the logging infrastructure.
The Four Interfaces
Recommended: Accept ILogger<T>
The host application registers logging in DI, and ILogger<OrderService> is automatically resolved with the category name "YourLibrary.OrderService":
Log output includes the category name, making it easy to filter:
When to Accept ILoggerFactory
Use ILoggerFactory when your library creates multiple internal classes that each need their own logger:
This is common in libraries like Entity Framework Core that create loggers for different subsystems (SQL, change tracking, migrations).
When to Accept Plain ILogger
Use non-generic ILogger when you want maximum flexibility and do not care about the category name:
This works but the category name depends on what the caller passes. The host application may have trouble filtering logs because the category is not predictable.
Never Accept ILoggerProvider
ILoggerProvider represents a specific logging output (console, file, Application Insights). A single ILogger aggregates all providers. If you inject ILoggerProvider, you bypass the aggregation and only log to one output, missing the others.
NullLogger for Optional Logging
If logging is optional in your library, accept a nullable ILogger or use NullLogger:
NullLogger<T>.Instance implements ILogger<T> but discards all log messages. This avoids null checks throughout your code.
Decision Table
| Scenario | Accept | Why |
| Single class needs logging | ILogger<T> | Correct category, standard DI pattern |
| Library creates many internal loggers | ILoggerFactory | Create loggers with custom categories |
| Utility method, category unimportant | ILogger | Maximum flexibility |
| Logging is optional | ILogger<T> with NullLogger default | No null checks needed |
| Never | ILoggerProvider | Implementation detail, bypasses aggregation |
Common Pitfalls
- Injecting
ILoggerProviderinstead ofILoggerFactory:ILoggerProvideris a single log output (e.g., console only).ILoggerFactoryaggregates all configured providers. UsingILoggerProvidermeans your library's logs only go to one destination, ignoring others. - Creating a new
LoggerFactoryinside the library: CallingLoggerFactory.Create()inside your library creates a separate logging pipeline disconnected from the host's configuration. Always accept the logger through DI so the host application controls filtering, formatting, and outputs. - Using
ILoggerwithout a category: Non-genericILoggerhas no category name, making it impossible for the host to filter your library's logs separately. UseILogger<T>so logs are categorized by class name. - Not using structured logging: Passing string interpolation (
$"Order {id} processed") loses structured data. Use message templates ("Order {OrderId} processed", id) so log providers can index and query byOrderId. - Making logging a required dependency: If your library throws when no logger is provided, it forces all consumers to set up logging even if they do not want it. Use
NullLogger<T>.Instanceas a default so logging is opt-in.
Summary
- Accept
ILogger<T>for standard single-class logging with automatic category naming - Accept
ILoggerFactorywhen creating loggers for multiple internal subsystems - Avoid
ILoggerProvider— it is an implementation detail that bypasses log aggregation - Use
NullLogger<T>.Instanceas a default to make logging optional - Always use structured logging with message templates, not string interpolation
- Let the host application control log filtering and output through DI configuration
Related reading
- Should I use Amazon's AWS Virtual Private Cloud VPC
- Should I use AWS Elastic Beanstalk or the Amazon EC2 Container Service ECS to scale Docker containers?
- Should I use docker-compose up or run?
- Show metrics in Grafana from the Kubernetes Pod that was scraped last by Prometheus
- Should I worry about This async method lacks 'await' operators and will run synchronously warning
- Should I worry about This async method lacks 'await' operators and will run synchronously warning
- Shut down server in TensorFlow
- Shut down server in TensorFlow

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.