inspy-logger 2.2__tar.gz → 3.0.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {inspy_logger-2.2 → inspy_logger-3.0.dev2}/LICENSE.md +20 -20
- inspy_logger-3.0.dev2/PKG-INFO +94 -0
- inspy_logger-3.0.dev2/README.md +54 -0
- inspy_logger-3.0.dev2/inspy_logger/Scripts/main.py +91 -0
- {inspy_logger-2.2 → inspy_logger-3.0.dev2}/inspy_logger/__about__.py +33 -33
- inspy_logger-3.0.dev2/inspy_logger/__init__.py +326 -0
- {inspy_logger-2.2 → inspy_logger-3.0.dev2}/inspy_logger/errors/__init__.py +121 -101
- inspy_logger-3.0.dev2/inspy_logger/helpers/__init__.py +189 -0
- inspy_logger-3.0.dev2/inspy_logger/helpers/cache.py +140 -0
- {inspy_logger-2.2 → inspy_logger-3.0.dev2}/inspy_logger/helpers/network.py +388 -388
- {inspy_logger-2.2 → inspy_logger-3.0.dev2}/inspy_logger/manifest.py +40 -40
- inspy_logger-3.0.dev2/inspy_logger/version.py +287 -0
- inspy_logger-3.0.dev2/pyproject.toml +56 -0
- inspy_logger-2.2/PKG-INFO +0 -24
- inspy_logger-2.2/inspy_logger/__init__.py +0 -725
- inspy_logger-2.2/inspy_logger/helpers/__init__.py +0 -5
- inspy_logger-2.2/inspy_logger/helpers/filters.py +0 -11
- inspy_logger-2.2/pyproject.toml +0 -31
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
### Do What the Fuck You Want To - Public License (v3.0 3, Sept. 2020)
|
|
2
|
-
----
|
|
3
|
-
----
|
|
4
|
-
|
|
5
|
-
Copyright (C) 2020 Taylor-Jayde Blackstone <t.blackstone@inspyre.tech>
|
|
6
|
-
|
|
7
|
-
----
|
|
8
|
-
|
|
9
|
-
Everyone is permitted to copy and distribute verbatim or modified
|
|
10
|
-
copies of this license document, and changing it is allowed as long
|
|
11
|
-
as the name is changed. That's my name, you can't have it.
|
|
12
|
-
|
|
13
|
-
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
14
|
-
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
15
|
-
|
|
16
|
-
- You just **DO WHAT THE FUCK YOU WANT TO**.
|
|
17
|
-
- You **don't** blame me for **shit**. _I don't care_ how much smoke your hard-drive spewed out.
|
|
18
|
-
- If someone else did what the fuck they wanted to and you're _**triggered**_ I'm **not** responsible.
|
|
19
|
-
|
|
20
|
-
Contact <dev.null@inspyre.tech> with any complaints.
|
|
1
|
+
### Do What the Fuck You Want To - Public License (v3.0 3, Sept. 2020)
|
|
2
|
+
----
|
|
3
|
+
----
|
|
4
|
+
|
|
5
|
+
Copyright (C) 2020 Taylor-Jayde Blackstone <t.blackstone@inspyre.tech>
|
|
6
|
+
|
|
7
|
+
----
|
|
8
|
+
|
|
9
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
|
10
|
+
copies of this license document, and changing it is allowed as long
|
|
11
|
+
as the name is changed. That's my name, you can't have it.
|
|
12
|
+
|
|
13
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
14
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
15
|
+
|
|
16
|
+
- You just **DO WHAT THE FUCK YOU WANT TO**.
|
|
17
|
+
- You **don't** blame me for **shit**. _I don't care_ how much smoke your hard-drive spewed out.
|
|
18
|
+
- If someone else did what the fuck they wanted to and you're _**triggered**_ I'm **not** responsible.
|
|
19
|
+
|
|
20
|
+
Contact <dev.null@inspyre.tech> with any complaints.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: inspy-logger
|
|
3
|
+
Version: 3.0.dev2
|
|
4
|
+
Summary: Colorable, scalable logger for CLI
|
|
5
|
+
Home-page: https://github.com/Inspyre-Softworks/inSPy-Logger
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: cli,color,logging,log,terminal,console,colorama,colorlog,rich
|
|
8
|
+
Author: Taylor-Jayde Blackstone
|
|
9
|
+
Author-email: tayjaybabee@gmail.com
|
|
10
|
+
Requires-Python: >=3.7,<4.0
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: System :: Logging
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Requires-Dist: appdirs (>=1.4.4,<2.0.0)
|
|
27
|
+
Requires-Dist: colorama (>=0.4.6,<0.5.0)
|
|
28
|
+
Requires-Dist: colorlog (>=6.7,<7.0)
|
|
29
|
+
Requires-Dist: humanize (>=4.4.0,<5.0.0)
|
|
30
|
+
Requires-Dist: luddite (>=1.0.1,<2.0.0)
|
|
31
|
+
Requires-Dist: packaging (>=21.0,<22.0)
|
|
32
|
+
Requires-Dist: python-box (>=6.1.0,<7.0.0)
|
|
33
|
+
Requires-Dist: requests (>=2.28.1,<3.0.0)
|
|
34
|
+
Requires-Dist: rich (>=13.5.2,<14.0.0)
|
|
35
|
+
Requires-Dist: setuptools-autover (>=1.0.2,<2.0.0)
|
|
36
|
+
Project-URL: Documentation, https://inspy-logger.readthedocs.io/en/latest/
|
|
37
|
+
Project-URL: Repository, https://github.com/Inspyre-Softworks/inSPy-Logger
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# inSPy-Logger
|
|
41
|
+

|
|
42
|
+

|
|
43
|
+

|
|
44
|
+

|
|
45
|
+

|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
### Prerequisites:
|
|
51
|
+
|
|
52
|
+
**Platform/Env**:
|
|
53
|
+
* **Python**: ^3.6.3
|
|
54
|
+
* **PIP**: (_If you want to install via PIP, that is_)
|
|
55
|
+
|
|
56
|
+
**inSPy-Logger Runtime Dependencies**:
|
|
57
|
+
I am providing this list of dependencies for transparency and for instances where one would not be able to install inSPy-Logger via PyPi's package manager. It is **highly** recommended you use `python3 -m pip install inspy_logger==<ver>` to install inSPy-Logger 2.0+
|
|
58
|
+
|
|
59
|
+
* [colorlog](https://pypi.org/project/colorlog) (^4.2.1)
|
|
60
|
+
* [setuptools-autover](https://pypi.org/project/setuptools-autover) = (^1.0.2)
|
|
61
|
+
* [luddite](https://pypi.org/project/luddite) = (^1.0.1)
|
|
62
|
+
* [packaging](https://pypi.org/project/packaging) = (^20.4)
|
|
63
|
+
|
|
64
|
+
### Installation via Pip on Python 3.6.3+ (recommended method):
|
|
65
|
+
|
|
66
|
+
- `$> python3 -m pip install inspy_logger==<version>`
|
|
67
|
+
|
|
68
|
+
#### Test out InspyLogger:
|
|
69
|
+
|
|
70
|
+
```python3
|
|
71
|
+
|
|
72
|
+
import inspy_logger
|
|
73
|
+
|
|
74
|
+
# Set up a log device object. The first parameter is the root loggers name, and the second is the debug level
|
|
75
|
+
log_device = inspy_logger.InspyLogger('LogName', 'debug')
|
|
76
|
+
|
|
77
|
+
# Start a running log from that device
|
|
78
|
+
log = log_device.start()
|
|
79
|
+
|
|
80
|
+
# Output our own logger lines:
|
|
81
|
+
log.debug('This is a debug log entry')
|
|
82
|
+
log.info('This is an info log entry')
|
|
83
|
+
log.warning('This is a warning log entry!')
|
|
84
|
+
log.error('This is an error log entry!')
|
|
85
|
+
log.exception('This is an exception log entry!')
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If you run the code above you'll get output similar to this:
|
|
90
|
+
|
|
91
|
+

|
|
92
|
+
|
|
93
|
+
v2.0+ repository for inSPy-Logger
|
|
94
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# inSPy-Logger
|
|
2
|
+

|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### Prerequisites:
|
|
12
|
+
|
|
13
|
+
**Platform/Env**:
|
|
14
|
+
* **Python**: ^3.6.3
|
|
15
|
+
* **PIP**: (_If you want to install via PIP, that is_)
|
|
16
|
+
|
|
17
|
+
**inSPy-Logger Runtime Dependencies**:
|
|
18
|
+
I am providing this list of dependencies for transparency and for instances where one would not be able to install inSPy-Logger via PyPi's package manager. It is **highly** recommended you use `python3 -m pip install inspy_logger==<ver>` to install inSPy-Logger 2.0+
|
|
19
|
+
|
|
20
|
+
* [colorlog](https://pypi.org/project/colorlog) (^4.2.1)
|
|
21
|
+
* [setuptools-autover](https://pypi.org/project/setuptools-autover) = (^1.0.2)
|
|
22
|
+
* [luddite](https://pypi.org/project/luddite) = (^1.0.1)
|
|
23
|
+
* [packaging](https://pypi.org/project/packaging) = (^20.4)
|
|
24
|
+
|
|
25
|
+
### Installation via Pip on Python 3.6.3+ (recommended method):
|
|
26
|
+
|
|
27
|
+
- `$> python3 -m pip install inspy_logger==<version>`
|
|
28
|
+
|
|
29
|
+
#### Test out InspyLogger:
|
|
30
|
+
|
|
31
|
+
```python3
|
|
32
|
+
|
|
33
|
+
import inspy_logger
|
|
34
|
+
|
|
35
|
+
# Set up a log device object. The first parameter is the root loggers name, and the second is the debug level
|
|
36
|
+
log_device = inspy_logger.InspyLogger('LogName', 'debug')
|
|
37
|
+
|
|
38
|
+
# Start a running log from that device
|
|
39
|
+
log = log_device.start()
|
|
40
|
+
|
|
41
|
+
# Output our own logger lines:
|
|
42
|
+
log.debug('This is a debug log entry')
|
|
43
|
+
log.info('This is an info log entry')
|
|
44
|
+
log.warning('This is a warning log entry!')
|
|
45
|
+
log.error('This is an error log entry!')
|
|
46
|
+
log.exception('This is an exception log entry!')
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If you run the code above you'll get output similar to this:
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
v2.0+ repository for inSPy-Logger
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
File:
|
|
4
|
+
inspy_logger/Scripts/main.py
|
|
5
|
+
|
|
6
|
+
Author:
|
|
7
|
+
Inspyre Softworks
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
INSPY_LOG_LEVEL = 'info'
|
|
11
|
+
|
|
12
|
+
from inspy_logger.version import parse_version, get_full_version_name, PyPiVersionInfo
|
|
13
|
+
import sys
|
|
14
|
+
from rich import print
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
from argparse import ArgumentParser
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
parser = ArgumentParser('inspy-logger-version', description='Displays version information for inspy-logger.')
|
|
20
|
+
|
|
21
|
+
parser.add_argument('-v', '--version', action='version', version=parse_version())
|
|
22
|
+
|
|
23
|
+
subparsers = parser.add_subparsers(title='subcommands', dest='subcommand')
|
|
24
|
+
|
|
25
|
+
update_parser =subparsers.add_parser('update', help='Checks for updates to inspy-logger.')
|
|
26
|
+
|
|
27
|
+
update_parser.add_argument('-p', '--pre-release', action='store_true', help='When checking for updates, include pre-release versions.')
|
|
28
|
+
|
|
29
|
+
parsed_args = parser.parse_args()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if parsed_args.subcommand == 'update':
|
|
33
|
+
INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = parsed_args.pre_release
|
|
34
|
+
else:
|
|
35
|
+
INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK = False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
pypi_info = PyPiVersionInfo(include_pre_release_for_update_check=INCLUDE_PRE_RELEASE_FOR_UPDATE_CHECK)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def update():
|
|
42
|
+
"""
|
|
43
|
+
Checks for updates to inspy-logger.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
None
|
|
47
|
+
|
|
48
|
+
Since:
|
|
49
|
+
v3.0
|
|
50
|
+
"""
|
|
51
|
+
if pypi_info.update_available:
|
|
52
|
+
print(f'\n\n[bold green]Update Available![/bold green] New version: [bold cyan]{pypi_info.new_version_available}[/bold cyan]')
|
|
53
|
+
else:
|
|
54
|
+
print(f'\n\n[bold green]No update available.[/bold green] Current version: [bold cyan]{parse_version()}[/bold cyan] {"which is newer than the available version on PyPi.org" if pypi_info.installed_newer_than_latest else ""}')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_version_info():
|
|
58
|
+
# Create a table
|
|
59
|
+
table = Table(show_header=False, show_lines=True, expand=True, border_style='bright_blue',
|
|
60
|
+
row_styles=['none', 'dim'])
|
|
61
|
+
|
|
62
|
+
# Add columns
|
|
63
|
+
table.add_column('Property', style='cyan', width=20)
|
|
64
|
+
table.add_column('Value', justify='center')
|
|
65
|
+
|
|
66
|
+
# Add rows
|
|
67
|
+
table.add_row('Version', parse_version(), )
|
|
68
|
+
table.add_row('Full Version Name', get_full_version_name())
|
|
69
|
+
|
|
70
|
+
if pypi_info.update_available:
|
|
71
|
+
table.add_row('Update Available', f'[bold green]Yes[/bold green]')
|
|
72
|
+
table.add_row('Latest Version', f'{pypi_info.new_version_available_num}')
|
|
73
|
+
|
|
74
|
+
table.add_row('Python Executable Path', sys.executable)
|
|
75
|
+
table.add_row('Python Version', sys.version)
|
|
76
|
+
|
|
77
|
+
print(table)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main():
|
|
82
|
+
|
|
83
|
+
if parsed_args.subcommand == 'update':
|
|
84
|
+
return update()
|
|
85
|
+
|
|
86
|
+
return get_version_info()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == '__main__':
|
|
91
|
+
main()
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
File: inspy_logger/__about__.py
|
|
4
|
-
Project: inSPy-Logger
|
|
5
|
-
Description:
|
|
6
|
-
Holds information about the inSPy-Logger program and its version.
|
|
7
|
-
|
|
8
|
-
Created: 11/3/22 - 21:05:06
|
|
9
|
-
|
|
10
|
-
Since:
|
|
11
|
-
v2.1.1
|
|
12
|
-
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
__prog__ = 'inSPy-Logger'
|
|
16
|
-
__version__ = '
|
|
17
|
-
__authors__ = [
|
|
18
|
-
('Inspyre-Softworks', 'https://inspyre.tech'),
|
|
19
|
-
('Taylor-Jayde Blackstone', '<t.blackstone@inspyre.tech')
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
'''
|
|
23
|
-
File Change History:
|
|
24
|
-
11/5/22 - 4:21 AM (v2.1.2):
|
|
25
|
-
- Added description to docstring. (target: 2.1.2)
|
|
26
|
-
|
|
27
|
-
11/5/22 - 4:41 AM:
|
|
28
|
-
- Version bump (target: 2.1.3)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Version bump (
|
|
32
|
-
|
|
33
|
-
'''
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
File: inspy_logger/__about__.py
|
|
4
|
+
Project: inSPy-Logger
|
|
5
|
+
Description:
|
|
6
|
+
Holds information about the inSPy-Logger program and its version.
|
|
7
|
+
|
|
8
|
+
Created: 11/3/22 - 21:05:06
|
|
9
|
+
|
|
10
|
+
Since:
|
|
11
|
+
v2.1.1
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__prog__ = 'inSPy-Logger'
|
|
16
|
+
__version__ = '3.0-dev.1'
|
|
17
|
+
__authors__ = [
|
|
18
|
+
('Inspyre-Softworks', 'https://inspyre.tech'),
|
|
19
|
+
('Taylor-Jayde Blackstone', '<t.blackstone@inspyre.tech')
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
'''
|
|
23
|
+
File Change History:
|
|
24
|
+
11/5/22 - 4:21 AM (v2.1.2):
|
|
25
|
+
- Added description to docstring. (target: 2.1.2)
|
|
26
|
+
|
|
27
|
+
11/5/22 - 4:41 AM:
|
|
28
|
+
- Version bump (target: 2.1.3)
|
|
29
|
+
|
|
30
|
+
08/16/2023 - 13:49 (v3.0):
|
|
31
|
+
- Version bump (v2.1.3 -> v3.0-dev.1)
|
|
32
|
+
|
|
33
|
+
'''
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import inspect
|
|
3
|
+
import logging
|
|
4
|
+
from rich.logging import RichHandler
|
|
5
|
+
from inspy_logger.__about__ import __prog__ as PROG_NAME
|
|
6
|
+
from inspy_logger.helpers import find_variable_in_call_stack
|
|
7
|
+
|
|
8
|
+
# Let's set up some constants.
|
|
9
|
+
LEVEL_MAP = [
|
|
10
|
+
('debug', logging.DEBUG),
|
|
11
|
+
('info', logging.INFO),
|
|
12
|
+
('warning', logging.WARNING),
|
|
13
|
+
('error', logging.ERROR),
|
|
14
|
+
('critical', logging.CRITICAL),
|
|
15
|
+
('fatal', logging.FATAL),
|
|
16
|
+
]
|
|
17
|
+
"""
|
|
18
|
+
List[Tuple[str, int]]:
|
|
19
|
+
A list of tuples containing the name of a logging level and it's corresponding logging level constant.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
LEVELS = [level[0] for level in LEVEL_MAP]
|
|
23
|
+
"""The list of level names."""
|
|
24
|
+
|
|
25
|
+
DEFAULT_LOGGING_LEVEL = logging.DEBUG
|
|
26
|
+
|
|
27
|
+
logging_level = getattr
|
|
28
|
+
|
|
29
|
+
from inspy_logger.helpers import (
|
|
30
|
+
translate_to_logging_level,
|
|
31
|
+
clean_module_name,
|
|
32
|
+
CustomFormatter,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Logger:
|
|
37
|
+
"""
|
|
38
|
+
A Singleton class responsible for managing the logging mechanisms of the application.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
instances = {}
|
|
42
|
+
|
|
43
|
+
def __new__(cls, name, *args, **kwargs):
|
|
44
|
+
"""
|
|
45
|
+
Creates or returns an existing instance of the Logger class for the provided name.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
if name not in cls.instances:
|
|
49
|
+
instance = super(Logger, cls).__new__(cls)
|
|
50
|
+
cls.instances[name] = instance
|
|
51
|
+
return instance
|
|
52
|
+
return cls.instances[name]
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
name,
|
|
57
|
+
console_level=DEFAULT_LOGGING_LEVEL,
|
|
58
|
+
file_level=logging.DEBUG,
|
|
59
|
+
filename="app.log",
|
|
60
|
+
parent=None,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Initializes a logger instance.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
if not hasattr(self, "logger"):
|
|
67
|
+
self.__name = name
|
|
68
|
+
self.logger = logging.getLogger(name)
|
|
69
|
+
self.logger.setLevel(logging.DEBUG)
|
|
70
|
+
determined_level = console_level
|
|
71
|
+
|
|
72
|
+
if isinstance(console_level, str):
|
|
73
|
+
determined_level = translate_to_logging_level(console_level)
|
|
74
|
+
|
|
75
|
+
self.__console_level = determined_level
|
|
76
|
+
self.filename = filename
|
|
77
|
+
self.__file_level = file_level or DEFAULT_LOGGING_LEVEL
|
|
78
|
+
self.parent = parent
|
|
79
|
+
|
|
80
|
+
self.logger.debug("Initializing Logger")
|
|
81
|
+
|
|
82
|
+
# Remove existing handlers
|
|
83
|
+
for handler in self.logger.handlers[:]:
|
|
84
|
+
self.logger.removeHandler(handler)
|
|
85
|
+
|
|
86
|
+
self.logger.propagate = False
|
|
87
|
+
|
|
88
|
+
self.set_up_console()
|
|
89
|
+
self.set_up_file()
|
|
90
|
+
self.children = []
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def device(self):
|
|
94
|
+
return self.logger
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def name(self):
|
|
98
|
+
return self.logger.name
|
|
99
|
+
|
|
100
|
+
def set_up_console(self):
|
|
101
|
+
"""
|
|
102
|
+
Configures and attaches a console handler to the logger.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
self.logger.debug("Setting up console handler")
|
|
106
|
+
console_handler = RichHandler(
|
|
107
|
+
show_level=True, markup=True, rich_tracebacks=True, tracebacks_show_locals=True
|
|
108
|
+
)
|
|
109
|
+
formatter = CustomFormatter(
|
|
110
|
+
f"[{self.logger.name}] %(message)s"
|
|
111
|
+
)
|
|
112
|
+
console_handler.setFormatter(formatter)
|
|
113
|
+
console_handler.setLevel(self.__console_level)
|
|
114
|
+
self.logger.addHandler(console_handler)
|
|
115
|
+
|
|
116
|
+
def set_up_file(self):
|
|
117
|
+
"""
|
|
118
|
+
Configures and attaches a file handler to the logger.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
self.logger.debug("Setting up file handler")
|
|
122
|
+
file_handler = logging.FileHandler(self.filename)
|
|
123
|
+
file_handler.setLevel(self.__file_level)
|
|
124
|
+
formatter = CustomFormatter(
|
|
125
|
+
"%(asctime)s - [%(name)s] - %(levelname)s - %(message)s |-| %(filename)s:%(lineno)d"
|
|
126
|
+
)
|
|
127
|
+
file_handler.setFormatter(formatter)
|
|
128
|
+
self.logger.addHandler(file_handler)
|
|
129
|
+
|
|
130
|
+
def set_level(self, console_level=None, file_level=None):
|
|
131
|
+
"""
|
|
132
|
+
Updates the logging levels for both console and file handlers.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
self.logger.debug("Setting log levels")
|
|
136
|
+
if console_level is not None:
|
|
137
|
+
self.logger.handlers[0].setLevel(console_level)
|
|
138
|
+
for child in self.children:
|
|
139
|
+
child.set_level(console_level=console_level)
|
|
140
|
+
|
|
141
|
+
if file_level is not None:
|
|
142
|
+
self.logger.handlers[1].setLevel(file_level)
|
|
143
|
+
for child in self.children:
|
|
144
|
+
child.set_level(file_level=file_level)
|
|
145
|
+
|
|
146
|
+
def get_child(self, name=None, console_level=None, file_level=None):
|
|
147
|
+
self.logger.debug("Getting child logger")
|
|
148
|
+
console_level = console_level or DEFAULT_LOGGING_LEVEL
|
|
149
|
+
caller_frame = inspect.stack()[1]
|
|
150
|
+
|
|
151
|
+
if name is None:
|
|
152
|
+
name = caller_frame.function
|
|
153
|
+
|
|
154
|
+
caller_self = caller_frame.frame.f_locals.get("self", None)
|
|
155
|
+
separator = ":" if caller_self and hasattr(caller_self, name) else "."
|
|
156
|
+
child_logger_name = f"{self.logger.name}{separator}{name}"
|
|
157
|
+
|
|
158
|
+
for child in self.children:
|
|
159
|
+
if child.logger.name == child_logger_name:
|
|
160
|
+
return child
|
|
161
|
+
|
|
162
|
+
child_logger = Logger(child_logger_name, console_level, file_level, parent=self)
|
|
163
|
+
self.children.append(child_logger)
|
|
164
|
+
return child_logger
|
|
165
|
+
|
|
166
|
+
def get_child_names(self):
|
|
167
|
+
"""
|
|
168
|
+
Fetches the names of all child loggers associated with this logger instance.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
self.logger.debug("Getting child logger names")
|
|
172
|
+
return [child.logger.name for child in self.children]
|
|
173
|
+
|
|
174
|
+
def get_parent(self):
|
|
175
|
+
"""
|
|
176
|
+
Fetches the parent logger associated with this logger instance.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
self.logger.debug("Getting parent logger")
|
|
180
|
+
return self.parent
|
|
181
|
+
|
|
182
|
+
def find_child_by_name(self, name: str, case_sensitive=True, exact_match=False):
|
|
183
|
+
self.logger.debug(f'Searching for child with name: {name}')
|
|
184
|
+
results = []
|
|
185
|
+
|
|
186
|
+
if not case_sensitive:
|
|
187
|
+
name = name.lower()
|
|
188
|
+
|
|
189
|
+
for logger in self.children:
|
|
190
|
+
logger_name = logger.name if case_sensitive else logger.name.lower()
|
|
191
|
+
if exact_match and name == logger_name:
|
|
192
|
+
return logger
|
|
193
|
+
elif not exact_match and name in logger_name:
|
|
194
|
+
results.append(logger)
|
|
195
|
+
|
|
196
|
+
return results
|
|
197
|
+
|
|
198
|
+
def __repr__(self):
|
|
199
|
+
name = self.name
|
|
200
|
+
hex_id = hex(id(self))
|
|
201
|
+
if self.parent is not None:
|
|
202
|
+
parent_part = f' | Parent Logger: {self.parent.name} |'
|
|
203
|
+
if self.children:
|
|
204
|
+
parent_part += f' | Number of children: {len(self.children)} |'
|
|
205
|
+
else:
|
|
206
|
+
parent_part = f' | This is a root logger with {len(self.children)} children. '
|
|
207
|
+
|
|
208
|
+
if parent_part.endswith('|'):
|
|
209
|
+
parent_part = str(parent_part[:-2])
|
|
210
|
+
|
|
211
|
+
return f'<Logger: {name} at {hex_id}{parent_part}>'
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
found_level = find_variable_in_call_stack('INSPY_LOG_LEVEL', DEFAULT_LOGGING_LEVEL)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
LOG_DEVICE = Logger(PROG_NAME, found_level)
|
|
218
|
+
MOD_LOG_DEVICE = LOG_DEVICE.get_child("log_engine", found_level)
|
|
219
|
+
MOD_LOGGER = MOD_LOG_DEVICE.logger
|
|
220
|
+
MOD_LOGGER.debug(f"Started logger for {__name__}.")
|
|
221
|
+
|
|
222
|
+
add_child = LOG_DEVICE.get_child
|
|
223
|
+
|
|
224
|
+
InspyLogger = Logger
|
|
225
|
+
|
|
226
|
+
def _get_parent_logging_device():
|
|
227
|
+
"""
|
|
228
|
+
Determines the parent logging device by inspecting the caller's log_device or parent_log_device attribute.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Logger: The parent logging device.
|
|
232
|
+
"""
|
|
233
|
+
MOD_LOGGER.debug("Determining parent logging device")
|
|
234
|
+
caller_frame = inspect.currentframe().f_back
|
|
235
|
+
caller_locals = caller_frame.f_locals
|
|
236
|
+
|
|
237
|
+
if "logger" in caller_locals:
|
|
238
|
+
return caller_locals["logger"]
|
|
239
|
+
elif "parent_log_device" in caller_locals:
|
|
240
|
+
return caller_locals["parent_log_device"]
|
|
241
|
+
else:
|
|
242
|
+
raise ValueError("Unable to determine the parent logging device.")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class Loggable:
|
|
246
|
+
"""
|
|
247
|
+
A metaclass to enhance classes with logging capabilities. Classes that inherit from
|
|
248
|
+
'Loggable' can instantly access a logger without manually setting it up. This logger
|
|
249
|
+
is derived from a parent logger, ensuring consistent logging behavior and hierarchy.
|
|
250
|
+
|
|
251
|
+
Attributes:
|
|
252
|
+
- log_device: The logger device associated with the instance of the class.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def __init__(self, parent_log_device=None, **kwargs):
|
|
256
|
+
self.parent_log_device = parent_log_device
|
|
257
|
+
self.__log_name = self.__class__.__name__
|
|
258
|
+
if self.parent_log_device is not None:
|
|
259
|
+
self.__log_device = self.parent_log_device.get_child(
|
|
260
|
+
self.__class__.__name__
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
self.__log_device = _get_parent_logging_device().get_child(
|
|
264
|
+
self.__class__.__name__
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def log_device(self):
|
|
269
|
+
return self.__log_device
|
|
270
|
+
|
|
271
|
+
@log_device.setter
|
|
272
|
+
def log_device(self, new):
|
|
273
|
+
if not isinstance(new, Logger):
|
|
274
|
+
raise TypeError('log_device must be of type "Logger"')
|
|
275
|
+
|
|
276
|
+
self.__log_device = new
|
|
277
|
+
|
|
278
|
+
def create_child_logger(self, name=None, override=False):
|
|
279
|
+
"""
|
|
280
|
+
Creates and returns a child logger of this object's logger.
|
|
281
|
+
|
|
282
|
+
Parameters:
|
|
283
|
+
name (str, optional): The name of the child logger.
|
|
284
|
+
If not provided, the name of the calling function is used.
|
|
285
|
+
override (bool, optional): A flag to override the membership check. Defaults to False.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Logger: An instance of the Logger class that represents the child logger.
|
|
289
|
+
"""
|
|
290
|
+
if not override:
|
|
291
|
+
self.__is_member__()
|
|
292
|
+
|
|
293
|
+
if name is None:
|
|
294
|
+
name = inspect.stack()[1][
|
|
295
|
+
3
|
|
296
|
+
] # Get the name of the calling function if no name is provided
|
|
297
|
+
|
|
298
|
+
return self.log_device.get_child(name)
|
|
299
|
+
|
|
300
|
+
def __is_member__(self):
|
|
301
|
+
"""
|
|
302
|
+
Checks whether the caller of this method is a member of the same class.
|
|
303
|
+
|
|
304
|
+
Raises:
|
|
305
|
+
PermissionError: If the caller of this method is not a member of the same class.
|
|
306
|
+
"""
|
|
307
|
+
log_device = self.log_device.get_child("__is_member__")
|
|
308
|
+
log = log_device.logger
|
|
309
|
+
|
|
310
|
+
current_frame = inspect.currentframe()
|
|
311
|
+
log.debug(f"Current frame: {current_frame}")
|
|
312
|
+
|
|
313
|
+
caller_frame = current_frame.f_back
|
|
314
|
+
log.debug(f"Caller frame: {caller_frame}")
|
|
315
|
+
|
|
316
|
+
caller_self = caller_frame.f_locals.get("self", None)
|
|
317
|
+
log.debug(f"Caller self: {caller_self}")
|
|
318
|
+
|
|
319
|
+
log.debug("Checking if caller is a member of this class...")
|
|
320
|
+
if not isinstance(caller_self, self.__class__):
|
|
321
|
+
raise PermissionError(
|
|
322
|
+
"Access denied.\n"
|
|
323
|
+
f"Method can only be accessed by members of the same class. {caller_self.__class__.__name__} is not such a member"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
log.debug(f"Access granted to {caller_self.__class__.__name__}")
|