Logging¶
dotenvmodel logs through Python's standard logging module under the "dotenvmodel" logger. With no logging setup, warnings and errors surface on stderr via Python's last-resort handler — for example, the No .env files found warning. Call configure_logging() for the INFO/DEBUG detail (which files were read, how each field resolved) and formatted output.
Enabling Logging¶
Use configure_logging() to enable logging at a specific level:
from dotenvmodel import configure_logging, DotEnvConfig, Field
# Enable INFO level logging
configure_logging("INFO")
class Config(DotEnvConfig):
database_url: str = Field()
config = Config.load()
Log Levels¶
# DEBUG - Most verbose, shows all operations including file searches
configure_logging("DEBUG")
# INFO - Shows file loading and configuration status
configure_logging("INFO")
# WARNING - Only shows warnings (e.g., missing .env files)
configure_logging("WARNING")
# ERROR - Only shows errors
configure_logging("ERROR")
Default level
configure_logging() uses WARNING when called with no level and no DOTENVMODEL_LOG_LEVEL environment variable set. Warnings and errors surface even with no setup at all (via Python's last-resort handler); configure_logging() is what adds INFO/DEBUG detail and the formatted output.
Log Output Example¶
Here's what you'll see at INFO level when loading configuration:
2025-12-05 00:33:40,312 - dotenvmodel - INFO - Loading Config configuration
2025-12-05 00:33:40,312 - dotenvmodel - INFO - Loading configuration for environment: dev
2025-12-05 00:33:40,312 - dotenvmodel - INFO - Reading .env file: /home/user/myapp/.env
2025-12-05 00:33:40,313 - dotenvmodel - INFO - Reading .env file: /home/user/myapp/.env.dev
2025-12-05 00:33:40,313 - dotenvmodel - INFO - Successfully loaded 2 file(s): /home/user/myapp/.env, /home/user/myapp/.env.dev
2025-12-05 00:33:40,313 - dotenvmodel - INFO - Config configuration loaded successfully
Logged file paths are always absolute — resolve_env_dir() records the base directory as an absolute path, so the log names exactly which files were read.
At DEBUG level, you'll also see messages about files that were searched but not found:
2025-12-05 00:33:40,312 - dotenvmodel - DEBUG - /home/user/myapp/.env.local not found (skipping)
2025-12-05 00:33:40,313 - dotenvmodel - DEBUG - /home/user/myapp/.env.dev.local not found (skipping)
Using Environment Variables¶
DOTENVMODEL_LOG_LEVEL is read by configure_logging(): set it to choose the level configure_logging() applies when called without an explicit level. Call configure_logging() once in your entry point, then control verbosity from the environment:
The env var needs configure_logging()
DOTENVMODEL_LOG_LEVEL is only read inside configure_logging() — setting it alone, with no logging setup anywhere, changes nothing: warnings and errors still surface via the last-resort handler, exactly as before.
Custom Format String¶
Customize the log message format with format_string:
from dotenvmodel import configure_logging
# Compact format
configure_logging("INFO", format_string="[%(levelname)s] %(message)s")
# Output: [INFO] Loading Config configuration
# Include logger name only
configure_logging("DEBUG", format_string="%(name)s :: %(levelname)s :: %(message)s")
# Output: dotenvmodel :: DEBUG :: .env.local not found (skipping)
The default format is:
Custom Handler¶
Provide a custom logging handler for advanced use cases (e.g., writing to a file):
import logging
from dotenvmodel import configure_logging
# Log to a file instead of stdout
handler = logging.FileHandler("dotenvmodel.log")
handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
configure_logging("DEBUG", handler=handler)
Default handler
When handler is None (default), dotenvmodel uses a StreamHandler writing to stdout.
Disabling Logging¶
Use disable_logging() to turn off all dotenvmodel log output:
This is useful after temporarily enabling logging for debugging:
from dotenvmodel import configure_logging, disable_logging
# Enable for debugging
configure_logging("DEBUG")
config = AppConfig.load()
# Disable after debugging
disable_logging()
Using the Standard Logging Module Directly¶
dotenvmodel uses a named logger ("dotenvmodel") that integrates with Python's standard logging module. You can configure it directly for full control:
import logging
from dotenvmodel import LOGGER_NAME
# Get the dotenvmodel logger
logger = logging.getLogger(LOGGER_NAME)
logger.setLevel(logging.DEBUG)
# Add a custom handler
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(handler)
# Prevent propagation to root logger
logger.propagate = False
Integration with Application Logging¶
For structured logging or log aggregation (e.g., in FastAPI/gunicorn):
import logging
import json
from dotenvmodel import LOGGER_NAME
class JsonFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
return json.dumps(
{
"logger": record.name,
"level": record.levelname,
"message": record.getMessage(),
}
)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger = logging.getLogger(LOGGER_NAME)
logger.setLevel(logging.DEBUG) # without this, INFO/DEBUG records never reach the formatter
logger.addHandler(handler)
Without an explicit setLevel(), the logger's effective level is inherited from the root logger (WARNING by default), so INFO/DEBUG records are dropped before they ever reach the JsonFormatter.
Logger name
The logger name is available as LOGGER_NAME (value: "dotenvmodel"). Use this constant to avoid hardcoding the string.
See Also¶
- Logging Config API Reference —
configure_logging(),disable_logging(),LOGGER_NAME - Loading Configuration — What gets logged during the loading process