Skip to content

Commit 9ff7dcd

Browse files
committed
Add shutdown method
1 parent 9fcfbdf commit 9ff7dcd

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

src/picologging/__init__.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import atexit
12
import io
3+
import itertools
24
import os
35
import sys
46
import warnings
@@ -518,3 +520,42 @@ def makeLogRecord(dict):
518520
for k, v in dict.items():
519521
setattr(rv, k, v)
520522
return rv
523+
524+
525+
def shutdown(handlerList=None):
526+
"""
527+
Perform any cleanup actions in the logging system (e.g. flushing
528+
buffers).
529+
Should be called at application exit.
530+
"""
531+
handlers = [logger.handlers for logger in root.manager.loggerDict.values()] + [
532+
root.handlers
533+
]
534+
handlerList = list(itertools.chain(*handlers))
535+
536+
for h in reversed(handlerList):
537+
# errors might occur, for example, if files are locked
538+
# we just ignore them if raiseExceptions is not set
539+
try:
540+
if h:
541+
try:
542+
h.acquire()
543+
# MemoryHandlers might not want to be flushed on close,
544+
# but circular imports prevent us scoping this to just
545+
# those handlers. hence the default to True.
546+
if getattr(h, "flushOnClose", True):
547+
h.flush()
548+
h.close()
549+
except (OSError, ValueError):
550+
# Ignore errors which might be caused
551+
# because handlers have been closed but
552+
# references to them are still around at
553+
# application exit.
554+
pass
555+
finally:
556+
h.release()
557+
except: # ignore everything, as we're shutting down
558+
raise
559+
560+
561+
atexit.register(shutdown)

src/picologging/__init__.pyi

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,17 @@ from io import TextIOWrapper
44
from multiprocessing import Manager
55
from string import Template
66
from types import TracebackType
7-
from typing import Any, Generic, Optional, Pattern, TextIO, TypeVar, Union, overload
7+
from typing import (
8+
Any,
9+
Generic,
10+
Optional,
11+
Pattern,
12+
TextIO,
13+
TypeVar,
14+
Union,
15+
overload,
16+
Sequence,
17+
)
818

919
from _typeshed import StrPath, SupportsWrite
1020
from typing_extensions import Literal, TypeAlias
@@ -373,3 +383,6 @@ BASIC_FORMAT: str
373383

374384
def getLevelName(level: _Level) -> Any: ...
375385
def makeLogRecord(dict: Mapping[str, object]) -> LogRecord: ...
386+
def shutdown(
387+
handlerList: Sequence[Any] = ...,
388+
) -> None: ... # handlerList is undocumented

tests/unit/test_picologging.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44

55
import picologging
6+
from picologging.handlers import BufferingHandler
67

78
levels = [
89
(picologging.DEBUG, "DEBUG"),
@@ -150,3 +151,15 @@ def test_make_log_record():
150151
@pytest.mark.parametrize("encoding", ["utf-8", None])
151152
def test_basic_config_encoding(encoding):
152153
picologging.basicConfig(filename="test.txt", encoding=encoding)
154+
155+
156+
def test_shutdown():
157+
handler = BufferingHandler(capacity=1)
158+
logger = picologging.getLogger("test")
159+
logger.setLevel(picologging.DEBUG)
160+
logger.addHandler(handler)
161+
logger.debug("test")
162+
163+
picologging.shutdown()
164+
165+
assert handler.buffer == []

0 commit comments

Comments
 (0)