diff options
Diffstat (limited to 'testing/mozbase/mozlog/mozlog/handlers/base.py')
-rw-r--r-- | testing/mozbase/mozlog/mozlog/handlers/base.py | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/testing/mozbase/mozlog/mozlog/handlers/base.py b/testing/mozbase/mozlog/mozlog/handlers/base.py new file mode 100644 index 0000000000..59fc05cc1f --- /dev/null +++ b/testing/mozbase/mozlog/mozlog/handlers/base.py @@ -0,0 +1,124 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +import codecs +import locale +from threading import Lock + +import six + +from mozlog.handlers.messagehandler import MessageHandler +from mozlog.structuredlog import log_levels + + +class BaseHandler(object): + """A base handler providing message handling facilities to + derived classes. + """ + + def __init__(self, inner): + self.message_handler = MessageHandler() + if hasattr(inner, "message_handler"): + self.message_handler.wrapped.append(inner) + + +class LogLevelFilter(BaseHandler): + """Handler that filters out messages with action of log and a level + lower than some specified level. + + :param inner: Handler to use for messages that pass this filter + :param level: Minimum log level to process + """ + + def __init__(self, inner, level): + BaseHandler.__init__(self, inner) + self.inner = inner + self.level = log_levels[level.upper()] + + def __call__(self, item): + if item["action"] != "log" or log_levels[item["level"].upper()] <= self.level: + return self.inner(item) + + +class StreamHandler(BaseHandler): + """Handler for writing to a file-like object + + :param stream: File-like object to write log messages to + :param formatter: formatter to convert messages to string format + """ + + _lock = Lock() + + def __init__(self, stream, formatter): + BaseHandler.__init__(self, formatter) + assert stream is not None + if six.PY2: + # This is a hack to deal with the case where we are passed a + # StreamWriter (e.g. by mach for stdout). A StreamWriter requires + # the code to handle unicode in exactly the opposite way compared + # to a normal stream i.e. you always have to pass in a Unicode + # object rather than a string object. Cope with that by extracting + # the underlying raw stream. + if isinstance(stream, codecs.StreamWriter): + stream = stream.stream + + self.formatter = formatter + self.stream = stream + + def __call__(self, data): + """Write a log message. + + :param data: Structured log message dictionary.""" + formatted = self.formatter(data) + if not formatted: + return + with self._lock: + if six.PY3: + import io + + import mozfile + + source_enc = "utf-8" + target_enc = "utf-8" + if isinstance(self.stream, io.BytesIO): + target_enc = None + if hasattr(self.stream, "encoding"): + target_enc = self.stream.encoding + if target_enc is None: + target_enc = locale.getpreferredencoding() + + if isinstance(self.stream, io.StringIO) and isinstance( + formatted, bytes + ): + formatted = formatted.decode(source_enc, "replace") + elif ( + isinstance(self.stream, io.BytesIO) + or isinstance(self.stream, mozfile.NamedTemporaryFile) + ) and isinstance(formatted, str): + formatted = formatted.encode(target_enc, "replace") + elif isinstance(formatted, str): + # Suppress eventual surrogates, but keep as string. + # TODO: It is yet unclear how we can end up with + # surrogates here, see comment on patch on bug 1794401. + formatted_bin = formatted.encode(target_enc, "replace") + formatted = formatted_bin.decode(target_enc, "ignore") + + # It seems that under Windows we can have cp1252 encoding + # for the output stream and that not all unicode chars map + # well. We just ignore those errors here (they have no + # consequences for the executed tests, anyways). + try: + self.stream.write(formatted) + except (UnicodeEncodeError): + return + else: + if isinstance(formatted, six.text_type): + self.stream.write(formatted.encode("utf-8", "replace")) + elif isinstance(formatted, str): + self.stream.write(formatted) + else: + assert False, "Got output from the formatter of an unexpected type" + + self.stream.flush() |