__init__.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. import logging.config
  14. import logging.handlers
  15. import sys
  16. import traceback
  17. import weakref
  18. from daiquiri import output
  19. class KeywordArgumentAdapter(logging.LoggerAdapter):
  20. """Logger adapter to add keyword arguments to log record's extra data
  21. Keywords passed to the log call are added to the "extra"
  22. dictionary passed to the underlying logger so they are emitted
  23. with the log message and available to the format string.
  24. Special keywords:
  25. extra
  26. An existing dictionary of extra values to be passed to the
  27. logger. If present, the dictionary is copied and extended.
  28. """
  29. def process(self, msg, kwargs):
  30. # Make a new extra dictionary combining the values we were
  31. # given when we were constructed and anything from kwargs.
  32. extra = self.extra.copy()
  33. if 'extra' in kwargs:
  34. extra.update(kwargs.pop('extra'))
  35. # Move any unknown keyword arguments into the extra
  36. # dictionary.
  37. for name in list(kwargs.keys()):
  38. if name == 'exc_info':
  39. continue
  40. extra[name] = kwargs.pop(name)
  41. extra['_daiquiri_extra_keys'] = set(extra.keys())
  42. kwargs['extra'] = extra
  43. return msg, kwargs
  44. if sys.version_info.major == 2:
  45. def setLevel(self, level):
  46. """
  47. Set the specified level on the underlying logger.
  48. """
  49. self.logger.setLevel(level)
  50. _LOGGERS = weakref.WeakValueDictionary()
  51. def getLogger(name=None, **kwargs):
  52. """Build a logger with the given name.
  53. :param name: The name for the logger. This is usually the module
  54. name, ``__name__``.
  55. :type name: string
  56. """
  57. adapter = _LOGGERS.get(name)
  58. if not adapter:
  59. # NOTE(jd) Keep using the `adapter' variable here because so it's not
  60. # collected by Python since _LOGGERS contains only a weakref
  61. adapter = KeywordArgumentAdapter(logging.getLogger(name), kwargs)
  62. _LOGGERS[name] = adapter
  63. return adapter
  64. def setup(level=logging.WARNING, outputs=[output.STDERR], program_name=None,
  65. capture_warnings=True):
  66. """Setup Python logging.
  67. This will setup basic handlers for Python logging.
  68. :param level: Root log level.
  69. :param outputs: Iterable of outputs to log to.
  70. :param program_name: The name of the program. Auto-detected if not set.
  71. :param capture_warnings: Capture warnings from the `warnings' module.
  72. """
  73. root_logger = logging.getLogger(None)
  74. # Remove all handlers
  75. for handler in list(root_logger.handlers):
  76. root_logger.removeHandler(handler)
  77. # Add configured handlers
  78. for out in outputs:
  79. if isinstance(out, str):
  80. out = output.preconfigured.get(out)
  81. if out is None:
  82. raise RuntimeError("Output {} is not available".format(out))
  83. out.add_to_logger(root_logger)
  84. root_logger.setLevel(level)
  85. program_logger = logging.getLogger(program_name)
  86. def logging_excepthook(exc_type, value, tb):
  87. program_logger.critical(
  88. "".join(traceback.format_exception(exc_type, value, tb)))
  89. sys.excepthook = logging_excepthook
  90. if capture_warnings:
  91. logging.captureWarnings(True)
  92. def parse_and_set_default_log_levels(default_log_levels, separator='='):
  93. """Set default log levels for some loggers.
  94. :param default_log_levels: List of strings with format
  95. <logger_name><separator><log_level>
  96. """
  97. return set_default_log_levels((pair.split(separator, 1)
  98. for pair in default_log_levels))
  99. def set_default_log_levels(loggers_and_log_levels):
  100. """Set default log levels for some loggers.
  101. :param loggers_and_log_levels: List of tuple (logger name, level).
  102. """
  103. for logger, level in loggers_and_log_levels:
  104. if isinstance(level, str):
  105. level = level.upper()
  106. logging.getLogger(logger).setLevel(level)