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.
- {jaylog-0.3.2 → jaylog-0.3.3}/PKG-INFO +13 -1
- jaylog-0.3.3/README.md +42 -0
- jaylog-0.3.3/pyproject.toml +58 -0
- jaylog-0.3.2/pyproject.toml → jaylog-0.3.3/pyproject.toml.orig +1 -1
- jaylog-0.3.3/src/jaylog/_http.py +139 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/http_handler.py +10 -23
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/heartbeat_reporter.py +11 -16
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/metrics_reporter.py +10 -21
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/reporter.py +10 -22
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/logger.py +27 -7
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/settings.py +13 -6
- jaylog-0.3.2/README.md +0 -30
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/__init__.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/_compat.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/_version.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/colors.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/context.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/diagnostics.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/endpoints.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/filters.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/formatters.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/__init__.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/console_handler.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/handlers/file_handler.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/__init__.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/_safe.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/collectors.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_execution.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_git.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_python.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_schedule.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/detect_venv.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/identity.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/metrics.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/models.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/payload.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/schedule_parser.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/schedule_reporter.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/host/win32.py +0 -0
- {jaylog-0.3.2 → jaylog-0.3.3}/src/jaylog/runtime.py +0 -0
- {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.
|
|
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"]
|
|
@@ -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
|
|
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 =
|
|
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.
|
|
62
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
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
|
|
52
|
-
#: passado por `configure()
|
|
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
|
-
|
|
254
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
97
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
106
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
|
113
|
-
# um CA bundle
|
|
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
|
|
119
|
-
# corporativo pode
|
|
120
|
-
# `JAYLOG_LOG_HTTP_VERIFY=/caminho/corp-ca.pem`.
|
|
121
|
-
log_http_verify: bool | str =
|
|
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
|
|
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
|
|
File without changes
|