logmachine 2.1.0__tar.gz → 2.2.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.
File without changes
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logmachine
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Collaborative, beautiful logging system for distributed developers
5
5
  Author-email: Mugabo Gusenga <mugabo@bufferpunk.com>
6
6
  Project-URL: Homepage, https://logmachine.bufferpunk.com
7
- Project-URL: Documentation, https://github.com/Scion-Kin/logmachine
8
- Project-URL: Source, https://github.com/Scion-Kin/logmachine
9
- Project-URL: Tracker, https://github.com/Scion-Kin/logmachine/issues
7
+ Project-URL: Documentation, https://github.com/logmachine/python
8
+ Project-URL: Source, https://github.com/logmachine/python
9
+ Project-URL: Tracker, https://github.com/logmachine/python/issues
10
10
  Keywords: logging,devtools,collaborative,open source,cli,json,ansi
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: License :: OSI Approved :: MIT License
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Dynamic: license-file
18
18
 
19
- # 🧠 logmachine 2.1.0
19
+ # 🧠 LogMachine
20
20
 
21
21
  > Collaborative, beautiful logging system for distributed developers
22
22
 
@@ -50,11 +50,11 @@ pip install logmachine
50
50
  ### Basic Setup
51
51
 
52
52
  ```python
53
- from logmachine import ContribLog
53
+ from logmachine import LogMachine
54
54
 
55
55
  # Create a simple logger without central logging
56
- # Providing a noon-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
57
- logger = ContribLog("myapp", debug_level=1)
56
+ # Providing a non-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
57
+ logger = LogMachine("myapp", debug_level=1)
58
58
 
59
59
  logger.info("Hello, world!")
60
60
  logger.error("An error occurred!")
@@ -65,6 +65,17 @@ logger.warning("This is a warning message.")
65
65
 
66
66
  ### With Central Logging (HTTP or Socket.IO)
67
67
 
68
+ You can use the default logger with central logging pointing to "https://logmachine.bufferpunk.com"
69
+
70
+ ```python
71
+ from logmachine import default_logger
72
+ logger = default_logger()
73
+ logger.info("This log is sent to the LogMachine default central server!")
74
+ ```
75
+
76
+ This is the default central logging server for logmachine, and you can create your own room there for free.
77
+ To use your own central logging server, provide the configuration as shown below:
78
+
68
79
  ```python
69
80
  logger_config = {
70
81
  "url": "https://logmachine.bufferpunk.com", # Base server URL
@@ -74,7 +85,7 @@ logger_config = {
74
85
  "socketio": True, # Set False to use HTTP
75
86
  "socketio_path": "/api/socket.io/" # Optional
76
87
  }
77
- logger = ContribLog("with_central", debug_level=0, central=logger_config, socketio=True)
88
+ logger = LogMachine("with_central", debug_level=0, central=logger_config, socketio=True)
78
89
  logger.success("Central logging is working!")
79
90
  ```
80
91
 
@@ -105,8 +116,9 @@ Sample (terminal):
105
116
  ### Add Your Own Log Level
106
117
 
107
118
  ```python
108
- log.new_level("CRITICAL_HACK", 60)
109
- log.critical_hack("Zero day found!")
119
+ logger.new_level("CRITICAL_HACK", 60)
120
+ logger.new_level("CRITICAL_HACK", 60, color="\033[38;5;13m") # Optional color... does your girlfriend love pink? Maybe you should be in a relationship with your terminal.
121
+ logger.critical_hack("Zero day found!")
110
122
  ```
111
123
 
112
124
  ---
@@ -115,6 +127,9 @@ log.critical_hack("Zero day found!")
115
127
 
116
128
  ### Convert Logs to JSON
117
129
 
130
+ This is useful for sending logs to web dashboards or log collectors that expect JSON.
131
+ It reads the your log files, parses the log entries, and outputs them as JSON objects.
132
+
118
133
  ```python
119
134
  json_logs = log.jsonifier()
120
135
  for entry in json_logs:
@@ -1,22 +1,4 @@
1
- Metadata-Version: 2.4
2
- Name: logmachine
3
- Version: 2.1.0
4
- Summary: Collaborative, beautiful logging system for distributed developers
5
- Author-email: Mugabo Gusenga <mugabo@bufferpunk.com>
6
- Project-URL: Homepage, https://logmachine.bufferpunk.com
7
- Project-URL: Documentation, https://github.com/Scion-Kin/logmachine
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
18
-
19
- # 🧠 logmachine 2.1.0
1
+ # 🧠 LogMachine
20
2
 
21
3
  > Collaborative, beautiful logging system for distributed developers
22
4
 
@@ -50,11 +32,11 @@ pip install logmachine
50
32
  ### Basic Setup
51
33
 
52
34
  ```python
53
- from logmachine import ContribLog
35
+ from logmachine import LogMachine
54
36
 
55
37
  # Create a simple logger without central logging
56
- # Providing a noon-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
57
- logger = ContribLog("myapp", debug_level=1)
38
+ # Providing a non-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
39
+ logger = LogMachine("myapp", debug_level=1)
58
40
 
59
41
  logger.info("Hello, world!")
60
42
  logger.error("An error occurred!")
@@ -65,6 +47,17 @@ logger.warning("This is a warning message.")
65
47
 
66
48
  ### With Central Logging (HTTP or Socket.IO)
67
49
 
50
+ You can use the default logger with central logging pointing to "https://logmachine.bufferpunk.com"
51
+
52
+ ```python
53
+ from logmachine import default_logger
54
+ logger = default_logger()
55
+ logger.info("This log is sent to the LogMachine default central server!")
56
+ ```
57
+
58
+ This is the default central logging server for logmachine, and you can create your own room there for free.
59
+ To use your own central logging server, provide the configuration as shown below:
60
+
68
61
  ```python
69
62
  logger_config = {
70
63
  "url": "https://logmachine.bufferpunk.com", # Base server URL
@@ -74,7 +67,7 @@ logger_config = {
74
67
  "socketio": True, # Set False to use HTTP
75
68
  "socketio_path": "/api/socket.io/" # Optional
76
69
  }
77
- logger = ContribLog("with_central", debug_level=0, central=logger_config, socketio=True)
70
+ logger = LogMachine("with_central", debug_level=0, central=logger_config, socketio=True)
78
71
  logger.success("Central logging is working!")
79
72
  ```
80
73
 
@@ -105,8 +98,9 @@ Sample (terminal):
105
98
  ### Add Your Own Log Level
106
99
 
107
100
  ```python
108
- log.new_level("CRITICAL_HACK", 60)
109
- log.critical_hack("Zero day found!")
101
+ logger.new_level("CRITICAL_HACK", 60)
102
+ logger.new_level("CRITICAL_HACK", 60, color="\033[38;5;13m") # Optional color... does your girlfriend love pink? Maybe you should be in a relationship with your terminal.
103
+ logger.critical_hack("Zero day found!")
110
104
  ```
111
105
 
112
106
  ---
@@ -115,6 +109,9 @@ log.critical_hack("Zero day found!")
115
109
 
116
110
  ### Convert Logs to JSON
117
111
 
112
+ This is useful for sending logs to web dashboards or log collectors that expect JSON.
113
+ It reads the your log files, parses the log entries, and outputs them as JSON objects.
114
+
118
115
  ```python
119
116
  json_logs = log.jsonifier()
120
117
  for entry in json_logs:
@@ -0,0 +1 @@
1
+ from .main import LogMachine, default_logger
@@ -1,9 +1,13 @@
1
+ import atexit
1
2
  import os
2
3
  import re
3
4
  import json
4
5
  import logging
5
6
  import requests
6
7
  import socketio
8
+ import queue
9
+ from logging.handlers import QueueHandler, QueueListener
10
+
7
11
 
8
12
  def get_login():
9
13
  """
@@ -11,12 +15,17 @@ def get_login():
11
15
  :return: The login name of the current user.
12
16
  """
13
17
  try:
14
- return os.getlogin()
18
+ if not os.path.exists(os.path.expanduser("~/.cl_username")):
19
+ return os.getlogin()
20
+ else:
21
+ with open(os.path.expanduser("~/.cl_username"), 'r') as f:
22
+ os.environ['CL_USERNAME'] = f.read().strip()
23
+ return os.environ['CL_USERNAME']
15
24
  except Exception:
16
25
  return os.environ.get('USER', 'unknown')
17
26
 
18
27
 
19
- class RequestsTransporter(logging.StreamHandler):
28
+ class HTTPTransporter(logging.StreamHandler):
20
29
  """
21
30
  A class to handle the transport of log messages using HTTP requests.
22
31
  This class sends log messages to a central server via HTTP POST requests.
@@ -36,22 +45,19 @@ class RequestsTransporter(logging.StreamHandler):
36
45
  try:
37
46
  msg = self.format(record)
38
47
  super().emit(record)
39
- if self.central:
40
- if not self.central.get('room'):
41
- raise ValueError("""
42
- Central configuration must include 'room' for log transport.
43
- Example: {'url': 'http://central-server/api/logs', 'room': 'my_organization_name'}
44
- """)
45
-
46
- log_data = self.parse_log(msg)
47
- if log_data:
48
- response = requests.post(
49
- f"{self.central.get('url', '') + self.central.get('endpoint', '/api/logs')}?room={self.central.get('room', '')}",
50
- json=log_data,
51
- headers={"Content-Type": "application/json", **self.central.get('headers', {})}
52
- )
53
- if response.status_code != 200:
54
- raise Exception(f"Failed to send log to central: {response.text}")
48
+ if not self.central or not self.central.get('room'):
49
+ raise ValueError("""Central configuration must include 'room' for log transport.
50
+ Example: {'url': 'http://central-server/api/logs', 'room': 'my_organization_name'}""")
51
+
52
+ log_data = self.parse_log(msg)
53
+ if log_data:
54
+ response = requests.post(
55
+ f"{self.central.get('url', '') + self.central.get('endpoint', '/api/logs')}?room={self.central.get('room', '')}",
56
+ json=log_data,
57
+ headers={"Content-Type": "application/json", **self.central.get('headers', {})}
58
+ )
59
+ if response.status_code != 200:
60
+ raise Exception(f"Failed to send log to central: {response.text}")
55
61
 
56
62
  except Exception:
57
63
  self.handleError(record)
@@ -90,57 +96,67 @@ class SocketIOTransporter(logging.StreamHandler):
90
96
 
91
97
 
92
98
  class CustomFormatter(logging.Formatter):
93
- COLORS = {
94
- 'DEBUG': '\x1b[36m',
95
- 'INFO': '\x1b[34m',
96
- 'WARNING': '\x1b[33m',
97
- 'ERROR': '\x1b[31m',
98
- 'SUCCESS': '\x1b[32m'
99
- }
100
- RESET = '\x1b[0m'
101
- BOLD = '\x1b[1m'
102
- LEVEL_FORMATS = {
103
- 'DEBUG': BOLD + '[ DEBUG ]' + RESET,
104
- 'INFO': BOLD + '[ INFO ]' + RESET,
105
- 'WARNING': BOLD + '[ WARNING ]' + RESET,
106
- 'ERROR': BOLD + '[ ERROR ]' + RESET,
107
- 'SUCCESS': BOLD + '[ SUCCESS ]' + RESET
108
- }
99
+ def __init__(self, *args, **kwargs):
100
+ super().__init__(*args, **kwargs)
101
+ self.colors = {
102
+ 'DEBUG': '\x1b[36m',
103
+ 'INFO': '\x1b[34m',
104
+ 'WARNING': '\x1b[33m',
105
+ 'ERROR': '\x1b[31m',
106
+ 'SUCCESS': '\x1b[32m'
107
+ }
108
+ self.reset = '\x1b[0m'
109
+ self.bold = '\x1b[1m'
110
+ self.level_formats = {
111
+ 'DEBUG': self.bold + '[ DEBUG ]' + self.reset,
112
+ 'INFO': self.bold + '[ INFO ]' + self.reset,
113
+ 'WARNING': self.bold + '[ WARNING ]' + self.reset,
114
+ 'ERROR': self.bold + '[ ERROR ]' + self.reset,
115
+ 'SUCCESS': self.bold + '[ SUCCESS ]' + self.reset
116
+ }
117
+
118
+ def set_color(self, levelname: str, color_code: str):
119
+ """
120
+ Set a custom color for a specific log level.
121
+ :param levelname: The name of the log level (e.g., 'DEBUG', 'INFO').
122
+ :param color_code: The ANSI color code to use for the specified log level.
123
+ """
124
+ self.colors[levelname] = color_code
125
+ self.level_formats[levelname] = f"{self.bold}[ {levelname} ]{self.reset}"
109
126
 
110
127
  def format(self, record) -> str:
111
128
  username = os.environ.get('CL_USERNAME') or get_login()
112
129
 
113
130
  levelname = record.levelname
114
- color = self.COLORS.get(levelname, '')
115
- level_fmt = self.LEVEL_FORMATS.get(levelname, f'{levelname}')
116
- level_fmt = f"{color}{level_fmt}{self.RESET}"
131
+ color = self.colors.get(levelname, '')
132
+ level_fmt = self.level_formats.get(levelname, f'{levelname}')
133
+ level_fmt = f"{color}{level_fmt}{self.reset}"
117
134
  record.asctime = self.formatTime(record, self.datefmt)
118
135
  module_file = record.pathname
119
136
  parent_dir = os.path.basename(os.path.dirname(record.pathname)) if module_file != '<stdin>' else 'stdin'
120
137
 
121
- return f"""{self.COLORS.get('DEBUG')}({username}{self.RESET} @ {self.COLORS.get('WARNING') + parent_dir + self.RESET}) 🤌 CL Timing: {color}[ {record.asctime} ]{self.RESET}
138
+ return f"""{self.colors.get('DEBUG')}({username}{self.reset} @ {self.colors.get('WARNING') + parent_dir + self.reset}) 🤌 CL Timing: {color}[ {record.asctime} ]{self.reset}
122
139
  {level_fmt} {record.getMessage()}
123
140
  🏁"""
124
141
 
125
- class ContribLog(logging.Logger):
126
- SUCCESS = 25
127
-
128
- def __init__(self, *args, **kwargs) -> None:
129
- super().__init__(*args, level=logging.DEBUG)
130
- logging.addLevelName(self.SUCCESS, "SUCCESS")
142
+ class LogMachine(logging.Logger):
143
+ def __init__(self, name="", *args, **kwargs) -> None:
144
+ super().__init__(name, *args, level=int(kwargs.get('debug_level', 0)))
145
+ logging.addLevelName(25, "SUCCESS")
131
146
  self.log_file = kwargs.get('log_file', 'logs.log')
132
147
  self.error_file = kwargs.get('error_file', 'errors.log')
133
148
  self.debug_level = int(kwargs.get('debug_level', 0))
134
149
  self.verbose = kwargs.get('verbose', False)
135
150
  self.central = kwargs.get('central', None)
136
- self.setLevel(logging.DEBUG)
151
+ self.queue = queue.Queue()
137
152
 
138
153
  # Remove existing handlers
139
- self.handlers = []
154
+ for h in self.handlers[:]:
155
+ self.removeHandler(h)
140
156
 
141
157
  # File handlers
142
158
  fh = logging.FileHandler(self.log_file)
143
- fh.setLevel(logging.DEBUG)
159
+ fh.setLevel(self.debug_level)
144
160
  eh = logging.FileHandler(self.error_file)
145
161
  eh.setLevel(logging.ERROR)
146
162
 
@@ -148,7 +164,7 @@ class ContribLog(logging.Logger):
148
164
  if self.central:
149
165
  """
150
166
  The central uses usernames to group logs.
151
- OS usernames are used to identify the use, meaning names can clash.
167
+ OS usernames are used to identify the user, meaning names can clash.
152
168
  Therefore, we avoid a user having to define a username, rather, ask the central server to provide it.
153
169
  After getting the username, we store it in the user's home directory in a file named `.cl_username`.
154
170
  This way, the user can change it at any time, and it will be used in all future logs without needing to request it again.
@@ -158,42 +174,37 @@ class ContribLog(logging.Logger):
158
174
  login = get_login()
159
175
  response = requests.get(f"{self.central.get('url', '')}/api/get_username?base={login}")
160
176
  if response.status_code == 200:
161
- os.environ['CL_USERNAME'] = response.json().get('username', 'unknown') # Unknown will probably never be reached, but it's a fallback.
177
+ os.environ['CL_USERNAME'] = response.json().get('username') or 'unknown' # Unknown will probably never be reached, but it's a fallback.
162
178
  if os.environ.get('CL_USERNAME') != 'unknown':
163
179
  with open(os.path.expanduser("~/.cl_username"), 'w') as f:
164
180
  f.write(os.environ['CL_USERNAME'])
165
181
  else:
166
182
  os.environ['CL_USERNAME'] = 'unknown'
167
- except Exception as e:
183
+ except Exception:
168
184
  os.environ['CL_USERNAME'] = 'unknown'
169
185
  else:
170
- with open(os.path.expanduser("~/.cl_username"), 'r') as f:
171
- os.environ['CL_USERNAME'] = f.read().strip()
186
+ get_login()
172
187
 
173
188
  if not kwargs.get('attached', False) and not self.central.get('socketio', False):
174
- ch = RequestsTransporter(log_parser=self.parse_log, central=self.central)
189
+ ch = HTTPTransporter(log_parser=self.parse_log, central=self.central)
175
190
  else:
176
191
  ch = SocketIOTransporter(log_parser=self.parse_log, central=self.central)
177
-
178
192
  else:
179
193
  ch = logging.StreamHandler()
180
194
 
181
195
  ch.setLevel(logging.DEBUG)
182
196
 
183
- formatter = CustomFormatter('%(asctime)s %(levelname)s %(message)s', datefmt='%Y-%m-%dT%H:%M:%S%z')
184
- fh.setFormatter(formatter)
185
- eh.setFormatter(formatter)
186
- ch.setFormatter(formatter)
187
-
188
- self.addHandler(fh)
189
- self.addHandler(eh)
190
- self.addHandler(ch)
197
+ self.formatter = CustomFormatter('%(asctime)s %(levelname)s %(message)s', datefmt='%Y-%m-%dT%H:%M:%S%z')
198
+ fh.setFormatter(self.formatter)
199
+ eh.setFormatter(self.formatter)
200
+ ch.setFormatter(self.formatter)
201
+ self.addHandler(QueueHandler(self.queue))
191
202
 
192
203
  # Filter console output based on debug_level
193
204
  class DebugLevelFilter(logging.Filter):
194
205
  def __init__(self, debug_level):
195
206
  super().__init__()
196
- self.debug_level = int(debug_level)
207
+ self.debug_level = debug_level
197
208
 
198
209
  def filter(self, record):
199
210
  if self.debug_level == 0:
@@ -212,12 +223,22 @@ class ContribLog(logging.Logger):
212
223
  return record.levelname in allowed
213
224
 
214
225
  ch.addFilter(DebugLevelFilter(self.debug_level if not self.verbose else 0))
226
+ self.listener = QueueListener(self.queue, fh, eh, ch)
227
+ self.listener.start()
228
+ atexit.register(self.listener.stop)
229
+ self.info("LogMachine initialized with debug level {} with{}".format(
230
+ self.debug_level,
231
+ self.central and
232
+ f" central logging to {self.central.get('url', '')}" or
233
+ "out central logging"
234
+ )
235
+ )
215
236
 
216
237
  def success(self, msg, *args, **kwargs) -> None:
217
- if self.isEnabledFor(self.SUCCESS):
218
- self._log(self.SUCCESS, msg, args, stacklevel=2, **kwargs)
238
+ if self.isEnabledFor(25):
239
+ self._log(25, msg, args, stacklevel=2, **kwargs)
219
240
 
220
- def new_level(self, level_name: str, level_num: int):
241
+ def new_level(self, level_name: str, level_num: int, ansi_color="\x1b[37m") -> None:
221
242
  """
222
243
  Dynamically add a new logging level.
223
244
  :param level_name: Name of the new logging level.
@@ -228,8 +249,9 @@ class ContribLog(logging.Logger):
228
249
  logging.addLevelName(level_num, level_name)
229
250
  setattr(self, level_name.lower(), lambda msg, *args, **kwargs: self._log(level_num, msg, args, stacklevel=2, **kwargs))
230
251
  self.setLevel(min(self.level, level_num)) # Ensure the logger's level is set appropriately
252
+ self.formatter.set_color(level_name, ansi_color) # Add color formatting for the new level
231
253
 
232
- def parse_log(self, log_text) -> dict:
254
+ def parse_log(self, log_text) -> dict | None:
233
255
  log_text = log_text.strip()
234
256
  ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
235
257
  end_escape = re.compile(r'🏁')
@@ -240,7 +262,7 @@ class ContribLog(logging.Logger):
240
262
  header_match = re.search(header_pattern, clean)
241
263
 
242
264
  if not header_match:
243
- return None
265
+ return
244
266
 
245
267
  user, module, timestamp = header_match.groups()
246
268
  lines = clean.splitlines()
@@ -257,7 +279,7 @@ class ContribLog(logging.Logger):
257
279
  "timestamp": timestamp,
258
280
  "message": end_escape.sub('', message).strip()
259
281
  }
260
-
282
+
261
283
  def jsonifier(self) -> list:
262
284
  """
263
285
  Reads the log file and returns a list of JSON objects representing each log entry.
@@ -278,4 +300,12 @@ class ContribLog(logging.Logger):
278
300
  return log_entries
279
301
 
280
302
 
281
- logging.setLoggerClass(ContribLog)
303
+ def default_logger():
304
+ return LogMachine('default_logger', debug_level=0, verbose=False, central={
305
+ 'url': 'https://logmachine.bufferpunk.com',
306
+ 'room': f'{get_login()}_logs',
307
+ 'headers': {}
308
+ })
309
+
310
+
311
+ logging.setLoggerClass(LogMachine)
@@ -1,4 +1,22 @@
1
- # 🧠 logmachine 2.1.0
1
+ Metadata-Version: 2.4
2
+ Name: logmachine
3
+ Version: 2.2.0
4
+ Summary: Collaborative, beautiful logging system for distributed developers
5
+ Author-email: Mugabo Gusenga <mugabo@bufferpunk.com>
6
+ Project-URL: Homepage, https://logmachine.bufferpunk.com
7
+ Project-URL: Documentation, https://github.com/logmachine/python
8
+ Project-URL: Source, https://github.com/logmachine/python
9
+ Project-URL: Tracker, https://github.com/logmachine/python/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
18
+
19
+ # 🧠 LogMachine
2
20
 
3
21
  > Collaborative, beautiful logging system for distributed developers
4
22
 
@@ -32,11 +50,11 @@ pip install logmachine
32
50
  ### Basic Setup
33
51
 
34
52
  ```python
35
- from logmachine import ContribLog
53
+ from logmachine import LogMachine
36
54
 
37
55
  # Create a simple logger without central logging
38
- # Providing a noon-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
39
- logger = ContribLog("myapp", debug_level=1)
56
+ # Providing a non-empty string initializes the logger with that name, else the root logger is used to collect every single log in the python process.
57
+ logger = LogMachine("myapp", debug_level=1)
40
58
 
41
59
  logger.info("Hello, world!")
42
60
  logger.error("An error occurred!")
@@ -47,6 +65,17 @@ logger.warning("This is a warning message.")
47
65
 
48
66
  ### With Central Logging (HTTP or Socket.IO)
49
67
 
68
+ You can use the default logger with central logging pointing to "https://logmachine.bufferpunk.com"
69
+
70
+ ```python
71
+ from logmachine import default_logger
72
+ logger = default_logger()
73
+ logger.info("This log is sent to the LogMachine default central server!")
74
+ ```
75
+
76
+ This is the default central logging server for logmachine, and you can create your own room there for free.
77
+ To use your own central logging server, provide the configuration as shown below:
78
+
50
79
  ```python
51
80
  logger_config = {
52
81
  "url": "https://logmachine.bufferpunk.com", # Base server URL
@@ -56,7 +85,7 @@ logger_config = {
56
85
  "socketio": True, # Set False to use HTTP
57
86
  "socketio_path": "/api/socket.io/" # Optional
58
87
  }
59
- logger = ContribLog("with_central", debug_level=0, central=logger_config, socketio=True)
88
+ logger = LogMachine("with_central", debug_level=0, central=logger_config, socketio=True)
60
89
  logger.success("Central logging is working!")
61
90
  ```
62
91
 
@@ -87,8 +116,9 @@ Sample (terminal):
87
116
  ### Add Your Own Log Level
88
117
 
89
118
  ```python
90
- log.new_level("CRITICAL_HACK", 60)
91
- log.critical_hack("Zero day found!")
119
+ logger.new_level("CRITICAL_HACK", 60)
120
+ logger.new_level("CRITICAL_HACK", 60, color="\033[38;5;13m") # Optional color... does your girlfriend love pink? Maybe you should be in a relationship with your terminal.
121
+ logger.critical_hack("Zero day found!")
92
122
  ```
93
123
 
94
124
  ---
@@ -97,6 +127,9 @@ log.critical_hack("Zero day found!")
97
127
 
98
128
  ### Convert Logs to JSON
99
129
 
130
+ This is useful for sending logs to web dashboards or log collectors that expect JSON.
131
+ It reads the your log files, parses the log entries, and outputs them as JSON objects.
132
+
100
133
  ```python
101
134
  json_logs = log.jsonifier()
102
135
  for entry in json_logs:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "logmachine"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "Collaborative, beautiful logging system for distributed developers"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.7"
@@ -17,9 +17,9 @@ classifiers = [
17
17
 
18
18
  [project.urls]
19
19
  Homepage = "https://logmachine.bufferpunk.com"
20
- Documentation = "https://github.com/Scion-Kin/logmachine"
21
- Source = "https://github.com/Scion-Kin/logmachine"
22
- Tracker = "https://github.com/Scion-Kin/logmachine/issues"
20
+ Documentation = "https://github.com/logmachine/python"
21
+ Source = "https://github.com/logmachine/python"
22
+ Tracker = "https://github.com/logmachine/python/issues"
23
23
 
24
24
  [build-system]
25
25
  requires = ["setuptools>=61.0", "websocket-client", "python-socketio", "requests"]
@@ -1 +0,0 @@
1
- from .main import ContribLog
File without changes