File: //proc/1526/root/lib/python3/dist-packages/twisted/logger/_flatten.py
# -*- test-case-name: twisted.logger.test.test_flatten -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Code related to "flattening" events; that is, extracting a description of all
relevant fields from the format string and persisting them for later
examination.
"""
from string import Formatter
from collections import defaultdict
from twisted.python.compat import unicode
aFormatter = Formatter()
class KeyFlattener(object):
    """
    A L{KeyFlattener} computes keys for the things within curly braces in
    PEP-3101-style format strings as parsed by L{string.Formatter.parse}.
    """
    def __init__(self):
        """
        Initialize a L{KeyFlattener}.
        """
        self.keys = defaultdict(lambda: 0)
    def flatKey(self, fieldName, formatSpec, conversion):
        """
        Compute a string key for a given field/format/conversion.
        @param fieldName: A format field name.
        @type fieldName: L{str}
        @param formatSpec: A format spec.
        @type formatSpec: L{str}
        @param conversion: A format field conversion type.
        @type conversion: L{str}
        @return: A key specific to the given field, format and conversion, as
            well as the occurrence of that combination within this
            L{KeyFlattener}'s lifetime.
        @rtype: L{str}
        """
        result = (
            "{fieldName}!{conversion}:{formatSpec}"
            .format(
                fieldName=fieldName,
                formatSpec=(formatSpec or ""),
                conversion=(conversion or ""),
            )
        )
        self.keys[result] += 1
        n = self.keys[result]
        if n != 1:
            result += "/" + str(self.keys[result])
        return result
def flattenEvent(event):
    """
    Flatten the given event by pre-associating format fields with specific
    objects and callable results in a L{dict} put into the C{"log_flattened"}
    key in the event.
    @param event: A logging event.
    @type event: L{dict}
    """
    if event.get("log_format", None) is None:
        return
    if "log_flattened" in event:
        fields = event["log_flattened"]
    else:
        fields = {}
    keyFlattener = KeyFlattener()
    for (literalText, fieldName, formatSpec, conversion) in (
        aFormatter.parse(event["log_format"])
    ):
        if fieldName is None:
            continue
        if conversion != "r":
            conversion = "s"
        flattenedKey = keyFlattener.flatKey(fieldName, formatSpec, conversion)
        structuredKey = keyFlattener.flatKey(fieldName, formatSpec, "")
        if flattenedKey in fields:
            # We've already seen and handled this key
            continue
        if fieldName.endswith(u"()"):
            fieldName = fieldName[:-2]
            callit = True
        else:
            callit = False
        field = aFormatter.get_field(fieldName, (), event)
        fieldValue = field[0]
        if conversion == "r":
            conversionFunction = repr
        else:  # Above: if conversion is not "r", it's "s"
            conversionFunction = unicode
        if callit:
            fieldValue = fieldValue()
        flattenedValue = conversionFunction(fieldValue)
        fields[flattenedKey] = flattenedValue
        fields[structuredKey] = fieldValue
    if fields:
        event["log_flattened"] = fields
def extractField(field, event):
    """
    Extract a given format field from the given event.
    @param field: A string describing a format field or log key.  This is the
        text that would normally fall between a pair of curly braces in a
        format string: for example, C{"key[2].attribute"}.  If a conversion is
        specified (the thing after the C{"!"} character in a format field) then
        the result will always be L{unicode}.
    @type field: L{str} (native string)
    @param event: A log event.
    @type event: L{dict}
    @return: A value extracted from the field.
    @rtype: L{object}
    @raise KeyError: if the field is not found in the given event.
    """
    keyFlattener = KeyFlattener()
    [[literalText, fieldName, formatSpec, conversion]] = aFormatter.parse(
        "{" + field + "}"
    )
    key = keyFlattener.flatKey(fieldName, formatSpec, conversion)
    if "log_flattened" not in event:
        flattenEvent(event)
    return event["log_flattened"][key]
def flatFormat(event):
    """
    Format an event which has been flattened with L{flattenEvent}.
    @param event: A logging event.
    @type event: L{dict}
    @return: A formatted string.
    @rtype: L{unicode}
    """
    fieldValues = event["log_flattened"]
    s = []
    keyFlattener = KeyFlattener()
    formatFields = aFormatter.parse(event["log_format"])
    for literalText, fieldName, formatSpec, conversion in formatFields:
        s.append(literalText)
        if fieldName is not None:
            key = keyFlattener.flatKey(
                    fieldName, formatSpec, conversion or "s")
            s.append(unicode(fieldValues[key]))
    return u"".join(s)