Skip to content

Commit 488f399

Browse files
authored
Ensure cleanup when forked process ends (#785)
1 parent 2155e44 commit 488f399

File tree

1 file changed

+15
-1
lines changed

1 file changed

+15
-1
lines changed

logfire/_internal/config.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,7 @@ def fix_pid(): # pragma: no cover
946946
set_meter_provider(self._meter_provider)
947947

948948
@atexit.register
949-
def _exit_open_spans(): # type: ignore[reportUnusedFunction] # pragma: no cover
949+
def exit_open_spans(): # pragma: no cover
950950
# Ensure that all open spans are closed when the program exits.
951951
# OTEL registers its own atexit callback in the tracer/meter providers to shut them down.
952952
# Registering this callback here after the OTEL one means that this runs first.
@@ -961,6 +961,20 @@ def _exit_open_spans(): # type: ignore[reportUnusedFunction] # pragma: no cove
961961
# which would log a warning "Calling end() on an ended span."
962962
span.end = lambda *_, **__: None # type: ignore
963963

964+
# atexit isn't called in forked processes, patch os._exit to ensure cleanup.
965+
# https://github.com/pydantic/logfire/issues/779
966+
original_os_exit = os._exit
967+
968+
def patched_os_exit(code: int): # pragma: no cover
969+
try:
970+
exit_open_spans()
971+
self.force_flush()
972+
except: # noqa # weird errors can happen during shutdown, ignore them *all* with a bare except
973+
pass
974+
return original_os_exit(code)
975+
976+
os._exit = patched_os_exit
977+
964978
self._initialized = True
965979

966980
# set up context propagation for ThreadPoolExecutor and ProcessPoolExecutor

0 commit comments

Comments
 (0)