mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-25 08:09:31 +00:00
ouroboros/contracts/record_contract.py names the subset of Ouroboros's local JSONL records an external observer may rely on: fifteen anchor rows (task lifecycle, model rounds and usage, model errors, tool calls, worker crashes, supervisor stalls) with narrow guaranteed keys fixed within RECORD_CONTRACT_VERSION 1, their nullable, optional and content-bearing fields, the correlation ids, the planes (canonical logs and archive, the task's own log directory, and child drives deleted after retention), and the envelope, rotation, replica, legacy and accounting rules, plus the coverage gaps. It describes what the writers already write and changes none of them; money is read from the accounting views by API, never summed from llm_usage. tests/test_record_contract.py proves every guaranteed field against its writer: the row literals of the named writer modules by AST, and the tools rows through the real tool loop (start, settlement, wait end, a refused browser call) with the replica rule checked in both planes. Within a version the guaranteed sets are pinned exactly. docs/examples/log_collector is a Vector setup that tails the live logs, the archive generations it missed and child-drive events, raises max_line_bytes for ~300 KB rows, and forwards only anchor rows and their metadata (no content, no money). docs/examples/error_tracker is an in-process extension that starts a Sentry-compatible SDK with frame locals, source context, default integrations and PII off, keeps only the event's identity, message and exception, and masks secrets with redact_projection; its README says what still leaves. tests/test_monitoring_examples.py checks both against the passport. DEPLOYMENT.md points to the passport and the examples; PERSISTENCE.md names the passport for anchor rows and corrects two statements (child drives are pruned by the ~300 s reconcile pass, not at startup; tools rows hold redacted, truncated args). Architecture chapters 01 and 11 list the module (grants added), the domain map and generated inventories are regenerated.
48 lines
2 KiB
Python
48 lines
2 KiB
Python
"""Send this process's ERROR log records to a Sentry-compatible error tracker (example).
|
|
|
|
The extension has no declared dependencies and no binary files, so it loads in
|
|
process in the server and in every pool worker. The SDK comes from Ouroboros's
|
|
own environment, and SENTRY_DSN names the destination; without it the SDK sends
|
|
nothing.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
import sentry_sdk
|
|
from sentry_sdk.integrations.atexit import AtexitIntegration
|
|
from sentry_sdk.integrations.dedupe import DedupeIntegration
|
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
|
|
from ouroboros.observability import redact_projection
|
|
|
|
|
|
# The parts of an error event a tracker needs. The logging integration also copies a
|
|
# record's `extra` dict, which holds whatever a caller logged, so it is not forwarded.
|
|
_EVENT_FIELDS = ("event_id", "timestamp", "level", "logger", "platform", "sdk", "server_name", "release",
|
|
"environment", "tags", "fingerprint", "logentry", "message", "exception")
|
|
|
|
|
|
def _before_send(event, hint):
|
|
return redact_projection({key: event[key] for key in _EVENT_FIELDS if key in event}).value
|
|
|
|
|
|
def register(api):
|
|
role = "worker" if os.environ.get("OUROBOROS_IN_WORKER") == "1" else "server"
|
|
sentry_sdk.init(
|
|
include_local_variables=False, # frame locals hold prompts, keys and file contents
|
|
include_source_context=False,
|
|
send_default_pii=False,
|
|
default_integrations=False, # no auto-instrumented model clients, no trace headers on their requests
|
|
integrations=[
|
|
LoggingIntegration(level=None, event_level=logging.ERROR),
|
|
DedupeIntegration(),
|
|
AtexitIntegration(),
|
|
],
|
|
max_request_body_size="never",
|
|
trace_propagation_targets=[],
|
|
before_send=_before_send,
|
|
server_name=f"ouroboros-{role}", # not the machine's hostname
|
|
release=f"ouroboros@{api.get_runtime_info().get('app_version', '')}",
|
|
)
|
|
api.on_unload(lambda: sentry_sdk.get_client().close(timeout=1))
|