diff --git a/agent-notes/improve-crash-lifetime.md b/agent-notes/improve-crash-lifetime.md new file mode 100644 index 0000000..b75d883 --- /dev/null +++ b/agent-notes/improve-crash-lifetime.md @@ -0,0 +1,19 @@ +# Goal + +Keep fatal traceback output attached to an open error-log stream through shutdown. + +# State + +Pass the existing error-handler stream directly to faulthandler. Remove the wrapper that only forwarded fileno. Package shutdown and an exit callback disable package-enabled fault handling before logging closes its files. This PR is independently based on integrated master and contains no UTF-8 PR commits. + +# Decisions and why + +faulthandler retains a file descriptor, not a logging handler. Its stream must stay open until fault handling is disabled. The exit callback runs before logging's earlier registered shutdown callback. Disabling is a no-op when this package has not enabled fault handling. Fatal dumps remain raw output rather than formatted log records. + +# Surprises + +The removed StreamToLogger never redirected log writes; its description was misleading. + +# Next step + +Read this PR's existing package checks. Subsequent setup/reconfiguration changes must retain the same file-lifetime ordering. No merge or release. diff --git a/logger_setup/crashlogger.py b/logger_setup/crashlogger.py index e485b91..710768f 100644 --- a/logger_setup/crashlogger.py +++ b/logger_setup/crashlogger.py @@ -1,21 +1,22 @@ -import logging +import atexit import faulthandler -CRASHLOGGER_NAME = "APPCRASH" +_enabled = False -class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - """ +def enable_crashlogger(error_handler): + global _enabled + faulthandler.enable(error_handler.stream) + _enabled = True - def __init__(self, handler): - self.handler = handler - def fileno(self): - return self.handler.stream.fileno() +def disable_crashlogger(): + global _enabled + if _enabled: + faulthandler.disable() + _enabled = False -def enable_crashlogger(error_handler): - stream = StreamToLogger(error_handler) - faulthandler.enable(stream) +# logging registers its shutdown before importing this module. Exit callbacks +# run in reverse order, so release the fault descriptor before logs are closed. +atexit.register(disable_crashlogger) diff --git a/logger_setup/logger_setup.py b/logger_setup/logger_setup.py index dd82619..82baf19 100644 --- a/logger_setup/logger_setup.py +++ b/logger_setup/logger_setup.py @@ -86,4 +86,5 @@ def setup_logging( def shutdown(): + crashlogger.disable_crashlogger() logging.shutdown()