Skip to content

Commit

Permalink
**[FEATURE]** Added move() functionality
Browse files Browse the repository at this point in the history
* **[FEATURE]** Added move() functionality [see: Function move()](README.md#function_move)
* **[BUG_FIX]** Fixed a bug where release does not actually releases the resources
* **[CI_FIX]** Added missing pytest which was removed with the coverage
  • Loading branch information
sevketcaba committed Dec 6, 2023
1 parent 466930c commit 72abd65
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 34 deletions.
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,33 @@ logger.set_format([
)
logger.console("Hello World!")
# Output : [DEBUG] Hello World!
```
```


### <a name="function_move"></a> Move the log file to another location
```
logger.move(new_file: str, option: LogMoveOption)
# Output : Hello World!
```
### Available LogMoveOption
- MOVE_AND_APPEND
Moves the log file to the destination
Deletes if there's already a file in the destination
Appends to the moved file
Old file is obviously deleted
- COPY_AND_APPEND
Copies the log file to the destination
Deletes if there's already a file in the destination
Appends to the moved file
Old file is obviously remains
- KEEP_AND_APPEND
Keeps the old log file
Appends if there's already a file in the destination
Creates a new file if not
- KEEP_AND_INIT
Keeps the old log file
Creates a new file in the destination
- DELETE_AND_INIT
Deletes the old log file
Creates a new file in the destination
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ classifiers = [ # Optional
"Programming Language :: Python :: 3 :: Only",
]
dependencies = [ # Optional
"parameterized"
]
[project.optional-dependencies] # Optional
dev = ["black", "flake8", "mypy"]
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
parameterized
1 change: 1 addition & 0 deletions src/uglylogger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
LogLevel,
LogFormatBlock,
Logger,
LogMoveOption,
)
176 changes: 149 additions & 27 deletions src/uglylogger/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import inspect
import os
import typing
import shutil
from typing import Tuple, Callable


class LogColorMode(IntEnum):
Expand Down Expand Up @@ -102,6 +104,40 @@ class LogFormatBlock(Flag):
FUNCTION = auto()


class LogMoveOption(IntEnum):
"""LogMoveOption"""

"""Moves the log file to the destination
Deletes if there's already a file in the destination
Appends to the moved file
Old file is obviously deleted
"""
MOVE_AND_APPEND = (1,)

"""Copies the log file to the destination
Deletes if there's already a file in the destination
Appends to the moved file
Old file is obviously remains
"""
COPY_AND_APPEND = (2,)

"""Keeps the old log file
Appends if there's already a file in the destination
Creates a new file if not
"""
KEEP_AND_APPEND = (3,)

"""Keeps the old log file
Creates a new file in the destination
"""
KEEP_AND_INIT = (4,)

"""Deletes the old log file
Creates a new file in the destination
"""
DELETE_AND_INIT = (5,)


class Logger:
"""The infamous ugly logger class"""

Expand Down Expand Up @@ -158,8 +194,24 @@ def __init__(
Logger.DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"

self._name = name
self._init(file, permanent, append, color_mode)

def __del__(self) -> None:
"""Destructor
Releases the resources like handlers if logger is not permanent
"""
if not self._permanent:
self.release()

self._logger = logging.getLogger(name)
def _init(
self,
file: str | None = None,
permanent: bool = False,
append: bool = True,
color_mode: LogColorMode = LogColorMode.COLORED,
) -> None:
self._logger = logging.getLogger(self._name)
handlers = self._logger.handlers
if len(handlers) > 0:
# Load attributes from logger
Expand Down Expand Up @@ -197,26 +249,27 @@ def __init__(
self._file_handler.setLevel(logging.DEBUG)
self._logger.addHandler(self._file_handler)

def __del__(self):
"""Destructor
Releases the resources like handlers if logger is not permanent
"""
if not self._permanent:
self.release()

def release(self):
def release(self) -> None:
"""Releases the resources of the logger, like handlers etc."""
if self._console_handler is not None:
self._console_handler.close()
del self._console_handler
if self._logger is not None:
self._logger.removeHandler(self._console_handler)
# del self._console_handler
self._console_handler = None
if self._file_handler is not None:
self._file_handler.close()
del self._file_handler
if self._logger is not None:
self._logger.removeHandler(self._file_handler)
# del self._file_handler
self._file_handler = None
del self._logger
self._logger = None

self._file = None
self._color_mode = LogColorMode.COLORED
self._permanent = False

if self._name in logging.Logger.manager.loggerDict:
del logging.Logger.manager.loggerDict[self._name]

Expand All @@ -232,16 +285,18 @@ def DateTimeToStr(dt) -> str:
"""
return dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]

def set_color_mode(self, mode: LogColorMode):
def set_color_mode(self, mode: LogColorMode) -> None:
"""Sets the color mode
Args:
mode (LogColorMode): Color Mode
"""
self._colored = mode == LogColorMode.COLORED

def _build_handler_filter(self, handler: str):
def handler_filter(record: LogRecord):
def _build_handler_filter(
self, handler: str
) -> Callable[[LogRecord], bool]:
def handler_filter(record: LogRecord) -> bool:
if hasattr(record, "block"):
if record.block == handler:
return False
Expand All @@ -256,7 +311,7 @@ def _color_str(self, color: LogColor) -> str:
def _msg_to_str(self, msg: typing.Any) -> str:
return str(msg, "utf-8") if type(msg) is bytes else str(msg)

def _get_file_line_func(self):
def _get_file_line_func(self) -> Tuple[str | None, str | None, int | None]:
stack = inspect.stack()
this_fil = str(stack[1][1])
fil = None
Expand All @@ -278,7 +333,7 @@ def _get_file_line_func(self):

return (None, None, None) # pragma: no cover

def _format(self, msg: typing.Any, level: LogLevel):
def _format(self, msg: typing.Any, level: LogLevel) -> str:
formatted = ""
fil = None
fun = None
Expand Down Expand Up @@ -317,22 +372,22 @@ def _format(self, msg: typing.Any, level: LogLevel):

def _colored_format(
self, msg: typing.Any, color: LogColor, level: LogLevel
):
) -> str:
if self._colored:
return (
self._color_str(color) + self._format(msg, level) + "\033[0m"
)
return self._format(msg, level)

def set_format(self, fmt: list = []):
def set_format(self, fmt: list = []) -> None:
self._format_arr = fmt

def console_oneline(
self,
msg: typing.Any,
color: LogColor = LogColor.BLACK,
console_width: int = 100,
):
) -> None:
if console_width <= 0:
return
msg_str = self._msg_to_str(msg)
Expand Down Expand Up @@ -365,7 +420,7 @@ def console(
msg: typing.Any,
color: LogColor | None = None,
level: LogLevel = LogLevel.DEBUG,
):
) -> None:
if self._logger is None:
return # pragma: no cover
match level:
Expand Down Expand Up @@ -442,7 +497,7 @@ def log(
color: LogColor | None = None,
level: LogLevel = LogLevel.DEBUG,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs both to the file and to the console
Args:
Expand All @@ -462,7 +517,7 @@ def debug(
msg: typing.Any,
color: LogColor | None = None,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs as debug
Args:
Expand All @@ -482,7 +537,7 @@ def info(
msg: typing.Any,
color: LogColor | None = None,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs as info
Args:
Expand All @@ -502,7 +557,7 @@ def warning(
msg: typing.Any,
color: LogColor | None = None,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs as warning
Args:
Expand All @@ -522,7 +577,7 @@ def error(
msg: typing.Any,
color: LogColor | None = None,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs as error
Args:
Expand All @@ -542,7 +597,7 @@ def critical(
msg: typing.Any,
color: LogColor | None = None,
output: LogOutput = LogOutput.ALL,
):
) -> None:
"""Logs as critical
Args:
Expand All @@ -556,3 +611,70 @@ def critical(
self.console(msg, color, LogLevel.CRITICAL)
if self._file_handler is not None and LogOutput.FILE in output:
self.file(msg, LogLevel.CRITICAL)

def move(
self,
new_file: str,
option: LogMoveOption = LogMoveOption.MOVE_AND_APPEND,
) -> None:
"""Moves the log file to a new destination
Args:
new_file (str): New Log File
option (LogMoveOption): how to behave, Defaults to MOVE_AND_APPEND
otherwise uses color by the LogLevel. Defaults to None.
"""

if self._file is None:
return

permanent = self._permanent
color_mode = self._color_mode
old_file = self._file
self.release()

new_file_abs = os.path.abspath(new_file)
# new_dir = os.path.dirname(new_file_abs)
# if not os.path.exists(new_dir):
# os.makedirs(new_dir)

append: bool = False
match option:
case LogMoveOption.MOVE_AND_APPEND:
# delete if there's a file in the destionation
if os.path.exists(new_file_abs):
os.remove(new_file_abs)
# move the file to the destination
shutil.move(old_file, new_file_abs)
# append to the new file
append = True
case LogMoveOption.COPY_AND_APPEND:
# delete if there's a file in the destionation
if os.path.exists(new_file_abs):
os.remove(new_file_abs)
# copy the file to the destination
shutil.copy(old_file, new_file_abs)
# append to the new file
append = True
case LogMoveOption.KEEP_AND_APPEND:
# don't delete the old file
# don't delete if there's a file in the destionation
# append to the new/existing file
append = True
case LogMoveOption.DELETE_AND_INIT:
# delete the old file
os.remove(old_file)
# delete if there's a file in the destionation
if os.path.exists(new_file_abs):
os.remove(new_file_abs)
# don't append to the new file
append = False
case LogMoveOption.KEEP_AND_INIT:
# don't delete the old file
# delete if there's a file in the destionation
if os.path.exists(new_file_abs):
os.remove(new_file_abs)
# don't append to the new file
append = False

self._init(new_file_abs, permanent, append, color_mode)
Loading

0 comments on commit 72abd65

Please sign in to comment.