"""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