Major fixes and new features
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
353
venv/lib/python3.12/site-packages/structlog/_output.py
Normal file
353
venv/lib/python3.12/site-packages/structlog/_output.py
Normal file
@@ -0,0 +1,353 @@
|
||||
# SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the MIT License. See the LICENSE file in the root of this
|
||||
# repository for complete details.
|
||||
|
||||
"""
|
||||
Logger classes responsible for output.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from pickle import PicklingError
|
||||
from sys import stderr, stdout
|
||||
from typing import IO, Any, BinaryIO, TextIO
|
||||
|
||||
from structlog._utils import until_not_interrupted
|
||||
|
||||
|
||||
WRITE_LOCKS: dict[IO[Any], threading.Lock] = {}
|
||||
|
||||
|
||||
def _get_lock_for_file(file: IO[Any]) -> threading.Lock:
|
||||
lock = WRITE_LOCKS.get(file)
|
||||
if lock is None:
|
||||
lock = threading.Lock()
|
||||
WRITE_LOCKS[file] = lock
|
||||
|
||||
return lock
|
||||
|
||||
|
||||
class PrintLogger:
|
||||
"""
|
||||
Print events into a file.
|
||||
|
||||
Arguments:
|
||||
|
||||
file: File to print to. (default: `sys.stdout`)
|
||||
|
||||
>>> from structlog import PrintLogger
|
||||
>>> PrintLogger().info("hello")
|
||||
hello
|
||||
|
||||
Useful if you follow `current logging best practices
|
||||
<logging-best-practices>`.
|
||||
|
||||
Also very useful for testing and examples since `logging` is finicky in
|
||||
doctests.
|
||||
|
||||
.. versionchanged:: 22.1.0
|
||||
The implementation has been switched to use `print` for better
|
||||
monkeypatchability.
|
||||
"""
|
||||
|
||||
def __init__(self, file: TextIO | None = None):
|
||||
self._file = file or stdout
|
||||
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __getstate__(self) -> str:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if self._file is stdout:
|
||||
return "stdout"
|
||||
|
||||
if self._file is stderr:
|
||||
return "stderr"
|
||||
|
||||
raise PicklingError(
|
||||
"Only PrintLoggers to sys.stdout and sys.stderr can be pickled."
|
||||
)
|
||||
|
||||
def __setstate__(self, state: Any) -> None:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if state == "stdout":
|
||||
self._file = stdout
|
||||
else:
|
||||
self._file = stderr
|
||||
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __deepcopy__(self, memodict: dict[str, object]) -> PrintLogger:
|
||||
"""
|
||||
Create a new PrintLogger with the same attributes. Similar to pickling.
|
||||
"""
|
||||
if self._file not in (stdout, stderr):
|
||||
raise copy.error(
|
||||
"Only PrintLoggers to sys.stdout and sys.stderr "
|
||||
"can be deepcopied."
|
||||
)
|
||||
|
||||
newself = self.__class__(self._file)
|
||||
|
||||
newself._lock = _get_lock_for_file(newself._file)
|
||||
|
||||
return newself
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<PrintLogger(file={self._file!r})>"
|
||||
|
||||
def msg(self, message: str) -> None:
|
||||
"""
|
||||
Print *message*.
|
||||
"""
|
||||
f = self._file if self._file is not stdout else None
|
||||
with self._lock:
|
||||
until_not_interrupted(print, message, file=f, flush=True)
|
||||
|
||||
log = debug = info = warn = warning = msg
|
||||
fatal = failure = err = error = critical = exception = msg
|
||||
|
||||
|
||||
class PrintLoggerFactory:
|
||||
r"""
|
||||
Produce `PrintLogger`\ s.
|
||||
|
||||
To be used with `structlog.configure`\ 's ``logger_factory``.
|
||||
|
||||
Arguments:
|
||||
|
||||
file: File to print to. (default: `sys.stdout`)
|
||||
|
||||
Positional arguments are silently ignored.
|
||||
|
||||
.. versionadded:: 0.4.0
|
||||
"""
|
||||
|
||||
def __init__(self, file: TextIO | None = None):
|
||||
self._file = file
|
||||
|
||||
def __call__(self, *args: Any) -> PrintLogger:
|
||||
return PrintLogger(self._file)
|
||||
|
||||
|
||||
class WriteLogger:
|
||||
"""
|
||||
Write events into a file.
|
||||
|
||||
Arguments:
|
||||
|
||||
file: File to print to. (default: `sys.stdout`)
|
||||
|
||||
>>> from structlog import WriteLogger
|
||||
>>> WriteLogger().info("hello")
|
||||
hello
|
||||
|
||||
Useful if you follow
|
||||
`current logging best practices <logging-best-practices>`.
|
||||
|
||||
Also very useful for testing and examples since `logging` is finicky in
|
||||
doctests.
|
||||
|
||||
A little faster and a little less versatile than `structlog.PrintLogger`.
|
||||
|
||||
.. versionadded:: 22.1.0
|
||||
"""
|
||||
|
||||
def __init__(self, file: TextIO | None = None):
|
||||
self._file = file or sys.stdout
|
||||
self._write = self._file.write
|
||||
self._flush = self._file.flush
|
||||
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __getstate__(self) -> str:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if self._file is stdout:
|
||||
return "stdout"
|
||||
|
||||
if self._file is stderr:
|
||||
return "stderr"
|
||||
|
||||
raise PicklingError(
|
||||
"Only WriteLoggers to sys.stdout and sys.stderr can be pickled."
|
||||
)
|
||||
|
||||
def __setstate__(self, state: Any) -> None:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if state == "stdout":
|
||||
self._file = stdout
|
||||
else:
|
||||
self._file = stderr
|
||||
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __deepcopy__(self, memodict: dict[str, object]) -> WriteLogger:
|
||||
"""
|
||||
Create a new WriteLogger with the same attributes. Similar to pickling.
|
||||
"""
|
||||
if self._file not in (sys.stdout, sys.stderr):
|
||||
raise copy.error(
|
||||
"Only WriteLoggers to sys.stdout and sys.stderr "
|
||||
"can be deepcopied."
|
||||
)
|
||||
|
||||
newself = self.__class__(self._file)
|
||||
|
||||
newself._write = newself._file.write
|
||||
newself._flush = newself._file.flush
|
||||
newself._lock = _get_lock_for_file(newself._file)
|
||||
|
||||
return newself
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<WriteLogger(file={self._file!r})>"
|
||||
|
||||
def msg(self, message: str) -> None:
|
||||
"""
|
||||
Write and flush *message*.
|
||||
"""
|
||||
with self._lock:
|
||||
until_not_interrupted(self._write, message + "\n")
|
||||
until_not_interrupted(self._flush)
|
||||
|
||||
log = debug = info = warn = warning = msg
|
||||
fatal = failure = err = error = critical = exception = msg
|
||||
|
||||
|
||||
class WriteLoggerFactory:
|
||||
r"""
|
||||
Produce `WriteLogger`\ s.
|
||||
|
||||
To be used with `structlog.configure`\ 's ``logger_factory``.
|
||||
|
||||
Arguments:
|
||||
|
||||
file: File to print to. (default: `sys.stdout`)
|
||||
|
||||
Positional arguments are silently ignored.
|
||||
|
||||
.. versionadded:: 22.1.0
|
||||
"""
|
||||
|
||||
def __init__(self, file: TextIO | None = None):
|
||||
self._file = file
|
||||
|
||||
def __call__(self, *args: Any) -> WriteLogger:
|
||||
return WriteLogger(self._file)
|
||||
|
||||
|
||||
class BytesLogger:
|
||||
r"""
|
||||
Writes bytes into a file.
|
||||
|
||||
Arguments:
|
||||
file: File to print to. (default: `sys.stdout`\ ``.buffer``)
|
||||
|
||||
Useful if you follow `current logging best practices
|
||||
<logging-best-practices>` together with a formatter that returns bytes
|
||||
(e.g. `orjson <https://github.com/ijl/orjson>`_).
|
||||
|
||||
.. versionadded:: 20.2.0
|
||||
"""
|
||||
__slots__ = ("_file", "_write", "_flush", "_lock")
|
||||
|
||||
def __init__(self, file: BinaryIO | None = None):
|
||||
self._file = file or sys.stdout.buffer
|
||||
self._write = self._file.write
|
||||
self._flush = self._file.flush
|
||||
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __getstate__(self) -> str:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if self._file is sys.stdout.buffer:
|
||||
return "stdout"
|
||||
|
||||
if self._file is sys.stderr.buffer:
|
||||
return "stderr"
|
||||
|
||||
raise PicklingError(
|
||||
"Only BytesLoggers to sys.stdout and sys.stderr can be pickled."
|
||||
)
|
||||
|
||||
def __setstate__(self, state: Any) -> None:
|
||||
"""
|
||||
Our __getattr__ magic makes this necessary.
|
||||
"""
|
||||
if state == "stdout":
|
||||
self._file = sys.stdout.buffer
|
||||
else:
|
||||
self._file = sys.stderr.buffer
|
||||
|
||||
self._write = self._file.write
|
||||
self._flush = self._file.flush
|
||||
self._lock = _get_lock_for_file(self._file)
|
||||
|
||||
def __deepcopy__(self, memodict: dict[str, object]) -> BytesLogger:
|
||||
"""
|
||||
Create a new BytesLogger with the same attributes. Similar to pickling.
|
||||
"""
|
||||
if self._file not in (sys.stdout.buffer, sys.stderr.buffer):
|
||||
raise copy.error(
|
||||
"Only BytesLoggers to sys.stdout and sys.stderr "
|
||||
"can be deepcopied."
|
||||
)
|
||||
|
||||
newself = self.__class__(self._file)
|
||||
|
||||
newself._write = newself._file.write
|
||||
newself._flush = newself._file.flush
|
||||
newself._lock = _get_lock_for_file(newself._file)
|
||||
|
||||
return newself
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<BytesLogger(file={self._file!r})>"
|
||||
|
||||
def msg(self, message: bytes) -> None:
|
||||
"""
|
||||
Write *message*.
|
||||
"""
|
||||
with self._lock:
|
||||
until_not_interrupted(self._write, message + b"\n")
|
||||
until_not_interrupted(self._flush)
|
||||
|
||||
log = debug = info = warn = warning = msg
|
||||
fatal = failure = err = error = critical = exception = msg
|
||||
|
||||
|
||||
class BytesLoggerFactory:
|
||||
r"""
|
||||
Produce `BytesLogger`\ s.
|
||||
|
||||
To be used with `structlog.configure`\ 's ``logger_factory``.
|
||||
|
||||
Arguments:
|
||||
|
||||
file: File to print to. (default: `sys.stdout`\ ``.buffer``)
|
||||
|
||||
Positional arguments are silently ignored.
|
||||
|
||||
.. versionadded:: 20.2.0
|
||||
"""
|
||||
__slots__ = ("_file",)
|
||||
|
||||
def __init__(self, file: BinaryIO | None = None):
|
||||
self._file = file
|
||||
|
||||
def __call__(self, *args: Any) -> BytesLogger:
|
||||
return BytesLogger(self._file)
|
||||
Reference in New Issue
Block a user