Is it a good practice to have logger as a singleton?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Using a singleton for logging is widely considered acceptable — and most major logging frameworks already implement this pattern internally. Python's logging.getLogger(), Java's LoggerFactory.getLogger(), and .NET's ILoggerFactory all return shared instances keyed by name. The singleton pattern works well for loggers because loggers are stateless services (they format and route messages), they need global accessibility, and creating multiple instances for the same log target wastes resources. However, dependency injection of logger interfaces is preferred in modern architectures for testability.
Why Singleton Works for Loggers
Logging frameworks use a registry pattern (a form of singleton): the first call creates the logger, subsequent calls return the cached instance. This ensures all code using the same logger name shares configuration (handlers, level, formatters).
Singleton Logger Implementation
Dependency Injection Alternative (Recommended)
Dependency injection decouples the class from the specific logging implementation. In tests, you can inject a mock logger to verify log output without touching the console or files.
Testing with Injected Loggers
With a singleton logger, verifying log output in tests requires intercepting the logger's handlers or using test-specific appenders — which is more complex than injecting a mock.
When Singleton Logger Is Fine
Common Pitfalls
- Coupling code to a specific logging implementation: A singleton
AppLoggerthat directly usesjava.util.loggingorprint()makes it impossible to switch logging backends. Use a logging facade (SLF4J, Python'slogging) or inject an interface. - Thread safety in custom singletons: Without proper synchronization (
volatile+ double-checked locking in Java,threading.Lockin Python), two threads can create separate instances. Use the framework's built-in singleton (getLogger) instead of writing your own. - Difficult to test: Singleton loggers make it hard to verify log output in unit tests. You must either capture handler output or use reflection to replace the singleton. Dependency injection solves this cleanly.
- Hidden dependency: Classes that call
AppLogger.getInstance()internally have an invisible dependency. Constructor injection makes dependencies explicit, improving readability and refactoring safety. - Configuration ordering issues: Singleton loggers initialized before configuration is loaded may use default settings. Lazy initialization or configuration-first startup sequences prevent this problem.
Summary
- Singleton is an acceptable pattern for loggers because logging frameworks already use it internally
logging.getLogger()(Python),LoggerFactory.getLogger()(Java),ILoggerFactory(.NET) are built-in singleton registries- Dependency injection of logger interfaces is preferred in modern architectures for testability
- Custom singleton loggers require thread-safe initialization (double-checked locking or framework-provided patterns)
- For library code, use a logging facade (SLF4J) rather than a concrete singleton
- Don't build a custom singleton logger when the standard library already provides one
Related reading
- Is it common practice to have a shared and dedicated assembly or library for messages in a distributed system?
- Is it okay to place frontend and backend of the application in same pod in kubernetes
- Is it possible for a vector clock to be greater than another but they are not ancestor related?
- Is it possible to add a node to a cluster, where data is replicated synchronously?
- Is it unnecessary to put super in constructor?
- Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
- Is it possible to add members to Aeron Cluster w/o reconfiguring existing ones?
- Is it possible to create a topic with Kafka Rest Proxy?

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.