ext_colorlog.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. """
  2. The ColorLog Extension provides logging based on the standard
  3. ``logging`` module and is a drop-in replacement for the default log
  4. handler :class:`cement.ext.ext_logging.LoggingLogHandler`.
  5. Requirements
  6. ------------
  7. * ColorLog (``pip install colorlog``)
  8. Configuration
  9. -------------
  10. This handler honors all of the same configuration settings as the
  11. ``LoggingLogHandler`` including:
  12. * level
  13. * file
  14. * to_console
  15. * rotate
  16. * max_bytes
  17. * max_files
  18. In addition, it also supports:
  19. * colorize_file_log
  20. * colorize_console_log
  21. A sample config section (in any config file) might look like:
  22. .. code-block:: text
  23. [log.colorlog]
  24. file = /path/to/config/file
  25. level = info
  26. to_console = true
  27. rotate = true
  28. max_bytes = 512000
  29. max_files = 4
  30. colorize_file_log = false
  31. colorize_console_log = true
  32. Usage
  33. -----
  34. .. code-block:: python
  35. from cement.core.foundation import CementApp
  36. class MyApp(CementApp):
  37. class Meta:
  38. label = 'myapp'
  39. extensions = ['colorlog']
  40. log_handler = 'colorlog'
  41. with MyApp() as app:
  42. app.run()
  43. app.log.debug('This is my debug message')
  44. app.log.info('This is my info message')
  45. app.log.warn('This is my warning message')
  46. app.log.error('This is my error message')
  47. app.log.fatal('This is my critical message')
  48. The colors can be customized by passing in a ``colors`` dictionary mapping
  49. overriding the ``ColorLogHandler.Meta.colors`` meta-data:
  50. .. code-block:: python
  51. from cement.core.foundation import CementApp
  52. from cement.ext.ext_colorlog import ColorLogHandler
  53. COLORS = {
  54. 'DEBUG': 'cyan',
  55. 'INFO': 'green',
  56. 'WARNING': 'yellow',
  57. 'ERROR': 'red',
  58. 'CRITICAL': 'red,bg_white',
  59. }
  60. class MyApp(CementApp):
  61. class Meta:
  62. label = 'myapp'
  63. log_handler = ColorLogHandler(colors=COLORS)
  64. Or by sub-classing and creating your own custom class:
  65. .. code-block:: python
  66. from cement.core.foundation import CementApp
  67. from cement.ext.ext_colorlog import ColorLogHandler
  68. class MyCustomLog(ColorLogHandler):
  69. class Meta:
  70. label = 'my_custom_log'
  71. colors = {
  72. 'DEBUG': 'cyan',
  73. 'INFO': 'green',
  74. 'WARNING': 'yellow',
  75. 'ERROR': 'red',
  76. 'CRITICAL': 'red,bg_white',
  77. }
  78. class MyApp(CementApp):
  79. class Meta:
  80. label = 'myapp'
  81. log_handler = MyCustomLog
  82. """
  83. import os
  84. import sys
  85. import logging
  86. from colorlog import ColoredFormatter
  87. from ..ext.ext_logging import LoggingLogHandler
  88. from ..utils.misc import is_true
  89. class ColorLogHandler(LoggingLogHandler):
  90. """
  91. This class implements the :class:`cement.core.log.ILog` interface. It is
  92. a sub-class of :class:`cement.ext.ext_logging.LoggingLogHandler` which is
  93. based on the standard :py:class:`logging` library, and adds colorized
  94. console output using the
  95. `ColorLog <https://pypi.python.org/pypi/colorlog>`_ library.
  96. **Note** This extension has an external dependency on ``colorlog``. You
  97. must include ``colorlog`` in your applications dependencies as Cement
  98. explicitly does **not** include external dependencies for optional
  99. extensions.
  100. """
  101. class Meta:
  102. """Handler meta-data."""
  103. #: The string identifier of the handler.
  104. label = "colorlog"
  105. #: Color mapping for each log level
  106. colors = {
  107. 'DEBUG': 'cyan',
  108. 'INFO': 'green',
  109. 'WARNING': 'yellow',
  110. 'ERROR': 'red',
  111. 'CRITICAL': 'red,bg_white',
  112. }
  113. #: Default configuration settings. Will be overridden by the same
  114. #: settings in any application configuration file under a
  115. #: ``[log.colorlog]`` block.
  116. config_defaults = dict(
  117. file=None,
  118. level='INFO',
  119. to_console=True,
  120. rotate=False,
  121. max_bytes=512000,
  122. max_files=4,
  123. colorize_file_log=False,
  124. colorize_console_log=True,
  125. )
  126. #: Formatter class to use for non-colorized logging (non-tty, file,
  127. #: etc)
  128. formatter_class_without_color = logging.Formatter
  129. #: Formatter class to use for colorized logging
  130. formatter_class = ColoredFormatter
  131. def _get_console_format(self):
  132. format = super(ColorLogHandler, self)._get_console_format()
  133. colorize = self.app.config.get('log.colorlog', 'colorize_console_log')
  134. if sys.stdout.isatty() or 'CEMENT_TEST' in os.environ:
  135. if is_true(colorize):
  136. format = "%(log_color)s" + format
  137. return format
  138. def _get_file_format(self):
  139. format = super(ColorLogHandler, self)._get_file_format()
  140. colorize = self.app.config.get('log.colorlog', 'colorize_file_log')
  141. if is_true(colorize):
  142. format = "%(log_color)s" + format
  143. return format
  144. def _get_console_formatter(self, format):
  145. colorize = self.app.config.get('log.colorlog', 'colorize_console_log')
  146. if sys.stdout.isatty() or 'CEMENT_TEST' in os.environ:
  147. if is_true(colorize):
  148. formatter = self._meta.formatter_class(
  149. format,
  150. log_colors=self._meta.colors
  151. )
  152. else:
  153. formatter = self._meta.formatter_class_without_color(
  154. format
  155. )
  156. else:
  157. klass = self._meta.formatter_class_without_color # pragma: nocover
  158. formatter = klass(format) # pragma: nocover
  159. return formatter
  160. def _get_file_formatter(self, format):
  161. colorize = self.app.config.get('log.colorlog', 'colorize_file_log')
  162. if is_true(colorize):
  163. formatter = self._meta.formatter_class(
  164. format,
  165. log_colors=self._meta.colors
  166. )
  167. else:
  168. formatter = self._meta.formatter_class_without_color(format)
  169. return formatter
  170. def load(app):
  171. app.handler.register(ColorLogHandler)