Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

_flatten.py 4.9KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. # -*- test-case-name: twisted.logger.test.test_flatten -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Code related to "flattening" events; that is, extracting a description of all
  6. relevant fields from the format string and persisting them for later
  7. examination.
  8. """
  9. from collections import defaultdict
  10. from string import Formatter
  11. from typing import Any, Dict, Optional
  12. from ._interfaces import LogEvent
  13. aFormatter = Formatter()
  14. class KeyFlattener:
  15. """
  16. A L{KeyFlattener} computes keys for the things within curly braces in
  17. PEP-3101-style format strings as parsed by L{string.Formatter.parse}.
  18. """
  19. def __init__(self) -> None:
  20. """
  21. Initialize a L{KeyFlattener}.
  22. """
  23. self.keys: Dict[str, int] = defaultdict(lambda: 0)
  24. def flatKey(
  25. self, fieldName: str, formatSpec: Optional[str], conversion: Optional[str]
  26. ) -> str:
  27. """
  28. Compute a string key for a given field/format/conversion.
  29. @param fieldName: A format field name.
  30. @param formatSpec: A format spec.
  31. @param conversion: A format field conversion type.
  32. @return: A key specific to the given field, format and conversion, as
  33. well as the occurrence of that combination within this
  34. L{KeyFlattener}'s lifetime.
  35. """
  36. if formatSpec is None:
  37. formatSpec = ""
  38. if conversion is None:
  39. conversion = ""
  40. result = "{fieldName}!{conversion}:{formatSpec}".format(
  41. fieldName=fieldName,
  42. formatSpec=formatSpec,
  43. conversion=conversion,
  44. )
  45. self.keys[result] += 1
  46. n = self.keys[result]
  47. if n != 1:
  48. result += "/" + str(self.keys[result])
  49. return result
  50. def flattenEvent(event: LogEvent) -> None:
  51. """
  52. Flatten the given event by pre-associating format fields with specific
  53. objects and callable results in a L{dict} put into the C{"log_flattened"}
  54. key in the event.
  55. @param event: A logging event.
  56. """
  57. if event.get("log_format", None) is None:
  58. return
  59. if "log_flattened" in event:
  60. fields = event["log_flattened"]
  61. else:
  62. fields = {}
  63. keyFlattener = KeyFlattener()
  64. for (literalText, fieldName, formatSpec, conversion) in aFormatter.parse(
  65. event["log_format"]
  66. ):
  67. if fieldName is None:
  68. continue
  69. if conversion != "r":
  70. conversion = "s"
  71. flattenedKey = keyFlattener.flatKey(fieldName, formatSpec, conversion)
  72. structuredKey = keyFlattener.flatKey(fieldName, formatSpec, "")
  73. if flattenedKey in fields:
  74. # We've already seen and handled this key
  75. continue
  76. if fieldName.endswith("()"):
  77. fieldName = fieldName[:-2]
  78. callit = True
  79. else:
  80. callit = False
  81. field = aFormatter.get_field(fieldName, (), event)
  82. fieldValue = field[0]
  83. if conversion == "r":
  84. conversionFunction = repr
  85. else: # Above: if conversion is not "r", it's "s"
  86. conversionFunction = str
  87. if callit:
  88. fieldValue = fieldValue()
  89. flattenedValue = conversionFunction(fieldValue)
  90. fields[flattenedKey] = flattenedValue
  91. fields[structuredKey] = fieldValue
  92. if fields:
  93. event["log_flattened"] = fields
  94. def extractField(field: str, event: LogEvent) -> Any:
  95. """
  96. Extract a given format field from the given event.
  97. @param field: A string describing a format field or log key. This is the
  98. text that would normally fall between a pair of curly braces in a
  99. format string: for example, C{"key[2].attribute"}. If a conversion is
  100. specified (the thing after the C{"!"} character in a format field) then
  101. the result will always be str.
  102. @param event: A log event.
  103. @return: A value extracted from the field.
  104. @raise KeyError: if the field is not found in the given event.
  105. """
  106. keyFlattener = KeyFlattener()
  107. [[literalText, fieldName, formatSpec, conversion]] = aFormatter.parse(
  108. "{" + field + "}"
  109. )
  110. assert fieldName is not None
  111. key = keyFlattener.flatKey(fieldName, formatSpec, conversion)
  112. if "log_flattened" not in event:
  113. flattenEvent(event)
  114. return event["log_flattened"][key]
  115. def flatFormat(event: LogEvent) -> str:
  116. """
  117. Format an event which has been flattened with L{flattenEvent}.
  118. @param event: A logging event.
  119. @return: A formatted string.
  120. """
  121. fieldValues = event["log_flattened"]
  122. keyFlattener = KeyFlattener()
  123. s = []
  124. for literalText, fieldName, formatSpec, conversion in aFormatter.parse(
  125. event["log_format"]
  126. ):
  127. s.append(literalText)
  128. if fieldName is not None:
  129. key = keyFlattener.flatKey(fieldName, formatSpec, conversion or "s")
  130. s.append(str(fieldValues[key]))
  131. return "".join(s)