#!/usr/bin/env python
# coding=utf-8
# aeneas is a Python/C library and a set of tools
# to automagically synchronize audio and text (aka forced alignment)
#
# Copyright (C) 2012-2013, Alberto Pettarin (www.albertopettarin.it)
# Copyright (C) 2013-2015, ReadBeyond Srl (www.readbeyond.it)
# Copyright (C) 2015-2017, Alberto Pettarin (www.albertopettarin.it)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This module contains the following classes:
* :class:`~aeneas.logger.Loggable`, a base class supporting logging and runtime configuration;
* :class:`~aeneas.logger.Logger`, a logger class for debugging and performance profiling.
"""
from __future__ import absolute_import
from __future__ import print_function
import datetime
import io
from aeneas.runtimeconfiguration import RuntimeConfiguration
import aeneas.globalfunctions as gf
[docs]class Logger(object):
"""
A logger class for debugging and performance profiling.
:param bool tee: if ``True``, tee (i.e., log and print to stdout)
:param int indentation: the initial indentation of the log
:param bool tee_show_datetime: if ``True``, print date and time when teeing
"""
DEBUG = "DEBU"
""" ``DEBUG`` severity """
INFO = "INFO"
""" ``INFO`` severity """
WARNING = "WARN"
""" ``WARNING`` severity """
CRITICAL = "CRIT"
""" ``CRITICAL`` severity """
ERROR = "ERRO"
""" ``ERROR`` message """
SUCCESS = "SUCC"
""" ``SUCCESS`` message """
def __init__(self, tee=False, indentation=0, tee_show_datetime=True):
self.entries = []
self.tee = tee
self.indentation = indentation
self.tee_show_datetime = tee_show_datetime
def __len__(self):
return len(self.entries)
def __unicode__(self):
return self.pretty_print()
def __str__(self):
return gf.safe_str(self.__unicode__())
def __repr__(self):
return u"Logger(tee=%s, indentation=%d, tee_show_datetime=%s)" % (
self.tee,
self.indentation,
self.tee_show_datetime
)
@property
def tee(self):
"""
If ``True``, tee (i.e., log and print to stdout).
:rtype: bool
"""
return self.__tee
@tee.setter
def tee(self, tee):
self.__tee = tee
@property
def tee_show_datetime(self):
"""
If ``True``, print date and time when teeing.
:rtype: bool
"""
return self.__tee_show_datetime
@tee_show_datetime.setter
def tee_show_datetime(self, tee_show_datetime):
self.__tee_show_datetime = tee_show_datetime
@property
def indentation(self):
"""
The current indentation of the log.
Useful to visually distinguish log levels.
:rtype: int
"""
return self.__indentation
@indentation.setter
def indentation(self, indentation):
self.__indentation = indentation
[docs] def pretty_print(self, as_list=False, show_datetime=True):
"""
Return a Unicode string pretty print of the log entries.
:param bool as_list: if ``True``, return a list of Unicode strings,
one for each entry, instead of a Unicode string
:param bool show_datetime: if ``True``, show the date and time of the entries
:rtype: string or list of strings
"""
ppl = [entry.pretty_print(show_datetime) for entry in self.entries]
if as_list:
return ppl
return u"\n".join(ppl)
[docs] def log(self, message, severity=INFO, tag=u""):
"""
Add a given message to the log, and return its time.
:param string message: the message to be added
:param severity: the severity of the message
:type severity: :class:`~aeneas.logger.Logger`
:param string tag: the tag associated with the message;
usually, the name of the class generating the entry
:rtype: datetime
"""
entry = _LogEntry(
severity=severity,
time=datetime.datetime.now(),
tag=tag,
indentation=self.indentation,
message=self._sanitize(message)
)
self.entries.append(entry)
if self.tee:
gf.safe_print(entry.pretty_print(show_datetime=self.tee_show_datetime))
return entry.time
[docs] def clear(self):
"""
Clear the contents of the log.
"""
self.entries = []
[docs] def write(self, path):
"""
Output the log to file.
:param string path: the path of the log file to be written
"""
with io.open(path, "w", encoding="utf-8") as log_file:
log_file.write(self.pretty_print())
@classmethod
def _sanitize(cls, message):
"""
Sanitize the given message,
dealing with multiple arguments
and/or string formatting.
:param message: the log message to be sanitized
:type message: string or list of strings
:rtype: string
"""
if isinstance(message, list):
if len(message) == 0:
sanitized = u"Empty log message"
elif len(message) == 1:
sanitized = message[0]
else:
sanitized = message[0] % tuple(message[1:])
else:
sanitized = message
if not gf.is_unicode(sanitized):
raise TypeError("The given log message is not a Unicode string")
return sanitized
class _LogEntry(object):
"""
A structure for a log entry.
"""
def __init__(self, message, severity, tag, indentation, time):
self.message = message
self.severity = severity
self.tag = tag
self.indentation = indentation
self.time = time
def pretty_print(self, show_datetime=True):
"""
Returns a Unicode string containing
the pretty printing of a given log entry.
:param bool show_datetime: if ``True``, print the date and time of the entry
:rtype: string
"""
if show_datetime:
return u"[%s] %s %s%s: %s" % (
self.severity,
gf.object_to_unicode(self.time),
u" " * self.indentation,
self.tag,
self.message
)
return u"[%s] %s%s: %s" % (
self.severity,
u" " * self.indentation,
self.tag,
self.message
)
@property
def message(self):
"""
The message of this log entry.
:rtype: string
"""
return self.__message
@message.setter
def message(self, message):
self.__message = message
@property
def severity(self):
"""
The severity of this log entry.
:rtype: :class:`~aeneas.logger.Logger`
"""
return self.__severity
@severity.setter
def severity(self, severity):
self.__severity = severity
@property
def tag(self):
"""
The tag of this log entry.
:rtype: string
"""
return self.__tag
@tag.setter
def tag(self, tag):
self.__tag = tag
@property
def indentation(self):
"""
The indentation of this log entry.
:rtype: string
"""
return self.__indentation
@indentation.setter
def indentation(self, indentation):
self.__indentation = indentation
@property
def time(self):
"""
The date and time of this log entry.
:rtype: datetime.time
"""
return self.__time
@time.setter
def time(self, time):
self.__time = time
[docs]class Loggable(object):
"""
A base class supporting logging and runtime configuration.
:param logger: the logger object
:type logger: :class:`~aeneas.logger.Logger`
:param rconf: the runtime configuration object
:type rconf: :class:`~aeneas.runtimeconfiguration.RuntimeConfiguration`
"""
TAG = u"Loggable"
def __init__(self, logger=None, rconf=None):
self.logger = logger if logger is not None else Logger()
self.rconf = rconf if rconf is not None else RuntimeConfiguration()
def _log(self, message, severity=Logger.DEBUG):
"""
Log generic message
:param string message: the message to log
:param string severity: the message severity
:rtype: datetime
"""
return self.logger.log(message, severity, self.TAG)
[docs] def log_exc(self, message, exc=None, critical=True, raise_type=None):
"""
Log exception, and possibly raise exception.
:param string message: the message to log
:param Exception exc: the original exception
:param bool critical: if ``True``, log as :data:`aeneas.logger.Logger.CRITICAL`;
otherwise as :data:`aeneas.logger.Logger.WARNING`
:param Exception raise_type: if not ``None``, raise this Exception type
"""
log_function = self.log_crit if critical else self.log_warn
log_function(message)
if exc is not None:
log_function([u"%s", exc])
if raise_type is not None:
raise_message = message
if exc is not None:
raise_message = u"%s : %s" % (message, exc)
raise raise_type(raise_message)
[docs] def log(self, message):
"""
Log DEBUG message, and return its time.
:param string message: the message to log
:rtype: datetime
"""
return self._log(message)
[docs] def log_info(self, message):
"""
Log INFO message, and return its time.
:param string message: the message to log
:rtype: datetime
"""
return self._log(message, Logger.INFO)
[docs] def log_warn(self, message):
"""
Log WARNING message, and return its time.
:param string message: the message to log
:rtype: datetime
"""
return self._log(message, Logger.WARNING)
[docs] def log_crit(self, message):
"""
Log CRITICAL message, and return its time.
:param string message: the message to log
:rtype: datetime
"""
return self._log(message, Logger.CRITICAL)