From b9ec217d1ed37a25b7a1417cf138134e43e7d519 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 26 Dec 2004 15:50:53 +0000 Subject: [PATCH] - add logging to the test class git-svn-id: file:///home/www/usr01/svn/gnuviechadmin/gnuviech.info/gnuviechadmin/trunk@86 a67ec6bc-e5d5-0310-a910-815c51eb3124 --- backend/gnuviech/__init__.py | 64 ++-- backend/gnuviech/tools.py | 20 +- backend/gvadm.preferences | 2 + backend/log4py.conf | 6 + backend/log4py.py | 593 +++++++++++++++++++++++++++++++++++ backend/test.py | 47 ++- 6 files changed, 681 insertions(+), 51 deletions(-) create mode 100644 backend/log4py.conf create mode 100644 backend/log4py.py diff --git a/backend/gnuviech/__init__.py b/backend/gnuviech/__init__.py index d30dffb..5d0bcb6 100644 --- a/backend/gnuviech/__init__.py +++ b/backend/gnuviech/__init__.py @@ -4,6 +4,7 @@ """ import os +from log4py import Logger, FileAppender, LOGLEVEL_DEBUG class GNVPrefs: """This class has static variables for the settings of the GNUViech @@ -28,40 +29,53 @@ class GNVPrefs: WEBHOMEDIR = HOMEDIR+"/www/" WEBLOGDIR = WEBHOMEDIR+"logs/" WEBSTATSDIR = WEBHOMEDIR+"stats/" + LOGDIR = BASEPREFIX+"/var/log" + LOGFILE = LOGDIR+"/gnvadm.log" # load custom settings execfile("gvadm.preferences") + def __init__(self): + self.setupDirs() + def __repr__(self): items = dir(self) items.sort() return "gnuviech.GNVPrefs\n\t" + "\n\t".join(["%s = %s" % (item, getattr(self, item)) for item in items if getattr(self, item).__class__ in (str, int, list, dict)]) + "\n" + def setupDirs(self): + """Setup the directories and files required for proper operation of the + GNUViech administration tool.""" + for directory in (self.BASEPREFIX, + self.BASEPREFIX+"/etc", + self.BASEPREFIX+"/var", + self.GVADMDIR, + self.EXIMCONFDIR, + self.VIRTUALDOMDIR, + self.HOMEDIR, + self.POPHOMEDIR, + self.WEBHOMEDIR, + self.WEBLOGDIR, + self.WEBSTATSDIR, + self.LOGDIR): + if (not os.access(directory, os.R_OK & os.X_OK)): + print "making %s." % directory + os.mkdir(directory) + for required in (self.BASEPREFIX+"/etc/passwd", + self.BASEPREFIX+"/etc/shadow", + self.EXIMCONFDIR+"eximpasswords"): + if (not os.access(required, os.R_OK)): + print "creating %s." % required + file = open(required, "w") + file.close() + + def getLogger(self, instance): + logger = Logger().get_instance(instance) + logger.remove_all_targets() + logger.add_target(FileAppender(self.LOGFILE)) + logger.set_loglevel(LOGLEVEL_DEBUG) + return logger + class NoAdmDirError(Exception): """This exception is raised if the admin directory does'nt exist.""" pass - -def setupDirs(): - """Setup the directories and files required for proper operation of the - GNUViech administration tool.""" - for directory in (GNVPrefs.BASEPREFIX, - GNVPrefs.BASEPREFIX+"/etc", - GNVPrefs.GVADMDIR, - GNVPrefs.EXIMCONFDIR, - GNVPrefs.VIRTUALDOMDIR, - GNVPrefs.HOMEDIR, - GNVPrefs.POPHOMEDIR, - GNVPrefs.WEBHOMEDIR, - GNVPrefs.WEBLOGDIR, - GNVPrefs.WEBSTATSDIR): - if (not os.access(directory, os.R_OK & os.X_OK)): - print "making %s." % directory - os.mkdir(directory) - for required in (GNVPrefs.BASEPREFIX+"/etc/passwd", - GNVPrefs.BASEPREFIX+"/etc/shadow", - GNVPrefs.EXIMCONFDIR+"eximpasswords"): - if (not os.access(required, os.R_OK)): - print "creating %s." % required - file = open(required, "w") - file.close() - diff --git a/backend/gnuviech/tools.py b/backend/gnuviech/tools.py index e2c312b..827b495 100644 --- a/backend/gnuviech/tools.py +++ b/backend/gnuviech/tools.py @@ -3,18 +3,18 @@ (c) 2004 Jan Dittberner """ -from random import Random +import random, re from gnuviech import GNVPrefs def generatePassword(): - r = Random() - devrnd = open("/dev/random", "r") - r.seed(ord(devrnd.read(1))) - devrnd.close() return "".join([chr(char) for char in - r.sample(GNVPrefs.PWDCHARS, - r.randint(GNVPrefs.PWDMINLENGTH, - GNVPrefs.PWDMAXLENGTH))]) + random.sample(GNVPrefs.PWDCHARS, + random.randint(GNVPrefs.PWDMINLENGTH, + GNVPrefs.PWDMAXLENGTH))]) -# regex für email check -# p = re.compile(u"^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9\-]+(\.|[a-zA-Z0-9\-]+)*\.[a-z]{2,5})$") +def checkEmail(email): + """Returns a match object if the given email address is syntactically + correct otherwise it returns None""" + # regex for email check + p = re.compile(r'^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9\-]+(\.|[a-zA-Z0-9\-]+)*\.[a-z]{2,5})$') + return p.search(email) diff --git a/backend/gvadm.preferences b/backend/gvadm.preferences index f0043f6..76590fa 100644 --- a/backend/gvadm.preferences +++ b/backend/gvadm.preferences @@ -8,3 +8,5 @@ POPHOMEDIR = HOMEDIR+"/mail/" WEBHOMEDIR = HOMEDIR+"/www/" WEBLOGDIR = WEBHOMEDIR+"logs/" WEBSTATSDIR = WEBHOMEDIR+"stats/" +LOGDIR = BASEPREFIX+"/var/log" +LOGFILE = LOGDIR+"/gnvadm.log" diff --git a/backend/log4py.conf b/backend/log4py.conf new file mode 100644 index 0000000..f8f7157 --- /dev/null +++ b/backend/log4py.conf @@ -0,0 +1,6 @@ +[Default] +Ansi: false +LogLevel: Normal + +[Test] +LogLevel: Debug \ No newline at end of file diff --git a/backend/log4py.py b/backend/log4py.py new file mode 100644 index 0000000..19c97bf --- /dev/null +++ b/backend/log4py.py @@ -0,0 +1,593 @@ +""" + +Python logging module - Version 1.3 + +Loglevels: + + LOGLEVEL_NONE, LOGLEVEL_ERROR, LOGLEVEL_NORMAL, LOGLEVEL_VERBOSE, LOGLEVEL_DEBUG + +Format-Parameters: + + %C -- The name of the current class. + %D -- Program duration since program start. + %d -- Program duration for the last step (last output). + %F -- The name of the current function. + %f -- Current filename + %L -- Log type (Error, Warning, Debug or Info) + %M -- The actual message. + %N -- The current line number. + %T -- Current time (human readable). + %t -- Current time (machine readable) + %U -- Current fully qualified module/file. + %u -- Current module/file. + %x -- NDC (nested diagnostic contexts). + +Pre-defined Formats: + + FMT_SHORT -- %M + FMT_MEDIUM -- [ %C.%F ] %D: %M + FMT_LONG -- %T %L %C [%F] %x%M + FMT_DEBUG -- %T [%D (%d)] %L %C [%F (%N)] %x%M + +""" + +# Logging levels +LOGLEVEL_NONE = 1 << 0 +LOGLEVEL_ERROR = 1 << 1 +LOGLEVEL_NORMAL = 1 << 2 +LOGLEVEL_VERBOSE = 1 << 3 +LOGLEVEL_DEBUG = 1 << 4 + +# Pre-defined format strings +FMT_SHORT = "%M" +FMT_MEDIUM = "[ %C.%F ] %D: %M" +FMT_LONG = "%T %L %C [%F] %x%M" +FMT_DEBUG = "%T [%D (%d)] %L %C [%F (%N)] %x%M" + +# Special logging targets +TARGET_MYSQL = "MySQL" +TARGET_POSTGRES = "Postgres" +TARGET_SYSLOG = "Syslog" +TARGET_SYS_STDOUT = "sys.stdout" +TARGET_SYS_STDERR = "sys.stderr" +TARGET_SYS_STDOUT_ALIAS = "stdout" +TARGET_SYS_STDERR_ALIAS = "stderr" + +SPECIAL_TARGETS = [ TARGET_MYSQL, TARGET_POSTGRES, TARGET_SYSLOG, TARGET_SYS_STDOUT, TARGET_SYS_STDERR, TARGET_SYS_STDOUT_ALIAS, TARGET_SYS_STDERR_ALIAS ] + +# Configuration files +CONFIGURATION_FILES = {} +CONFIGURATION_FILES[1] = "log4py.conf" # local directory +CONFIGURATION_FILES[2] = "$HOME/.log4py.conf" # hidden file in the home directory +CONFIGURATION_FILES[3] = "/etc/log4py.conf" # system wide file + +# Constants for the FileAppender +ROTATE_NONE = 0 +ROTATE_DAILY = 1 +ROTATE_WEEKLY = 2 +ROTATE_MONTHLY = 3 + +# The following constants are of internal interest only + +# Message constants (used for ansi colors and for logtype %L) +MSG_DEBUG = 1 << 0 +MSG_WARN = 1 << 1 +MSG_ERROR = 1 << 2 +MSG_INFO = 1 << 3 + +# Boolean constants +TRUE = "TRUE" +FALSE = "FALSE" + +# Color constants +BLACK = 30 +RED = 31 +GREEN = 32 +YELLOW = 33 +BLUE = 34 +PURPLE = 35 +AQUA = 36 +WHITE = 37 + +LOG_MSG = { MSG_DEBUG: "DEBUG", MSG_WARN: "WARNING", MSG_ERROR: "ERROR", MSG_INFO: "INFO"} +LOG_COLORS = { MSG_DEBUG: [WHITE, BLACK, FALSE], MSG_WARN: [WHITE, BLACK, FALSE], MSG_ERROR: [WHITE, BLACK, TRUE], MSG_INFO: [WHITE, BLACK, FALSE]} +LOG_LEVELS = { "DEBUG": LOGLEVEL_DEBUG, "VERBOSE": LOGLEVEL_VERBOSE, "NORMAL": LOGLEVEL_NORMAL, "NONE": LOGLEVEL_NONE, "ERROR": LOGLEVEL_ERROR } + +SECTION_DEFAULT = "Default" + +from time import time, strftime, localtime +from types import StringType, ClassType, InstanceType, FileType, TupleType +from string import zfill, atoi, lower, upper, join, replace, split, strip +from re import sub +from ConfigParser import ConfigParser, NoOptionError +from os import stat, rename + +import sys +import traceback +import os +import copy +import socket +import locale +if (os.name == "posix"): + import syslog + +try: + import MySQLdb + mysql_available = TRUE +except: + mysql_available = FALSE + +def get_homedirectory(): + if (sys.platform == "win32"): + if (os.environ.has_key("USERPROFILE")): + return os.environ["USERPROFILE"] + else: + return "C:\\" + else: + if (os.environ.has_key("HOME")): + return os.environ["HOME"] + else: + # No home directory set + return "" + +# This is the main class for the logging module + +class Logger: + + cache = {} + instance = None + configfiles = [] + hostname = socket.gethostname() + + def __init__(self, useconfigfiles = TRUE, customconfigfiles = None): + """ **(private)** Class initalization & customization. """ + if (customconfigfiles): + if (type(customconfigfiles) == StringType): + customconfigfiles = [customconfigfiles] + Logger.configfiles = customconfigfiles + + if (not Logger.instance): + self.__Logger_setdefaults() + if (useconfigfiles == TRUE): + self.__Logger_appendconfigfiles(Logger.configfiles) + # read the default options + self.__Logger_parse_options() + + self.__Logger_timeinit = time() + self.__Logger_timelaststep = self.__Logger_timeinit + + Logger.instance = self + + if (useconfigfiles == TRUE): + # read and pre-cache settings for named classids + self.__Logger_cache_options() + + def get_root(self): + """ Provides a way to change the base logger object's properties. """ + return Logger.instance + + def get_instance(self, classid = "Main", use_cache = TRUE): + """ Either get the cached logger instance or create a new one + + Note that this is safe, even if you have your target set to sys.stdout + or sys.stderr + """ + + cache = Logger.cache + + if (type(classid) == ClassType): + classid = classid.__name__ + elif (type(classid) == InstanceType): + classid = classid.__class__.__name__ + + # classid has to be lowercase, because the ConfigParser returns sections lowercase + classid = lower(classid) + + if ((cache.has_key(classid)) and (use_cache == TRUE)): + cat = Logger.cache[classid] + else: + instance = Logger.instance + + # test for targets which won't deep copy + targets = instance.__Logger_targets + deepcopyable = TRUE + for i in range(len(targets)): + if (type(targets[i]) == FileType): + deepcopyable = FALSE + if (deepcopyable == FALSE): + # swap the non-copyable target out for a moment + del instance.__Logger_targets + cat = copy.deepcopy(instance) + instance.__Logger_targets = targets + cat.__Logger_targets = targets + else: + cat = copy.deepcopy(instance) + + cat.__Logger_classname = classid + # new categories have their own private Nested Diagnostic Contexts + self.__Logger_ndc = [] + self.__Logger_classid = classid + + cat.debug("Class %s instantiated" % classid) + if (use_cache == TRUE): + cache[classid] = cat + + return cat + + # Log-target handling (add, remove, set, remove_all) + + def add_target(self, target, *args): + """ Add a target to the logger targets. """ + if (not target in self.__Logger_targets): + if (target == TARGET_MYSQL): + if (mysql_available == TRUE): + # Required parameters: dbhost, dbname, dbuser, dbpass, dbtable + try: + self.__Logger_mysql_connection = MySQLdb.connect(host=args[0], db=args[1], user=args[2], passwd=args[3]) + self.__Logger_mysql_cursor = self.__Logger_mysql_connection.cursor() + self.__Logger_mysql_tablename = args[4] + self.__Logger_targets.append(target) + except MySQLdb.OperationalError, detail: + self.error("MySQL connection failed: %s" % detail) + else: + self.error("MySQL target not added - Python-mysql not available") + else: + if (type(target) == StringType): + if (target not in SPECIAL_TARGETS): + # This is a filename + target = FileAppender(target, self.__Logger_rotation) + if ((target == TARGET_SYSLOG) and (os.name != "posix")): + self.warn("TARGET_SYSLOG is not available on non-posix platforms!") + else: + self.__Logger_targets.append(target) + + def remove_target(self, target): + """ Remove a target from the logger targets. """ + if (target in self.__Logger_targets): + if (target == TARGET_MYSQL): + self.__Logger_mysql_connection.close() + self.__Logger_targets.remove(target) + + def set_target(self, target): + """ Set a single target. """ + if (type(target) == StringType): + if (target not in SPECIAL_TARGETS): + # File target + target = FileAppender(target, self.__Logger_rotation) + self.__Logger_targets = [ target ] + + def remove_all_targets(self): + """ Remove all targets from the logger targets. """ + self.__Logger_targets=[] + + def get_targets(self): + """ Returns all defined targets. """ + return self.__Logger_targets + + # Methods to set properties + + def set_loglevel(self, loglevel): + """ Set the loglevel for the current instance. """ + self.__Logger_loglevel = loglevel + + def set_formatstring(self, formatstring): + """ Set a format string. """ + self.__Logger_formatstring = formatstring + + def set_use_ansi_codes(self, useansicodes): + """ Use ansi codes for output to the console (TRUE or FALSE). """ + self.__Logger_useansicodes = useansicodes + + def set_time_format(self, timeformat): + """ Set the time format (default: loaded from the system locale). """ + self.__Logger_timeformat = timeformat + + def set_rotation(self, rotation): + """ Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY """ + self.__Logger_rotation = rotation + for i in range(len(self.__Logger_targets)): + target = self.__Logger_targets[i] + if (isinstance(target, FileAppender)): + target.set_rotation(rotation) + + # Method to get properties + + def get_loglevel(self): + """ Returns the current loglevel. """ + return self.__Logger_loglevel + + def get_formatstring(self): + """ Returns the current format string. """ + return self.__Logger_formatstring + + def get_use_ansi_codes(self): + """ Returns, wether ansi codes are being used or not. """ + return self.__Logger_useansicodes + + def get_time_format(self): + """ Returns the current time format. """ + return self.__Logger_timeformat + + def get_rotation(self): + """ Returns the current rotation setting. """ + return self.__Logger_rotation + + # Methods to push and pop trace messages for nested contexts + + def push(self, message): + """ Add a trace message. """ + self.__Logger_ndc.append(message) + + def pop(self): + """ Remove the topmost trace message. """ + ct = len(self.__Logger_ndc) + if (ct): + del(self.__Logger_ndc[ct-1]) + + def clear_ndc(self): + """ Clears all NDC messages. """ + self.__Logger_ndc = [] + + # Methods to actually print messages + + def debug(self, *messages): + """ Write a debug message. """ + if (self.__Logger_loglevel >= LOGLEVEL_DEBUG): + message = self.__Logger_collate_messages(messages) + self.__Logger_showmessage(message, MSG_DEBUG) + + def warn(self, *messages): + """ Write a warning message. """ + if (self.__Logger_loglevel >= LOGLEVEL_VERBOSE): + message = self.__Logger_collate_messages(messages) + self.__Logger_showmessage(message, MSG_WARN) + + def error(self, *messages): + """ Write a error message. """ + if (self.__Logger_loglevel >= LOGLEVEL_ERROR): + message = self.__Logger_collate_messages(messages) + self.__Logger_showmessage(message, MSG_ERROR) + + def info(self, *messages): + """ Write a info message. """ + if (self.__Logger_loglevel >= LOGLEVEL_NORMAL): + message = self.__Logger_collate_messages(messages) + self.__Logger_showmessage(message, MSG_INFO) + + # Private methods of the Logger class - you never have to use those directly + + def __Logger_collate_messages(self, messages): + """ **(private)** Create a single string from a number of messages. """ + return strip(reduce(lambda x, y: "%s%s" % (x, y), messages)) + + def __Logger_tracestack(self): + """ **(private)** Analyze traceback stack and set linenumber and functionname. """ + stack = traceback.extract_stack() + self.__Logger_module = stack[-4][0] + self.__Logger_linenumber = stack[-4][1] + self.__Logger_functionname = stack[-4][2] + self.__Logger_filename = stack[-4][0] + if (self.__Logger_functionname == "?"): + self.__Logger_functionname = "Main" + + def __Logger_setdefaults(self): + """ **(private)** Set default values for internal variables. """ + locale.setlocale(locale.LC_ALL) + self.__Logger_classid = None + self.__Logger_targets = [ TARGET_SYS_STDOUT ] # default target = sys.stdout + self.__Logger_formatstring = FMT_LONG + self.__Logger_loglevel = LOGLEVEL_NORMAL + self.__Logger_rotation = ROTATE_NONE + self.__Logger_useansicodes = FALSE + self.__Logger_functionname = "" + self.__Logger_filename = "" + self.__Logger_linenumber = -1 + try: + self.__Logger_timeformat = "%s %s" % (locale.nl_langinfo(locale.D_FMT), locale.nl_langinfo(locale.T_FMT)) + except (AttributeError): + self.__Logger_timeformat = "%d.%m.%Y %H:%M:%S" + self.__Logger_classname = None + self.__Logger_configfilename = "" + self.__Logger_module = "" + self.__Logger_ndc = [] # ndc = Nested Diagnostic Context + + def __Logger_find_config(self): + """ **(private)** Search for configuration files. """ + if (not self.__Logger_configfilename): + priorities = CONFIGURATION_FILES.keys() + priorities.sort() + configfilename = "" + for i in range(len(priorities)): + filename = CONFIGURATION_FILES[priorities[i]] + home_directory = get_homedirectory() + if (os.sep == "\\"): + home_directory = replace(home_directory, "\\", "\\\\") + filename = sub("\$HOME", home_directory, filename) + if (os.path.exists(filename)): + configfilename = filename + break + self.__Logger_configfilename = configfilename + return self.__Logger_configfilename + + def __Logger_parse_options(self, section = SECTION_DEFAULT): + """ **(private)** Parse main options from config file. """ + configfilename = self.__Logger_find_config() + + if (configfilename != ""): + parser = ConfigParser() + parser.read(configfilename) + self.__Logger_set_instance_options(parser, section, self) + return TRUE + + def __Logger_set_instance_options(self, parser, section, instance): + """ **(private)** Set the options for a given instance from the parser section """ + + for i in range(len(parser.options(section))): + option = lower(parser.options(section)[i]) + value = parser.get(section, option) + if (option == "format"): + instance.set_formatstring(value) + elif (option == "timeformat"): + instance.set_time_format(value) + elif (option == "ansi"): + instance.set_use_ansi_codes(upper(value)) + elif (option == "loglevel"): + instance.set_loglevel(LOG_LEVELS[upper(value)]) + elif (option == "target"): + splitted = split(value, ",") + instance.remove_all_targets() + for i in range(len(splitted)): + instance.add_target(strip(splitted[i])) + + def __Logger_cache_options(self): + """ **(private)** Read and cache debug levels for categories from config file. """ + configfilename = self.__Logger_find_config() + + if (configfilename != ""): + parser = ConfigParser() + parser.read(configfilename) + + for i in range(len(parser.sections())): + section = parser.sections()[i] + if (section != SECTION_DEFAULT): + instance = self.get_instance(section) + self.__Logger_set_instance_options(parser, section, instance) + return TRUE + + def __Logger_appendconfigfiles(self, filenames): + """ **(private)** Append a filename to the list of configuration files. """ + filenames.reverse() + for i in range(len(filenames)): + keys = CONFIGURATION_FILES.keys() + CONFIGURATION_FILES[min(keys) - 1] = filenames[i] + + def __Logger_get_ndc(self): + """ **(private)** Returns the NDC (nested diagnostic context) joined with single-spaces. """ + if (len(self.__Logger_ndc)): + return join(self.__Logger_ndc) + else: + return "" + + def __Logger_showmessage(self, message, messagesource): + """ **(private)** Writes a message to all targets set. """ + + if (isinstance(message, Exception)): + (exc_type, exc_value, tb) = sys.exc_info() + exception_summary = traceback.format_exception(exc_type, exc_value, tb) + message = 'Exception caught:\n' + for line in exception_summary: + message = "%s%s" % (message, line) + + currenttime = time() + self.__Logger_tracestack() + timedifference = "%.3f" % (currenttime - self.__Logger_timeinit) + timedifflaststep = "%.3f" % (currenttime - self.__Logger_timelaststep) + self.__Logger_timelaststep = currenttime + milliseconds = int(round((currenttime - long(currenttime)) * 1000)) + timeformat = sub("%S", "%S." + (zfill(milliseconds, 3)), self.__Logger_timeformat) + currentformattedtime = strftime(timeformat, localtime(currenttime)) + + line = self.__Logger_formatstring + line = sub("%C", str(self.__Logger_classname), line) + line = sub("%D", timedifference, line) + line = sub("%d", timedifflaststep, line) + line = sub("%F", self.__Logger_functionname, line) + line = sub("%f", self.__Logger_filename, line) + line = sub("%U", self.__Logger_module, line) + line = sub("%u", os.path.split(self.__Logger_module)[-1], line) + ndc = self.__Logger_get_ndc() + if (ndc != ""): + line = sub("%x", "%s - " % ndc, line) + else: + line = sub("%x", "", line) + message = replace(message, "\\", "\\\\") + if (self.__Logger_useansicodes == TRUE): + line = sub("%L", self.__Logger_ansi(LOG_MSG[messagesource], messagesource), line) + line = sub("%M", self.__Logger_ansi(message, messagesource), line) + else: + line = sub("%L", LOG_MSG[messagesource], line) + line = sub("%M", message, line) + line = sub("%N", str(self.__Logger_linenumber), line) + line = sub("%T", currentformattedtime, line) + line = sub("%t", `currenttime`, line) + + for i in range(len(self.__Logger_targets)): + target = self.__Logger_targets[i] + if (target == TARGET_MYSQL): + sqltime = strftime("'%Y-%m-%d', '%H:%M:%S'", localtime(currenttime)) + sqlStatement = "INSERT INTO %s (host, facility, level, date, time, program, msg) VALUES ('%s', '%s', '%s', %s, '%s', '%s')" % (self.__Logger_mysql_tablename, self.hostname, self.__Logger_functionname, LOG_MSG[messagesource], sqltime, str(self.__Logger_classname), sub("'", "`", message + " " + ndc)) + self.__Logger_mysql_cursor.execute(sqlStatement) + elif (target == TARGET_SYSLOG): + # We don't need time and stuff here + syslog.syslog(message) + elif (isinstance(target, FileAppender)): + target.writeline(line) + elif (target == sys.stdout) or (lower(target) == TARGET_SYS_STDOUT) or (lower(target) == TARGET_SYS_STDOUT_ALIAS): + sys.stdout.write("%s\n" % line) + elif (target == sys.stderr) or (lower(target) == TARGET_SYS_STDERR) or (lower(target) == TARGET_SYS_STDERR_ALIAS): + sys.stderr.write("%s\n" % line) + else: + target.write("%s\n" % line) + + def __Logger_ansi(self, text, messagesource): + """ **(private)** Converts plain text to ansi text. """ + bold = LOG_COLORS[messagesource][2] + fg = str(LOG_COLORS[messagesource][0]) + bg = LOG_COLORS[messagesource][1] + if (bold == TRUE): + fg = "%s;1" % fg + bg = bg + 10 + text = "\033[%d;%sm%s\033[0m" % (bg, fg, text) + return text + +class FileAppender: + + def __init__(self, filename, rotation = ROTATE_NONE): + """ **(private)** Class initalization & customization. """ + self.__FileAppender_filename = sub("\$HOME", get_homedirectory(), filename) + self.__FileAppender_filename = os.path.expanduser(self.__FileAppender_filename) + self.__FileAppender_filename = os.path.expandvars(self.__FileAppender_filename) + self.__FileAppender_rotation = rotation + + def __FileAppender_rotate(self, modification_time): + """ **(private)** Check, wether the file has to be rotated yet or not. """ + if (self.__FileAppender_rotation == ROTATE_DAILY): + strftime_mask = "%Y%j" + elif (self.__FileAppender_rotation == ROTATE_WEEKLY): + strftime_mask = "%Y%W" + elif (self.__FileAppender_rotation == ROTATE_MONTHLY): + strftime_mask = "%Y%m" + return (strftime(strftime_mask, localtime(time())) != strftime(strftime_mask, localtime(modification_time))) + + def __FileAppender_date_string(self, modification_time): + """ **(private)** Returns a new filename for the rotated file with the appropriate time included. """ + if (self.__FileAppender_rotation == ROTATE_DAILY): + return strftime("%Y-%m-%d", localtime(modification_time)) + elif (self.__FileAppender_rotation == ROTATE_WEEKLY): + return strftime("%Y-Week %W", localtime(modification_time)) + elif (self.__FileAppender_rotation == ROTATE_MONTHLY): + return strftime("%Y-Month %m", localtime(modification_time)) + + def get_rotation(self): + """ Returns the current rotation setting. """ + return self.__FileAppender_rotation + + def set_rotation(self, rotation): + """ Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY """ + self.__FileAppender_rotation = rotation + + def write(self, text): + """ Write some text to the file appender. """ + if ((os.path.exists(self.__FileAppender_filename)) and (self.__FileAppender_rotation != ROTATE_NONE)): + statinfo = stat(self.__FileAppender_filename) + if (self.__FileAppender_rotate(statinfo[8])): + splitted = os.path.splitext(self.__FileAppender_filename) + target_file = "%s-%s%s" % (splitted[0], self.__FileAppender_date_string(statinfo[8]), splitted[1]) + rename(self.__FileAppender_filename, target_file) + file = open(self.__FileAppender_filename, "a") + file.write(text) + file.close() + + def writeline(self, text): + """ Write some text including newline to the file appender. """ + self.write("%s\n" % text) diff --git a/backend/test.py b/backend/test.py index d7af80a..e03c443 100644 --- a/backend/test.py +++ b/backend/test.py @@ -1,20 +1,35 @@ -import gnuviech +import gnuviech, sys import gnuviech.tools -if __name__ == "__main__": - print gnuviech.GNVPrefs() - minlen = 0 - avglen = 0 - maxlen = 0 - pwds = 20 - for i in range(pwds): - pwd = gnuviech.tools.generatePassword() - print "%02d: %s (%d)" % (i, pwd, len(pwd)) - if (minlen == 0) or (len(pwd) < minlen): minlen = len(pwd) - if (len(pwd) > maxlen): maxlen = len(pwd) - avglen += len(pwd) - avglen = avglen/pwds - print """average password length: %d +class Test: + def __init__(self, prefs): + self.logger = prefs.getLogger(self) + self.prefs = prefs + + def doTest(self): + self.logger.debug(str(prefs)) + minlen = 0 + avglen = 0 + maxlen = 0 + pwds = 20 + for i in range(pwds): + pwd = gnuviech.tools.generatePassword() + self.logger.debug("%02d: %s (%d)" % (i, pwd, len(pwd))) + if (minlen == 0) or (len(pwd) < minlen): minlen = len(pwd) + if (len(pwd) > maxlen): maxlen = len(pwd) + avglen += len(pwd) + avglen = avglen/pwds + self.logger.debug("""average password length: %d minimum password length: %d -maximum password length: %d""" % (avglen, minlen, maxlen) +maximum password length: %d""" % (avglen, minlen, maxlen)) + + for address in ('jan@dittberner.info', 'jan', 'jan@gnuelf#test.de', + 'd.arnstadt@gmx.net'): + if gnuviech.tools.checkEmail(address): + self.logger.debug("%s is a valid email address." % address) + else: + self.logger.debug("%s is an invalid email address." % address) +if __name__ == "__main__": + prefs = gnuviech.GNVPrefs() + Test(prefs).doTest()