elven-logs-interceptor-python 0.1.2__tar.gz → 0.1.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.
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/PKG-INFO +1 -1
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/pyproject.toml +1 -1
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/application/log_service.py +3 -3
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/buffer/memory_buffer.py +5 -1
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/transport/loki_json_transport.py +1 -1
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/transport/loki_protobuf_transport.py +1 -1
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/presentation/factory.py +3 -3
- elven_logs_interceptor_python-0.1.3/test-apps/elven-live-demo/.env.example +49 -0
- elven_logs_interceptor_python-0.1.3/test-apps/elven-live-demo/README.md +17 -0
- elven_logs_interceptor_python-0.1.3/test-apps/elven-live-demo/app.py +97 -0
- elven_logs_interceptor_python-0.1.3/test-apps/elven-observability-smoke/run.sh +21 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_memory_buffer_extra.py +27 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/.github/workflows/ci.yml +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/.github/workflows/publish.yml +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/.gitignore +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/ARCHITECTURE.md +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/Makefile +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/README.md +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/basic_app.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/fastapi_integration.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/full_config_reference.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/high_volume.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/tracking_usage.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/scripts/publish.sh +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/application/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/application/config_service.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/config.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/domain/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/domain/entities.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/domain/interfaces.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/domain/value_objects.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/buffer/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/circuit_breaker/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/circuit_breaker/circuit_breaker.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/base.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/brotli_compressor.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/factory.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/gzip_compressor.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/compression/noop_compressor.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/context/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/context/context_provider.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/dlq/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/dlq/file_dlq.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/dlq/memory_dlq.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/filter/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/filter/log_filter.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/interceptors/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/interceptors/runtime_interceptor.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/memory/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/memory/memory_tracker.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/metrics/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/metrics/metrics_collector.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/transport/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/transport/resilient_transport.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/transport/transport_factory.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/workers/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/infrastructure/workers/worker_pool.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/celery.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/django.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/fastapi.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/flask.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/logging_handler.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/loguru.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/integrations/structlog.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/preload.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/presentation/__init__.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/types.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/src/logs_interceptor/utils.py +0 -0
- {elven_logs_interceptor_python-0.1.2/test-apps/elven-observability-smoke → elven_logs_interceptor_python-0.1.3/test-apps/elven-live-demo}/run.sh +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/test-apps/elven-observability-smoke/.env.example +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/test-apps/elven-observability-smoke/README.md +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/test-apps/elven-observability-smoke/app.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/integration/test_api.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_circuit_breaker_extra.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_config_service.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_core_components.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_env_config.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_log_filter_extra.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_log_service_unit.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_loki_json_transport.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_protobuf_transport_safety.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_resilient_transport.py +0 -0
- {elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/tests/unit/test_utils_extra.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "elven-logs-interceptor-python"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Production-grade logs interceptor for Python with Loki transport, resilience, batching, and framework integrations."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -22,11 +22,11 @@ try:
|
|
|
22
22
|
except Exception: # pragma: no cover
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
|
-
otel_trace: Any = None
|
|
26
25
|
try:
|
|
27
|
-
from opentelemetry import trace as
|
|
26
|
+
from opentelemetry import trace as _otel_trace # type: ignore[import-not-found]
|
|
28
27
|
except Exception: # pragma: no cover - optional dependency
|
|
29
|
-
|
|
28
|
+
_otel_trace = None
|
|
29
|
+
otel_trace: Any = _otel_trace
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@dataclass(slots=True)
|
|
@@ -165,12 +165,16 @@ class MemoryBuffer:
|
|
|
165
165
|
return
|
|
166
166
|
|
|
167
167
|
def _on_timer() -> None:
|
|
168
|
+
callback: Callable[[], None] | None = None
|
|
168
169
|
with self._lock:
|
|
169
170
|
self._flush_timer = None
|
|
170
171
|
if self._destroyed:
|
|
171
172
|
return
|
|
172
173
|
if self._flush_callback and self._entries:
|
|
173
|
-
self._flush_callback
|
|
174
|
+
callback = self._flush_callback
|
|
175
|
+
|
|
176
|
+
if callback is not None:
|
|
177
|
+
callback()
|
|
174
178
|
|
|
175
179
|
self._flush_timer = threading.Timer(self._config.flush_interval / 1000, _on_timer)
|
|
176
180
|
self._flush_timer.daemon = True
|
|
@@ -88,7 +88,7 @@ class LokiJsonTransport:
|
|
|
88
88
|
headers: dict[str, str] = {
|
|
89
89
|
"Content-Type": "application/json",
|
|
90
90
|
"X-Scope-OrgID": self._config.tenant_id,
|
|
91
|
-
"User-Agent": "elven-logs-interceptor-python/0.1.
|
|
91
|
+
"User-Agent": "elven-logs-interceptor-python/0.1.3",
|
|
92
92
|
**self._extra_headers,
|
|
93
93
|
}
|
|
94
94
|
if self._config.auth_token:
|
|
@@ -94,7 +94,7 @@ class LokiProtobufTransport:
|
|
|
94
94
|
"Content-Type": "application/x-protobuf",
|
|
95
95
|
"Content-Encoding": "snappy",
|
|
96
96
|
"X-Scope-OrgID": self._config.tenant_id,
|
|
97
|
-
"User-Agent": "elven-logs-interceptor-python/0.1.
|
|
97
|
+
"User-Agent": "elven-logs-interceptor-python/0.1.3",
|
|
98
98
|
**self._extra_headers,
|
|
99
99
|
}
|
|
100
100
|
if self._config.auth_token:
|
|
@@ -15,11 +15,11 @@ from ..infrastructure.filter import LogFilter
|
|
|
15
15
|
from ..infrastructure.interceptors import RuntimeInterceptor
|
|
16
16
|
from ..infrastructure.transport import TransportFactory
|
|
17
17
|
|
|
18
|
-
otel_trace: Any = None
|
|
19
18
|
try:
|
|
20
|
-
from opentelemetry import trace as
|
|
19
|
+
from opentelemetry import trace as _otel_trace # type: ignore[import-not-found]
|
|
21
20
|
except Exception: # pragma: no cover - optional dependency
|
|
22
|
-
|
|
21
|
+
_otel_trace = None
|
|
22
|
+
otel_trace: Any = _otel_trace
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@dataclass(slots=True)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
LOGS_URL=https://loki.elvenobservability.com/loki/api/v1/push
|
|
2
|
+
LOGS_TENANT=elven
|
|
3
|
+
LOGS_TOKEN=replace-with-your-token
|
|
4
|
+
LOGS_APP_NAME=elven-live-demo
|
|
5
|
+
LOGS_APP_VERSION=1.0.0
|
|
6
|
+
LOGS_ENVIRONMENT=local
|
|
7
|
+
|
|
8
|
+
LOGS_COMPRESSION=gzip
|
|
9
|
+
LOGS_COMPRESSION_LEVEL=4
|
|
10
|
+
LOGS_COMPRESSION_THRESHOLD=1024
|
|
11
|
+
LOGS_CONNECTION_POOLING=true
|
|
12
|
+
LOGS_MAX_SOCKETS=50
|
|
13
|
+
LOGS_TIMEOUT=10000
|
|
14
|
+
LOGS_MAX_RETRIES=3
|
|
15
|
+
LOGS_RETRY_DELAY=1000
|
|
16
|
+
|
|
17
|
+
LOGS_BUFFER_MAX_SIZE=25
|
|
18
|
+
LOGS_BUFFER_FLUSH_INTERVAL=1500
|
|
19
|
+
LOGS_BUFFER_MAX_MEMORY_MB=128
|
|
20
|
+
LOGS_BUFFER_MAX_AGE=30000
|
|
21
|
+
LOGS_BUFFER_AUTO_FLUSH=true
|
|
22
|
+
|
|
23
|
+
LOGS_FILTER_LEVELS=debug,info,warn,error,fatal
|
|
24
|
+
LOGS_FILTER_SAMPLING_RATE=1.0
|
|
25
|
+
LOGS_FILTER_SANITIZE=true
|
|
26
|
+
LOGS_FILTER_MAX_MESSAGE_LENGTH=8192
|
|
27
|
+
|
|
28
|
+
LOGS_CIRCUIT_BREAKER_ENABLED=true
|
|
29
|
+
LOGS_CIRCUIT_BREAKER_FAILURE_THRESHOLD=20
|
|
30
|
+
LOGS_CIRCUIT_BREAKER_RESET_TIMEOUT=15000
|
|
31
|
+
LOGS_CIRCUIT_BREAKER_HALF_OPEN_REQUESTS=3
|
|
32
|
+
|
|
33
|
+
LOGS_DLQ_ENABLED=true
|
|
34
|
+
LOGS_DLQ_TYPE=file
|
|
35
|
+
LOGS_DLQ_MAX_SIZE=1000
|
|
36
|
+
LOGS_DLQ_MAX_RETRIES=3
|
|
37
|
+
LOGS_DLQ_BASE_PATH=./.logs-dlq
|
|
38
|
+
|
|
39
|
+
LOGS_MAX_CONCURRENT_FLUSHES=5
|
|
40
|
+
LOGS_INTERCEPT_CONSOLE=true
|
|
41
|
+
LOGS_PRESERVE_ORIGINAL_CONSOLE=true
|
|
42
|
+
LOGS_ENABLE_METRICS=true
|
|
43
|
+
LOGS_ENABLE_HEALTH_CHECK=true
|
|
44
|
+
LOGS_DEBUG=false
|
|
45
|
+
LOGS_SILENT_ERRORS=false
|
|
46
|
+
LOGS_ENABLED=true
|
|
47
|
+
|
|
48
|
+
LOGS_LABEL_SERVICE=elven-live-demo
|
|
49
|
+
LOGS_LABEL_ENVIRONMENT=local
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Elven Live Demo
|
|
2
|
+
|
|
3
|
+
Local Python app to exercise `elven-logs-interceptor-python` against the Elven Loki endpoint.
|
|
4
|
+
|
|
5
|
+
## Run
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd /Users/leonardozwirtes/Documents/Projects/logs-interceptor-python
|
|
9
|
+
chmod +x test-apps/elven-live-demo/run.sh
|
|
10
|
+
./test-apps/elven-live-demo/run.sh
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Notes
|
|
14
|
+
|
|
15
|
+
- The tracked config template is `.env.example`.
|
|
16
|
+
- The real token lives only in local `.env`, which is ignored by Git.
|
|
17
|
+
- The package import remains `logs_interceptor`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import random
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from logs_interceptor import destroy, init, logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def emit_startup_logs() -> None:
|
|
11
|
+
logger.info("demo app started", {"component": "startup"})
|
|
12
|
+
logger.track_event("demo_started", {"source": "local-run", "at": time.time()})
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def emit_business_logs() -> None:
|
|
16
|
+
for idx in range(10):
|
|
17
|
+
logger.info(
|
|
18
|
+
"processing item",
|
|
19
|
+
{
|
|
20
|
+
"idx": idx,
|
|
21
|
+
"value": random.randint(100, 999),
|
|
22
|
+
"duration_ms": round(random.uniform(5, 30), 2),
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger.warn("slow dependency detected", {"dependency": "inventory-service", "latency_ms": 187})
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
raise RuntimeError("simulated business exception")
|
|
30
|
+
except RuntimeError as exc:
|
|
31
|
+
logger.error(
|
|
32
|
+
"handled domain error",
|
|
33
|
+
{
|
|
34
|
+
"error": str(exc),
|
|
35
|
+
"kind": "business",
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def emit_sync_context_logs() -> None:
|
|
41
|
+
def _run() -> None:
|
|
42
|
+
logger.info("sync request started", {"method": "GET", "path": "/products"})
|
|
43
|
+
print("plain print still works and is intercepted")
|
|
44
|
+
|
|
45
|
+
logger.with_context(
|
|
46
|
+
{
|
|
47
|
+
"request_id": "demo-sync-request-1",
|
|
48
|
+
"trace_id": "demo-sync-trace-1",
|
|
49
|
+
"span_id": "demo-sync-span-1",
|
|
50
|
+
},
|
|
51
|
+
_run,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def emit_async_context_logs() -> None:
|
|
56
|
+
async def _run() -> None:
|
|
57
|
+
logger.info("async job started", {"job": "catalog-refresh"})
|
|
58
|
+
await asyncio.sleep(0.05)
|
|
59
|
+
logger.track_event("catalog_refresh_finished", {"ok": True})
|
|
60
|
+
|
|
61
|
+
await logger.with_context_async(
|
|
62
|
+
{
|
|
63
|
+
"request_id": "demo-async-request-1",
|
|
64
|
+
"trace_id": "demo-async-trace-1",
|
|
65
|
+
"span_id": "demo-async-span-1",
|
|
66
|
+
},
|
|
67
|
+
_run,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def print_runtime_summary() -> None:
|
|
72
|
+
metrics = logger.get_metrics()
|
|
73
|
+
health = logger.get_health()
|
|
74
|
+
print(
|
|
75
|
+
{
|
|
76
|
+
"processed": metrics.get("logs_processed"),
|
|
77
|
+
"dropped": metrics.get("logs_dropped"),
|
|
78
|
+
"flush_count": metrics.get("flush_count"),
|
|
79
|
+
"error_count": metrics.get("error_count"),
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
print({"health": health})
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main() -> None:
|
|
86
|
+
init()
|
|
87
|
+
emit_startup_logs()
|
|
88
|
+
emit_business_logs()
|
|
89
|
+
emit_sync_context_logs()
|
|
90
|
+
asyncio.run(emit_async_context_logs())
|
|
91
|
+
logger.flush()
|
|
92
|
+
print_runtime_summary()
|
|
93
|
+
destroy()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -Eeuo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
6
|
+
|
|
7
|
+
if [[ ! -f "${SCRIPT_DIR}/.env" ]]; then
|
|
8
|
+
echo "Missing .env at ${SCRIPT_DIR}/.env" >&2
|
|
9
|
+
if [[ -f "${SCRIPT_DIR}/.env.example" ]]; then
|
|
10
|
+
echo "Create it with: cp ${SCRIPT_DIR}/.env.example ${SCRIPT_DIR}/.env" >&2
|
|
11
|
+
fi
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
set -a
|
|
16
|
+
source "${SCRIPT_DIR}/.env"
|
|
17
|
+
set +a
|
|
18
|
+
|
|
19
|
+
export PYTHONPATH="${ROOT_DIR}/src:${PYTHONPATH:-}"
|
|
20
|
+
|
|
21
|
+
python3 "${SCRIPT_DIR}/app.py"
|
|
@@ -106,6 +106,33 @@ def test_schedule_flush_callback_runs_with_entries() -> None:
|
|
|
106
106
|
buffer.destroy()
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def test_schedule_flush_callback_does_not_hold_buffer_lock() -> None:
|
|
110
|
+
callback_started = threading.Event()
|
|
111
|
+
release_callback = threading.Event()
|
|
112
|
+
add_completed = threading.Event()
|
|
113
|
+
buffer = _buffer(flush_interval=5, auto_flush=True)
|
|
114
|
+
|
|
115
|
+
def flush_callback() -> None:
|
|
116
|
+
callback_started.set()
|
|
117
|
+
release_callback.wait(timeout=1)
|
|
118
|
+
|
|
119
|
+
buffer.set_flush_callback(flush_callback)
|
|
120
|
+
buffer.add(_entry("1", "2026-01-01T00:00:00+00:00"))
|
|
121
|
+
assert callback_started.wait(timeout=1)
|
|
122
|
+
|
|
123
|
+
def add_entry() -> None:
|
|
124
|
+
buffer.add(_entry("2", "2026-01-01T00:00:01+00:00"))
|
|
125
|
+
add_completed.set()
|
|
126
|
+
|
|
127
|
+
add_thread = threading.Thread(target=add_entry)
|
|
128
|
+
add_thread.start()
|
|
129
|
+
assert add_completed.wait(timeout=0.2)
|
|
130
|
+
|
|
131
|
+
release_callback.set()
|
|
132
|
+
add_thread.join(timeout=1)
|
|
133
|
+
buffer.destroy()
|
|
134
|
+
|
|
135
|
+
|
|
109
136
|
def test_schedule_flush_destroyed_guard_inside_timer() -> None:
|
|
110
137
|
event = threading.Event()
|
|
111
138
|
buffer = _buffer(flush_interval=40, auto_flush=False)
|
{elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/.github/workflows/ci.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/basic_app.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/examples/high_volume.py
RENAMED
|
File without changes
|
|
File without changes
|
{elven_logs_interceptor_python-0.1.2 → elven_logs_interceptor_python-0.1.3}/scripts/publish.sh
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|