mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-24 18:10:02 +02:00
update logging module
This commit is contained in:
parent
1241d7a128
commit
5d7a7c6bdb
@ -1,82 +1,111 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from types import FrameType
|
from copy import copy
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import loguru
|
import uvicorn.logging
|
||||||
import rpyc
|
|
||||||
from uvicorn.config import LOGGING_CONFIG
|
from uvicorn.config import LOGGING_CONFIG
|
||||||
|
|
||||||
import pydase.config
|
import pydase.config
|
||||||
|
|
||||||
ALLOWED_LOG_LEVELS = ["DEBUG", "INFO", "ERROR"]
|
|
||||||
|
class DefaultFormatter(uvicorn.logging.ColourizedFormatter):
|
||||||
|
"""
|
||||||
|
A custom log formatter class that:
|
||||||
|
|
||||||
|
* Outputs the LOG_LEVEL with an appropriate color.
|
||||||
|
* If a log call includes an `extras={"color_message": ...}` it will be used
|
||||||
|
for formatting the output, instead of the plain text message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def formatMessage(self, record: logging.LogRecord) -> str:
|
||||||
|
recordcopy = copy(record)
|
||||||
|
levelname = recordcopy.levelname
|
||||||
|
seperator = " " * (8 - len(recordcopy.levelname))
|
||||||
|
if self.use_colors:
|
||||||
|
levelname = self.color_level_name(levelname, recordcopy.levelno)
|
||||||
|
if "color_message" in recordcopy.__dict__:
|
||||||
|
recordcopy.msg = recordcopy.__dict__["color_message"]
|
||||||
|
recordcopy.__dict__["message"] = recordcopy.getMessage()
|
||||||
|
recordcopy.__dict__["levelprefix"] = levelname + seperator
|
||||||
|
return logging.Formatter.formatMessage(self, recordcopy)
|
||||||
|
|
||||||
|
def should_use_colors(self) -> bool:
|
||||||
|
return sys.stderr.isatty() # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
# from: https://github.com/Delgan/loguru section
|
def setup_logging(level: Optional[str | int] = None) -> None:
|
||||||
# "Entirely compatible with standard logging"
|
"""
|
||||||
class InterceptHandler(logging.Handler):
|
Configures the logging settings for the application.
|
||||||
def emit(self, record: logging.LogRecord) -> None:
|
|
||||||
# Ignore "asyncio.CancelledError" raised by uvicorn
|
|
||||||
if record.name == "uvicorn.error" and "CancelledError" in record.msg:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get corresponding Loguru level if it exists.
|
This function sets up logging with specific formatting and colorization of log
|
||||||
level: int | str
|
messages. The log level is determined based on the application's operation mode,
|
||||||
try:
|
with an option to override the level. By default, in a development environment, the
|
||||||
level = loguru.logger.level(record.levelname).name
|
log level is set to DEBUG, whereas in other environments, it is set to INFO.
|
||||||
except ValueError:
|
|
||||||
level = record.levelno
|
|
||||||
|
|
||||||
# Find caller from where originated the logged message.
|
Parameters:
|
||||||
frame: Optional[FrameType] = sys._getframe(6)
|
level (Optional[str | int]):
|
||||||
depth = 6
|
A specific log level to set for the application. If None, the log level is
|
||||||
while frame and frame.f_code.co_filename == logging.__file__:
|
determined based on the application's operation mode. Accepts standard log
|
||||||
frame = frame.f_back
|
level names ('DEBUG', 'INFO', etc.) and corresponding numerical values.
|
||||||
depth += 1
|
|
||||||
|
|
||||||
try:
|
Example:
|
||||||
msg = record.getMessage()
|
|
||||||
except TypeError:
|
|
||||||
# A `TypeError` is raised when the `msg` string expects more arguments
|
|
||||||
# than are provided by `args`. This can happen when intercepting log
|
|
||||||
# messages with a certain format, like
|
|
||||||
# > logger.debug("call: %s%r", method_name, *args) # in tiqi_rpc
|
|
||||||
# where `*args` unpacks a sequence of values that should replace
|
|
||||||
# placeholders in the string.
|
|
||||||
msg = record.msg % (record.args[0], record.args[2:]) # type: ignore
|
|
||||||
|
|
||||||
loguru.logger.opt(depth=depth, exception=record.exc_info).log(level, msg)
|
```python
|
||||||
|
>>> import logging
|
||||||
|
>>> setup_logging(logging.DEBUG)
|
||||||
|
>>> setup_logging("INFO")
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
def setup_logging(level: Optional[str] = None) -> None:
|
|
||||||
loguru.logger.debug("Configuring service logging.")
|
|
||||||
|
|
||||||
if pydase.config.OperationMode().environment == "development":
|
if pydase.config.OperationMode().environment == "development":
|
||||||
log_level = "DEBUG"
|
log_level = logging.DEBUG
|
||||||
else:
|
else:
|
||||||
log_level = "INFO"
|
log_level = logging.INFO
|
||||||
|
|
||||||
if level is not None and level in ALLOWED_LOG_LEVELS:
|
# If a level is specified, check whether it's a string or an integer.
|
||||||
log_level = level
|
if level is not None:
|
||||||
|
if isinstance(level, str):
|
||||||
|
# Convert known log level strings directly to their corresponding logging
|
||||||
|
# module constants.
|
||||||
|
level_name = level.upper() # Ensure level names are uppercase
|
||||||
|
if hasattr(logging, level_name):
|
||||||
|
log_level = getattr(logging, level_name)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid log level: {level}. Must be one of 'DEBUG', 'INFO', "
|
||||||
|
"'WARNING', 'ERROR', etc."
|
||||||
|
)
|
||||||
|
elif isinstance(level, int):
|
||||||
|
log_level = level # Directly use integer levels
|
||||||
|
else:
|
||||||
|
raise ValueError("Log level must be a string or an integer.")
|
||||||
|
|
||||||
loguru.logger.remove()
|
# Set the logger's level.
|
||||||
loguru.logger.add(sys.stderr, level=log_level)
|
logger.setLevel(log_level)
|
||||||
|
|
||||||
# set up the rpyc logger *before* adding the InterceptHandler to the logging module
|
# create console handler and set level to debug
|
||||||
rpyc.setup_logger(quiet=True) # type: ignore
|
ch = logging.StreamHandler()
|
||||||
|
|
||||||
logging.basicConfig(handlers=[InterceptHandler()], level=0)
|
# add formatter to ch
|
||||||
|
ch.setFormatter(
|
||||||
|
DefaultFormatter(
|
||||||
|
fmt="%(asctime)s.%(msecs)03d | %(levelprefix)s | %(name)s:%(funcName)s:%(lineno)d - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# add ch to logger
|
||||||
|
logger.addHandler(ch)
|
||||||
|
|
||||||
|
logger.debug("Configuring service logging.")
|
||||||
logging.getLogger("asyncio").setLevel(logging.INFO)
|
logging.getLogger("asyncio").setLevel(logging.INFO)
|
||||||
logging.getLogger("urllib3").setLevel(logging.INFO)
|
logging.getLogger("urllib3").setLevel(logging.INFO)
|
||||||
|
|
||||||
# overwriting the uvicorn logging config to use the loguru intercept handler
|
# configuring uvicorn logger
|
||||||
LOGGING_CONFIG["handlers"] = {
|
LOGGING_CONFIG["formatters"]["default"][
|
||||||
"default": {
|
"fmt"
|
||||||
"()": InterceptHandler,
|
] = "%(asctime)s.%(msecs)03d | %(levelprefix)s %(message)s"
|
||||||
"formatter": "default",
|
LOGGING_CONFIG["formatters"]["default"]["datefmt"] = "%Y-%m-%d %H:%M:%S"
|
||||||
},
|
|
||||||
"access": {
|
|
||||||
"()": InterceptHandler,
|
|
||||||
"formatter": "access",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user