jaylog 0.2.2__tar.gz → 0.2.4__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.
- {jaylog-0.2.2 → jaylog-0.2.4}/PKG-INFO +1 -1
- {jaylog-0.2.2 → jaylog-0.2.4}/pyproject.toml +1 -1
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/formatters.py +12 -5
- jaylog-0.2.4/src/jaylog/handlers/__init__.py +5 -0
- jaylog-0.2.4/src/jaylog/handlers/console_handler.py +43 -0
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/handlers/file_handler.py +2 -1
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/logger.py +18 -9
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/settings.py +9 -4
- jaylog-0.2.2/src/jaylog/handlers/__init__.py +0 -4
- {jaylog-0.2.2 → jaylog-0.2.4}/README.md +0 -0
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/__init__.py +0 -0
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/filters.py +0 -0
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/handlers/http_handler.py +0 -0
- {jaylog-0.2.2 → jaylog-0.2.4}/src/jaylog/models.py +0 -0
|
@@ -100,11 +100,18 @@ def build_log_entry_dict(record: logging.LogRecord) -> dict:
|
|
|
100
100
|
class PlainTextFormatter(logging.Formatter):
|
|
101
101
|
"""Human-readable single-line formatter for .log files."""
|
|
102
102
|
|
|
103
|
+
def __init__(self, show_service: bool = True) -> None:
|
|
104
|
+
super().__init__()
|
|
105
|
+
self.show_service = show_service
|
|
106
|
+
|
|
103
107
|
def format(self, record: logging.LogRecord) -> str:
|
|
104
108
|
entry = build_log_entry_dict(record)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
log_timestamp = datetime.fromisoformat(entry['log_timestamp']).strftime('%d/%m/%Y %X')
|
|
110
|
+
log_level = f'[{entry["log_level"]}]'
|
|
111
|
+
service_segment = f'[{entry["service"]}] | ' if self.show_service else ''
|
|
112
|
+
return (
|
|
113
|
+
f'{log_timestamp} '
|
|
114
|
+
f'{log_level.ljust(11)} | '
|
|
115
|
+
f'{service_segment}'
|
|
116
|
+
f"{entry['log_message']}"
|
|
109
117
|
)
|
|
110
|
-
return line
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from jaylog.formatters import build_log_entry_dict
|
|
6
|
+
|
|
7
|
+
_GREEN = "\033[32m"
|
|
8
|
+
_BLUE = "\033[34m"
|
|
9
|
+
_PURPLE = "\033[35m"
|
|
10
|
+
_YELLOW = "\033[33m"
|
|
11
|
+
_RED = "\033[31m"
|
|
12
|
+
_RESET = "\033[0m"
|
|
13
|
+
|
|
14
|
+
_LEVEL_COLORS = {
|
|
15
|
+
"DEBUG": _PURPLE,
|
|
16
|
+
"INFO": _BLUE,
|
|
17
|
+
"WARNING": _YELLOW,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _level_color(level: str) -> str:
|
|
22
|
+
return _LEVEL_COLORS.get(level, _RED)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ConsoleFormatter(logging.Formatter):
|
|
26
|
+
def __init__(self, show_service: bool = True) -> None:
|
|
27
|
+
super().__init__()
|
|
28
|
+
self.show_service = show_service
|
|
29
|
+
|
|
30
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
31
|
+
entry = build_log_entry_dict(record)
|
|
32
|
+
log_timestamp = datetime.fromisoformat(entry["log_timestamp"]).strftime("%d/%m/%Y %X")
|
|
33
|
+
log_level = f'[{entry["log_level"]}]'
|
|
34
|
+
colored_timestamp = f"{_GREEN}{log_timestamp}{_RESET}"
|
|
35
|
+
colored_level = f"{_level_color(entry['log_level'])}{log_level.ljust(11)}{_RESET}"
|
|
36
|
+
service_segment = f'[{entry["service"]}] | ' if self.show_service else ''
|
|
37
|
+
return f"{colored_timestamp} {colored_level} | {service_segment}{entry['log_message']}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class JaylogConsoleHandler(logging.StreamHandler):
|
|
41
|
+
def __init__(self, show_service: bool = True) -> None:
|
|
42
|
+
super().__init__(stream=sys.stdout)
|
|
43
|
+
self.setFormatter(ConsoleFormatter(show_service=show_service))
|
|
@@ -19,6 +19,7 @@ class JaylogFileHandler(RotatingFileHandler):
|
|
|
19
19
|
backup_count: int = 20,
|
|
20
20
|
retention_days: int = 7,
|
|
21
21
|
encoding: str = "utf-8",
|
|
22
|
+
show_service: bool = True,
|
|
22
23
|
) -> None:
|
|
23
24
|
filename.parent.mkdir(parents=True, exist_ok=True)
|
|
24
25
|
super().__init__(
|
|
@@ -28,7 +29,7 @@ class JaylogFileHandler(RotatingFileHandler):
|
|
|
28
29
|
encoding=encoding,
|
|
29
30
|
)
|
|
30
31
|
self.retention_days = retention_days
|
|
31
|
-
self.setFormatter(PlainTextFormatter())
|
|
32
|
+
self.setFormatter(PlainTextFormatter(show_service=show_service))
|
|
32
33
|
|
|
33
34
|
def doRollover(self) -> None:
|
|
34
35
|
super().doRollover()
|
|
@@ -6,6 +6,7 @@ from queue import Queue
|
|
|
6
6
|
|
|
7
7
|
from jaylog.filters import ExceptionFlagFilter
|
|
8
8
|
from jaylog.formatters import configure_screenshot
|
|
9
|
+
from jaylog.handlers.console_handler import JaylogConsoleHandler
|
|
9
10
|
from jaylog.handlers.file_handler import JaylogFileHandler
|
|
10
11
|
from jaylog.handlers.http_handler import JaylogHttpHandler
|
|
11
12
|
from jaylog.settings import JaylogSettings
|
|
@@ -105,16 +106,24 @@ def get_logger(name: str | None = None) -> logging.Logger:
|
|
|
105
106
|
# Build the actual (downstream) handlers
|
|
106
107
|
# ------------------------------------------------------------------
|
|
107
108
|
downstream: list[logging.Handler] = []
|
|
109
|
+
show_service = len(_settings_registry) > 1
|
|
110
|
+
|
|
111
|
+
if settings.log_dir is not None:
|
|
112
|
+
log_path = settings.log_dir / settings.log_filename
|
|
113
|
+
file_handler = JaylogFileHandler(
|
|
114
|
+
filename=log_path,
|
|
115
|
+
max_bytes=settings.log_max_bytes,
|
|
116
|
+
backup_count=settings.log_backup_count,
|
|
117
|
+
retention_days=settings.log_retention_days,
|
|
118
|
+
show_service=show_service,
|
|
119
|
+
)
|
|
120
|
+
file_handler.setLevel(settings.log_level)
|
|
121
|
+
downstream.append(file_handler)
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
backup_count=settings.log_backup_count,
|
|
114
|
-
retention_days=settings.log_retention_days,
|
|
115
|
-
)
|
|
116
|
-
file_handler.setLevel(settings.log_level)
|
|
117
|
-
downstream.append(file_handler)
|
|
123
|
+
if settings.log_console_enabled:
|
|
124
|
+
console_handler = JaylogConsoleHandler(show_service=show_service)
|
|
125
|
+
console_handler.setLevel(settings.log_level)
|
|
126
|
+
downstream.append(console_handler)
|
|
118
127
|
|
|
119
128
|
if settings.log_http_endpoint and settings.log_http_api_key:
|
|
120
129
|
http_handler = JaylogHttpHandler(
|
|
@@ -31,7 +31,7 @@ class JaylogSettings(BaseSettings):
|
|
|
31
31
|
|
|
32
32
|
# App identity
|
|
33
33
|
app_name: str
|
|
34
|
-
log_dir: Path
|
|
34
|
+
log_dir: Path | None = None
|
|
35
35
|
|
|
36
36
|
secrets_dir: Path | None = None
|
|
37
37
|
|
|
@@ -47,19 +47,24 @@ class JaylogSettings(BaseSettings):
|
|
|
47
47
|
log_http_timeout: float = 5.0
|
|
48
48
|
log_http_proxy: Optional[str] = None
|
|
49
49
|
|
|
50
|
+
# Console handler — desativar com JAYLOG_LOG_CONSOLE_ENABLED=false
|
|
51
|
+
log_console_enabled: bool = True
|
|
52
|
+
|
|
50
53
|
# Screenshot (log_img field) — desativar com JAYLOG_LOG_SCREENSHOT_ENABLED=false
|
|
51
54
|
log_screenshot_enabled: bool = False
|
|
52
55
|
|
|
53
56
|
@field_validator("log_dir", mode="after")
|
|
54
57
|
@classmethod
|
|
55
|
-
def validate_log_dir(cls, v: Path) -> Path:
|
|
56
|
-
if v.exists() and not v.is_dir():
|
|
58
|
+
def validate_log_dir(cls, v: Path | None) -> Path | None:
|
|
59
|
+
if v is not None and v.exists() and not v.is_dir():
|
|
57
60
|
raise ValueError(f"JAYLOG_LOG_DIR '{v}' exists but is not a directory")
|
|
58
61
|
return v
|
|
59
62
|
|
|
60
63
|
@computed_field
|
|
61
64
|
@property
|
|
62
|
-
def log_filename(self) -> Path:
|
|
65
|
+
def log_filename(self) -> Path | None:
|
|
66
|
+
if self.log_dir is None:
|
|
67
|
+
return None
|
|
63
68
|
return Path(f"{self.app_name}_{_HOSTNAME}_{_HOST_USERNAME}.log")
|
|
64
69
|
|
|
65
70
|
def reload_secrets(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|