jaylog 0.2.2__tar.gz → 0.2.5__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.5
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>
@@ -34,11 +34,12 @@ As variáveis usam o prefixo `JAYLOG_`. Podem ser definidas no ambiente do siste
34
34
  | Variável | obrigatório? | Padrão | Descrição |
35
35
  | ------------------------------- | ------------ | --------- | ----------------------------------------------------------------------- |
36
36
  | `JAYLOG_APP_NAME` | SIM | `null` | Nome do serviço/bot (usado no nome do arquivo de log) |
37
- | `JAYLOG_LOG_DIR` | SIM | `null` | Caminho do diretório onde os arquivos de log serão salvos |
37
+ | `JAYLOG_LOG_DIR` | NÃO | `null` | Caminho do diretório onde os arquivos de log serão salvos. Se omitido, o handler de arquivo é desativado |
38
38
  | `JAYLOG_LOG_LEVEL` | NÃO | `INFO` | Nível mínimo de log (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`) |
39
39
  | `JAYLOG_LOG_MAX_BYTES` | NÃO | `5242880` | Tamanho máximo do arquivo de log antes de rotacionar (bytes) |
40
40
  | `JAYLOG_LOG_BACKUP_COUNT` | NÃO | `5` | Quantidade de arquivos de backup mantidos após rotação |
41
41
  | `JAYLOG_LOG_RETENTION_DAYS` | NÃO | `7` | Dias para manter arquivos de log antigos |
42
+ | `JAYLOG_LOG_CONSOLE_ENABLED` | NÃO | `true` | Habilita saída colorida no console (`true`/`false`) |
42
43
  | `JAYLOG_LOG_HTTP_TIMEOUT` | NÃO | `5.0` | Timeout em segundos para o envio HTTP |
43
44
  | `JAYLOG_LOG_HTTP_ENDPOINT` | NÃO | `null` | URL do endpoint que receberá os logs |
44
45
  | `JAYLOG_LOG_HTTP_API_KEY` | NÃO | `null` | Chave de autenticação enviada no header `x-api-key` |
@@ -48,6 +49,10 @@ As variáveis usam o prefixo `JAYLOG_`. Podem ser definidas no ambiente do siste
48
49
 
49
50
  ## Como usar?
50
51
 
52
+ > [!IMPORTANT]
53
+ > A partir da versão 0.2.2, `configure()` **deve** ser chamado antes de `get_logger()`.
54
+ > Chamar `get_logger()` sem configuração prévia lança uma exceção.
55
+
51
56
  Existem alguns cenários diferentes onde a utilização desse lib pode mudar, abaixo estão os cenários mapeados e como realizar configuração para cada um.
52
57
 
53
58
  ## Arquivo único
@@ -59,13 +64,34 @@ JAYLOG_LOG_DIR=C:\logs
59
64
  ```
60
65
  __*main.py*__
61
66
  ```python
62
- from jaylog import JaylogSettings, get_logger
67
+ from jaylog import JaylogSettings, configure, get_logger
63
68
 
64
- logger = get_logger(JaylogSettings())
69
+ configure(JaylogSettings())
70
+
71
+ logger = get_logger()
65
72
 
66
73
  logger.info("Arquivo Único")
67
74
  ```
68
75
 
76
+ ## Apenas console (sem arquivo de log)
77
+
78
+ Basta omitir `JAYLOG_LOG_DIR`. O handler de console fica ativo por padrão.
79
+
80
+ __*.env.logging*__
81
+ ```env
82
+ JAYLOG_APP_NAME=meu-bot
83
+ ```
84
+ __*main.py*__
85
+ ```python
86
+ from jaylog import JaylogSettings, configure, get_logger
87
+
88
+ configure(JaylogSettings())
89
+
90
+ logger = get_logger()
91
+
92
+ logger.info("Saída apenas no console")
93
+ ```
94
+
69
95
  ## Múltiplos Arquivos
70
96
 
71
97
  __*.env.logging*__
@@ -79,8 +105,7 @@ __*main.py*__
79
105
  from jaylog import JaylogSettings, get_logger, configure
80
106
  from parse import parse_csv
81
107
 
82
- jaylog_settings = JaylogSettings()
83
- configure(jaylog_settings)
108
+ configure(JaylogSettings())
84
109
 
85
110
  logger = get_logger()
86
111
 
@@ -99,6 +124,31 @@ def parse_csv():
99
124
  logger.info("Múltiplos Arquivos - parse.py")
100
125
  ```
101
126
 
127
+ ## Múltiplos loggers nomeados
128
+
129
+ Quando o projeto possui serviços distintos, passe uma lista para `configure()`. Cada entrada usa seu próprio `app_name` e grava em arquivos separados. O campo `[service]` é exibido automaticamente no formato do log quando há mais de um logger registrado.
130
+
131
+ __*.env.logging*__
132
+ ```env
133
+ JAYLOG_LOG_DIR=C:\logs
134
+ ```
135
+
136
+ __*main.py*__
137
+ ```python
138
+ from jaylog import JaylogSettings, configure, get_logger
139
+
140
+ settings_order = JaylogSettings(app_name="ORDER-PROCESSOR")
141
+ settings_billing = JaylogSettings(app_name="BILLING")
142
+
143
+ configure([settings_order, settings_billing])
144
+
145
+ logger = get_logger("ORDER-PROCESSOR") # ou get_logger() — retorna o primeiro registrado
146
+ billing_logger = get_logger("BILLING")
147
+
148
+ logger.info("Pedido recebido")
149
+ billing_logger.info("Fatura emitida")
150
+ ```
151
+
102
152
 
103
153
 
104
154
  ## Alterando Caminho padrão do .env
@@ -111,10 +161,10 @@ JAYLOG_LOG_DIR=C:\logs
111
161
 
112
162
  __*main.py*__
113
163
  ```python
114
- from jaylog import JaylogSettings, get_logger
164
+ from jaylog import JaylogSettings, configure, get_logger
115
165
 
116
- settings = JaylogSettings(_env_file='development.env')
117
- logger = get_logger(settings)
166
+ configure(JaylogSettings(_env_file='development.env'))
167
+ logger = get_logger()
118
168
 
119
169
  logger.info("Alterando Caminho padrão do .env")
120
170
  ```
@@ -152,13 +202,13 @@ JAYLOG_LOG_HTTP_PROXY
152
202
 
153
203
  __*main.py*__
154
204
  ```python
155
- from jaylog import JaylogSettings, get_logger
205
+ from jaylog import JaylogSettings, configure, get_logger
156
206
 
157
- settings = JaylogSettings(
207
+ configure(JaylogSettings(
158
208
  _env_file='prodution.env',
159
209
  _secrets_dir='/foo/bar/secrets/'
160
- )
161
- logger = get_logger(settings)
210
+ ))
211
+ logger = get_logger()
162
212
 
163
213
  logger.info("Mensagem de log")
164
214
  ```
@@ -174,12 +224,11 @@ JAYLOG_SECRETS_DIR=/foo/bar/secrets
174
224
 
175
225
  __*main.py*__
176
226
  ```python
177
- from jaylog import JaylogSettings, get_logger
227
+ from jaylog import JaylogSettings, configure, get_logger
178
228
 
179
229
  # nesse caso é necessário usar a função de classe `reload_secrets`
180
230
  # pois o diretorio dos secrets foi passado via variável de ambiente
181
- settings = JaylogSettings(_env_file='prodution.env').reload_secrets()
182
-
183
- logger = get_logger(settings)
231
+ configure(JaylogSettings(_env_file='prodution.env').reload_secrets())
232
+ logger = get_logger()
184
233
 
185
234
  logger.info("Mensagem de log")
@@ -15,11 +15,12 @@ As variáveis usam o prefixo `JAYLOG_`. Podem ser definidas no ambiente do siste
15
15
  | Variável | obrigatório? | Padrão | Descrição |
16
16
  | ------------------------------- | ------------ | --------- | ----------------------------------------------------------------------- |
17
17
  | `JAYLOG_APP_NAME` | SIM | `null` | Nome do serviço/bot (usado no nome do arquivo de log) |
18
- | `JAYLOG_LOG_DIR` | SIM | `null` | Caminho do diretório onde os arquivos de log serão salvos |
18
+ | `JAYLOG_LOG_DIR` | NÃO | `null` | Caminho do diretório onde os arquivos de log serão salvos. Se omitido, o handler de arquivo é desativado |
19
19
  | `JAYLOG_LOG_LEVEL` | NÃO | `INFO` | Nível mínimo de log (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`) |
20
20
  | `JAYLOG_LOG_MAX_BYTES` | NÃO | `5242880` | Tamanho máximo do arquivo de log antes de rotacionar (bytes) |
21
21
  | `JAYLOG_LOG_BACKUP_COUNT` | NÃO | `5` | Quantidade de arquivos de backup mantidos após rotação |
22
22
  | `JAYLOG_LOG_RETENTION_DAYS` | NÃO | `7` | Dias para manter arquivos de log antigos |
23
+ | `JAYLOG_LOG_CONSOLE_ENABLED` | NÃO | `true` | Habilita saída colorida no console (`true`/`false`) |
23
24
  | `JAYLOG_LOG_HTTP_TIMEOUT` | NÃO | `5.0` | Timeout em segundos para o envio HTTP |
24
25
  | `JAYLOG_LOG_HTTP_ENDPOINT` | NÃO | `null` | URL do endpoint que receberá os logs |
25
26
  | `JAYLOG_LOG_HTTP_API_KEY` | NÃO | `null` | Chave de autenticação enviada no header `x-api-key` |
@@ -29,6 +30,10 @@ As variáveis usam o prefixo `JAYLOG_`. Podem ser definidas no ambiente do siste
29
30
 
30
31
  ## Como usar?
31
32
 
33
+ > [!IMPORTANT]
34
+ > A partir da versão 0.2.2, `configure()` **deve** ser chamado antes de `get_logger()`.
35
+ > Chamar `get_logger()` sem configuração prévia lança uma exceção.
36
+
32
37
  Existem alguns cenários diferentes onde a utilização desse lib pode mudar, abaixo estão os cenários mapeados e como realizar configuração para cada um.
33
38
 
34
39
  ## Arquivo único
@@ -40,13 +45,34 @@ JAYLOG_LOG_DIR=C:\logs
40
45
  ```
41
46
  __*main.py*__
42
47
  ```python
43
- from jaylog import JaylogSettings, get_logger
48
+ from jaylog import JaylogSettings, configure, get_logger
44
49
 
45
- logger = get_logger(JaylogSettings())
50
+ configure(JaylogSettings())
51
+
52
+ logger = get_logger()
46
53
 
47
54
  logger.info("Arquivo Único")
48
55
  ```
49
56
 
57
+ ## Apenas console (sem arquivo de log)
58
+
59
+ Basta omitir `JAYLOG_LOG_DIR`. O handler de console fica ativo por padrão.
60
+
61
+ __*.env.logging*__
62
+ ```env
63
+ JAYLOG_APP_NAME=meu-bot
64
+ ```
65
+ __*main.py*__
66
+ ```python
67
+ from jaylog import JaylogSettings, configure, get_logger
68
+
69
+ configure(JaylogSettings())
70
+
71
+ logger = get_logger()
72
+
73
+ logger.info("Saída apenas no console")
74
+ ```
75
+
50
76
  ## Múltiplos Arquivos
51
77
 
52
78
  __*.env.logging*__
@@ -60,8 +86,7 @@ __*main.py*__
60
86
  from jaylog import JaylogSettings, get_logger, configure
61
87
  from parse import parse_csv
62
88
 
63
- jaylog_settings = JaylogSettings()
64
- configure(jaylog_settings)
89
+ configure(JaylogSettings())
65
90
 
66
91
  logger = get_logger()
67
92
 
@@ -80,6 +105,31 @@ def parse_csv():
80
105
  logger.info("Múltiplos Arquivos - parse.py")
81
106
  ```
82
107
 
108
+ ## Múltiplos loggers nomeados
109
+
110
+ Quando o projeto possui serviços distintos, passe uma lista para `configure()`. Cada entrada usa seu próprio `app_name` e grava em arquivos separados. O campo `[service]` é exibido automaticamente no formato do log quando há mais de um logger registrado.
111
+
112
+ __*.env.logging*__
113
+ ```env
114
+ JAYLOG_LOG_DIR=C:\logs
115
+ ```
116
+
117
+ __*main.py*__
118
+ ```python
119
+ from jaylog import JaylogSettings, configure, get_logger
120
+
121
+ settings_order = JaylogSettings(app_name="ORDER-PROCESSOR")
122
+ settings_billing = JaylogSettings(app_name="BILLING")
123
+
124
+ configure([settings_order, settings_billing])
125
+
126
+ logger = get_logger("ORDER-PROCESSOR") # ou get_logger() — retorna o primeiro registrado
127
+ billing_logger = get_logger("BILLING")
128
+
129
+ logger.info("Pedido recebido")
130
+ billing_logger.info("Fatura emitida")
131
+ ```
132
+
83
133
 
84
134
 
85
135
  ## Alterando Caminho padrão do .env
@@ -92,10 +142,10 @@ JAYLOG_LOG_DIR=C:\logs
92
142
 
93
143
  __*main.py*__
94
144
  ```python
95
- from jaylog import JaylogSettings, get_logger
145
+ from jaylog import JaylogSettings, configure, get_logger
96
146
 
97
- settings = JaylogSettings(_env_file='development.env')
98
- logger = get_logger(settings)
147
+ configure(JaylogSettings(_env_file='development.env'))
148
+ logger = get_logger()
99
149
 
100
150
  logger.info("Alterando Caminho padrão do .env")
101
151
  ```
@@ -133,13 +183,13 @@ JAYLOG_LOG_HTTP_PROXY
133
183
 
134
184
  __*main.py*__
135
185
  ```python
136
- from jaylog import JaylogSettings, get_logger
186
+ from jaylog import JaylogSettings, configure, get_logger
137
187
 
138
- settings = JaylogSettings(
188
+ configure(JaylogSettings(
139
189
  _env_file='prodution.env',
140
190
  _secrets_dir='/foo/bar/secrets/'
141
- )
142
- logger = get_logger(settings)
191
+ ))
192
+ logger = get_logger()
143
193
 
144
194
  logger.info("Mensagem de log")
145
195
  ```
@@ -155,12 +205,11 @@ JAYLOG_SECRETS_DIR=/foo/bar/secrets
155
205
 
156
206
  __*main.py*__
157
207
  ```python
158
- from jaylog import JaylogSettings, get_logger
208
+ from jaylog import JaylogSettings, configure, get_logger
159
209
 
160
210
  # nesse caso é necessário usar a função de classe `reload_secrets`
161
211
  # pois o diretorio dos secrets foi passado via variável de ambiente
162
- settings = JaylogSettings(_env_file='prodution.env').reload_secrets()
163
-
164
- logger = get_logger(settings)
212
+ configure(JaylogSettings(_env_file='prodution.env').reload_secrets())
213
+ logger = get_logger()
165
214
 
166
215
  logger.info("Mensagem de log")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jaylog"
3
- version = "0.2.2"
3
+ version = "0.2.5"
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']).astimezone().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"]).astimezone().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