formatter.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. import logging
  13. try:
  14. from pythonjsonlogger import jsonlogger
  15. except ImportError:
  16. jsonlogger = None
  17. DEFAULT_FORMAT = (
  18. "%(asctime)s [%(process)d] %(color)s%(levelname)-8.8s "
  19. "%(name)s: %(message)s%(color_stop)s"
  20. )
  21. DEFAULT_EXTRAS_FORMAT = (
  22. "%(asctime)s [%(process)d] %(color)s%(levelname)-8.8s "
  23. "%(name)s%(extras)s: %(message)s%(color_stop)s"
  24. )
  25. class ColorFormatter(logging.Formatter):
  26. """Colorizes log output"""
  27. # TODO(jd) Allow configuration
  28. LEVEL_COLORS = {
  29. logging.DEBUG: '\033[00;32m', # GREEN
  30. logging.INFO: '\033[00;36m', # CYAN
  31. logging.WARN: '\033[01;33m', # BOLD YELLOW
  32. logging.ERROR: '\033[01;31m', # BOLD RED
  33. logging.CRITICAL: '\033[01;31m', # BOLD RED
  34. }
  35. COLOR_STOP = '\033[0m'
  36. def add_color(self, record):
  37. if getattr(record, "_stream_is_a_tty", False):
  38. record.color = self.LEVEL_COLORS[record.levelno]
  39. record.color_stop = self.COLOR_STOP
  40. else:
  41. record.color = ""
  42. record.color_stop = ""
  43. def remove_color(self, record):
  44. del record.color
  45. del record.color_stop
  46. def format(self, record):
  47. self.add_color(record)
  48. s = super(ColorFormatter, self).format(record)
  49. self.remove_color(record)
  50. return s
  51. class ExtrasFormatter(logging.Formatter):
  52. """Formats extra keywords into %(extras)s placeholder.
  53. Any keywords passed to a logging call will be formatted into a
  54. "extras" string and included in a logging message.
  55. Example:
  56. logger.info('my message', extra='keyword')
  57. will cause an "extras" string of:
  58. [extra: keyword]
  59. to be inserted into the format in place of %(extras)s.
  60. The optional `keywords` argument must be passed into the init
  61. function to enable this functionality. Without it normal daiquiri
  62. formatting will be applied. Any keywords included in the
  63. `keywords` parameter will not be included in the "extras" string.
  64. Special keywords:
  65. keywords
  66. A set of strings containing keywords to filter out of the
  67. "extras" string.
  68. extras_template
  69. A format string to use instead of '[{0}: {1}]'
  70. extras_separator
  71. What string to "join" multiple "extras" with.
  72. extras_prefix and extras_suffix
  73. Strings which will be prepended and appended to the "extras"
  74. string respectively. These will only be prepended if the
  75. "extras" string is not empty.
  76. """
  77. def __init__(self,
  78. keywords=None,
  79. extras_template='[{0}: {1}]',
  80. extras_separator=' ',
  81. extras_prefix=' ',
  82. extras_suffix='',
  83. *args, **kwargs):
  84. self.keywords = set() if keywords is None else keywords
  85. self.extras_template = extras_template
  86. self.extras_separator = extras_separator
  87. self.extras_prefix = extras_prefix
  88. self.extras_suffix = extras_suffix
  89. super(ExtrasFormatter, self).__init__(*args, **kwargs)
  90. def add_extras(self, record):
  91. if not hasattr(record, '_daiquiri_extra_keys'):
  92. record.extras = ''
  93. return
  94. extras = self.extras_separator.join(
  95. self.extras_template.format(k, getattr(record, k))
  96. for k in record._daiquiri_extra_keys
  97. if k != '_daiquiri_extra_keys' and k not in self.keywords
  98. )
  99. if extras != '':
  100. extras = self.extras_prefix + extras + self.extras_suffix
  101. record.extras = extras
  102. def remove_extras(self, record):
  103. del record.extras
  104. def format(self, record):
  105. self.add_extras(record)
  106. s = super(ExtrasFormatter, self).format(record)
  107. self.remove_extras(record)
  108. return s
  109. class ColorExtrasFormatter(ColorFormatter, ExtrasFormatter):
  110. """Combines functionality of ColorFormatter and ExtrasFormatter."""
  111. def format(self, record):
  112. self.add_color(record)
  113. s = ExtrasFormatter.format(self, record)
  114. self.remove_color(record)
  115. return s
  116. TEXT_FORMATTER = ColorExtrasFormatter(fmt=DEFAULT_EXTRAS_FORMAT)
  117. if jsonlogger:
  118. JSON_FORMATTER = jsonlogger.JsonFormatter()