Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/picologging/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import atexit
import io
import itertools
import os
import sys
import warnings
Expand Down Expand Up @@ -518,3 +520,42 @@ def makeLogRecord(dict):
for k, v in dict.items():
setattr(rv, k, v)
return rv


def shutdown(handlerList=None):
"""
Perform any cleanup actions in the logging system (e.g. flushing
buffers).
Should be called at application exit.
"""
handlers = [logger.handlers for logger in root.manager.loggerDict.values()] + [
root.handlers
]
handlerList = list(itertools.chain(*handlers))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is probably a smarter way to get the list of handlers to flush/close. Any suggestions?

The standard logging uses a Weakref instance to hold refs but I guess that's not possible between C and Python types?


for h in reversed(handlerList):
# errors might occur, for example, if files are locked
# we just ignore them if raiseExceptions is not set
try:
if h:
try:
h.acquire()
# MemoryHandlers might not want to be flushed on close,
# but circular imports prevent us scoping this to just
# those handlers. hence the default to True.
if getattr(h, "flushOnClose", True):
h.flush()
h.close()
except (OSError, ValueError):
# Ignore errors which might be caused
# because handlers have been closed but
# references to them are still around at
# application exit.
pass
finally:
h.release()
except: # ignore everything, as we're shutting down
raise


atexit.register(shutdown)
15 changes: 14 additions & 1 deletion src/picologging/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ from io import TextIOWrapper
from multiprocessing import Manager
from string import Template
from types import TracebackType
from typing import Any, Generic, Optional, Pattern, TextIO, TypeVar, Union, overload
from typing import (
Any,
Generic,
Optional,
Pattern,
TextIO,
TypeVar,
Union,
overload,
Sequence,
)

from _typeshed import StrPath, SupportsWrite
from typing_extensions import Literal, TypeAlias
Expand Down Expand Up @@ -373,3 +383,6 @@ BASIC_FORMAT: str

def getLevelName(level: _Level) -> Any: ...
def makeLogRecord(dict: Mapping[str, object]) -> LogRecord: ...
def shutdown(
handlerList: Sequence[Any] = ...,
) -> None: ... # handlerList is undocumented
13 changes: 13 additions & 0 deletions tests/unit/test_picologging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest

import picologging
from picologging.handlers import BufferingHandler

levels = [
(picologging.DEBUG, "DEBUG"),
Expand Down Expand Up @@ -150,3 +151,15 @@ def test_make_log_record():
@pytest.mark.parametrize("encoding", ["utf-8", None])
def test_basic_config_encoding(encoding):
picologging.basicConfig(filename="test.txt", encoding=encoding)


def test_shutdown():
handler = BufferingHandler(capacity=1)
logger = picologging.getLogger("test")
logger.setLevel(picologging.DEBUG)
logger.addHandler(handler)
logger.debug("test")

picologging.shutdown()

assert handler.buffer == []