logging-module 0.2.0__py3-none-any.whl
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.
- logging_module/__init__.py +32 -0
- logging_module/config.py +10 -0
- logging_module/file_utils.py +108 -0
- logging_module/my_logging.py +207 -0
- logging_module-0.2.0.dist-info/METADATA +151 -0
- logging_module-0.2.0.dist-info/RECORD +9 -0
- logging_module-0.2.0.dist-info/WHEEL +5 -0
- logging_module-0.2.0.dist-info/licenses/LICENSE +21 -0
- logging_module-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging Module - A comprehensive logging and file utility package.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Logging configuration and management
|
|
6
|
+
- File utilities for logging operations
|
|
7
|
+
- Tracing and debug capabilities
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .my_logging import Logger
|
|
11
|
+
from .config import GLOBAL_LOGGING_ENABLED, GLOBAL_LOG_TRACING_ENABLED
|
|
12
|
+
from .file_utils import (
|
|
13
|
+
FileUtilsError,
|
|
14
|
+
FileExecutionError,
|
|
15
|
+
FileOperator,
|
|
16
|
+
file_verify_path,
|
|
17
|
+
file_verify_file,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__version__ = "0.2.0"
|
|
21
|
+
__author__ = "Nathan T Nguyen"
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Logger",
|
|
25
|
+
"GLOBAL_LOGGING_ENABLED",
|
|
26
|
+
"GLOBAL_LOG_TRACING_ENABLED",
|
|
27
|
+
"FileUtilsError",
|
|
28
|
+
"FileExecutionError",
|
|
29
|
+
"FileOperator",
|
|
30
|
+
"file_verify_path",
|
|
31
|
+
"file_verify_file",
|
|
32
|
+
]
|
logging_module/config.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#######################################################################
|
|
2
|
+
#
|
|
3
|
+
# Logging Module Configuration
|
|
4
|
+
#
|
|
5
|
+
# Configuration for logging and tracing
|
|
6
|
+
#
|
|
7
|
+
#######################################################################
|
|
8
|
+
|
|
9
|
+
GLOBAL_LOGGING_ENABLED = True
|
|
10
|
+
GLOBAL_LOG_TRACING_ENABLED = True
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#######################################################################
|
|
2
|
+
#
|
|
3
|
+
# File Utils
|
|
4
|
+
#
|
|
5
|
+
# Utility functions for file and directory operations
|
|
6
|
+
# Handles path verification and error management
|
|
7
|
+
#
|
|
8
|
+
#######################################################################
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from enum import Enum
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FileUtilsError(str, Enum):
|
|
15
|
+
# Directory related errors
|
|
16
|
+
OUTSIDE_WORKING_DIR = "Error: Cannot list {directory} as it is outside the permitted working directory"
|
|
17
|
+
NOT_A_DIRECTORY = "Error: {directory} is not a directory"
|
|
18
|
+
# Read file errors
|
|
19
|
+
FILE_READ_OUTSIDE_WORKING_DIR = "Error: Cannot read {file_path} as it is outside the permitted working directory"
|
|
20
|
+
FILE_READ_NOT_FOUND_OR_NOT_REGULAR = 'Error: File not found or is not a regular file: "{file_path}"'
|
|
21
|
+
# Write file errors
|
|
22
|
+
FILE_WRITE_OUTSIDE_WORKING_DIR = 'Error: Cannot write to "{file_path}" as it is outside the permitted working directory'
|
|
23
|
+
FILE_WRITE_NOT_FOUND_OR_NOT_REGULAR = 'Error: File not found or is not a regular file: "{file_path}"\n Create the file before writing to it.'
|
|
24
|
+
# Executable file errors
|
|
25
|
+
FILE_EXECUTE_OUTSIDE_WORKING_DIR = 'Error: Cannot execute "{file_path}" as it is outside the permitted working directory'
|
|
26
|
+
FILE_EXECUTE_NOT_FOUND_OR_NOT_REGULAR = 'Error: File "{file_path}" not found.'
|
|
27
|
+
FILE_EXECUTE_NOT_PYTHON = 'Error: File "{file_path}" is not a Python file.'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class FileExecutionError(str, Enum):
|
|
31
|
+
EXECUTION_FAILED = "Error: executing Python file: {error_message}"
|
|
32
|
+
EXECUTION_TIMEOUT = 'Error: Execution of file "{file_path}" timed out after {timeout} seconds.'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FileOperator(str, Enum):
|
|
36
|
+
READ_FILE = "read_file"
|
|
37
|
+
WRITE_FILE = "write_file"
|
|
38
|
+
EXECUTE_FILE = "execute_file"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def file_verify_path(working_directory: str, directory: str) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Verify that *directory* (relative to *working_directory*) exists and is
|
|
44
|
+
inside the working directory.
|
|
45
|
+
|
|
46
|
+
Returns the resolved path on success, or a ``FileUtilsError`` string on
|
|
47
|
+
failure.
|
|
48
|
+
"""
|
|
49
|
+
path_to_directory = os.path.join(working_directory, directory)
|
|
50
|
+
if (
|
|
51
|
+
not path_to_directory.startswith(working_directory)
|
|
52
|
+
or ".." in os.path.relpath(path_to_directory, working_directory)
|
|
53
|
+
):
|
|
54
|
+
return FileUtilsError.OUTSIDE_WORKING_DIR.value.format(directory=directory)
|
|
55
|
+
|
|
56
|
+
if not os.path.isdir(path_to_directory):
|
|
57
|
+
return FileUtilsError.NOT_A_DIRECTORY.value.format(directory=directory)
|
|
58
|
+
|
|
59
|
+
return path_to_directory
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def file_verify_file(working_directory: str, file: str, options: FileOperator | None = None) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Verify that *file* (relative to *working_directory*) is valid for the
|
|
65
|
+
requested *options* operation.
|
|
66
|
+
|
|
67
|
+
Returns the resolved path on success, or a ``FileUtilsError`` string on
|
|
68
|
+
failure.
|
|
69
|
+
|
|
70
|
+
Raises
|
|
71
|
+
------
|
|
72
|
+
ValueError
|
|
73
|
+
If *options* is not a recognised ``FileOperator`` value.
|
|
74
|
+
"""
|
|
75
|
+
path_to_file = os.path.join(working_directory, file)
|
|
76
|
+
|
|
77
|
+
def _is_outside() -> bool:
|
|
78
|
+
return (
|
|
79
|
+
not path_to_file.startswith(working_directory)
|
|
80
|
+
or ".." in os.path.relpath(path_to_file, working_directory)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
match options:
|
|
84
|
+
case FileOperator.READ_FILE:
|
|
85
|
+
if _is_outside():
|
|
86
|
+
return FileUtilsError.FILE_READ_OUTSIDE_WORKING_DIR.value.format(file_path=path_to_file)
|
|
87
|
+
if not os.path.isfile(path_to_file):
|
|
88
|
+
return FileUtilsError.FILE_READ_NOT_FOUND_OR_NOT_REGULAR.value.format(file_path=path_to_file)
|
|
89
|
+
|
|
90
|
+
case FileOperator.WRITE_FILE:
|
|
91
|
+
if _is_outside():
|
|
92
|
+
return FileUtilsError.FILE_WRITE_OUTSIDE_WORKING_DIR.value.format(file_path=path_to_file)
|
|
93
|
+
# Write operations may target a not-yet-existing file; the caller is
|
|
94
|
+
# responsible for creating any missing parent directories. We only
|
|
95
|
+
# validate the path is inside the working directory and return it.
|
|
96
|
+
|
|
97
|
+
case FileOperator.EXECUTE_FILE:
|
|
98
|
+
if _is_outside():
|
|
99
|
+
return FileUtilsError.FILE_EXECUTE_OUTSIDE_WORKING_DIR.value.format(file_path=file)
|
|
100
|
+
if not os.path.isfile(path_to_file):
|
|
101
|
+
return FileUtilsError.FILE_EXECUTE_NOT_FOUND_OR_NOT_REGULAR.value.format(file_path=file)
|
|
102
|
+
if not path_to_file.endswith(".py"):
|
|
103
|
+
return FileUtilsError.FILE_EXECUTE_NOT_PYTHON.value.format(file_path=file)
|
|
104
|
+
|
|
105
|
+
case _:
|
|
106
|
+
raise ValueError(f"Invalid file operation option provided: {options!r}")
|
|
107
|
+
|
|
108
|
+
return path_to_file # reached by all arms that don't return early
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#######################################################################
|
|
2
|
+
#
|
|
3
|
+
# My Logging Module
|
|
4
|
+
#
|
|
5
|
+
# Provides logging functionality with timestamping and caller info
|
|
6
|
+
# Supports configurable logging and tracing options
|
|
7
|
+
#
|
|
8
|
+
#######################################################################
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import threading
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from logging_module.file_utils import FileUtilsError, file_verify_path
|
|
14
|
+
from inspect import getframeinfo, stack
|
|
15
|
+
from logging_module.config import GLOBAL_LOGGING_ENABLED, GLOBAL_LOG_TRACING_ENABLED
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Frame-filtering constants
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
# Prefixes of filenames/paths to always skip (logger internals, frozen frames)
|
|
23
|
+
_IGNORED_PREFIXES = (
|
|
24
|
+
"<frozen",
|
|
25
|
+
"<string>",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Basenames of framework/infra files to skip (blocklist fallback mode)
|
|
29
|
+
_IGNORED_FILES = {
|
|
30
|
+
"spawn.py",
|
|
31
|
+
"process.py",
|
|
32
|
+
"_subprocess.py",
|
|
33
|
+
"server.py",
|
|
34
|
+
"runners.py",
|
|
35
|
+
"config.py",
|
|
36
|
+
"importer.py",
|
|
37
|
+
"__init__.py",
|
|
38
|
+
"cli.py",
|
|
39
|
+
"discover.py",
|
|
40
|
+
"my_logging.py", # skip this file itself
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Logger
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
class Logger:
|
|
48
|
+
"""
|
|
49
|
+
Lightweight file logger with optional call-stack tracing.
|
|
50
|
+
|
|
51
|
+
Usage
|
|
52
|
+
-----
|
|
53
|
+
log = Logger()
|
|
54
|
+
log("Something happened")
|
|
55
|
+
log.log("Something happened", print_to_console=True, console_message="Hey!")
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
log_dir : str
|
|
60
|
+
Directory (relative or absolute) where log files are written.
|
|
61
|
+
Defaults to ``./logs``.
|
|
62
|
+
project_dir : str | None
|
|
63
|
+
Root of your project source tree. When supplied, only frames whose
|
|
64
|
+
absolute path starts with this directory (and are not inside
|
|
65
|
+
``site-packages``) are considered "your code". When ``None`` the
|
|
66
|
+
module falls back to the blocklist defined by ``_IGNORED_FILES`` /
|
|
67
|
+
``_IGNORED_PREFIXES``.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
DEFAULT_LOG_DIR = "logs"
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
log_dir: str = DEFAULT_LOG_DIR,
|
|
75
|
+
project_dir: str | None = None,
|
|
76
|
+
):
|
|
77
|
+
self._enable: bool = True
|
|
78
|
+
self._enable_log_tracing: bool = False
|
|
79
|
+
self._log_dir: str = log_dir
|
|
80
|
+
self._project_dir: str | None = (
|
|
81
|
+
os.path.abspath(project_dir) if project_dir is not None else None
|
|
82
|
+
)
|
|
83
|
+
self._lock = threading.Lock()
|
|
84
|
+
|
|
85
|
+
# ------------------------------------------------------------------
|
|
86
|
+
# Public interface
|
|
87
|
+
# ------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
def __call__(
|
|
90
|
+
self,
|
|
91
|
+
message: str,
|
|
92
|
+
print_to_console: bool = False,
|
|
93
|
+
console_message: str = "",
|
|
94
|
+
) -> None:
|
|
95
|
+
self.log(message, print_to_console=print_to_console, console_message=console_message)
|
|
96
|
+
|
|
97
|
+
def log(
|
|
98
|
+
self,
|
|
99
|
+
message: str,
|
|
100
|
+
print_to_console: bool = False,
|
|
101
|
+
console_message: str = "",
|
|
102
|
+
) -> None:
|
|
103
|
+
if not GLOBAL_LOGGING_ENABLED or not self._enable:
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
now = datetime.now()
|
|
107
|
+
date = now.strftime("%Y-%m-%d")
|
|
108
|
+
time_str = now.strftime("%H:%M:%S")
|
|
109
|
+
timestamp = f"{date} {time_str}"
|
|
110
|
+
|
|
111
|
+
logistic_data = f"[{timestamp}][{self.get_caller_info()}]"
|
|
112
|
+
padded_space = "".rjust(len(logistic_data) + 1)
|
|
113
|
+
formatted_message = str(message).replace("\n", f"\n{padded_space}")
|
|
114
|
+
log_entry = f"{logistic_data} {formatted_message}\n"
|
|
115
|
+
|
|
116
|
+
self._write_to_file(log_entry, date, time_str)
|
|
117
|
+
|
|
118
|
+
if print_to_console:
|
|
119
|
+
print(message if console_message == "" else console_message)
|
|
120
|
+
|
|
121
|
+
def enable_logging(self, enable_logging: bool) -> None:
|
|
122
|
+
self._enable = enable_logging
|
|
123
|
+
|
|
124
|
+
def enable_log_tracing(self, enable_log_tracing: bool) -> None:
|
|
125
|
+
self._enable_log_tracing = enable_log_tracing
|
|
126
|
+
|
|
127
|
+
# ------------------------------------------------------------------
|
|
128
|
+
# Caller info
|
|
129
|
+
# ------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
def get_caller_info(self) -> str:
|
|
132
|
+
tracing_active = GLOBAL_LOG_TRACING_ENABLED and self._enable_log_tracing
|
|
133
|
+
frames = self._collect_project_frames()
|
|
134
|
+
|
|
135
|
+
if not frames:
|
|
136
|
+
return "unknown:0"
|
|
137
|
+
|
|
138
|
+
if not tracing_active:
|
|
139
|
+
return frames[0]
|
|
140
|
+
|
|
141
|
+
return ">".join(frames)
|
|
142
|
+
|
|
143
|
+
# ------------------------------------------------------------------
|
|
144
|
+
# Private helpers
|
|
145
|
+
# ------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
def _is_project_frame(self, filename: str) -> bool:
|
|
148
|
+
"""
|
|
149
|
+
Return True when *filename* belongs to project code.
|
|
150
|
+
|
|
151
|
+
Allowlist mode (project_dir set):
|
|
152
|
+
Frame must live inside the project directory and not inside
|
|
153
|
+
a ``site-packages`` folder.
|
|
154
|
+
|
|
155
|
+
Blocklist mode (project_dir is None):
|
|
156
|
+
Frame must not match any entry in ``_IGNORED_FILES`` or
|
|
157
|
+
``_IGNORED_PREFIXES``.
|
|
158
|
+
"""
|
|
159
|
+
# Always skip frozen / string pseudo-files regardless of mode
|
|
160
|
+
if any(filename.startswith(prefix) for prefix in _IGNORED_PREFIXES):
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
if self._project_dir is not None:
|
|
164
|
+
abs_filename = os.path.abspath(filename)
|
|
165
|
+
return (
|
|
166
|
+
abs_filename.startswith(self._project_dir)
|
|
167
|
+
and "site-packages" not in abs_filename
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Blocklist fallback
|
|
171
|
+
return os.path.basename(filename) not in _IGNORED_FILES
|
|
172
|
+
|
|
173
|
+
def _collect_project_frames(self) -> list[str]:
|
|
174
|
+
"""Walk the call stack and return frame strings for project code only."""
|
|
175
|
+
frames: list[str] = []
|
|
176
|
+
for frame_record in stack():
|
|
177
|
+
try:
|
|
178
|
+
info = getframeinfo(frame_record[0])
|
|
179
|
+
if self._is_project_frame(info.filename):
|
|
180
|
+
frames.append(f"{os.path.basename(info.filename)}:{info.lineno}")
|
|
181
|
+
except (IndexError, TypeError):
|
|
182
|
+
# IndexError: stack shorter than expected
|
|
183
|
+
# TypeError: frame_record is not subscriptable (e.g. test mocks using None)
|
|
184
|
+
continue
|
|
185
|
+
return frames
|
|
186
|
+
|
|
187
|
+
def _write_to_file(self, log_entry: str, date: str, time_str: str) -> None:
|
|
188
|
+
"""Create the log directory/file if needed, then append *log_entry*."""
|
|
189
|
+
log_dir_path = os.path.join("./", self._log_dir)
|
|
190
|
+
|
|
191
|
+
# Ensure the log directory exists
|
|
192
|
+
file_path = file_verify_path("./", self._log_dir)
|
|
193
|
+
if file_path == FileUtilsError.NOT_A_DIRECTORY.value.format(directory=self._log_dir):
|
|
194
|
+
os.makedirs(log_dir_path, exist_ok=True)
|
|
195
|
+
file_path = file_verify_path("./", self._log_dir) # re-verify after creation
|
|
196
|
+
|
|
197
|
+
# Bail out if the path is still an error (e.g. outside working dir)
|
|
198
|
+
if not isinstance(file_path, str) or file_path.startswith("Error"):
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
file_name = os.path.join(file_path, f"{date}_logging.txt")
|
|
202
|
+
with self._lock:
|
|
203
|
+
with open(file_name, "a") as log_file:
|
|
204
|
+
# Write a header only on the very first entry (empty file)
|
|
205
|
+
if log_file.tell() == 0:
|
|
206
|
+
log_file.write(f"Log file created on {date} at {time_str}\n")
|
|
207
|
+
log_file.write(log_entry)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logging-module
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A comprehensive logging and file utility module for Python applications
|
|
5
|
+
Author-email: Nathan T Nguyen <nhatnguyen2807@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: CreatorGithub, https://github.com/NhatNguyen5
|
|
8
|
+
Project-URL: Homepage, https://test.pypi.org/project/logging-module/
|
|
9
|
+
Project-URL: Documentation, https://github.com/NhatNguyen5/logging_module/wiki/Instructions
|
|
10
|
+
Project-URL: Repository, https://github.com/NhatNguyen5/logging-module.git
|
|
11
|
+
Project-URL: Issues, https://github.com/NhatNguyen5/logging_module/issues
|
|
12
|
+
Keywords: logging,file-utilities,tracing,debugging
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest; extra == "dev"
|
|
25
|
+
Requires-Dist: bump2version; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Logging Module
|
|
29
|
+
|
|
30
|
+
A comprehensive Python logging and file utility module designed for easy integration into FastAPI projects and other Python applications.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Flexible Logging**: Timestamp-based logging with caller information
|
|
35
|
+
- **File Utilities**: Safe file operations with error handling
|
|
36
|
+
- **Configuration**: Easy-to-configure logging and tracing options
|
|
37
|
+
- **Error Management**: Comprehensive error tracking and reporting
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
### From PyPI (when published)
|
|
42
|
+
```bash
|
|
43
|
+
pip install logging-module
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### From Local Directory
|
|
47
|
+
```bash
|
|
48
|
+
pip install ./logging_module
|
|
49
|
+
# or in editable mode for development
|
|
50
|
+
pip install -e ./logging_module
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from logging_module import Logger
|
|
57
|
+
|
|
58
|
+
# Create a logger instance
|
|
59
|
+
app_logger = Logger()
|
|
60
|
+
|
|
61
|
+
# Basic logging
|
|
62
|
+
app_logger.log("Application started")
|
|
63
|
+
|
|
64
|
+
# Log with console output
|
|
65
|
+
app_logger.log("Important message", print_to_console=True)
|
|
66
|
+
|
|
67
|
+
# Log with custom console message
|
|
68
|
+
app_logger.log("Debug info", print_to_console=True, console_message="DEBUG: Debug info")
|
|
69
|
+
|
|
70
|
+
# Use as a callable (shortcut for log())
|
|
71
|
+
app_logger("Quick log message")
|
|
72
|
+
|
|
73
|
+
# Control logging behavior
|
|
74
|
+
app_logger.enable_logging(False) # Disable this logger instance
|
|
75
|
+
app_logger.enable_log_tracing(True) # Enable detailed tracing for this instance
|
|
76
|
+
|
|
77
|
+
# Configure custom log directory
|
|
78
|
+
custom_logger = Logger(log_dir="my_logs")
|
|
79
|
+
|
|
80
|
+
# Configure project directory for better frame filtering
|
|
81
|
+
project_logger = Logger(project_dir="/path/to/project")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
### Logging Config (`config.py`)
|
|
87
|
+
- `GLOBAL_LOGGING_ENABLED`: Enable/disable logging (default: `True`)
|
|
88
|
+
- `GLOBAL_LOG_TRACING_ENABLED`: Enable/disable detailed tracing (default: `True`)
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from logging_module.config import GLOBAL_LOGGING_ENABLED, GLOBAL_LOG_TRACING_ENABLED
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Modules
|
|
95
|
+
|
|
96
|
+
### Logger (`my_logging.py`)
|
|
97
|
+
Main logging class providing timestamped, structured logging with caller information.
|
|
98
|
+
|
|
99
|
+
**Key Features:**
|
|
100
|
+
- Automatic timestamping and file logging
|
|
101
|
+
- Caller information tracking with configurable frame filtering
|
|
102
|
+
- Configurable log directory and project directory
|
|
103
|
+
- Thread-safe file operations
|
|
104
|
+
- Configurable tracing depth
|
|
105
|
+
- Console output options
|
|
106
|
+
- Instance-level enable/disable controls
|
|
107
|
+
|
|
108
|
+
### File Utilities (`file_utils.py`)
|
|
109
|
+
Safe file operations with comprehensive error handling:
|
|
110
|
+
- Path verification with security checks
|
|
111
|
+
- File verification for different operations (read/write/execute)
|
|
112
|
+
- Directory operations with working directory constraints
|
|
113
|
+
|
|
114
|
+
### Configuration (`config.py`)
|
|
115
|
+
Global configuration settings for logging behavior:
|
|
116
|
+
- `GLOBAL_LOGGING_ENABLED`: Master switch for all logging
|
|
117
|
+
- `GLOBAL_LOG_TRACING_ENABLED`: Master switch for tracing features
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
### Prerequisites
|
|
122
|
+
- Python 3.10+
|
|
123
|
+
|
|
124
|
+
### Installing in Development Mode
|
|
125
|
+
```bash
|
|
126
|
+
cd logging_module
|
|
127
|
+
pip install -e .
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Running Tests
|
|
131
|
+
```bash
|
|
132
|
+
# Install test dependencies
|
|
133
|
+
pip install -e ".[dev]"
|
|
134
|
+
|
|
135
|
+
# Run all tests
|
|
136
|
+
pytest
|
|
137
|
+
|
|
138
|
+
# Run with verbose output
|
|
139
|
+
pytest -v
|
|
140
|
+
|
|
141
|
+
# Run specific test file
|
|
142
|
+
pytest tests/test_my_logging.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
148
|
+
|
|
149
|
+
## Author
|
|
150
|
+
|
|
151
|
+
Nathan T Nguyen
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
logging_module/__init__.py,sha256=0RiE90B-839VDopobP3SsHyAPf3WCKAnJji3YVIvbSM,709
|
|
2
|
+
logging_module/config.py,sha256=K8Y3CcKjD4w24A_Tz8depSmcYjMY_QzxWkt4av9D8so,312
|
|
3
|
+
logging_module/file_utils.py,sha256=BVLG5e-Nl67Ml8zs8lJOfn-hoH0CVKK9fd1hgVvPFsE,4546
|
|
4
|
+
logging_module/my_logging.py,sha256=6Bgxn6XlQpM7c2gizzxcH1HmNqUDy9e6qqvAlWEOv1E,7346
|
|
5
|
+
logging_module-0.2.0.dist-info/licenses/LICENSE,sha256=83ihdQApyBWzHY6UZnYrOcepnw6WC_E1jwoEpz9ygVs,1072
|
|
6
|
+
logging_module-0.2.0.dist-info/METADATA,sha256=dJhXYn2wa76ON5E6ne2wjBycf9llj1W_bp791dhnNXA,4257
|
|
7
|
+
logging_module-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
logging_module-0.2.0.dist-info/top_level.txt,sha256=CgIjeGmYeHkbeuqIPkKaakc32MnVsoaJncFXLTwn5sQ,15
|
|
9
|
+
logging_module-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nathan T Nguyen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
logging_module
|