handlers.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. # -*- coding: utf-8 -*-
  2. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  3. # not use this file except in compliance with the License. You may obtain
  4. # a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  10. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  11. # License for the specific language governing permissions and limitations
  12. # under the License.
  13. import logging
  14. import logging.config
  15. import logging.handlers
  16. try:
  17. from systemd import journal
  18. except ImportError:
  19. journal = None
  20. try:
  21. import syslog
  22. except ImportError:
  23. syslog = None
  24. # This is a copy of the numerical constants from syslog.h. The
  25. # definition of these goes back at least 20 years, and is specifically
  26. # 3 bits in a packed field, so these aren't likely to ever need
  27. # changing.
  28. SYSLOG_MAP = {
  29. "CRITICAL": 2,
  30. "ERROR": 3,
  31. "WARNING": 4,
  32. "WARN": 4,
  33. "INFO": 6,
  34. "DEBUG": 7,
  35. }
  36. class SyslogHandler(logging.Handler):
  37. """Syslog based handler. Only available on UNIX-like platforms."""
  38. def __init__(self, program_name, facility=None):
  39. # Default values always get evaluated, for which reason we avoid
  40. # using 'syslog' directly, which may not be available.
  41. facility = facility if facility is not None else syslog.LOG_USER
  42. if not syslog:
  43. raise RuntimeError("Syslog not available on this platform")
  44. super(SyslogHandler, self).__init__()
  45. syslog.openlog(program_name, 0, facility)
  46. def emit(self, record):
  47. priority = SYSLOG_MAP.get(record.levelname, 7)
  48. message = self.format(record)
  49. syslog.syslog(priority, message)
  50. class JournalHandler(logging.Handler):
  51. """Journald based handler. Only available on platforms using systemd."""
  52. def __init__(self, program_name):
  53. if not journal:
  54. raise RuntimeError("Systemd bindings do not exist")
  55. super(JournalHandler, self).__init__()
  56. self.program_name = program_name
  57. def emit(self, record):
  58. priority = SYSLOG_MAP.get(record.levelname, 7)
  59. message = self.format(record)
  60. extras = {
  61. 'CODE_FILE': record.pathname,
  62. 'CODE_LINE': record.lineno,
  63. 'CODE_FUNC': record.funcName,
  64. 'THREAD_NAME': record.threadName,
  65. 'PROCESS_NAME': record.processName,
  66. 'LOGGER_NAME': record.name,
  67. 'LOGGER_LEVEL': record.levelname,
  68. 'SYSLOG_IDENTIFIER': self.program_name,
  69. 'PRIORITY': priority
  70. }
  71. if record.exc_text:
  72. extras['EXCEPTION_TEXT'] = record.exc_text
  73. if record.exc_info:
  74. extras['EXCEPTION_INFO'] = record.exc_info
  75. if hasattr(record, "_daiquiri_extra_keys"):
  76. for k, v in record._daiquiri_extra_keys:
  77. if k != "_daiquiri_extra_keys":
  78. extras[k.upper()] = getattr(record, k)
  79. journal.send(message, **extras)
  80. class TTYDetectorStreamHandler(logging.StreamHandler):
  81. """Stream handler that adds a hint in the record if the stream is a TTY."""
  82. def format(self, record):
  83. if hasattr(self.stream, "isatty"):
  84. record._stream_is_a_tty = self.stream.isatty()
  85. else:
  86. record._stream_is_a_tty = False
  87. s = super(TTYDetectorStreamHandler, self).format(record)
  88. del record._stream_is_a_tty
  89. return s