File: //usr/lib/python2.7/site-packages/salt/_logging/mixins.py
# -*- coding: utf-8 -*-
'''
salt._logging.mixins
~~~~~~~~~~~~~~~~~~~~
Logging related mix-ins
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import sys
import logging
class NewStyleClassMixin(object):
'''
Simple new style class to make pylint shut up!
This is required because SaltLoggingClass can't subclass object directly:
'Cannot create a consistent method resolution order (MRO) for bases'
'''
class LoggingProfileMixin(object):
'''
Simple mix-in class to add a trace method to python's logging.
'''
def profile(self, msg, *args, **kwargs):
self.log(getattr(logging, 'PROFILE', 15), msg, *args, **kwargs)
class LoggingTraceMixin(object):
'''
Simple mix-in class to add a trace method to python's logging.
'''
def trace(self, msg, *args, **kwargs):
self.log(getattr(logging, 'TRACE', 5), msg, *args, **kwargs)
class LoggingGarbageMixin(object):
'''
Simple mix-in class to add a garbage method to python's logging.
'''
def garbage(self, msg, *args, **kwargs):
self.log(getattr(logging, 'GARBAGE', 5), msg, *args, **kwargs)
class LoggingMixinMeta(type):
'''
This class is called whenever a new instance of ``SaltLoggingClass`` is
created.
What this class does is check if any of the bases have a `trace()` or a
`garbage()` method defined, if they don't we add the respective mix-ins to
the bases.
'''
def __new__(mcs, name, bases, attrs):
include_profile = include_trace = include_garbage = True
bases = list(bases)
if name == 'SaltLoggingClass':
for base in bases:
if hasattr(base, 'trace'):
include_trace = False
if hasattr(base, 'garbage'):
include_garbage = False
if include_profile:
bases.append(LoggingProfileMixin)
if include_trace:
bases.append(LoggingTraceMixin)
if include_garbage:
bases.append(LoggingGarbageMixin)
return super(LoggingMixinMeta, mcs).__new__(mcs, name, tuple(bases), attrs)
class ExcInfoOnLogLevelFormatMixin(object):
'''
Logging handler class mixin to properly handle including exc_info on a per logging handler basis
'''
def format(self, record):
'''
Format the log record to include exc_info if the handler is enabled for a specific log level
'''
formatted_record = super(ExcInfoOnLogLevelFormatMixin, self).format(record)
exc_info_on_loglevel = getattr(record, 'exc_info_on_loglevel', None)
exc_info_on_loglevel_formatted = getattr(record, 'exc_info_on_loglevel_formatted', None)
if exc_info_on_loglevel is None and exc_info_on_loglevel_formatted is None:
return formatted_record
# If we reached this far it means the log record was created with exc_info_on_loglevel
# If this specific handler is enabled for that record, then we should format it to
# include the exc_info details
if self.level > exc_info_on_loglevel:
# This handler is not enabled for the desired exc_info_on_loglevel, don't include exc_info
return formatted_record
# If we reached this far it means we should include exc_info
if not record.exc_info_on_loglevel_instance and not exc_info_on_loglevel_formatted:
# This should actually never occur
return formatted_record
if record.exc_info_on_loglevel_formatted is None:
# Let's cache the formatted exception to avoid recurring conversions and formatting calls
if self.formatter is None: # pylint: disable=access-member-before-definition
self.formatter = logging._defaultFormatter
record.exc_info_on_loglevel_formatted = self.formatter.formatException(
record.exc_info_on_loglevel_instance
)
# Let's format the record to include exc_info just like python's logging formatted does
if formatted_record[-1:] != '\n':
formatted_record += '\n'
try:
formatted_record += record.exc_info_on_loglevel_formatted
except UnicodeError:
# According to the standard library logging formatter comments:
#
# Sometimes filenames have non-ASCII chars, which can lead
# to errors when s is Unicode and record.exc_text is str
# See issue 8924.
# We also use replace for when there are multiple
# encodings, e.g. UTF-8 for the filesystem and latin-1
# for a script. See issue 13232.
formatted_record += record.exc_info_on_loglevel_formatted.decode(
sys.getfilesystemencoding(),
'replace'
)
# Reset the record.exc_info_on_loglevel_instance because it might need
# to "travel" through a multiprocessing process and it might contain
# data which is not pickle'able
record.exc_info_on_loglevel_instance = None
return formatted_record