logmachine 0.1.0__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.
- logmachine-0.1.0/LICENSE +0 -0
- logmachine-0.1.0/PKG-INFO +17 -0
- logmachine-0.1.0/README.md +0 -0
- logmachine-0.1.0/logmachine/__init__.py +4 -0
- logmachine-0.1.0/logmachine/core.py +156 -0
- logmachine-0.1.0/logmachine.egg-info/PKG-INFO +17 -0
- logmachine-0.1.0/logmachine.egg-info/SOURCES.txt +10 -0
- logmachine-0.1.0/logmachine.egg-info/dependency_links.txt +1 -0
- logmachine-0.1.0/logmachine.egg-info/top_level.txt +1 -0
- logmachine-0.1.0/pyproject.toml +26 -0
- logmachine-0.1.0/setup.cfg +4 -0
- logmachine-0.1.0/tests/test_core.py +3 -0
logmachine-0.1.0/LICENSE
ADDED
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logmachine
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Collaborative, beautiful logging system for distributed developers
|
|
5
|
+
Author-email: Mugabo Gusenga <mugabo@bufferpunk.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Scion-Kin/logmachine
|
|
7
|
+
Project-URL: Documentation, https://github.com/Scion-Kin/logmachine#readme
|
|
8
|
+
Project-URL: Source, https://github.com/Scion-Kin/logmachine
|
|
9
|
+
Project-URL: Tracker, https://github.com/Scion-Kin/logmachine/issues
|
|
10
|
+
Keywords: logging,devtools,collaborative,open source,cli,json,ansi
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
File without changes
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
class CustomFormatter(logging.Formatter):
|
|
7
|
+
COLORS = {
|
|
8
|
+
'DEBUG': '\x1b[36m',
|
|
9
|
+
'INFO': '\x1b[34m',
|
|
10
|
+
'WARNING': '\x1b[33m',
|
|
11
|
+
'ERROR': '\x1b[31m',
|
|
12
|
+
'SUCCESS': '\x1b[32m'
|
|
13
|
+
}
|
|
14
|
+
RESET = '\x1b[0m'
|
|
15
|
+
BOLD = '\x1b[1m'
|
|
16
|
+
LEVEL_FORMATS = {
|
|
17
|
+
'DEBUG': BOLD + '[DEBUG]' + RESET,
|
|
18
|
+
'INFO': BOLD + '[INFO]' + RESET,
|
|
19
|
+
'WARNING': BOLD + '[WARNING]' + RESET,
|
|
20
|
+
'ERROR': BOLD + '[ERROR]' + RESET,
|
|
21
|
+
'SUCCESS': BOLD + '[SUCCESS]' + RESET
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def format(self, record):
|
|
25
|
+
try:
|
|
26
|
+
username = os.getlogin()
|
|
27
|
+
except Exception:
|
|
28
|
+
username = os.environ.get('USER', 'unknown')
|
|
29
|
+
|
|
30
|
+
levelname = record.levelname
|
|
31
|
+
color = self.COLORS.get(levelname, '')
|
|
32
|
+
level_fmt = self.LEVEL_FORMATS.get(levelname, f'[{levelname}]')
|
|
33
|
+
record.levelname = f"{color}{level_fmt}{self.RESET}"
|
|
34
|
+
record.asctime = self.formatTime(record, self.datefmt)
|
|
35
|
+
module_file = record.pathname
|
|
36
|
+
parent_dir = os.path.basename(os.path.dirname(module_file)) or 'unknown'
|
|
37
|
+
|
|
38
|
+
return f"""{self.COLORS.get('DEBUG')}({username}{self.RESET} @ {self.COLORS.get('WARNING') + parent_dir + self.RESET}) 🤌 CL Timing: {color}[ {record.asctime} ]{self.RESET}
|
|
39
|
+
{record.levelname} {record.getMessage()}
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
class ContribLog(logging.Logger):
|
|
43
|
+
SUCCESS = 25
|
|
44
|
+
|
|
45
|
+
def __init__(self, *args, **kwargs):
|
|
46
|
+
super().__init__(*args, **kwargs)
|
|
47
|
+
logging.addLevelName(self.SUCCESS, "SUCCESS")
|
|
48
|
+
self.log_file = kwargs.get('log_file', 'logs.log')
|
|
49
|
+
self.error_file = kwargs.get('error_file', 'errors.log')
|
|
50
|
+
self.verbose = kwargs.get('verbose', False)
|
|
51
|
+
self.debug_level = kwargs.get('debug_level', 0)
|
|
52
|
+
self.name = kwargs.get('name', 'CL Logger')
|
|
53
|
+
|
|
54
|
+
def success(self, msg, *args, **kwargs):
|
|
55
|
+
if self.isEnabledFor(self.SUCCESS):
|
|
56
|
+
self._log(self.SUCCESS, msg, args, **kwargs)
|
|
57
|
+
|
|
58
|
+
def get_logger(self):
|
|
59
|
+
# logger = logging.getLogger(self.name)
|
|
60
|
+
self.setLevel(logging.DEBUG)
|
|
61
|
+
|
|
62
|
+
# Remove existing handlers
|
|
63
|
+
self.handlers = []
|
|
64
|
+
|
|
65
|
+
# File handlers
|
|
66
|
+
fh = logging.FileHandler(self.log_file)
|
|
67
|
+
fh.setLevel(logging.DEBUG)
|
|
68
|
+
eh = logging.FileHandler(self.error_file)
|
|
69
|
+
eh.setLevel(logging.ERROR)
|
|
70
|
+
|
|
71
|
+
# Console handler
|
|
72
|
+
ch = logging.StreamHandler()
|
|
73
|
+
ch.setLevel(logging.DEBUG if self.verbose else logging.CRITICAL + 1)
|
|
74
|
+
|
|
75
|
+
formatter = CustomFormatter('%(asctime)s %(levelname)s %(message)s', datefmt='%Y-%m-%dT%H:%M:%S%z')
|
|
76
|
+
fh.setFormatter(formatter)
|
|
77
|
+
eh.setFormatter(formatter)
|
|
78
|
+
ch.setFormatter(formatter)
|
|
79
|
+
|
|
80
|
+
self.addHandler(fh)
|
|
81
|
+
self.addHandler(eh)
|
|
82
|
+
self.addHandler(ch)
|
|
83
|
+
|
|
84
|
+
# Filter console output based on debug_level
|
|
85
|
+
class DebugLevelFilter(logging.Filter):
|
|
86
|
+
def filter(self, record):
|
|
87
|
+
level_map = {
|
|
88
|
+
0: ['ERROR', 'SUCCESS', 'WARNING', 'INFO', 'DEBUG'],
|
|
89
|
+
1: ['ERROR'],
|
|
90
|
+
2: ['SUCCESS'],
|
|
91
|
+
3: ['WARNING'],
|
|
92
|
+
4: ['INFO'],
|
|
93
|
+
5: ['ERROR', 'WARNING'],
|
|
94
|
+
6: ['INFO', 'SUCCESS'],
|
|
95
|
+
7: ['ERROR', 'WARNING', 'INFO']
|
|
96
|
+
}
|
|
97
|
+
allowed = level_map.get(int(self.debug_level), [])
|
|
98
|
+
return record.levelname in allowed
|
|
99
|
+
|
|
100
|
+
ch.addFilter(DebugLevelFilter())
|
|
101
|
+
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
def parse_log(self, log_text):
|
|
105
|
+
ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
|
|
106
|
+
clean = ansi_escape.sub('', log_text)
|
|
107
|
+
|
|
108
|
+
# Match `(username @ folder) 🤌 CL Timing: [timestamp]`
|
|
109
|
+
header_pattern = r"\((.*?) @ (.*?)\) 🤌 CL Timing: \[ (.*?) \]"
|
|
110
|
+
header_match = re.search(header_pattern, clean)
|
|
111
|
+
|
|
112
|
+
if not header_match:
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
user, module, timestamp = header_match.groups()
|
|
116
|
+
lines = clean.splitlines()
|
|
117
|
+
level_line = lines[1] if len(lines) > 1 else ''
|
|
118
|
+
message = ' '.join(lines[2:]).strip()
|
|
119
|
+
|
|
120
|
+
# Extract level from second line: e.g., "[INFO] Message"
|
|
121
|
+
level_match = re.match(r'\[(\w+)\]', level_line)
|
|
122
|
+
level = level_match.group(1) if level_match else "UNKNOWN"
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
"user": user,
|
|
126
|
+
"module": module,
|
|
127
|
+
"level": level,
|
|
128
|
+
"timestamp": timestamp,
|
|
129
|
+
"message": ansi_escape.sub('', message)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def jsonifier(self) -> list:
|
|
134
|
+
"""
|
|
135
|
+
Reads the log file and returns a list of JSON objects representing each log entry.
|
|
136
|
+
Reserved for central web collection, intentionally not used in CLI.
|
|
137
|
+
Returns:
|
|
138
|
+
list: A list of JSON objects, each representing a log entry.
|
|
139
|
+
"""
|
|
140
|
+
log_entries = []
|
|
141
|
+
with open(self.log_file, 'r') as file:
|
|
142
|
+
content = file.read()
|
|
143
|
+
log_lines = content.split('\n\n') # Split by double newlines to separate
|
|
144
|
+
for line in log_lines:
|
|
145
|
+
if line.strip():
|
|
146
|
+
log_entry = self.parse_log(line)
|
|
147
|
+
if log_entry:
|
|
148
|
+
log_entries.append(json.dumps(log_entry))
|
|
149
|
+
|
|
150
|
+
return log_entries
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
logging.setLoggerClass(ContribLog)
|
|
154
|
+
|
|
155
|
+
# Best initialization example to keep the custom methods available
|
|
156
|
+
logger = ContribLog({ "verbose": '--verbose' in sys.argv, "debug_level": os.getenv('DEBUG_LEVEL', 0) }).get_logger()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logmachine
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Collaborative, beautiful logging system for distributed developers
|
|
5
|
+
Author-email: Mugabo Gusenga <mugabo@bufferpunk.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Scion-Kin/logmachine
|
|
7
|
+
Project-URL: Documentation, https://github.com/Scion-Kin/logmachine#readme
|
|
8
|
+
Project-URL: Source, https://github.com/Scion-Kin/logmachine
|
|
9
|
+
Project-URL: Tracker, https://github.com/Scion-Kin/logmachine/issues
|
|
10
|
+
Keywords: logging,devtools,collaborative,open source,cli,json,ansi
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.7
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
logmachine
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "logmachine"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Collaborative, beautiful logging system for distributed developers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.7"
|
|
7
|
+
license = { file = "LICENSE" }
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Mugabo Gusenga", email="mugabo@bufferpunk.com" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["logging", "devtools", "collaborative", "open source", "cli", "json", "ansi"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/Scion-Kin/logmachine"
|
|
20
|
+
Documentation = "https://github.com/Scion-Kin/logmachine#readme"
|
|
21
|
+
Source = "https://github.com/Scion-Kin/logmachine"
|
|
22
|
+
Tracker = "https://github.com/Scion-Kin/logmachine/issues"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["setuptools>=61.0"]
|
|
26
|
+
build-backend = "setuptools.build_meta"
|