A wrapper around the Python logging module that provides a more flexible and powerful logging solution.
Project description
LoggerPlus
A wrapper around Python's built-in logging module.
Goals
- Provide a drop-in replacement for Python's logging module.
- Provide highly verbose exception tracebacks with variable context. You will see the values of the variables at each and every level in the traceback.
- Provide extensive defaults for logging configuration so you don't have to worry about it.
- Provide colored logs in cross-platform-agnostic manner
- Almost zero overhead on the main thread.
- Fix some annoying issues with Python's logging module.
- Handle scenarios when the user forgets to initialize the logger, or uses it incorrectly. Using RobustLogger in any way shape or form should never throw an exception outside of the RobustLogger library itself (they will be logged to a file).
Installation
You can install the package via pip:
pip install LoggerPlus
Usage
To use the package, you can import it and use it as a drop-in replacement for Python's logging module.
from loggerplus import RobustLogger
if __name__ == "__main__":
logger = RobustLogger()
logger.debug("This is a debug message")
logger.info("This is an info message.")
logger.warning("This is a warning message.")
logger.error("This is an error message.")
logger.critical("This is a critical message.")
RobustLogger.debug("This is a debug message, correctly handling a user forgetting to construct RobustLogger.") # type: ignore[call-arg, arg-type]
# Test various edge case
RobustLogger("This is a test of __call__") # type: ignore[call-arg]
try:
raise RuntimeError("Test caught exception") # noqa: TRY301
except RuntimeError:
RobustLogger().exception("Message for a caught exception")
raise RuntimeError("Test uncaught exception")
The example code demonstrates how to use the RobustLogger class from the LoggerPlus package. It shows the following:
- Initialization: A
RobustLoggerinstance is created. - Logging Messages: Various log levels are used to log messages (
debug,info,warning,error,critical). - Handling Edge Cases: The
RobustLoggerclass is used directly to handle a case where the user forgets to construct the logger. - Exception Logging: The code demonstrates how to log caught and uncaught exceptions using the
exceptionmethod ofRobustLogger.
This example highlights the ease of use and robustness of the RobustLogger class.
How does it work?
LoggerPlus achieves its goals through the following mechanisms:
-
Drop-in Replacement: The
RobustRootLoggerclass is designed to be a drop-in replacement for Python's built-in logging module. It inherits fromlogging.Loggerand uses a metaclassMetaLoggerto ensure a singleton instance, making it easy to use without additional setup. -
Extensive Defaults: The
_setup_loggermethod inRobustRootLoggerconfigures multiple handlers and formatters by default. It sets up handlers for console output, file logging for different log levels, and exception logging. This ensures that users get a comprehensive logging setup out of the box. -
Colored Logs: The
ColoredConsoleHandlerclass extendslogging.StreamHandlerand uses thecoloramalibrary to provide colored log output. This makes logs easier to read and distinguish based on log levels. -
Minimal Main Thread Logic: The
_logmethod inRobustRootLoggerensures that logging is done in a separate thread usingThreadPoolExecutor. This prevents logging operations from blocking the main thread, improving performance and responsiveness. -
Robustness: The
RobustRootLoggerclass and its associated methods handle various edge cases, such as when the user forgets to initialize the logger or uses it incorrectly. TheCustomPrintToLoggerclass redirectsstdoutandstderrto the logger, ensuring that all output is captured. Additionally, theCustomExceptionFormatterandformat_exception_with_variablesfunctions provide detailed exception information, making it easier to debug issues. -
Dynamic Attribute Access: The
__getattribute__method inRobustRootLoggerensures that any attribute access is handled dynamically, allowing for robust error handling and logging. This method, combined with the metaclassMetaLogger, ensures that the logger instance is always available and properly configured.
Why not just use loguru?
Loguru is a more modern library, and it has a lot of features that Python's logging module does not have. Truthfully, I had not heard of Loguru, until AFTER I finished this library.
Loguru is a great library, and I recommend it if you want a more powerful and flexible logging solution. However, it is not a drop-in replacement for Python's logging module, and it requires a different way of logging to work.
Basically, if you want to use a different logging library, you have to change your code. LoggerPlus allows you to use the same API you're used to, but with more features.
Enhanced Traceback Functionality
A common problem I personally have is getting a traceback and not understanding context. What variables/attributes/arguments contents were? Unless I was in a debug environment, I'd have no idea. Users would send me tracebacks all the time, yet I'd have no idea what was happening at the point of the error.
LoggerPlus also provides enhanced traceback functionality. When an exception is raised, LoggerPlus captures and logs detailed information about the exception, including the stack trace and local variables at each frame. This makes it easier to debug issues by providing more context about the error.
For example, when a RuntimeError is raised, LoggerPlus logs the following information:
ERROR(root): Message for a caught exception
----------------------------------------------------------------
Traceback (most recent call last):
File "C:\GitHub\LoggerPlus\loggerplus\__init__.py", line 682, in <module>
raise RuntimeError("Test caught exception") # noqa: TRY301
RuntimeError: Test caught exception
RuntimeError: Test caught exception
Stack Trace Variables:
Function '<module>' at C:\GitHub\LoggerPlus\loggerplus\__init__.py:682:
__annotations__ = {}
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 10, 0, 'alpha', 0), 16777216)
logging = <module 'logging' from 'C:\\Program Files\\Python38\\lib\\logging\\__init__.py'>
multiprocessing = <module 'multiprocessing' from 'C:\\Program Files\\Python38\\lib\\multiprocessing\\__init__.py'>
os = <module 'os' from 'C:\\Program Files\\Python38\\lib\\os.py'>
shutil = <module 'shutil' from 'C:\\Program Files\\Python38\\lib\\shutil.py'>
sys = <module 'sys' (built-in)>
threading = <module 'threading' from 'C:\\Program Files\\Python38\\lib\\threading.py'>
time = <module 'time' (built-in)>
uuid = <module 'uuid' from 'C:\\Program Files\\Python38\\lib\\uuid.py'>
ThreadPoolExecutor = <class 'concurrent.futures.thread.ThreadPoolExecutor'>
contextmanager = <function contextmanager at 0x000001E0FF352AF0>
suppress = <class 'contextlib.suppress'>
RotatingFileHandler = <class 'logging.handlers.RotatingFileHandler'>
Path = <class 'pathlib.Path'>
TYPE_CHECKING = False
Any = typing.Any
ClassVar = typing.ClassVar
format_exception_with_variables = _lru_cache_wrapper(
)
UTF8StreamWrapper = <class '__main__.UTF8StreamWrapper'>
LOGGING_LOCK = <unlocked _thread.lock object at 0x000001E0FF915660>
THREAD_LOCAL = _local(
is_logging=True
)
logging_context = <function logging_context at 0x000001E0FFF34AF0>
get_this_child_pid = <function get_this_child_pid at 0x000001E0FFF34B80>
CustomPrintToLogger = <class '__main__.CustomPrintToLogger'>
SafeEncodingLogger = <class '__main__.SafeEncodingLogger'>
CustomExceptionFormatter = <class '__main__.CustomExceptionFormatter'>
ColoredConsoleHandler = <class '__main__.ColoredConsoleHandler'>
LogLevelFilter = <class '__main__.LogLevelFilter'>
_dir_requires_admin = <function _dir_requires_admin at 0x000001E0FFF34C10>
_delete_any_file_or_folder = <function _delete_any_file_or_folder at 0x000001E0FFF36700>
get_log_directory = <function get_log_directory at 0x000001E0FFF36790>
_get_fallback_log_dir = <function _get_fallback_log_dir at 0x000001E0FFF36820>
_safe_isfile = <function _safe_isfile at 0x000001E0FFF368B0>
_safe_isdir = <function _safe_isdir at 0x000001E0FFF36940>
_SpoofTypeAttributeAccess = <class '__main__._SpoofTypeAttributeAccess'>
_SpoofObjectAttributeAccess = <class '__main__._SpoofObjectAttributeAccess'>
MetaLogger = <class '__main__.MetaLogger'>
_safe_print = <function _safe_print at 0x000001E0FFF369D0>
RobustLogger = <class '__main__.RobustLogger'>
get_root_logger = <class '__main__.RobustLogger'>
logger = <RootLogger root (DEBUG)>
Traceback (most recent call last):
File "C:\GitHub\LoggerPlus\loggerplus\__init__.py", line 682, in <module>
raise RuntimeError("Test caught exception") # noqa: TRY301
RuntimeError: Test caught exception
----------------------------------------------------------------
This enhanced traceback functionality provides a comprehensive view of the error, making it easier to identify and fix issues in your code.
Overall, LoggerPlus provides a powerful and user-friendly logging solution that meets the goals
License
This project is licensed under the LGPLv2.1 License. See the LICENSE file for more details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for loggerplus-0.1.3-py3-none-any.whl
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 | 2a4fed4698061b66cf9e98f42b0b3d0dd42ad17a370d3fd2c83355a1222d198e |
|
| MD5 | 9352b6ac5127f266ca38a23a3f142cf1 |
|
| BLAKE2b-256 | 3e12e828c22bc710b10dc4ad954f67b4cc371c896cdb9e9fb58d0ee24f1e5144 |