added description to __init__.py; removed build and dist files

This commit is contained in:
2024-03-22 16:06:44 +01:00
parent 0fe530c6a2
commit 67ad6ccb94
10 changed files with 12 additions and 742 deletions
+3
View File
@@ -1,3 +1,6 @@
/.venv/
/.idea/
/test/log/
/build/
/dist/
/eh_logging.egg-info/
-618
View File
@@ -1,618 +0,0 @@
from __future__ import annotations
from functools import wraps
from logging import *
from logging import __all__ as logging__all__
from logging import handlers
from pathlib import Path as Path_
import os
import time
__all__ = tuple(logging__all__ + [
'Decorator',
'FormatterColor',
'StreamHandlerFormatted',
'add_default_handler',
'critical',
'debug',
'error',
'get_default_logger',
'get_default_logger_child',
'get_formatted_logger',
'info',
'log',
'set_default_level',
'set_default_logger',
'warning',
])
__version__ = '0.1.5'
__author__ = 'Eishausener <code@eishausener.de>'
__name__ = 'eh_logging'
##########
# config #
##########
# debug print (not formatted)
_DEBUG = False
class DEFAULT:
# -- Default Logger Name -- #
LOGGER_NAME = 'eh_logging'
# -- Logger Space -- #
SPACE_LOGGER_NAME = 11
SPACE_LEVEL = 8
SPACE_TIME = 19
####################
# helper functions #
####################
def Path(*args, **kwargs):
"""
Returns the Path from the pathlib module as String
:param args:
:param kwargs:
:return:
"""
return str(Path_(*args, **kwargs))
def print_debug(*args, **kwargs):
"""
only print to console if _DEBUG is True
:param args:
:param kwargs:
"""
if _DEBUG:
print(*args, **kwargs)
class _DefaultLogger:
"""
Helper class to hold only one instance of the default logger
"""
logger: Logger | None = None
def get(self):
print_debug(f'[get] addr: {id(self.logger)}, logger: {self.logger}')
return self.logger
def set(self, logger: Logger) -> None:
print_debug(f'[set] addr: {id(self.logger)}, logger: {self.logger}')
self.logger = logger
_default_logger = _DefaultLogger()
def _init():
"""
Initialize a logger with StreamHandler and set as default logger (without formatting).
If during the initialization process of the module console outputs must be done, this logger is used. In the end,
it is replaced with a formatted logger
"""
_changed = False
_logger = _default_logger.get()
if not _logger:
print_debug('[DEBUG] creating new logger')
_logger = getLogger(DEFAULT.LOGGER_NAME)
_changed = True
if not _logger.handlers:
print_debug(f'[DEBUG] adding stream handler to logger. (addr: {id(_logger)})')
_handler = StreamHandler()
_handler.name = 'eh_logging-stream-helper' # should not be displayed. only for internal use
_logger.addHandler(_handler)
_changed = True
if _changed:
_default_logger.set(_logger)
_init()
############################
# default logger functions #
############################
def get_default_logger() -> Logger:
"""
Returns the default logger
:return:
"""
return _default_logger.get()
def set_default_logger(logger: Logger) -> None:
"""
Sets the default logger
:param logger:
:return:
"""
_default_logger.set(logger)
def add_default_handler(handler: Handler) -> None:
"""
Adds the handler to the default logger
:param handler:
:return:
"""
logger = _default_logger.get()
logger.addHandler(handler)
_default_logger.set(logger)
def set_default_level(level: int) -> None:
"""
Sets the default logger level
:param level:
:return:
"""
logger = _default_logger.get()
logger.setLevel(level)
_default_logger.set(logger)
def get_default_logger_child(suffix: str) -> Logger:
"""
Returns a child logger of the default logger
:param suffix:
:return:
"""
logger = _default_logger.get()
return logger.getChild(suffix=suffix)
#########################
# root logger functions #
#########################
def log(level, msg, *args, **kwargs):
"""
Log 'msg % args' with the integer severity 'level' on the default logger.
"""
_logger = _default_logger.get()
_logger.log(level, msg, *args, **kwargs)
def debug(msg, *args, **kwargs):
"""
Log a message with severity 'DEBUG' on the default logger.
"""
_logger = _default_logger.get()
_logger.debug(msg, *args, **kwargs)
def info(msg, *args, **kwargs):
"""
Log a message with severity 'INFO' on the default logger.
"""
_logger = _default_logger.get()
_logger.info(msg, *args, **kwargs)
def warning(msg, *args, **kwargs):
"""
Log a message with severity 'WARNING' on the default logger.
"""
_logger = _default_logger.get()
_logger.warning(msg, *args, **kwargs)
def error(msg, *args, **kwargs):
"""
Log a message with severity 'ERROR' on the default logger.
"""
_logger = _default_logger.get()
_logger.error(msg, *args, **kwargs)
def critical(msg, *args, **kwargs):
"""
Log a message with severity 'CRITICAL' on the default logger.
"""
_logger = _default_logger.get()
_logger.critical(msg, *args, **kwargs)
#############
# Decorator #
#############
def _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
level, function, *args, **kwargs):
"""
Executes the function and returns the result of the function and does all the logging stuff
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:param level:
:param function:
:param args:
:param kwargs:
:return:
"""
if logger is None:
logger = _default_logger.get()
fname = function.__name__
msg = f'Function \x1b[3m{fname}\x1b[0m started'
if arg:
msg += f' with args: {args}'
if kwarg and not arg:
msg += f' with kwargs: {kwargs}'
if kwarg and arg:
msg += f' and with kwargs: {kwargs}'
logger.log(level, msg)
time_start = time.time()
value = function(*args, **kwargs)
time_needed = time.time() - time_start
if isinstance(time_needed, int):
time_needed = round(time_needed, decimal_places)
msg = f'Function \x1b[3m{fname}\x1b[0m finished in {time_needed}sec'
if return_value_type:
msg += f' with return value type {type(value)}'
if return_value and return_value_type:
msg += f' and value {value}'
if return_value and not return_value_type:
msg += f' with return value {value}'
logger.log(level, msg)
return value
class Decorator:
"""
Decorators to log
"""
@staticmethod
def func_logger(logger: Logger = None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2, level: int = DEBUG):
"""
Decorator to add logging to a function
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:param level:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
level, function, *args, **kwargs)
return wrapper
return decorator
@staticmethod
def debug(logger: Logger = None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2):
"""
Decorator to add logging to a function with the Level DEBUG
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
DEBUG, function, *args, **kwargs)
return wrapper
return decorator
@staticmethod
def info(logger=None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2):
"""
Decorator to add logging to a function with the Level INFO
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
INFO, function, *args, **kwargs)
return wrapper
return decorator
@staticmethod
def warning(logger=None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2):
"""
Decorator to add logging to a function with the Level WARNING
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
WARNING, function, *args, **kwargs)
return wrapper
return decorator
@staticmethod
def error(logger=None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2):
"""
Decorator to add logging to a function with the Level ERROR
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
ERROR, function, *args, **kwargs)
return wrapper
return decorator
@staticmethod
def critical(logger=None, arg: bool = False, kwarg: bool = False, return_value: bool = False,
return_value_type: bool = False, decimal_places: int = 2):
"""
Decorator to add logging to a function with the Level CRITICAL
:param logger:
:param arg:
:param kwarg:
:param return_value:
:param return_value_type:
:param decimal_places:
:return:
"""
def decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
nonlocal logger
return _decorator_log_by_level(logger, arg, kwarg, return_value, return_value_type, decimal_places,
CRITICAL, function, *args, **kwargs)
return wrapper
return decorator
#############
# Formatter #
#############
class FormatterColor(Formatter):
space_logger_name: int | None
space_level: int | None
space_time: int | None
time_format: str
def __init__(self, space_logger_name: int = None, space_level: int = None, space_time: int = None,
time_format: str = '%Y-%m-%d %H:%M:%S'):
"""
:param space_logger_name:
:param space_level:
:param space_time:
:param time_format:
"""
super().__init__()
temp_logger = getLogger('FormatterColor')
# space_logger_name
if space_logger_name is not None:
try:
self.space_logger_name = int(space_logger_name)
except ValueError:
self.space_logger_name = DEFAULT.SPACE_LOGGER_NAME
temp_logger.warning(
f'the type of space_logger_name must be int not {type(space_logger_name)}! Set to default '
f'value of 10')
else:
self.space_logger_name = DEFAULT.SPACE_LOGGER_NAME
# space_level
if space_level is not None:
try:
self.space_level = int(space_level)
except ValueError:
self.space_level = DEFAULT.SPACE_LEVEL
temp_logger.warning(
f'the type of space_level must be int not {type(space_logger_name)}! Set to default value of 8')
else:
self.space_level = DEFAULT.SPACE_LEVEL
# space_time
if space_time is not None:
try:
self.space_time = int(space_time)
except ValueError:
self.space_time = DEFAULT.SPACE_TIME
temp_logger.warning(
f'the type of space_time must be int not {type(space_logger_name)}! Set to default value of 19')
else:
self.space_time = DEFAULT.SPACE_TIME
# time_format
if time_format is not None:
self.time_format = time_format
# https://en.wikipedia.org/wiki/ANSI_escape_code
self.color_by_level = (
(DEBUG, '\033[38;5;85;1m'), # 85 Bold
(INFO, '\033[96;1m'), # LIGHTCYAN_EX Bold
(WARNING, '\033[93;1m'), # LIGHTYELLOW_EX Bold
(ERROR, '\033[31;1m'), # RED Bold
(CRITICAL, '\033[31;1;3;5m'), # RED Bold/Italic/blinking
)
self.formats = {
level: Formatter(
f'\x1b[30;1m%(asctime)-{self.space_time}s\x1b[0m {colour}%(levelname)-{self.space_level}s\x1b[0m '
f'\x1b[36m%(name)-{self.space_logger_name}s\x1b[0m %(message)s',
self.time_format,
)
for level, colour in self.color_by_level
}
def format(self, record: LogRecord):
formatter = self.formats.get(record.levelno)
if formatter is None:
formatter = self.formats[DEBUG]
output = formatter.format(record)
return output
class StreamHandlerFormatted(StreamHandler):
def __init__(self, level: int = None, stream=None):
"""
when the logger this handler will be added,
has a higher level, then this handler,
it will not log (this is normal)
the best is to set the logger to 1 (not 0)
:param level:
:param stream:
"""
super().__init__(stream=stream)
# set Formatter
self.formatter = FormatterColor()
# set level if it is available
if level is not None:
self.level = level
def get_formatted_logger(name: str, console=True, console_level: int = INFO, file=False, file_path: str = None,
file_level: int = WARNING, file_backup_count: int = None,
file_rotate: str = 'midnight', file_interval: int = 1, logger_level: int = 1,
space_logger_name: int = None, space_level: int = None, space_time: int = None,
time_format: str = '%Y-%m-%d %H:%M:%S') -> Logger:
"""
:param name: the name of the logger
:param console: if a StreamHandler should be added
:param console_level: the minimum level who will be in the console
:param file: if a FileHandler should be added
:param file_path: the full path of the logfile
:param file_level: the minimum level who will be in the logfile
:param file_backup_count:
:param file_rotate:
:param file_interval:
:param logger_level: should be lower or equal than handlers of it
:param space_logger_name:
:param space_level:
:param space_time:
:param time_format:
:return:
"""
logger = getLogger(name)
logger.setLevel(logger_level)
if console:
handler = StreamHandler()
handler.setFormatter(FormatterColor(space_logger_name, space_level, space_time, time_format))
handler.setLevel(console_level)
logger.addHandler(handler)
if console_level < logger_level:
get_default_logger_child('get_formatted_logger').warning(
f'the console (level {console_level}) will not log all, because of the logger level is {logger_level}')
if file:
if file_path is None:
get_default_logger_child('get_formatted_logger').warning(f'file_path is None. No file handler added')
else:
# check if log dir exists if not create it
file_path = os.path.abspath(file_path)
file_name = os.path.split(file_path)[-1]
if not file_name.endswith('.log'):
file_name += '.log'
file_path += '.log'
get_default_logger_child('get_formatted_logger').warning(f'file_path should be end with .log. adding '
f'.log to {file_name}')
if not os.path.exists((path := os.path.dirname(file_path))):
os.makedirs(path)
if file_backup_count is not None:
handler = handlers.TimedRotatingFileHandler(file_path, backupCount=file_backup_count,
when=file_rotate, interval=file_interval)
handler.namer = lambda name_logfile: name_logfile.replace('.log', '') + '.log'
else:
handler = FileHandler(file_path)
handler.setFormatter(FormatterColor())
handler.setLevel(file_level)
logger.addHandler(handler)
if file_level < logger_level:
get_default_logger_child('get_formatted_logger').warning(
f'the file (level {file_level}) will not log all, because of the logger level is {logger_level}')
return logger
def _init_default_logger():
"""
Initialize the default logger with a formatted logger
:return:
"""
_logger = getLogger(DEFAULT.LOGGER_NAME)
_formatter = FormatterColor()
_handler = StreamHandler()
_handler.setFormatter(_formatter)
_handler.name = 'eh_logging-stream-default' # should not be displayed. only for internal use
# remove all handlers to prevent duplicate handlers
for handler in _logger.handlers:
_logger.removeHandler(handler)
_logger.addHandler(_handler)
_default_logger.set(_logger)
_init_default_logger()
Binary file not shown.
BIN
View File
Binary file not shown.
-107
View File
@@ -1,107 +0,0 @@
Metadata-Version: 2.1
Name: eh_logging
Version: 0.1.5
Summary: Simple helper to get easier formatted logger from the python logging module
Home-page: https://git.eishausener.dev/Eishausener/eh_logging
Author: Eishausener <code@eishausener.de>
Author-email: code@eishausener.de
License: MIT
Project-URL: issue tracker, https://git.eishausener.dev/Eishausener/eh_logging/issues
Classifier: License :: OSI Approved :: MIT License
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: twine; extra == "dev"
Requires-Dist: wheel; extra == "dev"
Requires-Dist: setuptools; extra == "dev"
# eh_logging
> Simple helper to get easier formatted logger from the python logging module
# install
pip install git+https://git.eishausener.dev/Eishausener/eh_logging \
or \
pip install git+https://github.com/Eishausener/eh_logging
# usage
import eh-logging, create a formatted logger and use the logger
```python
# import
import eh_logging as logging
# create formatted logger
formatted_logger = logging.get_formatted_logger(
'formatted_logger',
console=True,
console_level=logging.DEBUG,
file=True,
file_path=r'log\formatted_logger.log',
file_level=logging.DEBUG,
file_backup_count=5,
file_rotate='h',
)
# use the logger
formatted_logger.debug('Example formatted DEBUG Message')
formatted_logger.info('Example formatted INFO Message')
formatted_logger.warning('Example formatted WARNING Message')
formatted_logger.error('Example formatted ERROR Message')
formatted_logger.critical('Example formatted CRITICAL Message')
```
use the decorator with the INFO level
```python
# import
import eh_logging as logging
# set default logger to DEBUG level to see output
logging.set_default_level(logging.DEBUG)
# use logging decorator with default logger
@logging.Decorator.info(arg=True, kwarg=True, return_value=True, decimal_places=1)
def example_function(first_param, second_param, *args, **kwargs):
return first_param, second_param, *args, *kwargs
# execute function
example_function('Hello', 'world', 'test', 'example', hello='world', world='test')
```
use the decorator with own logger
```python
# import
import eh_logging as logging
# create formatted logger
formatted_logger = logging.get_formatted_logger(
'example_decorator_logger',
console=True,
console_level=logging.DEBUG,
file=True,
file_path=r'log\decorator_logger.log',
file_level=logging.DEBUG,
file_backup_count=5,
file_rotate='h',
)
# use logging decorator with custom logger
@logging.Decorator.debug(logger=formatted_logger, arg=True, kwarg=True, return_value=True, decimal_places=1)
def example_function(first_param, second_param, *args, **kwargs):
return first_param, second_param, *args, *kwargs
# execute function
example_function('Hello', 'world', 'test', 'example', hello='world', world='test')
```
-9
View File
@@ -1,9 +0,0 @@
README.md
setup.py
eh_logging/__init__.py
eh_logging.egg-info/PKG-INFO
eh_logging.egg-info/SOURCES.txt
eh_logging.egg-info/dependency_links.txt
eh_logging.egg-info/requires.txt
eh_logging.egg-info/top_level.txt
test/test.py
-1
View File
@@ -1 +0,0 @@
-5
View File
@@ -1,5 +0,0 @@
[dev]
twine
wheel
setuptools
-1
View File
@@ -1 +0,0 @@
eh_logging
+9 -1
View File
@@ -1,3 +1,11 @@
"""
eh_logging
^^^^^^^^^^
Simple helper to get easier formatted logger from the python logging module
"""
from __future__ import annotations
from functools import wraps
@@ -25,7 +33,7 @@ __all__ = tuple(logging__all__ + [
'set_default_logger',
'warning',
])
__version__ = '0.1.5'
__version__ = '0.1.6'
__author__ = 'Eishausener <code@eishausener.de>'
__name__ = 'eh_logging'