Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/structlog/_output.py: 38%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SPDX-License-Identifier: MIT OR Apache-2.0
2# This file is dual licensed under the terms of the Apache License, Version
3# 2.0, and the MIT License. See the LICENSE file in the root of this
4# repository for complete details.
6"""
7Logger classes responsible for output.
8"""
10from __future__ import annotations
12import copy
13import sys
14import threading
16from pickle import PicklingError
17from sys import stderr, stdout
18from typing import IO, Any, BinaryIO, TextIO
21WRITE_LOCKS: dict[IO[Any], threading.Lock] = {}
24def _get_lock_for_file(file: IO[Any]) -> threading.Lock:
25 lock = WRITE_LOCKS.get(file)
26 if lock is None:
27 lock = threading.Lock()
28 WRITE_LOCKS[file] = lock
30 return lock
33class PrintLogger:
34 """
35 Print events into a file.
37 Args:
38 file: File to print to. (default: `sys.stdout`)
40 >>> from structlog import PrintLogger
41 >>> PrintLogger().info("hello")
42 hello
44 Useful if you follow `current logging best practices
45 <logging-best-practices>`.
47 Also very useful for testing and examples since `logging` is finicky in
48 doctests.
50 .. versionchanged:: 22.1.0
51 The implementation has been switched to use `print` for better
52 monkeypatchability.
53 """
55 def __init__(self, file: TextIO | None = None):
56 self._file = file or stdout
58 self._lock = _get_lock_for_file(self._file)
60 def __getstate__(self) -> str:
61 """
62 Our __getattr__ magic makes this necessary.
63 """
64 if self._file is stdout:
65 return "stdout"
67 if self._file is stderr:
68 return "stderr"
70 raise PicklingError(
71 "Only PrintLoggers to sys.stdout and sys.stderr can be pickled."
72 )
74 def __setstate__(self, state: Any) -> None:
75 """
76 Our __getattr__ magic makes this necessary.
77 """
78 if state == "stdout":
79 self._file = stdout
80 else:
81 self._file = stderr
83 self._lock = _get_lock_for_file(self._file)
85 def __deepcopy__(self, memodict: dict[str, object]) -> PrintLogger:
86 """
87 Create a new PrintLogger with the same attributes. Similar to pickling.
88 """
89 if self._file not in (stdout, stderr):
90 raise copy.error(
91 "Only PrintLoggers to sys.stdout and sys.stderr "
92 "can be deepcopied."
93 )
95 newself = self.__class__(self._file)
97 newself._lock = _get_lock_for_file(newself._file)
99 return newself
101 def __repr__(self) -> str:
102 return f"<PrintLogger(file={self._file!r})>"
104 def msg(self, message: str) -> None:
105 """
106 Print *message*.
107 """
108 f = self._file if self._file is not stdout else None
109 with self._lock:
110 print(message, file=f, flush=True)
112 log = debug = info = warn = warning = msg
113 fatal = failure = err = error = critical = exception = msg
116class PrintLoggerFactory:
117 r"""
118 Produce `PrintLogger`\ s.
120 To be used with `structlog.configure`\ 's ``logger_factory``.
122 Args:
123 file: File to print to. (default: `sys.stdout`)
125 Positional arguments are silently ignored.
127 .. versionadded:: 0.4.0
128 """
130 def __init__(self, file: TextIO | None = None):
131 self._file = file
133 def __call__(self, *args: Any) -> PrintLogger:
134 return PrintLogger(self._file)
137class WriteLogger:
138 """
139 Write events into a file.
141 Args:
142 file: File to print to. (default: `sys.stdout`)
144 >>> from structlog import WriteLogger
145 >>> WriteLogger().info("hello")
146 hello
148 Useful if you follow
149 `current logging best practices <logging-best-practices>`.
151 Also very useful for testing and examples since `logging` is finicky in
152 doctests.
154 A little faster and a little less versatile than `structlog.PrintLogger`.
156 .. versionadded:: 22.1.0
157 """
159 def __init__(self, file: TextIO | None = None):
160 self._file = file or sys.stdout
161 self._write = self._file.write
162 self._flush = self._file.flush
164 self._lock = _get_lock_for_file(self._file)
166 def __getstate__(self) -> str:
167 """
168 Our __getattr__ magic makes this necessary.
169 """
170 if self._file is stdout:
171 return "stdout"
173 if self._file is stderr:
174 return "stderr"
176 raise PicklingError(
177 "Only WriteLoggers to sys.stdout and sys.stderr can be pickled."
178 )
180 def __setstate__(self, state: Any) -> None:
181 """
182 Our __getattr__ magic makes this necessary.
183 """
184 if state == "stdout":
185 self._file = stdout
186 else:
187 self._file = stderr
189 self._lock = _get_lock_for_file(self._file)
191 def __deepcopy__(self, memodict: dict[str, object]) -> WriteLogger:
192 """
193 Create a new WriteLogger with the same attributes. Similar to pickling.
194 """
195 if self._file not in (sys.stdout, sys.stderr):
196 raise copy.error(
197 "Only WriteLoggers to sys.stdout and sys.stderr "
198 "can be deepcopied."
199 )
201 newself = self.__class__(self._file)
203 newself._write = newself._file.write
204 newself._flush = newself._file.flush
205 newself._lock = _get_lock_for_file(newself._file)
207 return newself
209 def __repr__(self) -> str:
210 return f"<WriteLogger(file={self._file!r})>"
212 def msg(self, message: str) -> None:
213 """
214 Write and flush *message*.
215 """
216 with self._lock:
217 self._write(message + "\n")
218 self._flush()
220 log = debug = info = warn = warning = msg
221 fatal = failure = err = error = critical = exception = msg
224class WriteLoggerFactory:
225 r"""
226 Produce `WriteLogger`\ s.
228 To be used with `structlog.configure`\ 's ``logger_factory``.
230 Args:
231 file: File to print to. (default: `sys.stdout`)
233 Positional arguments are silently ignored.
235 .. versionadded:: 22.1.0
236 """
238 def __init__(self, file: TextIO | None = None):
239 self._file = file
241 def __call__(self, *args: Any) -> WriteLogger:
242 return WriteLogger(self._file)
245class BytesLogger:
246 r"""
247 Writes bytes into a file.
249 Args:
250 file: File to print to. (default: `sys.stdout`\ ``.buffer``)
252 Useful if you follow `current logging best practices
253 <logging-best-practices>` together with a formatter that returns bytes
254 (e.g. `orjson <https://github.com/ijl/orjson>`_).
256 .. versionadded:: 20.2.0
257 """
259 __slots__ = ("_file", "_flush", "_lock", "_write")
261 def __init__(self, file: BinaryIO | None = None):
262 self._file = file or sys.stdout.buffer
263 self._write = self._file.write
264 self._flush = self._file.flush
266 self._lock = _get_lock_for_file(self._file)
268 def __getstate__(self) -> str:
269 """
270 Our __getattr__ magic makes this necessary.
271 """
272 if self._file is sys.stdout.buffer:
273 return "stdout"
275 if self._file is sys.stderr.buffer:
276 return "stderr"
278 raise PicklingError(
279 "Only BytesLoggers to sys.stdout and sys.stderr can be pickled."
280 )
282 def __setstate__(self, state: Any) -> None:
283 """
284 Our __getattr__ magic makes this necessary.
285 """
286 if state == "stdout":
287 self._file = sys.stdout.buffer
288 else:
289 self._file = sys.stderr.buffer
291 self._write = self._file.write
292 self._flush = self._file.flush
293 self._lock = _get_lock_for_file(self._file)
295 def __deepcopy__(self, memodict: dict[str, object]) -> BytesLogger:
296 """
297 Create a new BytesLogger with the same attributes. Similar to pickling.
298 """
299 if self._file not in (sys.stdout.buffer, sys.stderr.buffer):
300 raise copy.error(
301 "Only BytesLoggers to sys.stdout and sys.stderr "
302 "can be deepcopied."
303 )
305 newself = self.__class__(self._file)
307 newself._write = newself._file.write
308 newself._flush = newself._file.flush
309 newself._lock = _get_lock_for_file(newself._file)
311 return newself
313 def __repr__(self) -> str:
314 return f"<BytesLogger(file={self._file!r})>"
316 def msg(self, message: bytes) -> None:
317 """
318 Write *message*.
319 """
320 with self._lock:
321 self._write(message + b"\n")
322 self._flush()
324 log = debug = info = warn = warning = msg
325 fatal = failure = err = error = critical = exception = msg
328class BytesLoggerFactory:
329 r"""
330 Produce `BytesLogger`\ s.
332 To be used with `structlog.configure`\ 's ``logger_factory``.
334 Args:
335 file: File to print to. (default: `sys.stdout`\ ``.buffer``)
337 Positional arguments are silently ignored.
339 .. versionadded:: 20.2.0
340 """
342 __slots__ = ("_file",)
344 def __init__(self, file: BinaryIO | None = None):
345 self._file = file
347 def __call__(self, *args: Any) -> BytesLogger:
348 return BytesLogger(self._file)