feat: per-sink delivery, journal forwarding, and operational API
Persistence: - one delivery state per sink, with claim, crash recovery, and index replacement - retention for delivered and dead records - LMDB writes moved off the event loop Pipeline: - one attempt per dispatch, with the next try scheduled by next_retry_at - backpressure through max_queue_size - reload re-reads YAML; SIGTERM drains and SIGHUP reloads Plugins: - journald source, enricher and filter processors, Loki sink - built-ins register when entry points are missing API and CLI: - health, metrics, per-sink queue, and dead-letter replay - startup prints the pipeline on one line Tests: - dispatch, plugins, metrics, CLI, and persistence coverage
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from daena.api.metrics import MetricsServer, MetricsSnapshot, render_prometheus
|
||||
|
||||
|
||||
def test_render_prometheus_includes_sink_state() -> None:
|
||||
text = render_prometheus(
|
||||
MetricsSnapshot(
|
||||
running=True,
|
||||
stored_records=3,
|
||||
oldest_inflight_seconds=12,
|
||||
deliveries={("central", "pending"): 2, ("central", "delivered"): 1},
|
||||
sink_results={"central": {"delivered": 4, "failed": 1, "dead": 0}},
|
||||
)
|
||||
)
|
||||
assert "daena_up 1" in text
|
||||
assert "daena_records 3" in text
|
||||
assert "daena_oldest_inflight_seconds 12" in text
|
||||
assert 'daena_delivery_records{sink="central",state="pending"} 2' in text
|
||||
assert 'daena_sink_deliveries_total{sink="central",result="failed"} 1' in text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_metrics_server_serves_text() -> None:
|
||||
snapshot = MetricsSnapshot(
|
||||
running=True,
|
||||
stored_records=0,
|
||||
oldest_inflight_seconds=0,
|
||||
deliveries={},
|
||||
sink_results={},
|
||||
)
|
||||
server = MetricsServer("127.0.0.1", 0, lambda: snapshot, lambda: {"status": "ok"})
|
||||
await server.start()
|
||||
assert server._server is not None
|
||||
sockets = server._server.sockets
|
||||
assert sockets is not None
|
||||
port = sockets[0].getsockname()[1]
|
||||
reader, writer = await asyncio.open_connection("127.0.0.1", port)
|
||||
writer.write(b"GET /metrics HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
||||
await writer.drain()
|
||||
data = await reader.read()
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
await server.stop()
|
||||
assert b"200 OK" in data
|
||||
assert b"daena_up 1" in data
|
||||
Reference in New Issue
Block a user