فهرست مقاله
دو بار دیدهشدنِ یک پیام، بهتنهایی نشان نمیدهد تابعِ برنامه دو بار فراخوانی شده است. ممکن است همان رکورد از دو اتصالِ پیکربندی به مقصد برسد. برای تشخیص این وضعیت، مسیرِ پذیرشِ رویداد را از مسیرِ تحویل جدا میکنیم. نمونهٔ این درس سه logger خصوصی و مقصدهای حافظهای دارد؛ رویدادها تألیفیاند و فایل، سرور یا سامانهٔ جمعآوری واقعی در کار نیست. هدف، قابلبررسیکردنِ رفتارِ یک پیکربندی کوچک است.
۱. یک فراخوانی، چند مسیرِ خروجی؛ مسئله را کوچک کنیم

سه جایگاه را boundary، middle و leaf مینامیم. این نامها ترتیبِ پدر و فرزند را نشان میدهند: leaf زیرِ middle و middle زیرِ boundary قرار دارد. شاخصهای صفر، یک و دو در دادهٔ ورودی، همین سه جایگاهاند. نام واقعیِ loggerها پیشوندِ خصوصیِ یکتا دارد تا به تنظیماتِ logger موجودِ برنامه دست نزنیم؛ آن پیشوند وارد خروجیِ آموزشی نمیشود و نقشِ شناسهٔ کسبوکار ندارد.
در پیکربندیِ پایه، هر جایگاه یک handler دارد. برای رویداد E1 از leaf با سطح INFO، انتظارِ دستیِ مقصدها leaf، سپس middle و سپس boundary است. این سه تحویل از یک فراخوانی میآیند. وقتی یک شیء handler مشترک را هم به leaf و هم به boundary وصل کنیم، ترتیبِ برچسبها shared، middle و shared میشود. شمارِ تحویل، شمارِ فراخوانی یا شمارِ رخدادهای مستقل نیست.
تابع observe تاپلِ حداکثر هشت رویداد را میپذیرد. هر رویداد شناسهٔ ASCII یکتا در همان دسته، شاخصِ مبدأ و یکی از پنج سطحِ استانداردِ مثبت دارد. شناسه با حرف آغاز میشود و حداکثر دوازده نویسه دارد. سطحهای ۱۰ تا ۵۰ کدِ شدتاند؛ میلیثانیه، کیفیتِ داده یا نرخِ پردازش نیستند. بولی، زیرکلاسِ عدد و تبدیلِ ضمنیِ رشته به سطح را نمیپذیریم.
مرزِ بالایی همیشه propagate خاموش و سطحِ صریح دارد. بنابراین ارثبریِ سطح یا ادامهٔ تحویل از آن به root نمیرسد. مقصدها فقط ارجاعِ رکورد را در فهرست نگه میدارند. قالبِ پیام، JSON تکخطی و انتخابِ فیلدها مسئلههای دیگریاند که در گزارش ساختاریافتهٔ حسگر بررسی شدهاند؛ اینجا آن مثال را به جمعآورندهٔ عملیاتی تبدیل نمیکنیم.
۲. آستانهٔ logger با آستانهٔ handler یکسان نیست

در مرجعِ logging، انتشارِ رکورد به handlerهای بالاتر تعریف شده است. در مثالِ child_override، سطحِ leaf برابر DEBUG است، اما middle سطحِ ERROR دارد. رویدادِ INFO از leaf پذیرفته میشود و با handlerهای مجازِ سه جایگاه ملاقات میکند. سطحِ ERROR در middle، دروازهٔ دومِ آن رکوردِ منتشرشده نیست.
ارثبری را باید جدا آزمود. اگر سطحِ leaf را NOTSET بگذاریم، نخستین سطحِ صریحِ بالاتر، سطحِ مؤثرِ مبدأ میشود. در شاهدِ inherited، این سطح ERROR است؛ پس INFO پیش از رسیدن به فیلترِ مبدأ کنار میرود. مقایسهٔ دو شاهد روشن میکند چرا تغییرِ سطحِ یک فرزند از NOTSET به DEBUG میتواند مسیرِ پذیرش را عوض کند، بیآنکه سطحِ middle تغییر کرده باشد.
handler نیز آستانهٔ خودش را دارد. در شاهدِ handler_thresholds، مبدأ INFO را میپذیرد، اما handlerِ boundary حداقل WARNING و handlerِ middle حداقل ERROR میخواهند. تنها handlerِ leaf تحویل میگیرد. شمارندهٔ فیلترِ دو handler دیگر صفر میماند؛ چون آستانهٔ handler پیش از فیلترِ همان handler بررسی شده است. این شمارندهها مشاهداتِ فراخوانیاند، نه زمانسنجی یا برآوردِ هزینه.
رابطِ ما فقط همین سطحها و آرایشِ سهجایگاهی را دارد. سطحِ دلخواه، پیکربندیِ متنی، افزونهٔ خارجی و بازپیکربندیِ همزمان وارد قرارداد نشدهاند. برنامه در فرایندِ مستقل اجرا میشود و خاموشسازیِ سراسریِ logging را تغییر نمیدهد؛ اگر از پیش فعال باشد، نمونه صریحاً اجرا را رد میکند. انتخابِ این مرزها، شرطِ آزمایشِ قابلبازتولید است و نسخهٔ عمومیِ پیکربندیِ هر برنامه نیست.
۳. propagate و فیلترها در کدام نقطه اثر میگذارند؟

جریانِ ثبت در راهنمای رسمی، پذیرشِ مبدأ و تحویل به handler را دو مرحله نشان میدهد. فیلترِ مبدأ، رکوردِ ساختهشده را میبیند. اگر آن را رد کند، handlerها فراخوانی نمیشوند؛ پس شمارِ مشاهدهٔ فیلترِ مبدأ میتواند یک و شمارِ تحویل صفر باشد. این وضعیت را با کناررفتنِ رویداد در آستانهٔ مبدأ، که اصلاً به آن فیلتر نمیرسد، یکی نمیگیریم.
فیلترِ loggerِ بالاتر، رکوردِ فرزند را دوباره داوری نمیکند. برای نشاندادنِ تفاوت، یک رویداد را مستقیم از middle و دیگری را از leaf میفرستیم. فیلترِ ردکنندهٔ middle برای فراخوانیِ مستقیم اثر دارد، اما جلوی انتشارِ رویدادِ leaf را نمیگیرد. در مقابل، فیلترِ ردکنندهٔ handlerِ middle فقط همان تحویل را حذف میکند؛ handlerِ boundary هنوز میتواند رکورد را بگیرد.
وقتی propagate در middle خاموش باشد، handlerِ خودِ middle همچنان در مسیر است و ادامه به boundary متوقف میشود. خاموشکردنِ آن در leaf فقط handlerِ خودِ leaf را باقی میگذارد. همچنین دوبار addHandler با همان شیء روی همان logger، در این نمونه اتصالِ دوم نمیسازد؛ وصلکردنِ همان شیء به دو logger متفاوت، دو جایگاهِ تحویل است. هیچیک از این رفتارها حذفِ تکرارِ رویداد در منطقِ کسبوکار را پیاده نمیکند.
۴. کدِ کامل، شاهدهای دستی و مرجعِ مسیر مستقل

فایل logging_propagation_routing.py را ذخیره و در فرایندِ مستقلِ پایتون اجرا کنید. کلاس Capture در emit فقط رکورد را به فهرست میافزاید؛ دوباره logging یا پیکربندی را صدا نمیزند. فیلترها نیز فقط شمارنده و پاسخِ بولیِ ثابت دارند. نام و parentِ loggerها را تغییر نمیدهیم؛ رابطهٔ پدر و فرزند از getLogger به دست میآید و پیش از مشاهده بررسی میشود.
"""Finite standalone logging routes, not a server or durable log pipeline.
Three private named loggers; a boundary prevents propagation to the host root.
Observe actual logging dispatch, then compare a separate finite relation oracle.
"""
import itertools
import json
import logging
import platform
import re
import sys
import uuid
NODES = ("boundary", "middle", "leaf")
LEVELS = (10, 20, 30, 40, 50)
ID = re.compile(r"[A-Za-z][A-Za-z0-9_-]{0,11}")
KEYS = {"logger_levels", "propagate", "logger_filters", "handler_levels",
"handler_filters", "shared_handler"}
def profile(**updates):
result = {"logger_levels": (20, 0, 10), "propagate": (True, True),
"logger_filters": (True, True, True),
"handler_levels": (0, 0, 0),
"handler_filters": (True, True, True), "shared_handler": False}
result.update(updates)
return result
def validate(config, events):
if type(config) is not dict or set(config) != KEYS:
raise ValueError("exact_profile_keys")
for key in ("logger_levels", "logger_filters", "handler_levels", "handler_filters"):
if type(config[key]) is not tuple or len(config[key]) != 3:
raise ValueError("three_node_tuple")
if type(config["propagate"]) is not tuple or len(config["propagate"]) != 2:
raise ValueError("two_propagation_flags")
for value in config["logger_levels"] + config["handler_levels"]:
if type(value) is not int or value not in (0,) + LEVELS:
raise ValueError("standard_integer_level")
if config["logger_levels"][0] == 0:
raise ValueError("explicit_boundary_level")
for value in config["propagate"] + config["logger_filters"] + config["handler_filters"] + (config["shared_handler"],):
if type(value) is not bool:
raise ValueError("exact_boolean_flag")
if config["shared_handler"] and (
config["handler_levels"][0] != config["handler_levels"][2]
or config["handler_filters"][0] != config["handler_filters"][2]):
raise ValueError("one_shared_handler_one_configuration")
if type(events) is not tuple or len(events) > 8:
raise ValueError("at_most_eight_events")
seen = set()
for event in events:
if type(event) is not tuple or len(event) != 3:
raise ValueError("event_tuple")
event_id, origin, level = event
if type(event_id) is not str or ID.fullmatch(event_id) is None or event_id in seen:
raise ValueError("unique_bounded_ASCII_event_id")
if type(origin) is not int or origin not in (0, 1, 2):
raise ValueError("origin_index")
if type(level) is not int or level not in LEVELS:
raise ValueError("positive_standard_event_level")
seen.add(event_id)
class Capture(logging.Handler):
"""emit only appends supplied record references; no logging/configuration."""
def __init__(self, label, sink):
super().__init__()
self.label, self.sink = label, sink
def emit(self, record):
self.sink.append((self.label, record))
def observe(config, events):
validate(config, events) # All own arguments checked before private logger creation.
# UUID affects only private registry names, never the reported route data.
prefix = "lesson.routing." + uuid.uuid4().hex + ".boundary"
loggers = (logging.getLogger(prefix), logging.getLogger(prefix + ".middle"),
logging.getLogger(prefix + ".middle.leaf"))
assert loggers[2].parent is loggers[1] and loggers[1].parent is loggers[0]
before = [(item.level, item.propagate, tuple(item.handlers), tuple(item.filters))
for item in loggers]
assert all(not item.handlers and not item.filters for item in loggers)
sink, origin_records = [], []
origin_calls, handler_calls = [0, 0, 0], {}
handlers, attached, origin_filters = [], [], []
name_to_index = {item.name: index for index, item in enumerate(loggers)}
def origin_filter(index):
def accept(record):
origin_calls[index] += 1
origin_records.append(record)
return config["logger_filters"][index]
return accept
def handler_filter(label, index):
def accept(record):
handler_calls[label] += 1
return config["handler_filters"][index]
return accept
try:
for index, item in enumerate(loggers):
item.setLevel(config["logger_levels"][index])
item.propagate = False if index == 0 else config["propagate"][index - 1]
predicate = origin_filter(index)
item.addFilter(predicate)
origin_filters.append((item, predicate))
effective = [item.getEffectiveLevel() for item in loggers]
for index, item in enumerate(loggers):
if config["shared_handler"] and index == 2:
handler = handlers[0]
else:
label = "shared" if config["shared_handler"] and index == 0 else NODES[index]
handler = Capture(label, sink)
handler.setLevel(config["handler_levels"][index])
handler_calls[label] = 0
handler.addFilter(handler_filter(label, index))
handlers.append(handler)
item.addHandler(handler)
# addHandler has no second attachment to the same logger instance.
item.addHandler(handler)
attached.append((item, handler))
attachment_sizes = [len(item.handlers) for item in loggers]
for event_id, origin, level in events:
loggers[origin].log(level, event_id)
by_event = {record.msg: record for record in origin_records}
deliveries = [{"handler": label, "event_id": record.msg,
"origin": name_to_index[record.name], "level": record.levelno}
for label, record in sink]
same_records = all(record is by_event[record.msg] for _, record in sink)
answer = {"effective_levels": effective, "deliveries": deliveries,
"origin_filter_calls": list(origin_calls),
"handler_filter_calls": dict(handler_calls),
"origin_records_seen": [record.msg for record in origin_records],
"delivered_records_are_same_origin_objects": same_records,
"one_attachment_per_logger_after_duplicate_add": attachment_sizes == [1, 1, 1]}
finally:
for item, handler in attached:
item.removeHandler(handler)
for handler in handlers:
handler.close()
for item, predicate in origin_filters:
item.removeFilter(predicate)
for item, state in zip(loggers, before):
item.setLevel(state[0])
item.propagate = state[1]
restored = [(item.level, item.propagate, tuple(item.handlers), tuple(item.filters))
for item in loggers] == before
assert restored
answer["private_configuration_restored"] = restored
# Newly requested logger names remain registered normally; registry is not reset.
return answer
def relation_oracle(config, events):
"""Independent three-node admission relation; no logging/main calls.
Explicit reachability pairs replace a library parent traversal. The model
only covers our constant boolean predicates and fixed attachment layout.
"""
ll = config["logger_levels"]
effective = [ll[0], ll[1] or ll[0], ll[2] or ll[1] or ll[0]]
reach = {(0, 0), (1, 1), (2, 2)}
if config["propagate"][0]:
reach.add((1, 0))
if config["propagate"][1]:
reach.add((2, 1))
if config["propagate"][0]:
reach.add((2, 0))
labels = ("shared", "middle", "shared") if config["shared_handler"] else NODES
result, origins, calls, ids = [], [0, 0, 0], dict.fromkeys(labels, 0), []
for event_id, origin, level in events:
if level < effective[origin]:
continue
origins[origin] += 1
ids.append(event_id) # Logger filter runs after record creation.
if not config["logger_filters"][origin]:
continue
for destination in (2, 1, 0):
if (origin, destination) not in reach or level < config["handler_levels"][destination]:
continue
label = labels[destination]
calls[label] += 1
if config["handler_filters"][destination]:
result.append({"handler": label, "event_id": event_id,
"origin": origin, "level": level})
return {"effective_levels": effective, "deliveries": result,
"origin_filter_calls": origins, "handler_filter_calls": calls,
"origin_records_seen": ids}
def agrees(actual, expected):
return all(actual[key] == value for key, value in expected.items())
def rejects(call, message):
try:
call()
except ValueError as error:
return str(error) == message
return False
def run_example():
root = logging.getLogger()
# The standalone process uses the documented initial global disable setting.
# No call to logging.disable/basicConfig/setLogRecordFactory occurs here.
if root.manager.disable != 0:
raise RuntimeError("standalone_default_global_disable_required")
host_before = (root.level, tuple(root.handlers), tuple(root.filters),
logging.getLogRecordFactory(), root.manager.disable)
event = (("E1", 2, 20),)
all_routes = observe(profile(), event)
child_override = observe(profile(logger_levels=(20, 40, 10),
logger_filters=(False, False, True)), event)
inherited = observe(profile(logger_levels=(20, 40, 0)), event)
handler_threshold = observe(profile(handler_levels=(30, 40, 0)), event)
origin_veto = observe(profile(logger_filters=(True, True, False)), event)
direct_parent = observe(profile(logger_levels=(20, 10, 10),
logger_filters=(False, False, True)),
(("parent", 1, 20), ("child", 2, 20)))
handler_veto = observe(profile(handler_filters=(True, False, True)), event)
middle_stop = observe(profile(propagate=(False, True)), event)
leaf_stop = observe(profile(propagate=(True, False)), event)
shared = observe(profile(shared_handler=True), event)
low = observe(profile(logger_levels=(20, 0, 0)), (("debug", 2, 10),))
family_profiles, family_events, family_ok = 0, 0, True
family = itertools.product((0, 40), (0, 10), (False, True), (False, True),
(0, 30), (0, 30), (0, 30), (False, True), (False, True))
probes = (("debug", 2, 10), ("info", 2, 20), ("error", 2, 40))
for parent, child, up_mid, up_leaf, h0, h1, h2, origin_allow, middle_allow in family:
config = profile(logger_levels=(20, parent, child), propagate=(up_mid, up_leaf),
handler_levels=(h0, h1, h2), logger_filters=(False, False, origin_allow),
handler_filters=(True, middle_allow, True))
comparison = agrees(observe(config, probes), relation_oracle(config, probes))
family_ok = comparison and family_ok
family_profiles += 1
family_events += len(probes)
class IntSubclass(int):
pass
class StrSubclass(str):
pass
bad = []
for level in (True, 20.0, "INFO", -1, 60, IntSubclass(20)):
bad.append((profile(logger_levels=(20, 0, level)), event, "standard_integer_level"))
for flag in (0, 1, None, "true"):
bad.append((profile(propagate=(True, flag)), event, "exact_boolean_flag"))
bad += [(profile(logger_levels=(0, 0, 10)), event, "explicit_boundary_level"),
(profile(handler_levels=[0, 0, 0]), event, "three_node_tuple"),
(profile(propagate=(True,)), event, "two_propagation_flags"),
(profile(shared_handler=True, handler_levels=(20, 0, 30)), event,
"one_shared_handler_one_configuration"),
({**profile(), "unknown": 1}, event, "exact_profile_keys"),
([], event, "exact_profile_keys"),
(profile(), list(event), "at_most_eight_events"),
(profile(), tuple(("E" + str(i), 2, 20) for i in range(9)), "at_most_eight_events"),
(profile(), (["E1", 2, 20],), "event_tuple"),
(profile(), event + event, "unique_bounded_ASCII_event_id")]
for event_id in ("", "1bad", "X" * 13, "with space", "line\n", "alpha.beta", StrSubclass("ID"), 1):
bad.append((profile(), ((event_id, 2, 20),), "unique_bounded_ASCII_event_id"))
for origin in (True, -1, 3, 2.0):
bad.append((profile(), (("E1", origin, 20),), "origin_index"))
for level in (True, 0, 15, "INFO"):
bad.append((profile(), (("E1", 2, level),), "positive_standard_event_level"))
invalid_ok = all(rejects(lambda c=c, e=e: observe(c, e), why) for c, e, why in bad)
boundary_events = tuple(("E" + str(i), 2, 20) for i in range(8))
boundary = observe(profile(), boundary_events)
config = profile()
preserved = {key: value for key, value in config.items()}
fresh = observe(config, event)
fresh["deliveries"][0]["handler"] = "changed_return_only"
repeated = observe(config, event)
host_after = (root.level, tuple(root.handlers), tuple(root.filters),
logging.getLogRecordFactory(), root.manager.disable)
checks = {
"manual_one_event_three_handlers_in_origin_to_ancestor_order":
[row["handler"] for row in all_routes["deliveries"]] == ["leaf", "middle", "boundary"]
and all_routes["origin_records_seen"] == ["E1"],
"explicit_child_level_bypasses_ancestor_logger_levels_and_filters":
child_override["deliveries"] == all_routes["deliveries"]
and child_override["origin_filter_calls"] == [0, 0, 1],
"NOTSET_inherits_effective_level_and_origin_threshold_precedes_filter":
inherited["effective_levels"] == [20, 40, 40]
and inherited["deliveries"] == [] and inherited["origin_records_seen"] == []
and low["origin_filter_calls"] == [0, 0, 0],
"handler_thresholds_apply_after_origin_admission_without_extra_logger_gate":
[row["handler"] for row in handler_threshold["deliveries"]] == ["leaf"]
and handler_threshold["handler_filter_calls"] == {"boundary": 0, "middle": 0, "leaf": 1},
"origin_filter_veto_observes_created_record_but_stops_all_handler_dispatch":
origin_veto["origin_records_seen"] == ["E1"] and origin_veto["deliveries"] == []
and all(value == 0 for value in origin_veto["handler_filter_calls"].values()),
"ancestor_logger_filter_is_skipped_for_child_but_used_for_direct_origin":
direct_parent["origin_filter_calls"] == [0, 1, 1]
and [row["event_id"] for row in direct_parent["deliveries"]] == ["child"] * 3,
"handler_filter_veto_is_local_and_does_not_stop_other_ancestors":
[row["handler"] for row in handler_veto["deliveries"]] == ["leaf", "boundary"]
and handler_veto["handler_filter_calls"] == {"boundary": 1, "middle": 1, "leaf": 1},
"middle_propagation_false_includes_middle_handler_then_stops_above":
[row["handler"] for row in middle_stop["deliveries"]] == ["leaf", "middle"],
"origin_propagation_false_keeps_own_handler_only":
[row["handler"] for row in leaf_stop["deliveries"]] == ["leaf"],
"shared_handler_on_two_nodes_delivers_same_record_twice_without_deduplication":
[row["handler"] for row in shared["deliveries"]] == ["shared", "middle", "shared"]
and shared["handler_filter_calls"]["shared"] == 2
and shared["origin_filter_calls"] == [0, 0, 1]
and shared["delivered_records_are_same_origin_objects"] is True,
"adding_same_handler_twice_to_one_logger_does_not_duplicate_that_attachment":
all_routes["one_attachment_per_logger_after_duplicate_add"] is True
and shared["one_attachment_per_logger_after_duplicate_add"] is True,
"bounded_exact_input_contract_and_inclusive_eight_event_endpoint":
invalid_ok and len(boundary["origin_records_seen"]) == 8
and len(boundary["deliveries"]) == 24
and observe(profile(), ())["deliveries"] == [],
"finite_configuration_family_matches_independent_explicit_relation_oracle_ONE_group":
family_profiles == 512 and family_events == 1536 and family_ok,
"fresh_outputs_input_preservation_private_cleanup_and_host_configuration_unchanged":
repeated["deliveries"] == all_routes["deliveries"] and config == preserved
and fresh["deliveries"][0]["handler"] == "changed_return_only"
and repeated["private_configuration_restored"] is True and host_before == host_after,
}
assert len(checks) == 14 and all(type(value) is bool and value for value in checks.values()), checks
compact = lambda value: {"handlers": [row["handler"] for row in value["deliveries"]],
"origin_filter_calls": value["origin_filter_calls"],
"handler_filter_calls": value["handler_filter_calls"]}
return {"python": platform.python_version(), "scope": "standalone_private_three_logger_in_memory_fixture",
"node_order": list(NODES), "event_levels_are_severity_codes_not_time_or_units": True,
"manual_event": {"event_id": "E1", "origin": 2, "level": 20},
"manual_routes": {"all_handlers": compact(all_routes), "child_override": compact(child_override),
"NOTSET_inherits_ERROR": compact(inherited),
"handler_thresholds": compact(handler_threshold),
"origin_filter_veto": compact(origin_veto),
"middle_stop": compact(middle_stop), "leaf_stop": compact(leaf_stop),
"shared_handler": compact(shared)},
"independent_family_profiles_inside_ONE_group": family_profiles,
"independent_family_event_cases_inside_ONE_group": family_events,
"invalid_fixtures_inside_ONE_group": len(bad), "meaningful_named_groups": len(checks),
"no_file_server_network_thread_root_configuration_or_global_disable_changes": True,
"logger_registry_note": "Fresh private logger names remain registered normally; no claim of whole-registry restoration.",
"checks": checks}
if __name__ == "__main__":
payload = json.dumps(run_example(), ensure_ascii=True, indent=2, allow_nan=False) + "\n"
sys.stdout.buffer.write(payload.encode("utf-8"))
مرجعِ relation_oracle، logging یا observe را صدا نمیزند. برای سه جایگاه، زوجهای دسترسیِ مجاز را صریحاً میسازد و از روی جدولِ سطحها و پرچمها، فهرستِ موردانتظار را حساب میکند. شاهدهای سادهٔ بخشهای قبل نیز انتظارِ دستیِ جدا دارند. این مرجع فقط مدلِ محدودِ همین آرایش و فیلترهای ثابت است؛ شبیهسازِ همهٔ قابلیتهای logging یا جایگزینِ کتابخانه نیست.
خروجیِ ثبتشده چهارده گروهِ بولیِ نامدار دارد. خانوادهٔ محدود شامل ۵۱۲ پیکربندی و سه رویداد برای هرکدام است؛ ۱۵۳۶ حالتِ رویداد، داخلِ یک گروهِ مقایسه با مرجع قرار میگیرند. سیوشش ورودیِ نامعتبر هم یک گروهاند. افزودنِ تعدادِ حالتها، آنها را به گروههای مستقل تبدیل نمیکند. خروجیِ کامل، مسیرهای دستی و شمارندهها را نگه میدارد تا خواننده فقط به یک عبارتِ موفقیت تکیه نکند.
{
"python": "3.14.5",
"scope": "standalone_private_three_logger_in_memory_fixture",
"node_order": [
"boundary",
"middle",
"leaf"
],
"event_levels_are_severity_codes_not_time_or_units": true,
"manual_event": {
"event_id": "E1",
"origin": 2,
"level": 20
},
"manual_routes": {
"all_handlers": {
"handlers": [
"leaf",
"middle",
"boundary"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 1,
"middle": 1,
"leaf": 1
}
},
"child_override": {
"handlers": [
"leaf",
"middle",
"boundary"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 1,
"middle": 1,
"leaf": 1
}
},
"NOTSET_inherits_ERROR": {
"handlers": [],
"origin_filter_calls": [
0,
0,
0
],
"handler_filter_calls": {
"boundary": 0,
"middle": 0,
"leaf": 0
}
},
"handler_thresholds": {
"handlers": [
"leaf"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 0,
"middle": 0,
"leaf": 1
}
},
"origin_filter_veto": {
"handlers": [],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 0,
"middle": 0,
"leaf": 0
}
},
"middle_stop": {
"handlers": [
"leaf",
"middle"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 0,
"middle": 1,
"leaf": 1
}
},
"leaf_stop": {
"handlers": [
"leaf"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"boundary": 0,
"middle": 0,
"leaf": 1
}
},
"shared_handler": {
"handlers": [
"shared",
"middle",
"shared"
],
"origin_filter_calls": [
0,
0,
1
],
"handler_filter_calls": {
"shared": 2,
"middle": 1
}
}
},
"independent_family_profiles_inside_ONE_group": 512,
"independent_family_event_cases_inside_ONE_group": 1536,
"invalid_fixtures_inside_ONE_group": 36,
"meaningful_named_groups": 14,
"no_file_server_network_thread_root_configuration_or_global_disable_changes": true,
"logger_registry_note": "Fresh private logger names remain registered normally; no claim of whole-registry restoration.",
"checks": {
"manual_one_event_three_handlers_in_origin_to_ancestor_order": true,
"explicit_child_level_bypasses_ancestor_logger_levels_and_filters": true,
"NOTSET_inherits_effective_level_and_origin_threshold_precedes_filter": true,
"handler_thresholds_apply_after_origin_admission_without_extra_logger_gate": true,
"origin_filter_veto_observes_created_record_but_stops_all_handler_dispatch": true,
"ancestor_logger_filter_is_skipped_for_child_but_used_for_direct_origin": true,
"handler_filter_veto_is_local_and_does_not_stop_other_ancestors": true,
"middle_propagation_false_includes_middle_handler_then_stops_above": true,
"origin_propagation_false_keeps_own_handler_only": true,
"shared_handler_on_two_nodes_delivers_same_record_twice_without_deduplication": true,
"adding_same_handler_twice_to_one_logger_does_not_duplicate_that_attachment": true,
"bounded_exact_input_contract_and_inclusive_eight_event_endpoint": true,
"finite_configuration_family_matches_independent_explicit_relation_oracle_ONE_group": true,
"fresh_outputs_input_preservation_private_cleanup_and_host_configuration_unchanged": true
}
}
پس از هر مشاهده، handlerها برداشته و بسته میشوند و فیلتر، سطح و propagate خصوصی بازگردانده میشوند. تنظیماتِ خواندهشدهٔ root، کارخانهٔ رکورد و خاموشسازیِ سراسری نیز ثابت میمانند. نامهای خصوصیِ تازه طبق رفتارِ عادی در دفترِ loggerها ثبت میمانند؛ ادعای بازگرداندنِ کلِ دفتر یا مصرفِ حافظهٔ ثابت نداریم. نسخهٔ اجرای واقعی در JSON آمده و از نسخهٔ 3.14.8 مستنداتِ خواندهشده جداست.
این آزمایش تحویل در حافظه را میسنجد. ماندگاری، خرابیِ مقصد، ترتیبِ چند نخ و تحویلِ دقیقاً یکباره بررسی نشدهاند. حتی زمانِ روی دو سطر، موضوعِ مستقلی است؛ درسِ ساعتِ تکراری و UTC در لاگ تفسیرِ برچسبِ زمانی را جدا میآزماید. منابع در ۲۱ اکتبر ۲۰۲۶ خوانده شدهاند؛ این آموزشِ بنیادی، گزارشِ تغییرِ تازهٔ کتابخانه نیست.
۵. چهار پرسش پاسخدار برای عیبیابی مسیرِ لاگ

پرسش اول: middle سطحِ ERROR دارد؛ چرا INFO فرزند هنوز به handlerِ آن میرسد؟ پاسخ: leaf با سطحِ صریحِ خودش رویداد را پذیرفته است. هنگام انتشار، handlerِ middle مقصد است و سطحِ خودِ loggerِ middle دوباره اعمال نمیشود. آستانهٔ handler میتواند همان تحویل را رد کند.
پرسش دوم: آیا فیلترِ ردکننده روی boundary، همهٔ رویدادهای فرزند را متوقف میکند؟ پاسخ: خیر؛ فیلترِ logger برای ورود از همان logger است. برای محدودکردنِ تحویل به یک handler باید فیلترِ همان handler را در آرایشِ مناسب بررسی کرد؛ این پاسخ، سیاستِ دسترسی یا امنیتِ کل سامانه نیست.
پرسش سوم: shared دوبار دیده شده است؛ آیا باید یکی را بهعنوان خطای فراخوانی حذف کنیم؟ پاسخ: ابتدا اتصالها را بررسی کنید. در شاهدِ ما یک رکوردِ یکسان از دو جایگاه به همان handler رسیده است. حذفِ یک اتصالِ اضافی مسئلهٔ مسیر را حل میکند؛ حذفِ کورِ پیامهای هممتن ممکن است دو رخدادِ واقعی را یکی کند.
پرسش چهارم: برای داشتنِ خروجیِ تنها در middle، خاموشکردنِ propagate آن کافی است؟ پاسخ: خیر؛ این کار ادامه به boundary را میبندد، اما handlerِ leaf هنوز فعال است. اتصال و شرطِ پذیرشِ هر مقصدِ پایینتر را هم باید روشن کنید. شاهدِ middle_stop عمداً هر دو مقصدِ leaf و middle را حفظ کرده است.
منابع و مطالعهٔ بیشتر
بررسی منابع: ۲۹ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

