I am currently developing a core utils package where I want to set some logging properties (I know that this is not best practice, but it´s for interal purposes and intended to generate logs). When I now import the package nothing gets logged:
# core.__main__.py
class BaseLoggerConfig(BaseModel):
LOG_FORMAT: str = "%(levelprefix)s %(asctime)s %(name)s:%(lineno)d: %(message)s"
DATEFMT: str = "%Y-%m-%d %H:%M:%S"
LOG_LEVEL: int = logging.INFO
version: int = 1
disable_existing_loggers: bool = False
formatters: dict = {
"default": {
# "()": "uvicorn.logging.DefaultFormatter",
"fmt": LOG_FORMAT,
"datefmt": DATEFMT,
},
}
filters: dict = {}
handlers: dict = {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
}
}
loggers: dict = {}
def __init__(self, name: str, **data):
super().__init__(**data)
self.loggers[name] = {
"handlers": ["default"],
"level": self.LOG_LEVEL,
"propagate": False,
}
LOG_CONFIG = BaseLoggerConfig(__name__)
logging.config.dictConfig(LOG_CONFIG)
- core.__main__
Level: INFO
Handlers: ['StreamHandler']
I now have logging in my other files, like:
# core.utils
import logging
logger = logging.getLogger(__name__)
def test():
logger.info(f"I am a log from {__name__}")
# test.py
import logging
from core.utils import test
logger = logging.getLogger(__name__)
test()
What am I missing?
There are a couple of tweaks that need to be made here.
First, the file in which you configure your loggers should not be core/__main__.py
, it should be core/__init__.py
. __main__.py
is used when you want to python -m core
, which would run __main__.py
.
Second, the fmt
key in your formatter config should be called format
. You can see the official docs for logging.config
.
Third, in your LOG_FORMAT
, levelprefix
is not a valid field name. You probably want to use levelname
which is "INFO", "DEBUG", etc.
Putting it all together:
# core/__init__.py
import logging
import logging.config
from pydantic import BaseModel
class BaseLoggerConfig(BaseModel):
LOG_FORMAT: str = "%(levelname)s %(asctime)s %(name)s:%(lineno)d: %(message)s"
DATEFMT: str = "%Y-%m-%d %H:%M:%S"
LOG_LEVEL: int = logging.INFO
version: int = 1
disable_existing_loggers: bool = False
formatters: dict = {
"default": {
# "()": "uvicorn.logging.DefaultFormatter",
"format": LOG_FORMAT,
"datefmt": DATEFMT,
},
}
filters: dict = {}
handlers: dict = {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
}
}
loggers: dict = {}
def __init__(self, name: str, **data):
super().__init__(**data)
self.loggers[name] = {
"handlers": ["default"],
"level": self.LOG_LEVEL,
"propagate": False,
}
LOG_CONFIG = BaseLoggerConfig(__name__)
logging.config.dictConfig(LOG_CONFIG)
# core/utils.py
import logging
logger = logging.getLogger(__name__)
def test():
logger.info(f"I am a log from {__name__}")
# test.py
import logging
from core.utils import test
logger = logging.getLogger(__name__)
test()
Output:
INFO 2024-11-30 11:06:28 core.utils:8: I am a log from core.utils
EDIT: I realized that your %(levelprefix)s
comes from uvicorn
's formatter. In that case, you can uncomment your line
# "()": "uvicorn.logging.DefaultFormatter",
and undo changing the key to format
. Which means:
# core/__init__.py
# ...
class BaseLoggerConfig(BaseModel):
LOG_FORMAT: str = "%(levelprefix)s %(asctime)s %(name)s:%(lineno)d: %(message)s"
DATEFMT: str = "%Y-%m-%d %H:%M:%S"
# ...
formatters: dict = {
"default": {
# "()": "uvicorn.logging.DefaultFormatter",
"format": LOG_FORMAT,
"datefmt": DATEFMT,
},
}
# ...
Output: ("INFO" text is green)
INFO: 2024-12-01 14:13:14 core.utils:6: I am a log from core.utils