jaylog 0.3.2__tar.gz → 0.3.3__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.
Files changed (41) hide show
  1. {jaylog-0.3.2 → jaylog-0.3.3}/PKG-INFO +13 -1
  2. jaylog-0.3.3/README.md +42 -0
  3. jaylog-0.3.3/pyproject.toml +58 -0
  4. jaylog-0.3.2/pyproject.toml → jaylog-0.3.3/pyproject.toml.orig +1 -1
  5. jaylog-0.3.3/src/jaylog/_http.py +139 -0
  6. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/http_handler.py +10 -23
  7. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/heartbeat_reporter.py +11 -16
  8. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/metrics_reporter.py +10 -21
  9. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/reporter.py +10 -22
  10. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/logger.py +27 -7
  11. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/settings.py +13 -6
  12. jaylog-0.3.2/README.md +0 -30
  13. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/__init__.py +0 -0
  14. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/_compat.py +0 -0
  15. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/_version.py +0 -0
  16. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/colors.py +0 -0
  17. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/context.py +0 -0
  18. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/diagnostics.py +0 -0
  19. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/endpoints.py +0 -0
  20. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/filters.py +0 -0
  21. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/formatters.py +0 -0
  22. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/__init__.py +0 -0
  23. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/console_handler.py +0 -0
  24. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/file_handler.py +0 -0
  25. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/__init__.py +0 -0
  26. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/_safe.py +0 -0
  27. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/collectors.py +0 -0
  28. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_execution.py +0 -0
  29. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_git.py +0 -0
  30. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_python.py +0 -0
  31. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_schedule.py +0 -0
  32. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_venv.py +0 -0
  33. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/identity.py +0 -0
  34. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/metrics.py +0 -0
  35. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/models.py +0 -0
  36. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/payload.py +0 -0
  37. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/schedule_parser.py +0 -0
  38. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/schedule_reporter.py +0 -0
  39. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/win32.py +0 -0
  40. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/runtime.py +0 -0
  41. {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/screenshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: jaylog
3
- Version: 0.3.2
3
+ Version: 0.3.3
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>
@@ -48,3 +48,15 @@ logger = get_logger()
48
48
 
49
49
  logger.info("Olá, jaylog!")
50
50
  ```
51
+
52
+ ## Certificado TLS (HTTPS)
53
+
54
+ `JAYLOG_LOG_HTTP_VERIFY` controla a verificação do certificado em todos os envios HTTP (logs, host, métricas, heartbeat e agendas):
55
+
56
+ | Valor | Efeito |
57
+ |---|---|
58
+ | `true` (`1`, `yes`, `on`) | Verifica com o bundle padrão do `requests` (`certifi`, ou `REQUESTS_CA_BUNDLE` se definida) |
59
+ | `/caminho/corp-ca.pem` | Verifica com esse CA bundle — use para proxy corporativo com inspeção TLS. O caminho precisa existir: senão `JaylogSettings()` falha na inicialização |
60
+ | `false` (`0`, `no`, `off`) | Não verifica (inseguro); o jaylog avisa no stderr |
61
+
62
+ > **0.3.x:** o padrão ainda é `false` por compatibilidade e passa a ser `true` na **0.4.0**. Para antecipar, defina `JAYLOG_LOG_HTTP_VERIFY=true` ou o caminho do bundle corporativo.
jaylog-0.3.3/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # jaylog
2
+
3
+ Biblioteca de logging para Python com rotação de arquivos, saída colorida no console, envio HTTP para um endpoint remoto e registro automático do ambiente de execução (host reporting).
4
+
5
+ 📖 **Documentação completa:** https://gpocas.github.io/jaylog-book
6
+
7
+ ## Instalação
8
+
9
+ ```bash
10
+ pip install -U --no-cache-dir jaylog
11
+ ```
12
+
13
+ ## Uso rápido
14
+
15
+ __*.env.logging*__
16
+ ```env
17
+ JAYLOG_APP_NAME=meu-bot
18
+ JAYLOG_LOG_DIR=C:\logs
19
+ ```
20
+
21
+ __*main.py*__
22
+ ```python
23
+ from jaylog import JaylogSettings, configure, get_logger
24
+
25
+ configure(JaylogSettings())
26
+
27
+ logger = get_logger()
28
+
29
+ logger.info("Olá, jaylog!")
30
+ ```
31
+
32
+ ## Certificado TLS (HTTPS)
33
+
34
+ `JAYLOG_LOG_HTTP_VERIFY` controla a verificação do certificado em todos os envios HTTP (logs, host, métricas, heartbeat e agendas):
35
+
36
+ | Valor | Efeito |
37
+ |---|---|
38
+ | `true` (`1`, `yes`, `on`) | Verifica com o bundle padrão do `requests` (`certifi`, ou `REQUESTS_CA_BUNDLE` se definida) |
39
+ | `/caminho/corp-ca.pem` | Verifica com esse CA bundle — use para proxy corporativo com inspeção TLS. O caminho precisa existir: senão `JaylogSettings()` falha na inicialização |
40
+ | `false` (`0`, `no`, `off`) | Não verifica (inseguro); o jaylog avisa no stderr |
41
+
42
+ > **0.3.x:** o padrão ainda é `false` por compatibilidade e passa a ser `true` na **0.4.0**. Para antecipar, defina `JAYLOG_LOG_HTTP_VERIFY=true` ou o caminho do bundle corporativo.
@@ -0,0 +1,58 @@
1
+ [project]
2
+ name = "jaylog"
3
+ version = "0.3.3"
4
+ description = "Customized Python logging library with file rotation and HTTP forwarding"
5
+ readme = "README.md"
6
+ classifiers = [
7
+ "Topic :: Software Development :: Build Tools",
8
+ "Topic :: Software Development :: Libraries :: Python Modules",
9
+ "Natural Language :: Portuguese (Brazilian)",
10
+ "Development Status :: 5 - Production/Stable",
11
+ ]
12
+ requires-python = ">=3.10,<3.14"
13
+ dependencies = [
14
+ "pydantic>=2.0,<3.0",
15
+ "pydantic-settings>=2.0,<3.0",
16
+ "requests>=2.32,<3.0",
17
+ "pillow>=11.0,<13.0",
18
+ "psutil>=5.9.6,<8",
19
+ ]
20
+
21
+ [[project.authors]]
22
+ name = "Gpocas"
23
+ email = "gpocas01@gmail.com"
24
+
25
+ [project.urls]
26
+ repository = "https://github.com/Gpocas/jaylog"
27
+ "Bug Tracker" = "https://github.com/Gpocas/jaylog/issues"
28
+
29
+ [dependency-groups]
30
+ dev = [
31
+ "pytest>=8,<9",
32
+ "ruff>=0.6",
33
+ ]
34
+
35
+ [build-system]
36
+ requires = ["uv_build>=0.10.8,<0.11.0"]
37
+ build-backend = "uv_build"
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
41
+ addopts = "-q"
42
+
43
+ [tool.ruff]
44
+ line-length = 100
45
+ src = [
46
+ "src",
47
+ "tests",
48
+ ]
49
+
50
+ [tool.ruff.lint]
51
+ select = [
52
+ "E",
53
+ "F",
54
+ "I",
55
+ "UP",
56
+ "B",
57
+ ]
58
+ ignore = ["E501"]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jaylog"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Customized Python logging library with file rotation and HTTP forwarding"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,139 @@
1
+ """
2
+ Transporte HTTP compartilhado pelos clientes do jaylog (log, host, métricas,
3
+ heartbeat e agendas): verificação TLS, proxy, headers de protocolo e o aviso do
4
+ urllib3 sobre requisições não verificadas.
5
+
6
+ Tudo que diz respeito a ``verify`` mora aqui para que a troca do padrão (0.4.0)
7
+ aconteça num único lugar.
8
+ """
9
+
10
+ import re
11
+ import threading
12
+ import warnings
13
+ from pathlib import Path
14
+ from urllib.parse import urlsplit
15
+
16
+ import requests
17
+ import urllib3
18
+
19
+ from jaylog._version import PROTOCOL_VERSION, __version__
20
+ from jaylog.runtime import RUN_ID
21
+
22
+ #: ``False`` na 0.3.x por compatibilidade (ver ``JaylogSettings.log_http_verify``);
23
+ #: passa a ser ``True`` na 0.4.0. É o padrão de *todos* os clientes HTTP.
24
+ DEFAULT_VERIFY: bool = False
25
+
26
+ # Os mesmos conjuntos que o pydantic aceita para ``bool``.
27
+ _TRUE_VALUES = frozenset({"1", "on", "t", "true", "y", "yes"})
28
+ _FALSE_VALUES = frozenset({"0", "off", "f", "false", "n", "no"})
29
+
30
+
31
+ def parse_verify(value: object) -> bool | str:
32
+ """
33
+ Normaliza o valor de ``log_http_verify``.
34
+
35
+ Vindo de variável de ambiente tudo é texto, e o ``requests`` trata qualquer
36
+ ``str`` como caminho de CA bundle: ``"true"`` virava
37
+ ``OSError: invalid path: true`` em toda requisição. Aqui os textos booleanos
38
+ viram ``bool`` e o resto precisa ser um caminho existente (arquivo PEM ou
39
+ diretório preparado com ``c_rehash``), para que o erro apareça na
40
+ configuração e não em silêncio no envio.
41
+ """
42
+ if isinstance(value, bool):
43
+ return value
44
+ if isinstance(value, Path):
45
+ value = str(value)
46
+ if not isinstance(value, str):
47
+ raise ValueError(
48
+ f"log_http_verify deve ser true, false ou o caminho de um CA bundle; "
49
+ f"recebido {type(value).__name__}"
50
+ )
51
+
52
+ text = value.strip()
53
+ if text.lower() in _TRUE_VALUES:
54
+ return True
55
+ if text.lower() in _FALSE_VALUES:
56
+ return False
57
+ if not text:
58
+ raise ValueError("log_http_verify vazio; use true, false ou o caminho de um CA bundle")
59
+
60
+ path = Path(text).expanduser()
61
+ if not path.exists():
62
+ raise ValueError(f"log_http_verify: CA bundle não encontrado: {path}")
63
+ return str(path)
64
+
65
+
66
+ def jaylog_headers(api_key: str) -> dict[str, str]:
67
+ """Headers de autenticação e protocolo enviados em todo POST do jaylog."""
68
+ return {
69
+ "x-api-key": api_key,
70
+ "x-jaylog-version": __version__,
71
+ "x-jaylog-protocol": str(PROTOCOL_VERSION),
72
+ "x-jaylog-run-id": RUN_ID,
73
+ }
74
+
75
+
76
+ def proxies_for(proxy: str | None) -> dict[str, str] | None:
77
+ """Mapa de proxies do ``requests`` para um único proxy (ou ``None``)."""
78
+ return {"http": proxy, "https": proxy} if proxy else None
79
+
80
+
81
+ def prepare_session(api_key: str, proxy: str | None = None, session=None):
82
+ """
83
+ Sessão com os headers do jaylog e o proxy já aplicados.
84
+
85
+ ``verify`` **não** é fixado na sessão de propósito: com ``session.verify``
86
+ o ``requests`` deixa ``REQUESTS_CA_BUNDLE`` sobrepor um ``False`` explícito.
87
+ Os clientes passam ``verify=`` em cada ``post()``.
88
+ """
89
+ if session is None:
90
+ session = requests.Session()
91
+ session.headers.update(jaylog_headers(api_key))
92
+ proxies = proxies_for(proxy)
93
+ if proxies:
94
+ session.proxies.update(proxies)
95
+ return session
96
+
97
+
98
+ _silenced_hosts: set[str] = set()
99
+ _silence_lock = threading.Lock()
100
+
101
+
102
+ def silence_insecure_warning(endpoint: str | None, verify: bool | str) -> None:
103
+ """
104
+ Silencia o ``InsecureRequestWarning`` do urllib3 só para o host do endpoint,
105
+ e só quando a verificação está desligada.
106
+
107
+ Substitui o ``warnings.catch_warnings()`` que envolvia cada POST: ele troca
108
+ ``warnings.filters`` do processo inteiro e não é thread-safe — com cinco
109
+ threads do jaylog fazendo isso ao mesmo tempo, os filtros da aplicação
110
+ podiam ser perdidos ou ficar com o "ignore" vazado. Aqui é um filtro
111
+ permanente, instalado uma vez, restrito à mensagem do host do jaylog: o
112
+ aviso continua valendo para qualquer outro destino da aplicação.
113
+
114
+ Chamado na construção dos clientes, não no envio.
115
+ """
116
+ if verify is not False or not endpoint:
117
+ return
118
+ host = urlsplit(endpoint).hostname
119
+ if not host:
120
+ return
121
+ with _silence_lock:
122
+ if host in _silenced_hosts:
123
+ return
124
+ _silenced_hosts.add(host)
125
+ warnings.filterwarnings(
126
+ "ignore",
127
+ message=re.escape(f"Unverified HTTPS request is being made to host '{host}'"),
128
+ category=urllib3.exceptions.InsecureRequestWarning,
129
+ )
130
+
131
+
132
+ __all__ = [
133
+ "DEFAULT_VERIFY",
134
+ "jaylog_headers",
135
+ "parse_verify",
136
+ "prepare_session",
137
+ "proxies_for",
138
+ "silence_insecure_warning",
139
+ ]
@@ -1,15 +1,10 @@
1
1
  import json
2
2
  import logging
3
- import warnings
4
3
 
5
- import requests
6
- import urllib3
7
-
8
- from jaylog._version import PROTOCOL_VERSION, __version__
4
+ from jaylog._http import DEFAULT_VERIFY, prepare_session, silence_insecure_warning
9
5
  from jaylog.context import record_entry, record_screenshot
10
6
  from jaylog.diagnostics import emit as debug
11
7
  from jaylog.host import reporter
12
- from jaylog.runtime import RUN_ID
13
8
 
14
9
  #: O backend sinaliza por header que não conhece este `run_id` — normalmente
15
10
  #: porque o log correu na frente do POST de host. Ver `reporter.request_resend`.
@@ -52,18 +47,14 @@ class JaylogHttpHandler(logging.Handler):
52
47
  api_key: str,
53
48
  timeout: float = 5.0,
54
49
  proxy: str | None = None,
55
- verify: bool | str = False,
50
+ verify: bool | str = DEFAULT_VERIFY,
56
51
  ) -> None:
57
52
  super().__init__()
58
53
  self.endpoint = endpoint
59
54
  self.timeout = timeout
60
55
  self.verify = verify
61
- self.proxy = {"http": proxy, "https": proxy} if proxy else None
62
- self._session = requests.Session()
63
- self._session.headers["x-api-key"] = api_key
64
- self._session.headers["x-jaylog-version"] = __version__
65
- self._session.headers["x-jaylog-protocol"] = str(PROTOCOL_VERSION)
66
- self._session.headers["x-jaylog-run-id"] = RUN_ID
56
+ self._session = prepare_session(api_key, proxy)
57
+ silence_insecure_warning(endpoint, verify)
67
58
 
68
59
  def mapLogRecord(self, record: logging.LogRecord) -> dict:
69
60
  # Único ponto do programa que pede screenshot: é o único com para onde
@@ -75,8 +66,6 @@ class JaylogHttpHandler(logging.Handler):
75
66
  def emit(self, record: logging.LogRecord) -> None:
76
67
  try:
77
68
  diagnostic_record = bool(getattr(record, "jaylog_diagnostic", False))
78
- if self.proxy:
79
- self._session.proxies.update(self.proxy)
80
69
 
81
70
  fields = self.mapLogRecord(record)
82
71
  if not diagnostic_record:
@@ -86,14 +75,12 @@ class JaylogHttpHandler(logging.Handler):
86
75
  f"serviço={record.name}; nível={record.levelname}; "
87
76
  f"captura_anexada={fields.get('log_img') is not None}",
88
77
  )
89
- with warnings.catch_warnings():
90
- warnings.simplefilter("ignore", urllib3.exceptions.InsecureRequestWarning)
91
- response = self._session.post(
92
- self.endpoint,
93
- files=_to_multipart(fields),
94
- timeout=self.timeout,
95
- verify=self.verify,
96
- )
78
+ response = self._session.post(
79
+ self.endpoint,
80
+ files=_to_multipart(fields),
81
+ timeout=self.timeout,
82
+ verify=self.verify,
83
+ )
97
84
  if not diagnostic_record:
98
85
  debug(
99
86
  "http",
@@ -19,13 +19,11 @@ Sem buffer e sem backoff próprio: o ciclo já é de 60 s, e o único beat que i
19
19
  import sys
20
20
  import threading
21
21
  import time
22
- import warnings
23
22
  from dataclasses import dataclass
24
23
 
25
24
  import requests
26
- import urllib3
27
25
 
28
- from jaylog._version import PROTOCOL_VERSION, __version__
26
+ from jaylog._http import jaylog_headers, proxies_for, silence_insecure_warning
29
27
  from jaylog.diagnostics import emit as debug
30
28
  from jaylog.host import reporter as host_reporter
31
29
  from jaylog.runtime import RUN_ID
@@ -48,8 +46,9 @@ class HeartbeatTarget:
48
46
  interval: float = _DEFAULT_INTERVAL
49
47
  timeout: float = 5.0
50
48
  proxy: str | None = None
51
- #: seguro por padrão; quem decide o valor real é o `log_http_verify` do item,
52
- #: passado por `configure()` (hoje `False` por compatibilidade, `True` na 0.4.0)
49
+ #: seguro por padrão: o valor real vem do `log_http_verify` do item, sempre
50
+ #: passado por `configure()`. Não usa `DEFAULT_VERIFY` (False na 0.3.x) para
51
+ #: não rebaixar um padrão que já era seguro.
53
52
  verify: bool | str = True
54
53
 
55
54
 
@@ -104,6 +103,8 @@ class JaylogHeartbeatReporter:
104
103
 
105
104
  def register(self, targets: list[HeartbeatTarget]) -> None:
106
105
  """Substitui os alvos e descarta todo o estado anterior."""
106
+ for target in targets:
107
+ silence_insecure_warning(target.endpoint, target.verify)
107
108
  with self._lock:
108
109
  self._generation += 1
109
110
  self._targets = {t.service: t for t in targets}
@@ -241,17 +242,13 @@ class JaylogHeartbeatReporter:
241
242
  self._session = requests.Session()
242
243
  kwargs: dict = {
243
244
  "json": {"run_id": RUN_ID, "service": target.service},
244
- "headers": {
245
- "x-api-key": target.api_key,
246
- "x-jaylog-version": __version__,
247
- "x-jaylog-protocol": str(PROTOCOL_VERSION),
248
- "x-jaylog-run-id": RUN_ID,
249
- },
245
+ "headers": jaylog_headers(target.api_key),
250
246
  "timeout": timeout,
251
247
  "verify": target.verify,
252
248
  }
253
- if target.proxy:
254
- kwargs["proxies"] = {"http": target.proxy, "https": target.proxy}
249
+ proxies = proxies_for(target.proxy)
250
+ if proxies:
251
+ kwargs["proxies"] = proxies
255
252
 
256
253
  try:
257
254
  debug(
@@ -259,9 +256,7 @@ class JaylogHeartbeatReporter:
259
256
  f"requisição HTTP iniciada; método=POST; endpoint={target.endpoint}; "
260
257
  f"serviço={target.service}; timeout={timeout:g}s",
261
258
  )
262
- with warnings.catch_warnings():
263
- warnings.simplefilter("ignore", urllib3.exceptions.InsecureRequestWarning)
264
- response = self._session.post(target.endpoint, **kwargs)
259
+ response = self._session.post(target.endpoint, **kwargs)
265
260
  except Exception as exc:
266
261
  debug(
267
262
  "heartbeat",
@@ -15,13 +15,9 @@ amostras no buffer para o próximo ciclo; o buffer é limitado a 1 h.
15
15
  import sys
16
16
  import threading
17
17
  import time
18
- import warnings
19
18
  from collections import deque
20
19
 
21
- import requests
22
- import urllib3
23
-
24
- from jaylog._version import PROTOCOL_VERSION, __version__
20
+ from jaylog._http import DEFAULT_VERIFY, prepare_session, silence_insecure_warning
25
21
  from jaylog.diagnostics import emit as debug
26
22
  from jaylog.host import reporter as host_reporter
27
23
  from jaylog.runtime import RUN_ID
@@ -67,7 +63,7 @@ class JaylogMetricsReporter:
67
63
  interval: float = 60.0,
68
64
  timeout: float = 5.0,
69
65
  proxy: str | None = None,
70
- verify: bool | str = False,
66
+ verify: bool | str = DEFAULT_VERIFY,
71
67
  session=None,
72
68
  sampler=None,
73
69
  clock=time.time,
@@ -93,13 +89,8 @@ class JaylogMetricsReporter:
93
89
  self._stop = threading.Event()
94
90
  self._thread: threading.Thread | None = None
95
91
 
96
- self._session = session if session is not None else requests.Session()
97
- self._session.headers["x-api-key"] = api_key
98
- self._session.headers["x-jaylog-version"] = __version__
99
- self._session.headers["x-jaylog-protocol"] = str(PROTOCOL_VERSION)
100
- self._session.headers["x-jaylog-run-id"] = RUN_ID
101
- if proxy:
102
- self._session.proxies.update({"http": proxy, "https": proxy})
92
+ self._session = prepare_session(api_key, proxy, session)
93
+ silence_insecure_warning(endpoint, verify)
103
94
 
104
95
  # ------------------------------------------------------------------
105
96
  # ciclo de vida
@@ -199,14 +190,12 @@ class JaylogMetricsReporter:
199
190
  f"requisição HTTP iniciada; método=POST; endpoint={self.endpoint}; "
200
191
  f"serviço={self.service}; amostras={len(batch)}; timeout={effective_timeout:g}s",
201
192
  )
202
- with warnings.catch_warnings():
203
- warnings.simplefilter("ignore", urllib3.exceptions.InsecureRequestWarning)
204
- response = self._session.post(
205
- self.endpoint,
206
- json=body,
207
- timeout=effective_timeout,
208
- verify=self.verify,
209
- )
193
+ response = self._session.post(
194
+ self.endpoint,
195
+ json=body,
196
+ timeout=effective_timeout,
197
+ verify=self.verify,
198
+ )
210
199
  except Exception as exc:
211
200
  debug(
212
201
  "metrics",
@@ -20,15 +20,10 @@ precisar ser dono do reporter.
20
20
  import sys
21
21
  import threading
22
22
  import time
23
- import warnings
24
23
 
25
- import requests
26
- import urllib3
27
-
28
- from jaylog._version import PROTOCOL_VERSION, __version__
24
+ from jaylog._http import DEFAULT_VERIFY, prepare_session, silence_insecure_warning
29
25
  from jaylog.diagnostics import emit
30
26
  from jaylog.host.payload import build_host_payload
31
- from jaylog.runtime import RUN_ID
32
27
 
33
28
  #: Pausas entre tentativas: ~10 min de insistência no total. Depois disso o
34
29
  #: reporter dorme num Event até que alguém peça reenvio — continuar batendo
@@ -69,7 +64,7 @@ class JaylogHostReporter:
69
64
  *,
70
65
  timeout: float = 10.0,
71
66
  proxy: str | None = None,
72
- verify: bool | str = False,
67
+ verify: bool | str = DEFAULT_VERIFY,
73
68
  session=None,
74
69
  backoff=DEFAULT_BACKOFF,
75
70
  payload_factory=None,
@@ -102,13 +97,8 @@ class JaylogHostReporter:
102
97
  # portanto 0.0 não representa com segurança “nunca reenviado”.
103
98
  self._last_resend: float | None = None
104
99
 
105
- self._session = session if session is not None else requests.Session()
106
- self._session.headers["x-api-key"] = api_key
107
- self._session.headers["x-jaylog-version"] = __version__
108
- self._session.headers["x-jaylog-protocol"] = str(PROTOCOL_VERSION)
109
- self._session.headers["x-jaylog-run-id"] = RUN_ID
110
- if proxy:
111
- self._session.proxies.update({"http": proxy, "https": proxy})
100
+ self._session = prepare_session(api_key, proxy, session)
101
+ silence_insecure_warning(endpoint, verify)
112
102
 
113
103
  # ------------------------------------------------------------------
114
104
  # ciclo de vida
@@ -218,14 +208,12 @@ class JaylogHostReporter:
218
208
  f"recurso={self.noun}; serviço={self.service}",
219
209
  self.label,
220
210
  )
221
- with warnings.catch_warnings():
222
- warnings.simplefilter("ignore", urllib3.exceptions.InsecureRequestWarning)
223
- response = self._session.post(
224
- self.endpoint,
225
- json=payload,
226
- timeout=self.timeout,
227
- verify=self.verify,
228
- )
211
+ response = self._session.post(
212
+ self.endpoint,
213
+ json=payload,
214
+ timeout=self.timeout,
215
+ verify=self.verify,
216
+ )
229
217
  except Exception as exc:
230
218
  _debug(
231
219
  f"requisição HTTP falhou; endpoint={self.endpoint}; "
@@ -127,17 +127,37 @@ def _warn_insecure_transport(items: list[JaylogSettings]) -> None:
127
127
  if _insecure_transport_warned:
128
128
  return
129
129
  for item in items:
130
- endpoint = item.log_http_endpoint or ""
131
- if endpoint.startswith("https://") and item.log_http_verify is False:
132
- _insecure_transport_warned = True
133
- print(
130
+ if item.log_http_verify is not False or not _has_https_endpoint(item):
131
+ continue
132
+ _insecure_transport_warned = True
133
+ if "log_http_verify" in item.model_fields_set:
134
+ message = (
135
+ "[jaylog] verificação do certificado TLS DESATIVADA por "
136
+ "configuração (JAYLOG_LOG_HTTP_VERIFY=false). Os envios ficam "
137
+ "expostos a interceptação; prefira "
138
+ "JAYLOG_LOG_HTTP_VERIFY=/caminho/ca-bundle.pem."
139
+ )
140
+ else:
141
+ message = (
134
142
  "[jaylog] o certificado TLS do endpoint de log NÃO está sendo "
135
143
  "verificado (padrão atual). Defina "
136
144
  "JAYLOG_LOG_HTTP_VERIFY=/caminho/ca-bundle.pem (ou =true) para "
137
- "habilitar. O padrão passa a ser 'true' na 0.4.0.",
138
- file=sys.stderr,
145
+ "habilitar. O padrão passa a ser 'true' na 0.4.0."
139
146
  )
140
- return
147
+ print(message, file=sys.stderr)
148
+ return
149
+
150
+
151
+ def _has_https_endpoint(item: JaylogSettings) -> bool:
152
+ """Algum POST deste item (log, host, métricas, heartbeat, agendas) usa HTTPS?"""
153
+ endpoints = (
154
+ item.log_http_endpoint,
155
+ item.effective_host_endpoint,
156
+ item.effective_host_metrics_endpoint,
157
+ item.effective_host_heartbeat_endpoint,
158
+ item.effective_host_schedule_endpoint,
159
+ )
160
+ return any((endpoint or "").lower().startswith("https://") for endpoint in endpoints)
141
161
 
142
162
 
143
163
  def _host_payload_factory(settings: JaylogSettings):
@@ -3,6 +3,7 @@ from pathlib import Path
3
3
  from pydantic import computed_field, field_validator
4
4
  from pydantic_settings import BaseSettings, SettingsConfigDict
5
5
 
6
+ from jaylog._http import DEFAULT_VERIFY, parse_verify
6
7
  from jaylog.diagnostics import parse_handlers
7
8
  from jaylog.host import identity
8
9
 
@@ -109,16 +110,22 @@ class JaylogSettings(BaseSettings):
109
110
  # Screenshot (log_img field) — desativar com JAYLOG_LOG_SCREENSHOT_ENABLED=false
110
111
  log_screenshot_enabled: bool = False
111
112
 
112
- # Verificação do certificado TLS no envio HTTP. Aceita bool ou o caminho de
113
- # um CA bundle (`requests` entende os dois).
113
+ # Verificação do certificado TLS no envio HTTP: `true`, `false` ou o caminho
114
+ # de um CA bundle. Vale para todos os POSTs (log, host, métricas, heartbeat e
115
+ # agendas). Ver `jaylog._http.parse_verify` para os valores aceitos.
114
116
  #
115
117
  # O padrão continua `False` na 0.3.x: virar `True` derrubaria, no dia do
116
118
  # upgrade, todo bot atrás de proxy corporativo com inspeção TLS — uma
117
119
  # biblioteca de log não pode quebrar a aplicação que ela observa. É dívida
118
- # consciente, com data: o padrão vira `True` na 0.4.0. Quem já tem o bundle
119
- # corporativo pode antecipar com
120
- # `JAYLOG_LOG_HTTP_VERIFY=/caminho/corp-ca.pem`.
121
- log_http_verify: bool | str = False
120
+ # consciente, com data: o padrão vira `True` na 0.4.0
121
+ # (`jaylog._http.DEFAULT_VERIFY`). Quem já tem o bundle corporativo pode
122
+ # antecipar com `JAYLOG_LOG_HTTP_VERIFY=/caminho/corp-ca.pem`.
123
+ log_http_verify: bool | str = DEFAULT_VERIFY
124
+
125
+ @field_validator("log_http_verify", mode="before")
126
+ @classmethod
127
+ def _parse_log_http_verify(cls, value: object) -> bool | str:
128
+ return parse_verify(value)
122
129
 
123
130
  # ------------------------------------------------------------------
124
131
  # Registro de ambiente (host) — enviado uma vez por processo/serviço
jaylog-0.3.2/README.md DELETED
@@ -1,30 +0,0 @@
1
- # jaylog
2
-
3
- Biblioteca de logging para Python com rotação de arquivos, saída colorida no console, envio HTTP para um endpoint remoto e registro automático do ambiente de execução (host reporting).
4
-
5
- 📖 **Documentação completa:** https://gpocas.github.io/jaylog-book
6
-
7
- ## Instalação
8
-
9
- ```bash
10
- pip install -U --no-cache-dir jaylog
11
- ```
12
-
13
- ## Uso rápido
14
-
15
- __*.env.logging*__
16
- ```env
17
- JAYLOG_APP_NAME=meu-bot
18
- JAYLOG_LOG_DIR=C:\logs
19
- ```
20
-
21
- __*main.py*__
22
- ```python
23
- from jaylog import JaylogSettings, configure, get_logger
24
-
25
- configure(JaylogSettings())
26
-
27
- logger = get_logger()
28
-
29
- logger.info("Olá, jaylog!")
30
- ```
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
File without changes
File without changes