summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozlog/mozlog/handlers/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozlog/mozlog/handlers/base.py')
-rw-r--r--testing/mozbase/mozlog/mozlog/handlers/base.py124
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()