jaylog 0.2.4__tar.gz → 0.2.6__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.4
3
+ Version: 0.2.6
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.4"
3
+ version = "0.2.6"
4
4
  description = "Customized Python logging library with file rotation and HTTP forwarding"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -93,6 +93,7 @@ def build_log_entry_dict(record: logging.LogRecord) -> dict:
93
93
  "hostname": _HOSTNAME,
94
94
  "ipv4": _HOST_IP,
95
95
  "service_path": record.pathname,
96
+ "line_number": record.lineno,
96
97
  "log_img": _capture_screenshot() if (record.levelno >= logging.ERROR or is_exception) else None,
97
98
  }
98
99
 
@@ -106,7 +107,7 @@ class PlainTextFormatter(logging.Formatter):
106
107
 
107
108
  def format(self, record: logging.LogRecord) -> str:
108
109
  entry = build_log_entry_dict(record)
109
- log_timestamp = datetime.fromisoformat(entry['log_timestamp']).strftime('%d/%m/%Y %X')
110
+ log_timestamp = datetime.fromisoformat(entry['log_timestamp']).astimezone().strftime('%d/%m/%Y %X')
110
111
  log_level = f'[{entry["log_level"]}]'
111
112
  service_segment = f'[{entry["service"]}] | ' if self.show_service else ''
112
113
  return (
@@ -29,7 +29,7 @@ class ConsoleFormatter(logging.Formatter):
29
29
 
30
30
  def format(self, record: logging.LogRecord) -> str:
31
31
  entry = build_log_entry_dict(record)
32
- log_timestamp = datetime.fromisoformat(entry["log_timestamp"]).strftime("%d/%m/%Y %X")
32
+ log_timestamp = datetime.fromisoformat(entry["log_timestamp"]).astimezone().strftime("%d/%m/%Y %X")
33
33
  log_level = f'[{entry["log_level"]}]'
34
34
  colored_timestamp = f"{_GREEN}{log_timestamp}{_RESET}"
35
35
  colored_level = f"{_level_color(entry['log_level'])}{log_level.ljust(11)}{_RESET}"
@@ -50,7 +50,7 @@ class JaylogHttpHandler(logging.Handler):
50
50
  super().__init__()
51
51
  self.endpoint = endpoint
52
52
  self.timeout = timeout
53
- self.proxy = {'http': proxy, 'https': proxy}
53
+ self.proxy = {'http': proxy, 'https': proxy} if proxy else None
54
54
  self._session = requests.Session()
55
55
  self._session.headers["x-api-key"] = api_key
56
56
  self._session.headers["x-jaylog-version"] = _JAYLOG_VERSION
@@ -3,6 +3,7 @@ import logging
3
3
  import signal
4
4
  from logging.handlers import QueueHandler, QueueListener
5
5
  from queue import Queue
6
+ from typing import cast
6
7
 
7
8
  from jaylog.filters import ExceptionFlagFilter
8
9
  from jaylog.formatters import configure_screenshot
@@ -74,12 +75,26 @@ def get_logger(name: str | None = None) -> logging.Logger:
74
75
  configuração registrada. Chamadas repetidas com o mesmo nome retornam o
75
76
  **mesmo** logger, sem re-anexar handlers.
76
77
 
78
+ Se ``configure()`` ainda não tiver sido chamado, retorna um proxy
79
+ preguiçoso: nenhum erro é levantado aqui. O erro só aparece no primeiro
80
+ uso efetivo (``logger.info(...)``, ``logger.warning(...)`` etc.), o que
81
+ permite fazer ``logger = get_logger()`` no topo de um módulo antes de
82
+ ``configure()`` ter rodado em outro lugar.
83
+
77
84
  Architecture:
78
85
  logger → QueueHandler → Queue → QueueListener → [FileHandler, HttpHandler?]
79
86
 
80
87
  The QueueListener runs in a background thread so `emit()` never blocks the
81
88
  calling thread.
82
89
  """
90
+ if not _settings_registry:
91
+ # _LazyLogger não é um logging.Logger de verdade: é um proxy que
92
+ # delega (via __getattr__) para o logger real assim que ele existir.
93
+ return cast(logging.Logger, _LazyLogger(name))
94
+ return _build_logger(name)
95
+
96
+
97
+ def _build_logger(name: str | None) -> logging.Logger:
83
98
  if not _settings_registry:
84
99
  raise Exception(
85
100
  'Não é possivel retornar uma instancia de logger sem configuração\n'
@@ -109,6 +124,7 @@ def get_logger(name: str | None = None) -> logging.Logger:
109
124
  show_service = len(_settings_registry) > 1
110
125
 
111
126
  if settings.log_dir is not None:
127
+ assert settings.log_filename is not None
112
128
  log_path = settings.log_dir / settings.log_filename
113
129
  file_handler = JaylogFileHandler(
114
130
  filename=log_path,
@@ -158,6 +174,27 @@ def get_logger(name: str | None = None) -> logging.Logger:
158
174
  return logger
159
175
 
160
176
 
177
+ class _LazyLogger:
178
+ """
179
+ Proxy devolvido por ``get_logger()`` quando ``configure()`` ainda não
180
+ rodou. Resolve o logger real (e levanta o erro de configuração ausente,
181
+ se for o caso) apenas no primeiro atributo acessado — ou seja, na
182
+ primeira chamada de ``.info()``, ``.warning()`` etc.
183
+ """
184
+
185
+ def __init__(self, name: str | None) -> None:
186
+ self._name = name
187
+ self._resolved: logging.Logger | None = None
188
+
189
+ def _resolve(self) -> logging.Logger:
190
+ if self._resolved is None:
191
+ self._resolved = _build_logger(self._name)
192
+ return self._resolved
193
+
194
+ def __getattr__(self, item):
195
+ return getattr(self._resolve(), item)
196
+
197
+
161
198
  def shutdown(name: str | None = None) -> None:
162
199
  """
163
200
  Stop the QueueListener(s) gracefully, flushing any remaining records.
@@ -13,4 +13,5 @@ class LogEntry(BaseModel):
13
13
  hostname: str
14
14
  ipv4: str
15
15
  service_path: str
16
+ line_number: int
16
17
  log_img: Optional[str] = None
File without changes
File without changes
File without changes