VersaLog 2.6.0__tar.gz → 3.0.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.
- {versalog-2.6.0 → versalog-3.0.0}/PKG-INFO +10 -2
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/__init__.py +3 -3
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/core.py +29 -0
- versalog-3.0.0/VersaLog/integrations/django.py +19 -0
- versalog-3.0.0/VersaLog/integrations/webhook/__init__.py +3 -0
- versalog-3.0.0/VersaLog/integrations/webhook/formatters.py +17 -0
- versalog-3.0.0/VersaLog/integrations/webhook/handler.py +43 -0
- versalog-3.0.0/VersaLog/integrations/webhook/sender.py +16 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog.egg-info/PKG-INFO +10 -2
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog.egg-info/SOURCES.txt +5 -1
- {versalog-2.6.0 → versalog-3.0.0}/setup.py +1 -1
- versalog-2.6.0/VersaLog/integrations/django.py +0 -5
- {versalog-2.6.0 → versalog-3.0.0}/LICENSE +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/README.md +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/handler.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/integrations/__init__.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/integrations/auto.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/integrations/fastapi.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/integrations/flask.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog/integrations/generic.py +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog.egg-info/dependency_links.txt +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog.egg-info/requires.txt +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/VersaLog.egg-info/top_level.txt +0 -0
- {versalog-2.6.0 → versalog-3.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: VersaLog
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Versatile logging library.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -18,6 +18,14 @@ Requires-Python: >=3.8, <3.14
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: plyer
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: description
|
|
23
|
+
Dynamic: description-content-type
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
21
29
|
|
|
22
30
|
## What is VersaLog.py?
|
|
23
31
|
|
|
@@ -4,10 +4,10 @@ from .integrations.auto import auto_setup
|
|
|
4
4
|
from .integrations.fastapi import setup_fastapi_logging
|
|
5
5
|
from .integrations.flask import setup_flask_logging
|
|
6
6
|
from .integrations.django import setup_django_logging
|
|
7
|
+
from .integrations.webhook import WebhookHandler
|
|
7
8
|
|
|
8
9
|
__copyright__ = "Copyright 2025 by Kayu0514"
|
|
9
|
-
__version__ = "
|
|
10
|
+
__version__ = "3.0.0"
|
|
10
11
|
__author__ = "Kayu0514"
|
|
11
12
|
__url__ = "https://github.com/kayu0514"
|
|
12
|
-
|
|
13
|
-
__all__ = ["VersaLog", "VersaLogHandler", "auto_setup", "setup_fastapi_logging","setup_flask_logging", "setup_django_logging"]
|
|
13
|
+
__all__ = ["VersaLog", "VersaLogHandler", "WebhookHandler", "auto_setup", "setup_fastapi_logging","setup_flask_logging", "setup_django_logging"]
|
|
@@ -2,6 +2,8 @@ from typing import Optional, Union, List
|
|
|
2
2
|
from plyer import notification
|
|
3
3
|
from contextlib import contextmanager
|
|
4
4
|
|
|
5
|
+
from .integrations.webhook import WebhookHandler
|
|
6
|
+
|
|
5
7
|
import datetime
|
|
6
8
|
import inspect
|
|
7
9
|
import os
|
|
@@ -73,6 +75,7 @@ class VersaLog:
|
|
|
73
75
|
self.all_save = all_save
|
|
74
76
|
self.save_levels = save_levels
|
|
75
77
|
self.silent = silent
|
|
78
|
+
self._webhook_handlers = []
|
|
76
79
|
|
|
77
80
|
self.log_queue = queue.Queue()
|
|
78
81
|
self.worker_thread = threading.Thread(target=self._worker, daemon=True)
|
|
@@ -218,6 +221,32 @@ class VersaLog:
|
|
|
218
221
|
|
|
219
222
|
self._save_log(plain, types)
|
|
220
223
|
|
|
224
|
+
def add_webhook(self, url: str, level: str = "ERROR", *, format: str = "generic", timeout: float = 3.0, headers: Optional[dict] = None,):
|
|
225
|
+
"""Register a webhook destination for logs at or above ``level``.
|
|
226
|
+
|
|
227
|
+
``format`` can be ``"generic"``, ``"discord"``, or ``"slack"``.
|
|
228
|
+
The default ``"ERROR"`` sends ERROR and CRITICAL logs.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
handler = WebhookHandler(url=url, level=level, format=format, timeout=timeout, headers=headers)
|
|
232
|
+
self._webhook_handlers.append(handler)
|
|
233
|
+
return handler
|
|
234
|
+
|
|
235
|
+
def remove_webhook(self, handler) -> bool:
|
|
236
|
+
try:
|
|
237
|
+
self._webhook_handlers.remove(handler)
|
|
238
|
+
return True
|
|
239
|
+
except ValueError:
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
def _dispatch_webhooks(self, message: str, level: str) -> None:
|
|
243
|
+
for handler in tuple(self._webhook_handlers):
|
|
244
|
+
if handler.should_handle(level):
|
|
245
|
+
try:
|
|
246
|
+
handler.emit(message, level)
|
|
247
|
+
except Exception:
|
|
248
|
+
pass
|
|
249
|
+
|
|
221
250
|
def info(self, msg: str, tag: Optional[Union[str, List[str]]] = None) -> None:
|
|
222
251
|
self._Log(msg, "INFO", tag)
|
|
223
252
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from ..handler import VersaLogHandle
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def setup_django_logging(versalog):
|
|
6
|
+
handler = VersaLogHandler(versalog)
|
|
7
|
+
|
|
8
|
+
root = logging.getLogger()
|
|
9
|
+
for h in root.handlers[:]:
|
|
10
|
+
root.removeHandler(h)
|
|
11
|
+
root.setLevel(logging.NOTSET)
|
|
12
|
+
root.addHandler(handler)
|
|
13
|
+
|
|
14
|
+
for name in ["django", "django.request", "django.server", "django.db.backends"]:
|
|
15
|
+
logger = logging.getLogger(name)
|
|
16
|
+
for h in logger.handlers[:]:
|
|
17
|
+
logger.removeHandler(h)
|
|
18
|
+
logger.propagate = True
|
|
19
|
+
logger.setLevel(logging.NOTSET)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def format_payload(level, message, format="generic"):
|
|
5
|
+
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
6
|
+
if format == "generic":
|
|
7
|
+
return {
|
|
8
|
+
"source": "VersaLog",
|
|
9
|
+
"level": level,
|
|
10
|
+
"message": message,
|
|
11
|
+
"timestamp": timestamp,
|
|
12
|
+
}
|
|
13
|
+
if format == "discord":
|
|
14
|
+
return {"content": f"[{level}] {message}"}
|
|
15
|
+
if format == "slack":
|
|
16
|
+
return {"text": f"[{level}] {message}"}
|
|
17
|
+
raise ValueError("format must be 'generic', 'discord', or 'slack'")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .formatters import format_payload
|
|
2
|
+
from .sender import send_json
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LEVELS = {
|
|
6
|
+
"DEBUG": 10,
|
|
7
|
+
"INFO": 20,
|
|
8
|
+
"WARNING": 30,
|
|
9
|
+
"ERROR": 40,
|
|
10
|
+
"CRITICAL": 50,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WebhookHandler:
|
|
15
|
+
def __init__(self, url, level="ERROR", format="generic", timeout=3.0, headers=None):
|
|
16
|
+
if not isinstance(url, str) or not url.strip():
|
|
17
|
+
raise ValueError("url must be a non-empty string")
|
|
18
|
+
normalized_level = str(level).upper()
|
|
19
|
+
if normalized_level not in LEVELS:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
"level must be one of: " + ", ".join(LEVELS.keys())
|
|
22
|
+
)
|
|
23
|
+
if format not in ("generic", "discord", "slack"):
|
|
24
|
+
raise ValueError("format must be 'generic', 'discord', or 'slack'")
|
|
25
|
+
if timeout <= 0:
|
|
26
|
+
raise ValueError("timeout must be greater than 0")
|
|
27
|
+
|
|
28
|
+
self.url = url
|
|
29
|
+
self.level = normalized_level
|
|
30
|
+
self.format = format
|
|
31
|
+
self.timeout = timeout
|
|
32
|
+
self.headers = dict(headers or {})
|
|
33
|
+
self.last_success = None
|
|
34
|
+
|
|
35
|
+
def should_handle(self, level):
|
|
36
|
+
return LEVELS.get(str(level).upper(), 0) >= LEVELS[self.level]
|
|
37
|
+
|
|
38
|
+
def emit(self, message, level):
|
|
39
|
+
payload = format_payload(str(level).upper(), str(message), self.format)
|
|
40
|
+
self.last_success = send_json(
|
|
41
|
+
self.url, payload, timeout=self.timeout, headers=self.headers
|
|
42
|
+
)
|
|
43
|
+
return self.last_success
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from urllib.error import HTTPError, URLError
|
|
3
|
+
from urllib.request import Request, urlopen
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def send_json(url, payload, timeout=3.0, headers=None):
|
|
7
|
+
request_headers = {"Content-Type": "application/json; charset=utf-8"}
|
|
8
|
+
if headers:
|
|
9
|
+
request_headers.update(headers)
|
|
10
|
+
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
11
|
+
request = Request(url, data=data, headers=request_headers, method="POST")
|
|
12
|
+
try:
|
|
13
|
+
with urlopen(request, timeout=timeout) as response:
|
|
14
|
+
return 200 <= response.status < 300
|
|
15
|
+
except (HTTPError, URLError, TimeoutError, OSError, ValueError):
|
|
16
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: VersaLog
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Versatile logging library.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -18,6 +18,14 @@ Requires-Python: >=3.8, <3.14
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: plyer
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: description
|
|
23
|
+
Dynamic: description-content-type
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
21
29
|
|
|
22
30
|
## What is VersaLog.py?
|
|
23
31
|
|
|
@@ -14,4 +14,8 @@ VersaLog/integrations/auto.py
|
|
|
14
14
|
VersaLog/integrations/django.py
|
|
15
15
|
VersaLog/integrations/fastapi.py
|
|
16
16
|
VersaLog/integrations/flask.py
|
|
17
|
-
VersaLog/integrations/generic.py
|
|
17
|
+
VersaLog/integrations/generic.py
|
|
18
|
+
VersaLog/integrations/webhook/__init__.py
|
|
19
|
+
VersaLog/integrations/webhook/formatters.py
|
|
20
|
+
VersaLog/integrations/webhook/handler.py
|
|
21
|
+
VersaLog/integrations/webhook/sender.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|