Declarative Logging/shaare/gWRxQw
Declarative Logging Configuration
- Declarative configuration separates setup from code, making it easier to maintain and adjust.
- Python’s
logging.configmodule supports both INI-style (fileConfig) and dictionary-based (dictConfig) configurations. - Configuration objects can be loaded from files (INI, JSON, YAML) or defined in code.
- Benefits include environment-specific overrides, less boilerplate, and clearer visibility of logger/handler relationships.
INI-Style Configuration with fileConfig
- Uses an INI-format file to define loggers, handlers, and formatters.
- Sections:
[loggers],[handlers],[formatters], plus one section per named logger/handler/formatter. - Good for simple setups and backwards compatibility, but less flexible for dynamic structures.
Dictionary-Based Configuration with dictConfig
- Configuration defined as a Python dict, offering full programmatic control.
- Keys:
version,disable_existing_loggers, and mappings forformatters,handlers,loggers, and optionallyroot. - Easy to build or modify at runtime, and to serialize/deserialize via JSON/YAML.
Loading Configuration from JSON or YAML
- Store the same dict-based schema in a JSON/YAML file for external editing.
- Read and parse the file, then pass the resulting dict to
dictConfig. - Enables separation of concerns: ops teams can tweak logging without touching code.
Dynamic and Programmatic Adjustments
- You can modify the config dict at runtime before calling
dictConfig. - Handlers, formatters, and levels can be added, removed, or tweaked based on environment variables or feature flags.
- Example: switch file logging on/off depending on a
DEBUGflag.
import logging
import logging.config
import json
from typing import Any, Dict
"""
# Uncomment to test INI configuration
# Declarative logging configuration - INI-file
print("Declarative configuration using INI files")
print("---------\n")
config_path = "declarative-config.ini"
logging.config.fileConfig(
fname=config_path,
)
app_logger = logging.getLogger("app")
app_logger.debug("INI-style fileConfig is working!")
"""
"""
# Uncomment to test Dictionary configuration
# Declarative logging configuration - Dictionary config
print("Declarative configuration using dictionary config")
print("---------\n")
dict_config: Dict[str, Any] = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"simple": {"format": "%(levelname)-8s - %(message)s"}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout",
}
},
"loggers": {
"config.dict": {
"level": "DEBUG",
"handlers": ["console"],
}
},
}
logging.config.dictConfig(dict_config)
config_logger = logging.getLogger("config.dict")
config_logger.debug("dictConfig setup successfully")
config_logger.info("Info goes to console")
"""
"""
# Uncomment to test JSON configuration
# Declarative logging configuration - JSON config
print("Declarative configuration using JSON config")
print("---------\n")
config_path = "declarative-config.json"
with open(config_path, "r") as config_file:
json_config = json.load(config_file)
logging.config.dictConfig(json_config)
config_logger = logging.getLogger("config.json")
config_logger.debug("JSON config setup successfully")
config_logger.info("Info goes to console")
"""
# Dynamically building config
print("Dynamically building config")
print("---------\n")
base_config: Dict[str, Any] = {
"version": 1,
"disable_existing_loggers": True,
"handlers": {},
"formatters": {},
"loggers": {},
}
base_config["formatters"]["simple"] = {
"format": "%(levelname)-8s - %(message)s"
}
base_config["handlers"]["console"] = {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple",
"stream": "ext://sys.stdout",
}
base_config["loggers"]["config.dynamic"] = {
"level": "WARNING",
"handlers": ["console"],
}
def is_debug():
return True
if is_debug():
for logger, _config in base_config["loggers"].items():
base_config["loggers"][logger]["level"] = "DEBUG"
logging.config.dictConfig(base_config)
config_logger = logging.getLogger("config.dynamic")
config_logger.debug("Dynamic config setup successfully")
config_logger.info("Info goes to console")
Example of declarative-config.ini
[loggers]
keys=root,app
[handlers]
keys=consoleHandler,nullHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=INFO
handlers=consoleHandler
[logger_app]
level=DEBUG
handlers=nullHandler
qualname=app
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_nullHandler]
class=NullHandler
level=NOTSET
formatter=
args=()
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)-8s - %(message)s
Example of declarative-config.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"simple": { "format": "%(levelname)-8s - %(message)s" },
"detailed": {
"format": "%(asctime)s %(name)s [%(levelname)s]: %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "detailed",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"config.json": {
"level": "DEBUG"
}
},
"root": {
"level": "DEBUG",
"handlers": ["console"]
}
}
(97)