diff options
author | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:16:14 +0300 |
---|---|---|
committer | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:43:30 +0300 |
commit | b8cf9e88f4c5c64d9406af533d8948deb050d695 (patch) | |
tree | 218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/logger/_format.py | |
parent | 523f645a83a0ec97a0332dbc3863bb354c92a328 (diff) | |
download | ydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz |
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/logger/_format.py')
-rw-r--r-- | contrib/python/Twisted/py3/twisted/logger/_format.py | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py3/twisted/logger/_format.py b/contrib/python/Twisted/py3/twisted/logger/_format.py new file mode 100644 index 0000000000..4bc06ec40c --- /dev/null +++ b/contrib/python/Twisted/py3/twisted/logger/_format.py @@ -0,0 +1,373 @@ +# -*- test-case-name: twisted.logger.test.test_format -*- +# Copyright (c) Twisted Matrix Laboratories. +# See LICENSE for details. + +""" +Tools for formatting logging events. +""" + +from datetime import datetime as DateTime +from typing import Any, Callable, Iterator, Mapping, Optional, Union, cast + +from constantly import NamedConstant # type: ignore[import] + +from twisted.python._tzhelper import FixedOffsetTimeZone +from twisted.python.failure import Failure +from twisted.python.reflect import safe_repr +from ._flatten import aFormatter, flatFormat +from ._interfaces import LogEvent + +timeFormatRFC3339 = "%Y-%m-%dT%H:%M:%S%z" + + +def formatEvent(event: LogEvent) -> str: + """ + Formats an event as text, using the format in C{event["log_format"]}. + + This implementation should never raise an exception; if the formatting + cannot be done, the returned string will describe the event generically so + that a useful message is emitted regardless. + + @param event: A logging event. + + @return: A formatted string. + """ + return eventAsText( + event, + includeTraceback=False, + includeTimestamp=False, + includeSystem=False, + ) + + +def formatUnformattableEvent(event: LogEvent, error: BaseException) -> str: + """ + Formats an event as text that describes the event generically and a + formatting error. + + @param event: A logging event. + @param error: The formatting error. + + @return: A formatted string. + """ + try: + return "Unable to format event {event!r}: {error}".format( + event=event, error=error + ) + except BaseException: + # Yikes, something really nasty happened. + # + # Try to recover as much formattable data as possible; hopefully at + # least the namespace is sane, which will help you find the offending + # logger. + failure = Failure() + + text = ", ".join( + " = ".join((safe_repr(key), safe_repr(value))) + for key, value in event.items() + ) + + return ( + "MESSAGE LOST: unformattable object logged: {error}\n" + "Recoverable data: {text}\n" + "Exception during formatting:\n{failure}".format( + error=safe_repr(error), failure=failure, text=text + ) + ) + + +def formatTime( + when: Optional[float], + timeFormat: Optional[str] = timeFormatRFC3339, + default: str = "-", +) -> str: + """ + Format a timestamp as text. + + Example:: + + >>> from time import time + >>> from twisted.logger import formatTime + >>> + >>> t = time() + >>> formatTime(t) + u'2013-10-22T14:19:11-0700' + >>> formatTime(t, timeFormat="%Y/%W") # Year and week number + u'2013/42' + >>> + + @param when: A timestamp. + @param timeFormat: A time format. + @param default: Text to return if C{when} or C{timeFormat} is L{None}. + + @return: A formatted time. + """ + if timeFormat is None or when is None: + return default + else: + tz = FixedOffsetTimeZone.fromLocalTimeStamp(when) + datetime = DateTime.fromtimestamp(when, tz) + return str(datetime.strftime(timeFormat)) + + +def formatEventAsClassicLogText( + event: LogEvent, formatTime: Callable[[Optional[float]], str] = formatTime +) -> Optional[str]: + """ + Format an event as a line of human-readable text for, e.g. traditional log + file output. + + The output format is C{"{timeStamp} [{system}] {event}\\n"}, where: + + - C{timeStamp} is computed by calling the given C{formatTime} callable + on the event's C{"log_time"} value + + - C{system} is the event's C{"log_system"} value, if set, otherwise, + the C{"log_namespace"} and C{"log_level"}, joined by a C{"#"}. Each + defaults to C{"-"} is not set. + + - C{event} is the event, as formatted by L{formatEvent}. + + Example:: + + >>> from time import time + >>> from twisted.logger import formatEventAsClassicLogText + >>> from twisted.logger import LogLevel + >>> + >>> formatEventAsClassicLogText(dict()) # No format, returns None + >>> formatEventAsClassicLogText(dict(log_format="Hello!")) + u'- [-#-] Hello!\\n' + >>> formatEventAsClassicLogText(dict( + ... log_format="Hello!", + ... log_time=time(), + ... log_namespace="my_namespace", + ... log_level=LogLevel.info, + ... )) + u'2013-10-22T17:30:02-0700 [my_namespace#info] Hello!\\n' + >>> formatEventAsClassicLogText(dict( + ... log_format="Hello!", + ... log_time=time(), + ... log_system="my_system", + ... )) + u'2013-11-11T17:22:06-0800 [my_system] Hello!\\n' + >>> + + @param event: an event. + @param formatTime: A time formatter + + @return: A formatted event, or L{None} if no output is appropriate. + """ + eventText = eventAsText(event, formatTime=formatTime) + if not eventText: + return None + eventText = eventText.replace("\n", "\n\t") + return eventText + "\n" + + +class CallMapping(Mapping[str, Any]): + """ + Read-only mapping that turns a C{()}-suffix in key names into an invocation + of the key rather than a lookup of the key. + + Implementation support for L{formatWithCall}. + """ + + def __init__(self, submapping: Mapping[str, Any]) -> None: + """ + @param submapping: Another read-only mapping which will be used to look + up items. + """ + self._submapping = submapping + + def __iter__(self) -> Iterator[Any]: + return iter(self._submapping) + + def __len__(self) -> int: + return len(self._submapping) + + def __getitem__(self, key: str) -> Any: + """ + Look up an item in the submapping for this L{CallMapping}, calling it + if C{key} ends with C{"()"}. + """ + callit = key.endswith("()") + realKey = key[:-2] if callit else key + value = self._submapping[realKey] + if callit: + value = value() + return value + + +def formatWithCall(formatString: str, mapping: Mapping[str, Any]) -> str: + """ + Format a string like L{str.format}, but: + + - taking only a name mapping; no positional arguments + + - with the additional syntax that an empty set of parentheses + correspond to a formatting item that should be called, and its result + C{str}'d, rather than calling C{str} on the element directly as + normal. + + For example:: + + >>> formatWithCall("{string}, {function()}.", + ... dict(string="just a string", + ... function=lambda: "a function")) + 'just a string, a function.' + + @param formatString: A PEP-3101 format string. + @param mapping: A L{dict}-like object to format. + + @return: The string with formatted values interpolated. + """ + return str(aFormatter.vformat(formatString, (), CallMapping(mapping))) + + +def _formatEvent(event: LogEvent) -> str: + """ + Formats an event as a string, using the format in C{event["log_format"]}. + + This implementation should never raise an exception; if the formatting + cannot be done, the returned string will describe the event generically so + that a useful message is emitted regardless. + + @param event: A logging event. + + @return: A formatted string. + """ + try: + if "log_flattened" in event: + return flatFormat(event) + + format = cast(Optional[Union[str, bytes]], event.get("log_format", None)) + if format is None: + return "" + + # Make sure format is text. + if isinstance(format, str): + pass + elif isinstance(format, bytes): + format = format.decode("utf-8") + else: + raise TypeError(f"Log format must be str, not {format!r}") + + return formatWithCall(format, event) + + except BaseException as e: + return formatUnformattableEvent(event, e) + + +def _formatTraceback(failure: Failure) -> str: + """ + Format a failure traceback, assuming UTF-8 and using a replacement + strategy for errors. Every effort is made to provide a usable + traceback, but should not that not be possible, a message and the + captured exception are logged. + + @param failure: The failure to retrieve a traceback from. + + @return: The formatted traceback. + """ + try: + traceback = failure.getTraceback() + except BaseException as e: + traceback = "(UNABLE TO OBTAIN TRACEBACK FROM EVENT):" + str(e) + return traceback + + +def _formatSystem(event: LogEvent) -> str: + """ + Format the system specified in the event in the "log_system" key if set, + otherwise the C{"log_namespace"} and C{"log_level"}, joined by a C{"#"}. + Each defaults to C{"-"} is not set. If formatting fails completely, + "UNFORMATTABLE" is returned. + + @param event: The event containing the system specification. + + @return: A formatted string representing the "log_system" key. + """ + system = cast(Optional[str], event.get("log_system", None)) + if system is None: + level = cast(Optional[NamedConstant], event.get("log_level", None)) + if level is None: + levelName = "-" + else: + levelName = level.name + + system = "{namespace}#{level}".format( + namespace=cast(str, event.get("log_namespace", "-")), + level=levelName, + ) + else: + try: + system = str(system) + except Exception: + system = "UNFORMATTABLE" + return system + + +def eventAsText( + event: LogEvent, + includeTraceback: bool = True, + includeTimestamp: bool = True, + includeSystem: bool = True, + formatTime: Callable[[float], str] = formatTime, +) -> str: + r""" + Format an event as text. Optionally, attach timestamp, traceback, and + system information. + + The full output format is: + C{"{timeStamp} [{system}] {event}\n{traceback}\n"} where: + + - C{timeStamp} is the event's C{"log_time"} value formatted with + the provided C{formatTime} callable. + + - C{system} is the event's C{"log_system"} value, if set, otherwise, + the C{"log_namespace"} and C{"log_level"}, joined by a C{"#"}. Each + defaults to C{"-"} is not set. + + - C{event} is the event, as formatted by L{formatEvent}. + + - C{traceback} is the traceback if the event contains a + C{"log_failure"} key. In the event the original traceback cannot + be formatted, a message indicating the failure will be substituted. + + If the event cannot be formatted, and no traceback exists, an empty string + is returned, even if includeSystem or includeTimestamp are true. + + @param event: A logging event. + @param includeTraceback: If true and a C{"log_failure"} key exists, append + a traceback. + @param includeTimestamp: If true include a formatted timestamp before the + event. + @param includeSystem: If true, include the event's C{"log_system"} value. + @param formatTime: A time formatter + + @return: A formatted string with specified options. + + @since: Twisted 18.9.0 + """ + eventText = _formatEvent(event) + if includeTraceback and "log_failure" in event: + f = event["log_failure"] + traceback = _formatTraceback(f) + eventText = "\n".join((eventText, traceback)) + + if not eventText: + return eventText + + timeStamp = "" + if includeTimestamp: + timeStamp = "".join([formatTime(cast(float, event.get("log_time", None))), " "]) + + system = "" + if includeSystem: + system = "".join(["[", _formatSystem(event), "]", " "]) + + return "{timeStamp}{system}{eventText}".format( + timeStamp=timeStamp, + system=system, + eventText=eventText, + ) |