Some checks failed
Windows project source guards / test (push) Failing after 39s
Замер на win10: у «шумного» ядра каждая строка лога с новым временем, id соединения или портом становилась отдельной записью журнала, а весь журнал копировался в GUI на каждую строку и переформатировался — CPU и память GUI росли с аптаймом (при 15 тыс. записей +0,19 ядра, +33 МБ). - RuntimeErrorJournal: записи группируются без учёта чисел (последний исходный текст сохраняется), не больше 300 записей — вытесняется та, что дольше всех не повторялась; порядок снимка прежний. - Снимок журнала уходит в GUI не чаще раза в секунду; вкладка «Ошибки ядер» форматирует записи только когда открыта. - Сбор трафика по процессам: вместо набора всех id соединений за сессию — владелец живых соединений (удаляется при закрытии) и счётчик на процесс.
48 lines
2.3 KiB
Python
48 lines
2.3 KiB
Python
"""Журнал ошибок ядер: группировка «той же» ошибки и предел размера."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from xray_fluent.diagnostics.runtime_errors import MAX_JOURNAL_RECORDS, RuntimeErrorJournal, core_failure
|
|
|
|
|
|
class RuntimeErrorJournalBoundTests(unittest.TestCase):
|
|
def test_same_error_with_new_time_id_and_port_is_one_record(self) -> None:
|
|
journal = RuntimeErrorJournal()
|
|
for second in range(50):
|
|
journal.record(core_failure(
|
|
"xray", "runtime",
|
|
f"2026/09/26 16:{second:02d}:01 [Warning] [{1000 + second}] proxy: failed to dial 127.0.0.1:{40000 + second}",
|
|
))
|
|
records = journal.snapshot()
|
|
self.assertEqual(len(records), 1)
|
|
self.assertEqual(records[0].occurrences, 50)
|
|
self.assertIn("40049", records[0].failure.message) # показан последний исходный текст
|
|
|
|
def test_distinct_errors_are_kept_apart(self) -> None:
|
|
journal = RuntimeErrorJournal()
|
|
journal.record(core_failure("xray", "runtime", "[Warning] connection reset"))
|
|
journal.record(core_failure("xray", "runtime", "[Warning] certificate signed by unknown authority"))
|
|
self.assertEqual(len(journal.snapshot()), 2)
|
|
|
|
def test_journal_is_capped_and_evicts_least_recently_seen(self) -> None:
|
|
journal = RuntimeErrorJournal(max_records=5)
|
|
messages = [f"[Warning] distinct failure {chr(97 + i)}" for i in range(8)]
|
|
journal.record(core_failure("xray", "runtime", messages[0]))
|
|
for message in messages[1:]:
|
|
journal.record(core_failure("xray", "runtime", message))
|
|
journal.record(core_failure("xray", "runtime", messages[0])) # «a» видим постоянно
|
|
texts = [r.failure.message for r in journal.snapshot()]
|
|
self.assertEqual(len(texts), 5)
|
|
self.assertIn(messages[0], texts)
|
|
|
|
def test_default_cap(self) -> None:
|
|
journal = RuntimeErrorJournal()
|
|
for i in range(MAX_JOURNAL_RECORDS + 50):
|
|
journal.record(core_failure("sing-box", "runtime", f"ERROR unique failure kind {chr(65 + i % 26)}{'x' * (i // 26)}"))
|
|
self.assertEqual(len(journal), MAX_JOURNAL_RECORDS)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|