LoggingConfig¶
Why Esmerald Introduced LoggingConfig
¶
In building applications, logging is one of the most crucial parts of observability, debugging, and monitoring.
Originally, Esmerald relied on developers manually configuring their own logging systems. However, to provide a consistent,
extensible, and framework-integrated way to handle logging, Esmerald introduced a first-class logging system built around
the LoggingConfig
concept.
The goals of introducing LoggingConfig
were:
- Consistency: Applications have a predictable and reliable way of setting up logging.
- Flexibility: Support standard Python logging, Loguru, Structlog, or any custom backend.
- Simplicity: Only one central logger (
esmerald.logging.logger
) needs to be imported and used across the entire app. - Extensibility: Developers can easily subclass and provide their own custom logging configurations.
Esmerald now automatically configures a global logger
instance, based on the LoggingConfig
provided during startup.
How LoggingConfig
Works¶
LoggingConfig
is an abstract base class that defines how to configure the logging system.
Esmerald provides:
StandardLoggingConfig
โ for classic Pythonlogging
.
You can also implement your own custom logging backend by subclassing LoggingConfig
.
When setup_logging(logging_config)
is called during app startup (you don't have access to this as this is internal), Esmerald:
- Applies the provided
LoggingConfig
. - Binds the global
esmerald.logging.logger
to the correct backend.
Note
After configuration, importing and using from esmerald.logging import logger
will always point to the correct
logging system automatically.
Available Logging Backends¶
1. Standard Python Logging¶
Default behavior if nothing is specified.
from esmerald import Esmerald
app = Esmerald()
Or inside Esmerald
(with an example of a loguru logger):
import sys
from typing import Any
import loguru
from esmerald import LoggingConfig, Esmerald
class LoguruConfig(LoggingConfig):
def __init__(self, level: str, **kwargs):
self.level = level
def configure(self):
loguru.logger.remove()
loguru.logger.add(
sink=sys.stdout,
level=self.level,
format="{time} {level} {message}",
colorize=True,
)
def get_logger(self) -> Any:
return loguru.logger
logging_config = LoguruConfig(level="INFO")
app = Esmerald(logging_config=logging_config)
Defining a Custom Logging Configuration¶
You can easily define your own LoggingConfig
by subclassing it:
import sys
from typing import Any
import loguru
from esmerald import LoggingConfig
class CustomLoggingConfig(LoggingConfig):
def __init__(self, level: str, **kwargs):
self.level = level
self.options = kwargs
def configure(self):
"""
Configures the logger by removing any existing handlers and adding a new one.
"""
loguru.logger.remove()
loguru.logger.add(
sink=sys.stdout,
level=self.level,
format="{time} {level} {message}",
colorize=True,
)
def get_logger(self) -> Any:
"""
Returns the logger instance.
"""
return loguru.logger
When creating a custom logger you must declare the following methods:
configure()
- This method is called to set up the logging configuration.get_logger()
- This method should return the logger instance.
These methods are called during the application startup process and will make sure the logger is set up correctly using a common interface.
Usage:
from esmerald import Esmerald
app = Esmerald(logging_config=CustomLoggingConfig())
Using logging_config
via EsmeraldAPISettings
¶
If you are using settings classes to configure your app, you can pass the logging configuration there too:
from esmerald import CORSConfig, EsmeraldAPISettings
from esmerald.core.config import LoggingConfig
from myapp.logging import LoguruLoggingConfig
class CustomSettings(EsmeraldAPISettings):
@property
def logging_config(self) -> LoggingConfig:
return LoguruLoggingConfig(level="Debug")
Then when initializing Esmerald:
from esmerald import Esmerald
app = Esmerald(settings_config=AppSettings())
Tip
You can also initialise Esmerald settings via ESMERALD_SETTINGS_MODULE
as you can see in settings
documentation. This is useful for separating your settings from the application instance.
Esmerald will automatically extract and configure the logger from your settings class.
Accessing the Global Logger¶
Anywhere in your project, simply do:
from esmerald.logging import logger
logger.info("This is a log message.")
logger.error("This is an error log.")
No matter what backend you use (standard, Loguru, Structlog), the logger
will behave correctly.
Important Notes¶
- If you need to reconfigure logging during runtime (e.g., in tests), you should explicitly teardown/reset first.
- If no
logging_config
is provided, Esmerald defaults to a safeStandardLoggingConfig
.
Conclusion¶
LoggingConfig
provides a powerful, flexible, and unified way to configure logging in Esmerald applications.
It ensures that regardless of the logging backend used, your app's logging behavior is predictable, extensible, and simple to maintain.