From dc0da570ab2a77aa684c7e5ea5a07ad7f2b9abb6 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 26 Dec 2004 12:53:50 +0000 Subject: [PATCH] bringing log4py-1.3 into the main branch git-svn-id: file:///home/www/usr01/svn/gnuviechadmin/gnuviech.info/gnuviechadmin/trunk@85 a67ec6bc-e5d5-0310-a910-815c51eb3124 --- backend/log4py/database/mysql.readme | 17 + backend/log4py/database/mysql.sql | 25 + backend/log4py/doc/AUTHORS | 14 + backend/log4py/doc/COPYING | 1 + backend/log4py/doc/ChangeLog | 87 +++ backend/log4py/doc/LICENSE | 20 + backend/log4py/doc/html/footer.template | 14 + backend/log4py/doc/html/header.template | 10 + backend/log4py/doc/html/index.html | 47 ++ backend/log4py/doc/html/indices.html | 59 ++ .../log4py/doc/html/log4py.FileAppender.html | 58 ++ backend/log4py/doc/html/log4py.Logger.html | 98 +++ backend/log4py/doc/html/log4py.css | 86 +++ backend/log4py/doc/html/log4py.html | 160 +++++ backend/log4py/log4py-classtest.py | 39 ++ backend/log4py/log4py-test.py | 86 +++ backend/log4py/log4py.conf | 65 ++ backend/log4py/log4py.py | 593 ++++++++++++++++++ backend/log4py/readme.txt | 30 + backend/log4py/setup.py | 10 + 20 files changed, 1519 insertions(+) create mode 100644 backend/log4py/database/mysql.readme create mode 100644 backend/log4py/database/mysql.sql create mode 100644 backend/log4py/doc/AUTHORS create mode 100644 backend/log4py/doc/COPYING create mode 100644 backend/log4py/doc/ChangeLog create mode 100644 backend/log4py/doc/LICENSE create mode 100644 backend/log4py/doc/html/footer.template create mode 100644 backend/log4py/doc/html/header.template create mode 100644 backend/log4py/doc/html/index.html create mode 100644 backend/log4py/doc/html/indices.html create mode 100644 backend/log4py/doc/html/log4py.FileAppender.html create mode 100644 backend/log4py/doc/html/log4py.Logger.html create mode 100644 backend/log4py/doc/html/log4py.css create mode 100644 backend/log4py/doc/html/log4py.html create mode 100755 backend/log4py/log4py-classtest.py create mode 100755 backend/log4py/log4py-test.py create mode 100644 backend/log4py/log4py.conf create mode 100644 backend/log4py/log4py.py create mode 100644 backend/log4py/readme.txt create mode 100644 backend/log4py/setup.py diff --git a/backend/log4py/database/mysql.readme b/backend/log4py/database/mysql.readme new file mode 100644 index 0000000..378c7bf --- /dev/null +++ b/backend/log4py/database/mysql.readme @@ -0,0 +1,17 @@ +To use mysql as logging database, please perform the following steps: + +1. Create a database and table: + + "mysql -u root -p < mysql.sql" + + This will create a new database "syslog" and a table "logs" + +2. Grant access to the log table to one or more users: + + "mysql -u root -p" + "use syslog;" + "grant insert,select,update,delete on syslog.logs to log4py@localhost;" + +3. If log4py@localhost is a new user, don't forget to set a password: + + "mysqladmin -u log4py -p password mysecretpwd" diff --git a/backend/log4py/database/mysql.sql b/backend/log4py/database/mysql.sql new file mode 100644 index 0000000..8c04129 --- /dev/null +++ b/backend/log4py/database/mysql.sql @@ -0,0 +1,25 @@ +# Table structure for table `logs` + +CREATE DATABASE syslog; + +USE syslog; + +CREATE TABLE logs (host varchar(32) default NULL, + facility varchar(10) default NULL, + priority varchar(10) default NULL, + level varchar(10) default NULL, + tag varchar(10) default NULL, + date date default NULL, + time time default NULL, + program varchar(15) default NULL, + msg text, + seq int(10) unsigned NOT NULL auto_increment, + PRIMARY KEY (seq), + KEY host (host), + KEY seq (seq), + KEY program (program), + KEY time (time), + KEY date (date), + KEY priority (priority), + KEY facility (facility) + ) TYPE=MyISAM; diff --git a/backend/log4py/doc/AUTHORS b/backend/log4py/doc/AUTHORS new file mode 100644 index 0000000..15c887a --- /dev/null +++ b/backend/log4py/doc/AUTHORS @@ -0,0 +1,14 @@ +Code Maintainer: + + Martin Preishuber + +Developers: + + Martin Preishuber + Bruce Kroeze + +Contributors: + + Weiyi Yang + Emily Bache + Rico Hendriks diff --git a/backend/log4py/doc/COPYING b/backend/log4py/doc/COPYING new file mode 100644 index 0000000..322acc4 --- /dev/null +++ b/backend/log4py/doc/COPYING @@ -0,0 +1 @@ +See the file LICENSE in this directory. \ No newline at end of file diff --git a/backend/log4py/doc/ChangeLog b/backend/log4py/doc/ChangeLog new file mode 100644 index 0000000..663d3b2 --- /dev/null +++ b/backend/log4py/doc/ChangeLog @@ -0,0 +1,87 @@ +Version 1.3 + + - FileAppender class added (generic file rotation) + - log4py uses FileAppender class for logfiles (enables log file rotation) + - support for win32 platform + - $HOME is replaced correctly on win32 platform + +Version 1.2.1 + + - syslog support is only enabled on POSIX operating systems + +Version 1.2 + + - syslog support added (very simple by now) + - bug fixed with sys.stderr (thanks Alain) + - get_targets method added + - environment variables in filename targets get expanded now + - added %f as format parameter, which is replaced by the current filename + (thanks Rémi) + - targets which support the write method work now (thanks again Rémi) + - Changed to order in info/debug/error message so _collate_messages + will only be called if required + +Version 1.1.1 + + - new Format parameter %t added (Thanks Emily) + - possibility to log exceptions added (Thanks Rico) + - possibility to log filenames added (Thanks again Rico) + +Version 1.1 + + - README updated + - MySQL target added (thanks Weiyi Yang) + +Version 1.0.1 + + - Loglevel LOGLEVEL_ERROR added (to show only error messages) + +Version 1.0 + + - minor bug fixes + +Version 0.7.1 + + - HTML documentation added (generated from doc strings) + +Version 0.7: + + - changed the class name from Category to Logger (log4j 1.2 conform) + - bug fixes with environment variables on windows systems (thanks Colin) + - fixed some bug with escaping \ (thanks again Colin) + - the configuration file may include settings for various instances now. + - multiple target support added. + +Version 0.6: + + - nested diagnostic contexts added (Bruce) + - application loglevels added to config files (Bruce) + +Version 0.5: + + - included a patch of Bruce Kroeze (bruce@zefamily.org), which improves + the performance by reading the config file only once and makes log4py + safe for deepcopying. + +Version 0.4: + + - license change from GPL to MIT + - the usual minor bugfixes + +Version 0.3: + + - added format parameter %d (duration since last message) + - log4py is used for debugging (if available) + - some minor bug fixes + +Version 0.2: + + - support for configuration files added + - support for user configurable timeformats added + - removed some parameters from the class initialization + - changed the default target to None (instead of sys.stdout) to make + log4py deepcopyable + +Version 0.1: + + - initial release diff --git a/backend/log4py/doc/LICENSE b/backend/log4py/doc/LICENSE new file mode 100644 index 0000000..4f384c3 --- /dev/null +++ b/backend/log4py/doc/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2001 Martin Preishuber + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/log4py/doc/html/footer.template b/backend/log4py/doc/html/footer.template new file mode 100644 index 0000000..de709c1 --- /dev/null +++ b/backend/log4py/doc/html/footer.template @@ -0,0 +1,14 @@ + +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
diff --git a/backend/log4py/doc/html/header.template b/backend/log4py/doc/html/header.template new file mode 100644 index 0000000..4d33fc6 --- /dev/null +++ b/backend/log4py/doc/html/header.template @@ -0,0 +1,10 @@ + + + + + + + + +
Table of contents | Index 
+
diff --git a/backend/log4py/doc/html/index.html b/backend/log4py/doc/html/index.html new file mode 100644 index 0000000..f2112cb --- /dev/null +++ b/backend/log4py/doc/html/index.html @@ -0,0 +1,47 @@ + + + + + + + + + +Contents + + + + + + + + + + + +
Table of contents | Index 
+
+ +

Documented Python modules

+

Index

+ + + +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
+ + + diff --git a/backend/log4py/doc/html/indices.html b/backend/log4py/doc/html/indices.html new file mode 100644 index 0000000..c8b7b2e --- /dev/null +++ b/backend/log4py/doc/html/indices.html @@ -0,0 +1,59 @@ + + + + + + + + + +Index page + + + + + + + + + + + +
Table of contents | Index 
+
+ +

Index page

+

Table of contents

+F G L
+
+ +

F

+

FileAppender, Class (in Module log4py)

+ +

G

+

get_homedirectory, Function (in Module log4py)

+ +

L

+

log4py, Module

+

FileAppender, Class

+

get_homedirectory, Function

+

Logger, Class

+

Logger, Class (in Module log4py)

+ + +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
+ + + diff --git a/backend/log4py/doc/html/log4py.FileAppender.html b/backend/log4py/doc/html/log4py.FileAppender.html new file mode 100644 index 0000000..558bb5c --- /dev/null +++ b/backend/log4py/doc/html/log4py.FileAppender.html @@ -0,0 +1,58 @@ + + + + + + + + + +Class FileAppender + + + + + + + + + + + +
Table of contents | Index 
+
+ +

class FileAppender

+

Declared in module log4py

+ + + +

Synopsis

+
+class FileAppender:
+    def log4py.FileAppender.__FileAppender_date_string(self, modification_time) # (private) Returns a new filename for the rotated file with the appropriate time included. 
+    def log4py.FileAppender.__FileAppender_rotate(self, modification_time) # (private) Check, wether the file has to be rotated yet or not. 
+    def log4py.FileAppender.__init__(self, filename, rotation=0) # (private) Class initalization & customization. 
+    def log4py.FileAppender.get_rotation(self) #  Returns the current rotation setting. 
+    def log4py.FileAppender.set_rotation(self, rotation) #  Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY 
+    def log4py.FileAppender.write(self, text) #  Write some text to the file appender. 
+    def log4py.FileAppender.writeline(self, text) #  Write some text including newline to the file appender. 
+
+
+ +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
+ + + diff --git a/backend/log4py/doc/html/log4py.Logger.html b/backend/log4py/doc/html/log4py.Logger.html new file mode 100644 index 0000000..3167145 --- /dev/null +++ b/backend/log4py/doc/html/log4py.Logger.html @@ -0,0 +1,98 @@ + + + + + + + + + +Class Logger + + + + + + + + + + + +
Table of contents | Index 
+
+ +

class Logger

+

Declared in module log4py

+ + + +

Synopsis

+
+class Logger:
+    def log4py.Logger.__Logger_ansi(self, text, messagesource) # (private) Converts plain text to ansi text. 
+    def log4py.Logger.__Logger_appendconfigfiles(self, filenames) # (private) Append a filename to the list of configuration files. 
+    def log4py.Logger.__Logger_cache_options(self) # (private) Read and cache debug levels for categories from config file. 
+    def log4py.Logger.__Logger_collate_messages(self, messages) # (private) Create a single string from a number of messages. 
+    def log4py.Logger.__Logger_find_config(self) # (private) Search for configuration files. 
+    def log4py.Logger.__Logger_get_ndc(self) # (private) Returns the NDC (nested diagnostic context) joined with single-spaces. 
+    def log4py.Logger.__Logger_parse_options(self, section='Default') # (private) Parse main options from config file. 
+    def log4py.Logger.__Logger_set_instance_options(self, parser, section, instance) # (private) Set the options for a given instance from the parser section 
+    def log4py.Logger.__Logger_setdefaults(self) # (private) Set default values for internal variables. 
+    def log4py.Logger.__Logger_showmessage(self, message, messagesource) # (private) Writes a message to all targets set. 
+    def log4py.Logger.__Logger_tracestack(self) # (private) Analyze traceback stack and set linenumber and functionname. 
+    def log4py.Logger.__init__(self, useconfigfiles='TRUE', customconfigfiles=None) # (private) Class initalization & customization. 
+    def log4py.Logger.add_target(self, target, *args) #  Add a target to the logger targets. 
+    def log4py.Logger.clear_ndc(self) #  Clears all NDC messages. 
+    def log4py.Logger.debug(self, *messages) #  Write a debug message. 
+    def log4py.Logger.error(self, *messages) #  Write a error message. 
+    def log4py.Logger.get_formatstring(self) #  Returns the current format string. 
+    def log4py.Logger.get_instance(self, classid='Main', use_cache='TRUE') #  Either get the cached logger instance or create a new one
+    def log4py.Logger.get_loglevel(self) #  Returns the current loglevel. 
+    def log4py.Logger.get_root(self) #  Provides a way to change the base logger object's properties. 
+    def log4py.Logger.get_rotation(self) #  Returns the current rotation setting. 
+    def log4py.Logger.get_targets(self) #  Returns all defined targets. 
+    def log4py.Logger.get_time_format(self) #  Returns the current time format. 
+    def log4py.Logger.get_use_ansi_codes(self) #  Returns, wether ansi codes are being used or not. 
+    def log4py.Logger.info(self, *messages) #  Write a info message. 
+    def log4py.Logger.pop(self) #  Remove the topmost trace message. 
+    def log4py.Logger.push(self, message) #  Add a trace message. 
+    def log4py.Logger.remove_all_targets(self) #  Remove all targets from the logger targets. 
+    def log4py.Logger.remove_target(self, target) #  Remove a target from the logger targets. 
+    def log4py.Logger.set_formatstring(self, formatstring) #  Set a format string. 
+    def log4py.Logger.set_loglevel(self, loglevel) #  Set the loglevel for the current instance. 
+    def log4py.Logger.set_rotation(self, rotation) #  Set the file rotation mode to one of ROTATE_NONE, ROTATE_DAILY, ROTATE_WEEKLY, ROTATE_MONTHLY 
+    def log4py.Logger.set_target(self, target) #  Set a single target. 
+    def log4py.Logger.set_time_format(self, timeformat) #  Set the time format (default: loaded from the system locale). 
+    def log4py.Logger.set_use_ansi_codes(self, useansicodes) #  Use ansi codes for output to the console (TRUE or FALSE). 
+    def log4py.Logger.warn(self, *messages) #  Write a warning message. 
+
+dictionary cache = {}
+list configfiles = []
+string hostname = 'travelmate'
+None instance = None
+

log4py.Logger.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 +

+ + +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
+ + + diff --git a/backend/log4py/doc/html/log4py.css b/backend/log4py/doc/html/log4py.css new file mode 100644 index 0000000..8c73f8e --- /dev/null +++ b/backend/log4py/doc/html/log4py.css @@ -0,0 +1,86 @@ +// +// $Header: /cvsroot/log4py/log4py/doc/html/log4py.css,v 1.1 2003/04/09 19:02:06 preisl Exp $ +// +// Default style sheet for pythondoc HTML4 mode. +// +// Written by Daniel Larsson. +// +// $History: $ +// + +H1, H2, H3 { + font-family: Tahoma, Verdana, Arial, Helvetica; + font-style: bold; +} + +H1 { + font-size: 14px; +} + +H2 { + font-size: 13px; +} + +H3 { + font-size: 12px; +} + +//H1.ModuleName, H1.FunctionName, H1.ClassName { +// font-size: 18pt; +//} + +.ClassHead { +} + +BODY, TD { + font-family: Tahoma, Verdana, Arial, Helvetica; + font-size: 12px; +} + +.Body { +} + +SPAN.PyKeyword, SPAN.VariableType { + color: indigo; +} + +SPAN.ClassName { + color: blue; +} + +SPAN.FunctionName, SPAN.VariableName, SPAN.AliasName { + color: blue; +} + +.Argument { + color: brown; +} + +.OneLiner { + color: darkgreen; +} + +.IndexEntry0, .IndexEntry1, .IndexEntry2, .IndexEntry3 { + margin-top: 0; + margin-bottom: 0; +} + +.IndexEntry1 { + margin-left: 1em; +} + +.IndexEntry2 { + margin-left: 2em; +} + +.IndexEntry3 { + margin-left: 3em; +} + +A.DocLink, A.IndexLink { + text-decoration: none; +} + +A:hover { + text-decoration: underline; +} diff --git a/backend/log4py/doc/html/log4py.html b/backend/log4py/doc/html/log4py.html new file mode 100644 index 0000000..c202845 --- /dev/null +++ b/backend/log4py/doc/html/log4py.html @@ -0,0 +1,160 @@ + + + + + + + + + +Module log4py + + + + + + + + + + + +
Table of contents | Index 
+
+ +

Module log4py

+
class FileAppender
+class Logger
+def get_homedirectory()
+int AQUA = 36
+int BLACK = 30
+int BLUE = 34
+dictionary CONFIGURATION_FILES = {1: 'log4py.conf', 2: '$HOME/.log4py.conf', 3: '/etc/log4py.conf'}
+string FALSE = 'FALSE'
+string FMT_DEBUG = '%T [%D (%d)] %L %C [%F (%N)] %x%M'
+string FMT_LONG = '%T %L %C [%F] %x%M'
+string FMT_MEDIUM = '[ %C.%F ] %D: %M'
+string FMT_SHORT = '%M'
+int GREEN = 32
+int LOGLEVEL_DEBUG = 16
+int LOGLEVEL_ERROR = 2
+int LOGLEVEL_NONE = 1
+int LOGLEVEL_NORMAL = 4
+int LOGLEVEL_VERBOSE = 8
+dictionary LOG_COLORS = {1: [37, 30, 'FALSE'], 2: [37, 30, 'FALSE'], 4: [37, 30, 'TRUE'], 8: [37, 30, 'FALSE']}
+dictionary LOG_LEVELS = {'DEBUG': 16, 'ERROR': 2, 'NONE': 1, 'NORMAL': 4}
+dictionary LOG_MSG = {1: 'DEBUG', 2: 'WARNING', 4: 'ERROR', 8: 'INFO'}
+int MSG_DEBUG = 1
+int MSG_ERROR = 4
+int MSG_INFO = 8
+int MSG_WARN = 2
+int PURPLE = 35
+int RED = 31
+int ROTATE_DAILY = 1
+int ROTATE_MONTHLY = 3
+int ROTATE_NONE = 0
+int ROTATE_WEEKLY = 2
+string SECTION_DEFAULT = 'Default'
+list SPECIAL_TARGETS = ['MySQL', 'Postgres', 'Syslog', 'sys.stdout', 'sys.stderr', 'stdout', ...]
+string TARGET_MYSQL = 'MySQL'
+string TARGET_POSTGRES = 'Postgres'
+string TARGET_SYSLOG = 'Syslog'
+string TARGET_SYS_STDERR = 'sys.stderr'
+string TARGET_SYS_STDERR_ALIAS = 'stderr'
+string TARGET_SYS_STDOUT = 'sys.stdout'
+string TARGET_SYS_STDOUT_ALIAS = 'stdout'
+string TRUE = 'TRUE'
+int WHITE = 37
+int YELLOW = 33
+string __file__ = '/home/preisl/Software/Log4Py/log4py.py'
+string mysql_available = 'FALSE'
+alias ClassType = class (type type)
+alias FileType = file (type type)
+alias InstanceType = instance (type type)
+alias StringType = string (type type)
+alias TupleType = tuple (type type)
+

Description

+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 +
+ + +
+ + + + + +
+ Valid HTML 4.0! + Made with CSS + + Martin Preishuber, Martin.Preishuber@eclipt.at
+ Version 1.3 +
+ + + diff --git a/backend/log4py/log4py-classtest.py b/backend/log4py/log4py-classtest.py new file mode 100755 index 0000000..a16bd85 --- /dev/null +++ b/backend/log4py/log4py-classtest.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +"""Test class for log4py. + +You should get output like this: +-------- +20-10-2001 21:19 sharedlog.__init__ [DEBUG]- Shared log instantiated +20-10-2001 21:19 Main.get_instance [DEBUG]- Instantiated +20-10-2001 21:19 Main.Main [INFO]- in main +20-10-2001 21:19 c1.get_instance [DEBUG]- Instantiated +20-10-2001 21:19 c1.__init__ [INFO]- in c1 +20-10-2001 21:19 c2.get_instance [DEBUG]- Instantiated +20-10-2001 21:19 c2.__init__ [INFO]- in c2 +20-10-2001 21:19 c1.__init__ [INFO]- in c1 +-------- + +Author: Bruce Kroeze +""" + +from log4py import Logger, LOGLEVEL_DEBUG + +class c1: + def __init__(self): + log = Logger().get_instance(self) + log.info("in c1") + +class c2: + def __init__(self): + log = Logger().get_instance(self) + log.info("in c2") + +if (__name__ == '__main__'): + log = Logger("$HOME/log4py.conf").get_instance() + log.get_root().set_loglevel(LOGLEVEL_DEBUG) + log.info("in main") + + a = c1() + b = c2() + c = c1() diff --git a/backend/log4py/log4py-test.py b/backend/log4py/log4py-test.py new file mode 100755 index 0000000..0c69d33 --- /dev/null +++ b/backend/log4py/log4py-test.py @@ -0,0 +1,86 @@ +#!/usr/bin/python + +import log4py +import sys +from os import utime +from time import time + +class Log4PyTest: + + def __init__(self): + self.log4py = log4py.Logger().get_instance(self) + + def run(self): + self.log4py.error("error") + self.log4py.warn("warn") + self.log4py.info("info") + self.log4py.debug("debug") + + def output(self, message): + self.log4py.info(message) + +mytest = Log4PyTest() + +print "\nSettings from log4py.conf\n" +mytest.run() + +print "\nNormal level - Long format (written to $HOME/log4py-test.log)" +mytest.log4py.set_target("$HOME/log4py-test.log") +mytest.run() +mytest.log4py.set_target(sys.stdout) + +print "\nNormal level - Long format (ansi color)\n" +mytest.log4py.set_use_ansi_codes(log4py.TRUE) +mytest.run() +mytest.log4py.set_use_ansi_codes(log4py.FALSE) + +print "\nDebug level - Debug format\n" +mytest.log4py.set_formatstring(log4py.FMT_DEBUG) +mytest.log4py.set_loglevel(log4py.LOGLEVEL_DEBUG) +mytest.run() + +print "\nVerbose level - Medium format\n" +mytest.log4py.set_formatstring(log4py.FMT_MEDIUM) +mytest.log4py.set_loglevel(log4py.LOGLEVEL_VERBOSE) +mytest.run() + +print "\nVerbose level - User defined format\n" +mytest.log4py.set_formatstring("[ %u (%F) ] %D: %M") +mytest.log4py.set_loglevel(log4py.LOGLEVEL_VERBOSE) +mytest.run() + +print "\nNormal, long format - Testing Nested Diagnostic Context\n" +mytest.log4py.set_formatstring(log4py.FMT_LONG) +mytest.log4py.set_loglevel(log4py.LOGLEVEL_VERBOSE) +mytest.log4py.push("ndc1") +mytest.output("Should say \"ndc1\""); +mytest.log4py.push("ndc2") +mytest.output("Should say \"ndc1 ndc2\""); +mytest.log4py.pop() +mytest.output("Should say \"ndc1\""); +mytest.log4py.push("ndc3") +mytest.output("Should say \"ndc1 ndc3\""); +mytest.log4py.clear_ndc(); +mytest.output("Should not have any ndc items"); + +print "\nTesting MySQL target\n" +mytest.log4py.add_target(log4py.TARGET_MYSQL, "localhost", "syslog", "log4py", "mysecretpwd", "logs") +mytest.output("hello world") +mytest.log4py.remove_target(log4py.TARGET_MYSQL) + +print "\nTesting Syslog target\n" +mytest.log4py.add_target(log4py.TARGET_SYSLOG) +mytest.run() +mytest.log4py.remove_target(log4py.TARGET_SYSLOG) + +print "\nGetting all available targets\n" +print mytest.log4py.get_targets() + +print "\nTesting log-file rotation (log4py-rotation.log)\n" +mytest.log4py.set_target("log4py-rotation.log") +mytest.run() +mytest.log4py.set_rotation(log4py.ROTATE_DAILY) +yesterday = time() - 60 * 60 * 24 +utime("log4py-rotation.log", (yesterday, yesterday)) +mytest.run() +mytest.log4py.set_target(sys.stdout) diff --git a/backend/log4py/log4py.conf b/backend/log4py/log4py.conf new file mode 100644 index 0000000..87b19b5 --- /dev/null +++ b/backend/log4py/log4py.conf @@ -0,0 +1,65 @@ +# Log4Py configuration file +# +# The "Default" Sections contains default settings which can be overwritten +# by settings for different instances (see bottom of file) + +[Default] + +# Format is the output format of the lines. +# +# Possible parameters are: +# +# %C ..... class-name +# %D ..... program duration +# %d ..... duration for the last step (last output) +# %F ..... function name +# %L ..... logtype (Error, Warning, ...) +# %M ..... message +# %N ..... Line-number +# %T ..... current time +# +# Example formats are: +# +# Short: %M +# Medium: [ %C.%F ] %D: %M +# Long (default): %T %L %C [%F] - %M +# Debug: %T [%D (%d)] %L %C [%F (%N)] - %M + +Format: %T %L %C [%F] - %M + +# Target controls the output medium of the logging +# +# Possible values are: +# +# stdout (default): Standard output stream +# stderr: Error stream +# Any other filename +# +# Multiple targets can be specified as comma seperated list + +Target: stdout + +# Use ansi colors. Possible values are True or False (default) + +Ansi: False + +# TimeFormat is the format of the date and time as used by the +# Python strftime() function + +# TimeFormat: %d.%m.%Y %H:%M:%S + +# LogLevel controls the level of what you want to see +# +# Possible values are: +# +# None: No output (silent mode) +# Normal (default): Information- and Errormessages +# Verbose: Information-, Error- and Warningmessages +# Debug: Information-, Error-, Warning- and Debugmessages + +LogLevel: Normal + +# This is a section for the Log4PyTest class. + +[Log4PyTest] +LogLevel: Debug diff --git a/backend/log4py/log4py.py b/backend/log4py/log4py.py new file mode 100644 index 0000000..19c97bf --- /dev/null +++ b/backend/log4py/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/log4py/readme.txt b/backend/log4py/readme.txt new file mode 100644 index 0000000..b7a52b2 --- /dev/null +++ b/backend/log4py/readme.txt @@ -0,0 +1,30 @@ +Log4Py ReadMe: + +Log4Py is a Python logging module similar to log4j. It supports multiple levels +of logging and configurable output (either to stdout/stderr or to files). A +list of available format strings and ouput parameters can be found at the +beginning of log4py.py. + +Installation: + + Automatic: + Using Python's Distutils, you can execute: + "python setup.py install" + + Manually: + Either copy log4py.py into your project directory or to your site-packages directory. + +Usage: + + from log4py import Logger + + class foo: + def __init__(self): + cat = Logger().get_instance(self) + + Have a look at log4py-test.py and log4py-classtest.py for examples. + For logging to databases, please have a look at the database/ directory + +If you have any comments or questions, don't hesitate to contact me ;-) + +Martin diff --git a/backend/log4py/setup.py b/backend/log4py/setup.py new file mode 100644 index 0000000..fa06caf --- /dev/null +++ b/backend/log4py/setup.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name = "log4py", + version = "1.3", + description = "Python Logging Module", + py_modules = ["log4py"], + author = "Martin Preishuber " + )