diff options
| author | robot-piglet <[email protected]> | 2024-08-25 12:54:32 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2024-08-25 13:03:33 +0300 |
| commit | 4a64a813e1d34e732f35d8a65147974f76395a6f (patch) | |
| tree | a8da0dede5213f85e45b95047cfbdcf5427cf0b7 /contrib/python/Twisted/py3/twisted/logger/_logger.py | |
| parent | e9bbee265681b79a9ef9795bdc84cf6996f9cfec (diff) | |
Intermediate changes
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/logger/_logger.py')
| -rw-r--r-- | contrib/python/Twisted/py3/twisted/logger/_logger.py | 204 |
1 files changed, 195 insertions, 9 deletions
diff --git a/contrib/python/Twisted/py3/twisted/logger/_logger.py b/contrib/python/Twisted/py3/twisted/logger/_logger.py index cc428d87af1..b7f3e2e1213 100644 --- a/contrib/python/Twisted/py3/twisted/logger/_logger.py +++ b/contrib/python/Twisted/py3/twisted/logger/_logger.py @@ -6,8 +6,11 @@ Logger class. """ +from __future__ import annotations + from time import time -from typing import Any, Optional, cast +from types import TracebackType +from typing import Any, Callable, ContextManager, Optional, Protocol, cast from twisted.python.compat import currentframe from twisted.python.failure import Failure @@ -15,6 +18,87 @@ from ._interfaces import ILogObserver, LogTrace from ._levels import InvalidLogLevelError, LogLevel +class Operation(Protocol): + """ + An L{Operation} represents the success (or lack thereof) of code performed + within the body of the L{Logger.failureHandler} context manager. + """ + + @property + def succeeded(self) -> bool: + """ + Did the operation succeed? C{True} iff the code completed without + raising an exception; C{False} while the code is running and C{False} + if it raises an exception. + """ + + @property + def failure(self) -> Failure | None: + """ + Did the operation raise an exception? If so, this L{Failure} is that + exception. + """ + + @property + def failed(self) -> bool: + """ + Did the operation fail? C{True} iff the code raised an exception; + C{False} while the code is running and C{False} if it completed without + error. + """ + + +class _FailCtxMgr: + succeeded: bool = False + failure: Failure | None = None + + def __init__(self, fail: Callable[[Failure], None]) -> None: + self._fail = fail + + @property + def failed(self) -> bool: + return self.failure is not None + + def __enter__(self) -> Operation: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> bool: + if exc_type is not None: + failure = Failure() + self.failure = failure + self._fail(failure) + else: + self.succeeded = True + return True + + +class _FastFailCtxMgr: + def __init__(self, fail: Callable[[Failure], None]) -> None: + self._fail = fail + + def __enter__(self) -> None: + pass + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> bool: + if exc_type is not None: + failure = Failure() + self.failure = failure + self._fail(failure) + return True + + class Logger: """ A L{Logger} emits log messages to an observer. You should instantiate it @@ -164,25 +248,32 @@ class Logger: d.addErrback(lambda f: log.failure("While frobbing {knob}", f, knob=knob)) - This method is generally meant to capture unexpected exceptions in - code; an exception that is caught and handled somehow should be logged, - if appropriate, via L{Logger.error} instead. If some unknown exception + This method is meant to capture unexpected exceptions in code; an + exception that is caught and handled somehow should be logged, if + appropriate, via L{Logger.error} instead. If some unknown exception occurs and your code doesn't know how to handle it, as in the above - example, then this method provides a means to describe the failure in - nerd-speak. This is done at L{LogLevel.critical} by default, since no - corrective guidance can be offered to an user/administrator, and the - impact of the condition is unknown. + example, then this method provides a means to describe the failure. + This is done at L{LogLevel.critical} by default, since no corrective + guidance can be offered to an user/administrator, and the impact of the + condition is unknown. @param format: a message format using new-style (PEP 3101) formatting. The logging event (which is a L{dict}) is used to render this format string. + @param failure: a L{Failure} to log. If L{None}, a L{Failure} is created from the exception in flight. + @param level: a L{LogLevel} to use. + @param kwargs: additional key/value pairs to include in the event. Note that values which are later mutated may result in non-deterministic behavior from observers that schedule work for later execution. + + @see: L{Logger.failureHandler} + + @see: L{Logger.failuresHandled} """ if failure is None: failure = Failure() @@ -264,6 +355,101 @@ class Logger: """ self.emit(LogLevel.critical, format, **kwargs) + def failuresHandled( + self, format: str, level: LogLevel = LogLevel.critical, **kwargs: object + ) -> ContextManager[Operation]: + """ + Run some application code, logging a failure and emitting a traceback + in the event that any of it fails, but continuing on. For example:: + + log = Logger(...) + + def frameworkCode() -> None: + with log.failuresHandled("While frobbing {knob}:", knob=knob) as op: + frob(knob) + if op.succeeded: + log.info("frobbed {knob} successfully", knob=knob) + + This method is meant to capture unexpected exceptions from application + code; an exception that is caught and handled somehow should be logged, + if appropriate, via L{Logger.error} instead. If some unknown exception + occurs and your code doesn't know how to handle it, as in the above + example, then this method provides a means to describe the failure. + This is done at L{LogLevel.critical} by default, since no corrective + guidance can be offered to an user/administrator, and the impact of the + condition is unknown. + + @param format: a message format using new-style (PEP 3101) formatting. + The logging event (which is a L{dict}) is used to render this + format string. + + @param level: a L{LogLevel} to use. + + @param kwargs: additional key/value pairs to include in the event, if + it is emitted. Note that values which are later mutated may result + in non-deterministic behavior from observers that schedule work for + later execution. + + @see: L{Logger.failure} + @see: L{Logger.failureHandler} + + @return: A context manager which yields an L{Operation} which will have + either its C{succeeded} or C{failed} attribute set to C{True} upon + completion of the code within the code within the C{with} block. + """ + return _FailCtxMgr(lambda f: self.failure(format, f, level, **kwargs)) + + def failureHandler( + self, + staticMessage: str, + level: LogLevel = LogLevel.critical, + ) -> ContextManager[None]: + """ + For performance-sensitive frameworks that needs to handle potential + failures from frequently-called application code, and do not need to + include detailed structured information about the failure nor inspect + the result of the operation, this method returns a context manager that + will log exceptions and continue, that can be shared across multiple + invocations. It should be instantiated at module scope to avoid + additional object creations. + + For example:: + + log = Logger(...) + ignoringFrobErrors = log.failureHandler("while frobbing:") + + def hotLoop() -> None: + with ignoringFrobErrors: + frob() + + This method is meant to capture unexpected exceptions from application + code; an exception that is caught and handled somehow should be logged, + if appropriate, via L{Logger.error} instead. If some unknown exception + occurs and your code doesn't know how to handle it, as in the above + example, then this method provides a means to describe the failure in + nerd-speak. This is done at L{LogLevel.critical} by default, since no + corrective guidance can be offered to an user/administrator, and the + impact of the condition is unknown. + + @param format: a message format using new-style (PEP 3101) formatting. + The logging event (which is a L{dict}) is used to render this + format string. + + @param level: a L{LogLevel} to use. + + @see: L{Logger.failure} + + @return: A context manager which does not return a value, but will + always exit from exceptions. + """ + return _FastFailCtxMgr(lambda f: self.failure(staticMessage, f, level)) + _log = Logger() -_loggerFor = lambda obj: _log.__get__(obj, obj.__class__) + + +def _loggerFor(obj: object) -> Logger: + """ + Get a L{Logger} instance attached to the given class. + """ + return _log.__get__(obj, obj.__class__) |
