Utils/logger.py (122 lines of code) (raw):
import time
import sys
import string
# noinspection PyMethodMayBeStatic
class Logger(object):
"""
The Agent's logging assumptions are:
For Log, and LogWithPrefix all messages are logged to the
self.file_path and to the self.con_path. Setting either path
parameter to None skips that log. If Verbose is enabled, messages
calling the LogIfVerbose method will be logged to file_path yet
not to con_path. Error and Warn messages are normal log messages
with the 'ERROR:' or 'WARNING:' prefix added.
"""
def __init__(self, filepath, conpath, verbose=False):
"""
Construct an instance of Logger.
"""
self.file_path = filepath
self.con_path = conpath
self.verbose = verbose
def throttle_log(self, counter):
"""
Log everything up to 10, every 10 up to 100, then every 100.
"""
return (counter < 10) or ((counter < 100) and ((counter % 10) == 0)) or ((counter % 100) == 0)
def write_to_file(self, message):
"""
Write 'message' to logfile.
"""
if self.file_path:
try:
with open(self.file_path, "a") as F:
message = filter(lambda x: x in string.printable, message)
# encoding works different for between interpreter version, we are keeping separate implementation
# to ensure backward compatibility
if sys.version_info[0] == 3:
message = ''.join(list(message)).encode('ascii', 'ignore').decode("ascii", "ignore")
elif sys.version_info[0] == 2:
message = message.encode('ascii', 'ignore')
F.write(message + "\n")
except IOError as e:
pass
def write_to_console(self, message):
"""
Write 'message' to /dev/console.
This supports serial port logging if the /dev/console
is redirected to ttys0 in kernel boot options.
"""
if self.con_path:
try:
with open(self.con_path, "w") as C:
message = filter(lambda x: x in string.printable, message)
# encoding works different for between interpreter version, we are keeping separate implementation
# to ensure backward compatibility
if sys.version_info[0] == 3:
message = ''.join(list(message)).encode('ascii', 'ignore').decode("ascii", "ignore")
elif sys.version_info[0] == 2:
message = message.encode('ascii', 'ignore')
C.write(message + "\n")
except IOError as e:
pass
def log(self, message):
"""
Standard Log function.
Logs to self.file_path, and con_path
"""
self.log_with_prefix("", message)
def log_to_console(self, message):
"""
Logs message to console by pre-pending each line of 'message' with current time.
"""
log_prefix = self._get_log_prefix("")
for line in message.split('\n'):
line = log_prefix + line
self.write_to_console(line)
def log_to_file(self, message):
"""
Logs message to file by pre-pending each line of 'message' with current time.
"""
log_prefix = self._get_log_prefix("")
for line in message.split('\n'):
line = log_prefix + line
self.write_to_file(line)
def no_log(self, message):
"""
Don't Log.
"""
pass
def log_if_verbose(self, message):
"""
Only log 'message' if global Verbose is True.
"""
self.log_with_prefix_if_verbose('', message)
def log_with_prefix(self, prefix, message):
"""
Prefix each line of 'message' with current time+'prefix'.
"""
log_prefix = self._get_log_prefix(prefix)
for line in message.split('\n'):
line = log_prefix + line
self.write_to_file(line)
self.write_to_console(line)
def log_with_prefix_if_verbose(self, prefix, message):
"""
Only log 'message' if global Verbose is True.
Prefix each line of 'message' with current time+'prefix'.
"""
if self.verbose:
log_prefix = self._get_log_prefix(prefix)
for line in message.split('\n'):
line = log_prefix + line
self.write_to_file(line)
self.write_to_console(line)
def warning(self, message):
self.log_with_prefix("WARNING:", message)
def error_with_prefix(self, prefix, message):
self.log_with_prefix("ERROR: " + str(prefix), message)
def error(self, message):
"""
Call ErrorWithPrefix(message).
"""
self.error_with_prefix("", message)
def _get_log_prefix(self, prefix):
"""
Generates the log prefix with timestamp+'prefix'.
"""
t = time.localtime()
t = "%04u/%02u/%02u %02u:%02u:%02u " % (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
return t + prefix
# meant to be used with tests
# noinspection PyMethodMayBeStatic
class TestLogger(Logger):
def __init__(self):
super(Logger, self).__init__()
self.verbose = True
self.con_path = None
self.file_path = None
def _log_to_stdout(self, message):
sys.stdout.writelines(message)
sys.stdout.write("\n")
def write_to_file(self, message):
self._log_to_stdout(message)
def write_to_console(self, message):
self._log_to_stdout(message)
def log(self, message):
self._log_to_stdout(message)
def log_to_console(self, message):
self._log_to_stdout(message)
def log_to_file(self, message):
self._log_to_stdout(message)
def log_if_verbose(self, message):
self._log_to_stdout(message)
def log_with_prefix(self, prefix, message):
log_prefix = self._get_log_prefix(prefix)
for line in message.split('\n'):
line = log_prefix + line
self._log_to_stdout(line)
def log_with_prefix_if_verbose(self, prefix, message):
self.log_with_prefix(prefix, message)
def warning(self, message):
self.log_with_prefix("WARNING:", message)
def error_with_prefix(self, prefix, message):
self.log_with_prefix("ERROR:", message)
def error(self, message):
self.error_with_prefix("", message)
global global_shared_context_logger
try:
# test whether global_shared_context_logger has been assigned previously
_ = global_shared_context_logger
except NameError:
# previously not assigned, assign default value
# will assign global_shared_context_logger only once
global_shared_context_logger = Logger('/var/log/waagent.log', '/dev/console')
def log(message):
global_shared_context_logger.log(message)
def error(message):
global_shared_context_logger.error(message)
def warning(message):
global_shared_context_logger.warning(message)
def error_with_prefix(prefix, message):
global_shared_context_logger.error_with_prefix(prefix, message)
def log_if_verbose(message):
global_shared_context_logger.log_if_verbose(message)