_stdlib.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # -*- test-case-name: twisted.logger.test.test_stdlib -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Integration with Python standard library logging.
  6. """
  7. import logging as stdlibLogging
  8. from zope.interface import implementer
  9. from twisted.python.compat import _PY3, currentframe, unicode
  10. from ._levels import LogLevel
  11. from ._format import formatEvent
  12. from ._observer import ILogObserver
  13. # Mappings to Python's logging module
  14. toStdlibLogLevelMapping = {
  15. LogLevel.debug: stdlibLogging.DEBUG,
  16. LogLevel.info: stdlibLogging.INFO,
  17. LogLevel.warn: stdlibLogging.WARNING,
  18. LogLevel.error: stdlibLogging.ERROR,
  19. LogLevel.critical: stdlibLogging.CRITICAL,
  20. }
  21. def _reverseLogLevelMapping():
  22. """
  23. Reverse the above mapping, adding both the numerical keys used above and
  24. the corresponding string keys also used by python logging.
  25. @return: the reversed mapping
  26. """
  27. mapping = {}
  28. for logLevel, pyLogLevel in toStdlibLogLevelMapping.items():
  29. mapping[pyLogLevel] = logLevel
  30. mapping[stdlibLogging.getLevelName(pyLogLevel)] = logLevel
  31. return mapping
  32. fromStdlibLogLevelMapping = _reverseLogLevelMapping()
  33. @implementer(ILogObserver)
  34. class STDLibLogObserver(object):
  35. """
  36. Log observer that writes to the python standard library's C{logging}
  37. module.
  38. @note: Warning: specific logging configurations (example: network) can lead
  39. to this observer blocking. Nothing is done here to prevent that, so be
  40. sure to not to configure the standard library logging module to block
  41. when used in conjunction with this module: code within Twisted, such as
  42. twisted.web, assumes that logging does not block.
  43. @cvar defaultStackDepth: This is the default number of frames that it takes
  44. to get from L{STDLibLogObserver} through the logging module, plus one;
  45. in other words, the number of frames if you were to call a
  46. L{STDLibLogObserver} directly. This is useful to use as an offset for
  47. the C{stackDepth} parameter to C{__init__}, to add frames for other
  48. publishers.
  49. """
  50. defaultStackDepth = 4
  51. def __init__(self, name="twisted", stackDepth=defaultStackDepth):
  52. """
  53. @param loggerName: logger identifier.
  54. @type loggerName: C{str}
  55. @param stackDepth: The depth of the stack to investigate for caller
  56. metadata.
  57. @type stackDepth: L{int}
  58. """
  59. self.logger = stdlibLogging.getLogger(name)
  60. self.logger.findCaller = self._findCaller
  61. self.stackDepth = stackDepth
  62. def _findCaller(self, stackInfo=False, stackLevel=1):
  63. """
  64. Based on the stack depth passed to this L{STDLibLogObserver}, identify
  65. the calling function.
  66. @param stackInfo: Whether or not to construct stack information.
  67. (Currently ignored.)
  68. @type stackInfo: L{bool}
  69. @param stackLevel: The number of stack frames to skip when determining
  70. the caller (currently ignored; use stackDepth on the instance).
  71. @type stackLevel: L{int}
  72. @return: Depending on Python version, either a 3-tuple of (filename,
  73. lineno, name) or a 4-tuple of that plus stack information.
  74. @rtype: L{tuple}
  75. """
  76. f = currentframe(self.stackDepth)
  77. co = f.f_code
  78. if _PY3:
  79. extra = (None,)
  80. else:
  81. extra = ()
  82. return (co.co_filename, f.f_lineno, co.co_name) + extra
  83. def __call__(self, event):
  84. """
  85. Format an event and bridge it to Python logging.
  86. """
  87. level = event.get("log_level", LogLevel.info)
  88. failure = event.get('log_failure')
  89. if failure is None:
  90. excInfo = None
  91. else:
  92. excInfo = (
  93. failure.type, failure.value, failure.getTracebackObject())
  94. stdlibLevel = toStdlibLogLevelMapping.get(level, stdlibLogging.INFO)
  95. self.logger.log(
  96. stdlibLevel, StringifiableFromEvent(event), exc_info=excInfo)
  97. class StringifiableFromEvent(object):
  98. """
  99. An object that implements C{__str__()} in order to defer the work of
  100. formatting until it's converted into a C{str}.
  101. """
  102. def __init__(self, event):
  103. """
  104. @param event: An event.
  105. @type event: L{dict}
  106. """
  107. self.event = event
  108. def __unicode__(self):
  109. return formatEvent(self.event)
  110. def __bytes__(self):
  111. return unicode(self).encode("utf-8")
  112. if _PY3:
  113. __str__ = __unicode__
  114. else:
  115. __str__ = __bytes__