diff --git a/__init__.py b/__init__.py
index cc59997..b0079df 100644
--- a/__init__.py
+++ b/__init__.py
@@ -3,4 +3,4 @@
try:
from .src.beans_logging import *
except ImportError:
- from src.beans_logging import *
+ from src.beans_logging import * # type: ignore
diff --git a/auto.py b/auto.py
index e5d08d7..c84f5aa 100644
--- a/auto.py
+++ b/auto.py
@@ -3,4 +3,4 @@
try:
from .src.beans_logging.auto import *
except ImportError:
- from src.beans_logging.auto import *
+ from src.beans_logging.auto import * # type: ignore
diff --git a/src/beans_logging/__init__.py b/src/beans_logging/__init__.py
index afbadff..4246235 100644
--- a/src/beans_logging/__init__.py
+++ b/src/beans_logging/__init__.py
@@ -1,12 +1,16 @@
from __future__ import annotations
from .__version__ import __version__
-from .config import LoggerConfigPM
+from .schemas import LoguruHandlerPM, LogHandlerPM
+from .config import get_default_handlers, LoggerConfigPM
from ._core import Logger, logger, LoggerLoader
__all__ = [
"__version__",
+ "LoguruHandlerPM",
+ "LogHandlerPM",
+ "get_default_handlers",
"LoggerConfigPM",
"Logger",
"logger",
diff --git a/src/beans_logging/_builder.py b/src/beans_logging/_builder.py
index 97a829c..991a9b8 100644
--- a/src/beans_logging/_builder.py
+++ b/src/beans_logging/_builder.py
@@ -4,7 +4,7 @@
from pydantic import validate_call
-from ._constants import LogHandlerTypeEnum, LogLevelEnum
+from .constants import LogHandlerTypeEnum, LogLevelEnum
from .schemas import LogHandlerPM
from .config import LoggerConfigPM
from .sinks import std_sink
@@ -35,17 +35,15 @@ def build_handler(handler: LogHandlerPM, config: LoggerConfigPM) -> dict[str, An
dict[str, Any]: Loguru handler config as dictionary.
"""
- _handler_dict = handler.model_dump(by_alias=True, exclude_none=True)
-
- if _handler_dict.get("sink") is None:
- if _handler_dict.get("type") == LogHandlerTypeEnum.STD:
- _handler_dict["sink"] = std_sink
+ if handler.sink is None:
+ if handler.h_type == LogHandlerTypeEnum.STD:
+ handler.sink = std_sink
else:
raise ValueError(
"'sink' attribute is empty, required for any log handler except std handler!"
)
- _sink = _handler_dict.get("sink")
+ _sink = handler.sink
if isinstance(_sink, (str, Path)):
if not os.path.isabs(_sink):
_sink = os.path.join(config.default.file.logs_dir, _sink)
@@ -56,72 +54,75 @@ def build_handler(handler: LogHandlerPM, config: LoggerConfigPM) -> dict[str, An
if "{app_name}" in _sink:
_sink = _sink.format(app_name=config.app_name)
- _handler_dict["sink"] = _sink
+ handler.sink = _sink
- if _handler_dict.get("level") is None:
- if _handler_dict.get("error"):
- _handler_dict["level"] = config.default.level.err
+ if handler.level is None:
+ if handler.error:
+ handler.level = config.default.level.err
else:
- _handler_dict["level"] = config.default.level.base
+ handler.level = config.default.level.base
- if (_handler_dict.get("custom_serialize") is None) and _handler_dict.get(
- "serialize"
- ):
- _handler_dict["custom_serialize"] = config.default.custom_serialize
-
- if _handler_dict.get("custom_serialize"):
- _handler_dict["serialize"] = False
- _handler_dict["format"] = json_formatter
-
- if (_handler_dict.get("format") is None) and (not _handler_dict.get("serialize")):
- _handler_dict["format"] = config.default.format_str
-
- if _handler_dict.get("filter") is None:
- if _handler_dict.get("type") == LogHandlerTypeEnum.STD:
- _handler_dict["filter"] = use_std_filter
- elif _handler_dict.get("type") == LogHandlerTypeEnum.FILE:
- if _handler_dict.get("serialize") or _handler_dict.get("custom_serialize"):
- if _handler_dict.get("error"):
- _handler_dict["filter"] = use_file_json_err_filter
+ if (handler.custom_serialize is None) and handler.serialize:
+ handler.custom_serialize = config.default.custom_serialize
+
+ if handler.custom_serialize:
+ handler.serialize = False
+ handler.format_ = json_formatter
+
+ if (handler.format_ is None) and (not handler.serialize):
+ handler.format_ = config.default.format_str
+
+ if handler.filter_ is None:
+ if handler.h_type == LogHandlerTypeEnum.STD:
+ handler.filter_ = use_std_filter
+ elif handler.h_type == LogHandlerTypeEnum.FILE:
+ if handler.serialize or handler.custom_serialize:
+ if handler.error:
+ handler.filter_ = use_file_json_err_filter
else:
- _handler_dict["filter"] = use_file_json_filter
+ handler.filter_ = use_file_json_filter
else:
- if _handler_dict.get("error"):
- _handler_dict["filter"] = use_file_err_filter
+ if handler.error:
+ handler.filter_ = use_file_err_filter
else:
- _handler_dict["filter"] = use_file_filter
+ handler.filter_ = use_file_filter
else:
- _handler_dict["filter"] = use_all_filter
+ handler.filter_ = use_all_filter
- if _handler_dict.get("backtrace") is None:
- _handler_dict["backtrace"] = True
+ if handler.backtrace is None:
+ handler.backtrace = True
- if (_handler_dict.get("diagnose") is None) and (
- (_handler_dict.get("level") == LogLevelEnum.TRACE)
- or (_handler_dict.get("level") == 5)
+ if (handler.diagnose is None) and (
+ (handler.level == LogLevelEnum.TRACE) or (handler.level == 5)
):
- _handler_dict["diagnose"] = True
+ handler.diagnose = True
- if _handler_dict.get("type") == LogHandlerTypeEnum.FILE:
- if _handler_dict.get("enqueue") is None:
- _handler_dict["enqueue"] = True
+ if handler.h_type == LogHandlerTypeEnum.FILE:
+ if handler.enqueue is None:
+ handler.enqueue = True
- if _handler_dict.get("rotation") is None:
- _handler_dict["rotation"] = Rotator(
+ if handler.rotation is None:
+ handler.rotation = Rotator(
rotate_size=config.default.file.rotate_size,
rotate_time=config.default.file.rotate_time,
).should_rotate
- if _handler_dict.get("retention") is None:
- _handler_dict["retention"] = config.default.file.retention
-
- if _handler_dict.get("encoding") is None:
- _handler_dict["encoding"] = config.default.file.encoding
-
- _handler_dict.pop("type", None)
- _handler_dict.pop("error", None)
- _handler_dict.pop("custom_serialize", None)
- _handler_dict.pop("enabled", None)
+ if handler.retention is None:
+ handler.retention = config.default.file.retention
+
+ if handler.encoding is None:
+ handler.encoding = config.default.file.encoding
+
+ _handler_dict = handler.model_dump(
+ by_alias=True,
+ exclude_none=True,
+ exclude={
+ "enabled",
+ "h_type",
+ "error",
+ "custom_serialize",
+ },
+ )
return _handler_dict
diff --git a/src/beans_logging/_constants.py b/src/beans_logging/_constants.py
deleted file mode 100644
index 4f50a2c..0000000
--- a/src/beans_logging/_constants.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from enum import Enum
-
-
-class LogHandlerTypeEnum(str, Enum):
- STD = "STD"
- FILE = "FILE"
- SOCKET = "SOCKET"
- HTTP = "HTTP"
- SYSLOG = "SYSLOG"
- QUEUE = "QUEUE"
- MEMORY = "MEMORY"
- NULL = "NULL"
- CUSTOM = "CUSTOM"
- UNKNOWN = "UNKNOWN"
-
-
-class LogLevelEnum(str, Enum):
- TRACE = "TRACE"
- DEBUG = "DEBUG"
- INFO = "INFO"
- SUCCESS = "SUCCESS"
- WARNING = "WARNING"
- ERROR = "ERROR"
- CRITICAL = "CRITICAL"
-
-
-__all__ = [
- "LogHandlerTypeEnum",
- "LogLevelEnum",
-]
diff --git a/src/beans_logging/_core.py b/src/beans_logging/_core.py
index b4e6303..d5120bc 100644
--- a/src/beans_logging/_core.py
+++ b/src/beans_logging/_core.py
@@ -17,10 +17,11 @@
from potato_util import io as io_utils
# Internal modules
+from .constants import DEFAULT_LOGURU_HANDLER_NAME, DEFAULT_NO_HANDLER_NAME_PREFIX
from .schemas import LogHandlerPM, LoguruHandlerPM
from .config import LoggerConfigPM
from ._builder import build_handler
-from ._intercept import init_intercepter
+from .intercepters import add_intercepter
class LoggerLoader:
@@ -52,7 +53,7 @@ def __init__(
**kwargs,
) -> None:
- self.handlers_map = {"default.loguru_handler": 0}
+ self.handlers_map = {DEFAULT_LOGURU_HANDLER_NAME: 0}
if not config:
config = LoggerConfigPM()
@@ -63,15 +64,15 @@ def __init__(
self.config_path = config_path
if auto_load:
- self.load()
+ self.load(load_config_file=True)
@validate_call
- def load(self, load_config_file: bool = True) -> "Logger":
+ def load(self, load_config_file: bool = False) -> "Logger":
"""Load logger handlers based on logger config.
Args:
load_config_file (bool, optional): Whether to load config from file before loading handlers.
- Default is True.
+ Default is False.
Returns:
Logger: Main loguru logger instance.
@@ -84,7 +85,7 @@ def load(self, load_config_file: bool = True) -> "Logger":
for _key, _handler in self.config.handlers.items():
self.add_handler(name=_key, handler=_handler)
- init_intercepter(config=self.config)
+ add_intercepter(config=self.config)
return logger
def _load_config_file(self) -> None:
@@ -195,7 +196,7 @@ def add_handler(
_handler_id = logger.add(**_handler_dict)
if not name:
- name = f"log_handler.{uuid.uuid4().hex}"
+ name = f"{DEFAULT_NO_HANDLER_NAME_PREFIX}{uuid.uuid4().hex}"
self.handlers_map[name] = _handler_id
diff --git a/src/beans_logging/config.py b/src/beans_logging/config.py
index 412b83d..fb3d2ff 100644
--- a/src/beans_logging/config.py
+++ b/src/beans_logging/config.py
@@ -5,11 +5,19 @@
import potato_util as utils
from pydantic import Field, field_validator
-from ._constants import LogHandlerTypeEnum, LogLevelEnum
-from .schemas import ExtraBaseModel, LogHandlerPM, LoguruHandlerPM
-
-
-def _get_handlers() -> dict[str, LogHandlerPM]:
+from .constants import (
+ LogLevelEnum,
+ LogHandlerTypeEnum,
+ DEFAULT_ALL_STD_HANDLER_NAME,
+ DEFAULT_ALL_FILE_HANDLER_NAME,
+ DEFAULT_ERR_FILE_HANDLER_NAME,
+ DEFAULT_ALL_JSON_HANDLER_NAME,
+ DEFAULT_ERR_JSON_HANDLER_NAME,
+)
+from .schemas import ExtraBaseModel, LogHandlerPM
+
+
+def get_default_handlers() -> dict[str, LogHandlerPM]:
"""Get default log handlers.
Returns:
@@ -17,37 +25,37 @@ def _get_handlers() -> dict[str, LogHandlerPM]:
"""
_log_handlers: dict[str, LogHandlerPM] = {
- "default.all.std_handler": LogHandlerPM(
- type_=LogHandlerTypeEnum.STD,
+ DEFAULT_ALL_STD_HANDLER_NAME: LogHandlerPM(
+ h_type=LogHandlerTypeEnum.STD,
format_=(
"[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {extra[level_short]:<5} | "
"{name}:{line}]: {message}"
),
colorize=True,
),
- "default.all.file_handler": LogHandlerPM(
- type_=LogHandlerTypeEnum.FILE,
- sink="{app_name}.all.log",
+ DEFAULT_ALL_FILE_HANDLER_NAME: LogHandlerPM(
enabled=False,
+ h_type=LogHandlerTypeEnum.FILE,
+ sink="{app_name}.all.log",
),
- "default.err.file_handler": LogHandlerPM(
- type_=LogHandlerTypeEnum.FILE,
+ DEFAULT_ERR_FILE_HANDLER_NAME: LogHandlerPM(
+ enabled=False,
+ h_type=LogHandlerTypeEnum.FILE,
sink="{app_name}.err.log",
error=True,
- enabled=False,
),
- "default.all.json_handler": LogHandlerPM(
- type_=LogHandlerTypeEnum.FILE,
- sink="json/{app_name}.json.all.log",
- serialize=True,
+ DEFAULT_ALL_JSON_HANDLER_NAME: LogHandlerPM(
enabled=False,
+ h_type=LogHandlerTypeEnum.FILE,
+ sink="json/{app_name}.all.json.log",
+ serialize=True,
),
- "default.err.json_handler": LogHandlerPM(
- type_=LogHandlerTypeEnum.FILE,
- sink="json/{app_name}.json.err.log",
+ DEFAULT_ERR_JSON_HANDLER_NAME: LogHandlerPM(
+ enabled=False,
+ h_type=LogHandlerTypeEnum.FILE,
+ sink="json/{app_name}.err.json.log",
serialize=True,
error=True,
- enabled=False,
),
}
@@ -131,36 +139,54 @@ class LoggerConfigPM(ExtraBaseModel):
)
default: DefaultConfigPM = Field(default_factory=DefaultConfigPM)
intercept: InterceptConfigPM = Field(default_factory=InterceptConfigPM)
- handlers: dict[str, LogHandlerPM] = Field(default_factory=_get_handlers)
+ handlers: dict[str, LogHandlerPM] = Field(default_factory=get_default_handlers)
extra: ExtraConfigPM | None = Field(default_factory=ExtraConfigPM)
@field_validator("handlers", mode="before")
@classmethod
- def _check_handlers(cls, val: Any) -> Any:
- if val:
- if not isinstance(val, dict):
+ def _check_handlers(cls, val: Any) -> dict[str, LogHandlerPM]:
+
+ _default_handlers = get_default_handlers()
+
+ if not val:
+ val = _default_handlers
+ return val
+
+ if not isinstance(val, dict):
+ raise TypeError(
+ f"'handlers' attribute type {type(val).__name__} is invalid, must be a dict of or dict!"
+ )
+
+ for _key, _handler in val.items():
+ if not isinstance(_handler, (LogHandlerPM, dict)):
raise TypeError(
- f"'handlers' attribute type {type(val).__name__} is invalid, must be a dict of , "
- f" or dict!"
+ f"'handlers' attribute's '{_key}' key -> value type {type(_handler).__name__} is invalid, must be "
+ f" or dict!"
+ )
+
+ if isinstance(_handler, LogHandlerPM):
+ val[_key] = _handler.model_dump(
+ by_alias=True, exclude_unset=True, exclude_none=True
)
- for _i, _handler in val.items():
- if not isinstance(_handler, (LogHandlerPM, LoguruHandlerPM, dict)):
- raise TypeError(
- f"'handlers' attribute index {_i} type {type(_handler).__name__} is invalid, must be "
- f", or dict!"
- )
+ _default_dict = {
+ _key: _handler.model_dump(
+ by_alias=True, exclude_unset=True, exclude_none=True
+ )
+ for _key, _handler in _default_handlers.items()
+ }
+
+ if _default_dict != val:
+ val = utils.deep_merge(_default_dict, val)
- if isinstance(_handler, LoguruHandlerPM):
- val[_i] = LogHandlerPM(
- **_handler.model_dump(exclude_none=True, exclude_unset=True)
- )
- elif isinstance(_handler, dict):
- val[_i] = LogHandlerPM(**_handler)
+ for _key, _handler in val.items():
+ val[_key] = LogHandlerPM(**_handler)
return val
__all__ = [
"LoggerConfigPM",
+ "InterceptConfigPM",
+ "get_default_handlers",
]
diff --git a/src/beans_logging/constants.py b/src/beans_logging/constants.py
new file mode 100644
index 0000000..2105d83
--- /dev/null
+++ b/src/beans_logging/constants.py
@@ -0,0 +1,46 @@
+from enum import Enum
+
+
+class LogHandlerTypeEnum(str, Enum):
+ STD = "STD"
+ FILE = "FILE"
+ SOCKET = "SOCKET"
+ HTTP = "HTTP"
+ SYSLOG = "SYSLOG"
+ QUEUE = "QUEUE"
+ MEMORY = "MEMORY"
+ NULL = "NULL"
+ CUSTOM = "CUSTOM"
+ UNKNOWN = "UNKNOWN"
+
+
+class LogLevelEnum(str, Enum):
+ TRACE = "TRACE"
+ DEBUG = "DEBUG"
+ INFO = "INFO"
+ SUCCESS = "SUCCESS"
+ WARNING = "WARNING"
+ ERROR = "ERROR"
+ CRITICAL = "CRITICAL"
+
+
+DEFAULT_LOGURU_HANDLER_NAME = "default.loguru.handler"
+DEFAULT_ALL_STD_HANDLER_NAME = "default.all.std_handler"
+DEFAULT_ALL_FILE_HANDLER_NAME = "default.all.file_handler"
+DEFAULT_ERR_FILE_HANDLER_NAME = "default.err.file_handler"
+DEFAULT_ALL_JSON_HANDLER_NAME = "default.all.json_handler"
+DEFAULT_ERR_JSON_HANDLER_NAME = "default.err.json_handler"
+DEFAULT_NO_HANDLER_NAME_PREFIX = "log_handler."
+
+
+__all__ = [
+ "LogHandlerTypeEnum",
+ "LogLevelEnum",
+ "DEFAULT_LOGURU_HANDLER_NAME",
+ "DEFAULT_ALL_STD_HANDLER_NAME",
+ "DEFAULT_ALL_FILE_HANDLER_NAME",
+ "DEFAULT_ERR_FILE_HANDLER_NAME",
+ "DEFAULT_ALL_JSON_HANDLER_NAME",
+ "DEFAULT_ERR_JSON_HANDLER_NAME",
+ "DEFAULT_NO_HANDLER_NAME_PREFIX",
+]
diff --git a/src/beans_logging/_intercept.py b/src/beans_logging/intercepters.py
similarity index 97%
rename from src/beans_logging/_intercept.py
rename to src/beans_logging/intercepters.py
index c48a12c..65044d1 100644
--- a/src/beans_logging/_intercept.py
+++ b/src/beans_logging/intercepters.py
@@ -45,7 +45,7 @@ def emit(self, record: LogRecord) -> None:
@validate_call
-def init_intercepter(config: LoggerConfigPM) -> None:
+def add_intercepter(config: LoggerConfigPM) -> None:
"""Initialize log interceptor based on provided config.
Args:
@@ -102,5 +102,5 @@ def init_intercepter(config: LoggerConfigPM) -> None:
__all__ = [
"InterceptHandler",
- "init_intercepter",
+ "add_intercepter",
]
diff --git a/src/beans_logging/schemas.py b/src/beans_logging/schemas.py
index 23be9b6..9d112ab 100644
--- a/src/beans_logging/schemas.py
+++ b/src/beans_logging/schemas.py
@@ -19,7 +19,7 @@
from loguru import Record, Message
from pydantic import BaseModel, Field, ConfigDict, model_validator
-from ._constants import LogHandlerTypeEnum, LogLevelEnum
+from .constants import LogHandlerTypeEnum, LogLevelEnum
class ExtraBaseModel(BaseModel):
@@ -94,11 +94,7 @@ class LoguruHandlerPM(ExtraBaseModel):
class LogHandlerPM(LoguruHandlerPM):
- type_: LogHandlerTypeEnum = Field(
- default=LogHandlerTypeEnum.UNKNOWN,
- validation_alias="type",
- serialization_alias="type",
- )
+ h_type: LogHandlerTypeEnum = Field(default=LogHandlerTypeEnum.UNKNOWN)
sink: _SinkType | None = Field(default=None)
level: str | int | LogLevelEnum | None = Field(default=None)
custom_serialize: bool | None = Field(default=None)
diff --git a/templates/configs/logger.yml b/templates/configs/logger.yml
index 2c66041..f2772c8 100644
--- a/templates/configs/logger.yml
+++ b/templates/configs/logger.yml
@@ -20,28 +20,28 @@ logger:
mute_modules: []
handlers:
default.all.std_handler:
- type: STD
+ enabled: true
+ h_type: STD
format: "[{time:YYYY-MM-DD HH:mm:ss.SSS Z} | {extra[level_short]:<5} | {name}:{line}]: {message}"
colorize: true
- enabled: true
default.all.file_handler:
- type: FILE
- sink: "{app_name}.all.log"
enabled: false
+ h_type: FILE
+ sink: "{app_name}.all.log"
default.err.file_handler:
- type: FILE
+ enabled: false
+ h_type: FILE
sink: "{app_name}.err.log"
error: true
- enabled: false
default.all.json_handler:
- type: FILE
- sink: "json/{app_name}.json.all.log"
- serialize: true
enabled: false
+ h_type: FILE
+ sink: "json/{app_name}.all.json.log"
+ serialize: true
default.err.json_handler:
- type: FILE
- sink: "json/{app_name}.json.err.log"
+ enabled: false
+ h_type: FILE
+ sink: "json/{app_name}.err.json.log"
serialize: true
error: true
- enabled: false
extra: