log4py.py :  » Business-Application » ECLiPt-Roaster » eroaster-2.2.0-0.9a » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Business Application » ECLiPt Roaster 
ECLiPt Roaster » eroaster 2.2.0 0.9a » log4py.py
"""

Python logging module - Version 1.3.1

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__
        elif (type(classid) == StringType):
            pass
        else:
            # Unknown object, so convert it to a string to make it usable
            classid = str(classid)

        # 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", replace(self.__Logger_filename, "\\", "\\\\"), line)
        line = sub("%U", replace(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)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.