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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: jaylog
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Customized Python logging library with file rotation and HTTP forwarding
5
5
  Author: Gpocas
6
6
  Author-email: Gpocas <gpocas01@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jaylog"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  description = "Customized Python logging library with file rotation and HTTP forwarding"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -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
- line = (
106
- f"{entry['log_timestamp']} [{entry['log_level']}]"
107
- f" {entry['service']} {entry['hostname']}({entry['ipv4']}) {entry['username']}"
108
- f" | {entry['log_message']}"
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,5 @@
1
+ from .console_handler import JaylogConsoleHandler
2
+ from .file_handler import JaylogFileHandler
3
+ from .http_handler import JaylogHttpHandler
4
+
5
+ __all__ = ["JaylogConsoleHandler", "JaylogFileHandler", "JaylogHttpHandler"]
@@ -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
- log_path = settings.log_dir / settings.log_filename
110
- file_handler = JaylogFileHandler(
111
- filename=log_path,
112
- max_bytes=settings.log_max_bytes,
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):
@@ -1,4 +0,0 @@
1
- from .file_handler import JaylogFileHandler
2
- from .http_handler import JaylogHttpHandler
3
-
4
- __all__ = ["JaylogFileHandler", "JaylogHttpHandler"]
File without changes
File without changes
File without changes
File without changes