Files
ITWorx_MCP_Hub-Public/infrastructure/images/observability/collector.py
T

79 lines
2.6 KiB
Python

"""Minimal internal OTLP/HTTP collector for the optional observer profile.
It accepts bounded trace batches, keeps only counters in memory, and exposes
Prometheus text metrics. It never logs request bodies or trace attributes.
"""
from __future__ import annotations
import json
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Lock
MAX_BODY = 1024 * 1024
_lock = Lock()
_spans = 0
_batches = 0
class CollectorHandler(BaseHTTPRequestHandler):
server_version = "ITWorxOTLPCollector/1.0"
def do_GET(self) -> None: # noqa: N802
if self.path == "/healthz":
self._write(
HTTPStatus.OK,
b'{"status":"ok","component":"observability-collector"}',
"application/json",
)
return
if self.path == "/metrics":
with _lock:
body = (
f"itworx_otel_batches_total {_batches}\\nitworx_otel_spans_total {_spans}\\n"
).encode()
self._write(HTTPStatus.OK, body, "text/plain; version=0.0.4")
return
self.send_error(HTTPStatus.NOT_FOUND)
def do_POST(self) -> None: # noqa: N802
global _batches, _spans
if self.path != "/v1/traces":
self.send_error(HTTPStatus.NOT_FOUND)
return
length = int(self.headers.get("Content-Length", "0"))
if length <= 0 or length > MAX_BODY:
self.send_error(HTTPStatus.REQUEST_ENTITY_TOO_LARGE)
return
try:
document = json.loads(self.rfile.read(length))
batches = document.get("resourceSpans", [])
spans = sum(
len(scope.get("spans", []))
for batch in batches
for scope in batch.get("scopeSpans", [])
)
except (ValueError, TypeError, AttributeError):
self.send_error(HTTPStatus.BAD_REQUEST)
return
with _lock:
_batches += 1
_spans += spans
self._write(HTTPStatus.ACCEPTED, b"", "text/plain")
def _write(self, status: HTTPStatus, body: bytes, content_type: str) -> None:
self.send_response(status)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
if body:
self.wfile.write(body)
def log_message(self, _format: str, *_args: object) -> None:
return
if __name__ == "__main__":
ThreadingHTTPServer(("0.0.0.0", 4318), CollectorHandler).serve_forever() # noqa: S104