Created
March 11, 2021 04:06
-
-
Save rfinnie/0bdf4ba7306dde9350d2698ca040e72e to your computer and use it in GitHub Desktop.
Python logging precursor, circa 2001
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""A centralized abstraction layer for logging via facilities. | |
log = LogObj() | |
log.nothing = Nolog() | |
log.debug = File(file='debuglog.txt', flags='w', bufsize=0, longlog=1) | |
log.info = File(sys.stdout) | |
log.info.deps.append('debug') | |
log.error = Syslog(LOG_LOCAL5|LOG_ERR) | |
log.error.deps.append('info') | |
log.crit = File(sys.stderr, longlog=1) | |
log.crit.deps.append('debug') | |
print >> log.error, 'This will go to syslog, stdout, and debuglog.txt.' | |
log.debug.write('This won\'t get flushed until the line is complete, ') | |
log.debug.write('or the buffer is flushed.') | |
log.debug.flush() | |
log.nothing('The file object is also callable, ') | |
log.nothing('but since this is a Nolog object, nothing will be done here.\\n') | |
print >> log.crit, 'This is a call to stderr with no dependencies.' | |
""" | |
import sys, time, types, os | |
try: | |
from syslog import * | |
except ImportError: | |
# Simulate a syslog module if one doesn't exist (win32) | |
LOG_EMERG = 0 | |
LOG_ALERT = 0 | |
LOG_CRIT = 0 | |
LOG_ERR = 0 | |
LOG_WARNING = 0 | |
LOG_NOTICE = 0 | |
LOG_INFO = 0 | |
LOG_DEBUG = 0 | |
LOG_PID = 0 | |
LOG_CONS = 0 | |
LOG_NDELAY = 0 | |
LOG_NOWAIT = 0 | |
LOG_PERROR = 0 | |
LOG_KERN = 0 | |
LOG_USER = 0 | |
LOG_MAIL = 0 | |
LOG_DAEMON = 0 | |
LOG_AUTH = 0 | |
LOG_LPR = 0 | |
LOG_LOCAL0 = 0 | |
LOG_LOCAL1 = 0 | |
LOG_LOCAL2 = 0 | |
LOG_LOCAL3 = 0 | |
LOG_LOCAL4 = 0 | |
LOG_LOCAL5 = 0 | |
LOG_LOCAL6 = 0 | |
LOG_LOCAL7 = 0 | |
LOG_SYSLOG = 0 | |
LOG_CRON = 0 | |
LOG_UUCP = 0 | |
LOG_NEWS = 0 | |
def openlog(logname, logopt): | |
print >> sys.stderr, 'syslog.openlog(%s, %s)' % \ | |
(repr(logname), repr(logopt)) | |
def closelog(): | |
print >> sys.stderr, 'syslog.closelog()' | |
def syslog(logopt, buf): | |
print >> sys.stderr, 'syslog.syslog(%s, %s)' % \ | |
(repr(logopt), repr(buf)) | |
class LogObj: | |
def __repr__(self): | |
return repr(self.__dict__) | |
def __setattr__(self, k, v): | |
"""D[k] = v -> None. Sets an attribute with a PseudoFile object.""" | |
if isinstance(v, PseudoFile): | |
self.__dict__[k] = v | |
self.__dict__[k].parentdata = self.__dict__ | |
self.__dict__[k].facility = k | |
else: | |
raise AttributeError, "Value must be a PseudoFile object" | |
def __getattr__(self, k): | |
"""D[k] -> PseudoFile sub-classed object, Nolog if key doesn't exist""" | |
if k[:2] == '__': | |
return self.__dict__[k] | |
else: | |
if self.__dict__.has_key(k): | |
return self.__dict__[k] | |
else: | |
return Nolog() | |
def __getitem__(self, k): | |
"""D.k -> see __getattr__""" | |
return getattr(self, k) | |
def __setitem__(self, k, v): | |
"""D.k = v -> see __setattr__""" | |
return setattr(self, k, v) | |
def __len__(self): | |
"""len(D) -> number of keys in dictionary""" | |
return len(self.__dict__.keys()) | |
def __contains__(self, k): | |
"""k in D -> see has_key""" | |
return self.__dict__.has_key(k) | |
def __del__(self): | |
"""del(D) -> None. Clears the dictionary before deleting it.""" | |
self.clear() | |
def keys(self): | |
"""D.keys() -> list of D's keys""" | |
return self.__dict__.keys() | |
def values(self): | |
"""D.values() -> list of D's values""" | |
return self.__dict__.values() | |
def items(self): | |
"""D.items() -> list of D's (key, value) pairs, as 2-tuples""" | |
return self.__dict__.items() | |
def has_key(self, key): | |
"""D.has_key(k) -> 1 if D has a key k, else 0""" | |
return self.__dict__.has_key(key) | |
def get(self, key, default = None): | |
"""D.get(k[,d]) -> D[k] if D.has_key(k), else d. d defaults to None.""" | |
return self.__dict__.get(key, default) | |
def clear(self): | |
"""D.clear() -> None. Remove all items from D.""" | |
return self.__dict__.clear() | |
def copy(self): | |
"""D.copy() -> a shallow copy of D""" | |
out = self.__dict__.copy() | |
for i in out.keys(): | |
out[i].deps = [] | |
out[i].aprentdata = {} | |
out[i].facility = 'manual' | |
return out | |
def update(self, E): | |
"""D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]""" | |
for k in E.keys(): | |
setattr(self, k, E[k]) | |
return None | |
class PseudoFile: | |
def __init__(self): | |
"""D = PseudoFile() -> file-like object. Logging to this does nothing.""" | |
self.deps = [] | |
self.buffer = '' | |
self.iid = 'PseudoFile' | |
self.parentdata = {} | |
self.facility = 'manual' | |
def __del__(self): | |
"""del(D) -> None. Severs dependencies and closes the file.""" | |
self.deps = [] | |
self.parentdata = {} | |
self.close() | |
def __call__(self, message): | |
"""D() -> None. Logs a message.""" | |
self.write(message) | |
def flush(self): | |
"""D.flush() -> None. Flushes the line buffer.""" | |
pass | |
def write(self, message, iidlist=''): | |
"""D.write(message) -> None. Writes data to the line buffer.""" | |
if iidlist == '': | |
iidlist = [] | |
if self.iid not in iidlist: | |
iidlist.append(self.iid) | |
lines = message.split('\n') | |
if len(lines) == 1: | |
self.buffer += lines[0] | |
else: | |
for i in lines[:-1]: | |
self.buffer += i | |
self.flush() | |
if lines[-1] != '': | |
self.buffer += lines[-1] | |
for i in self.deps: | |
if i in self.parentdata.keys(): | |
self.parentdata[i].write(message, iidlist) | |
def writelines(self, mlist): | |
"""D.writelines(mlist) -> None. Calls D.write() over a list.""" | |
for message in mlist: | |
self.write(message) | |
def close(self): | |
"""D.close() -> None. Flushes the line buffer""" | |
self.flush() | |
def tell(self): | |
"""D.tell() -> 0. Always returns 0.""" | |
return 0 | |
def truncate(self, size=0): | |
"""D.truncate(size) -> None. Does nothing.""" | |
pass | |
class Nolog(PseudoFile): | |
def __init__(self): | |
"""D = Nolog() -> file-like object. Logging to this does nothing.""" | |
self.deps = [] | |
self.buffer = '' | |
self.iid = 'Nolog' | |
self.parentdata = {} | |
self.facility = 'manual' | |
class Syslog(PseudoFile): | |
logopt = 0 | |
def __init__(self, logopt=-1): | |
"""D = Syslog(logopt) -> file-like object. Logs to Syslog. | |
Keyword arguments: | |
logopt -- Syslog data inherited from syslog. Default: LOG_LOCAL5|LOG_ERR | |
""" | |
self.counter = 0 | |
if logopt == -1: | |
logopt = (LOG_LOCAL5|LOG_ERR) | |
self.filename = '.'.join(os.path.basename(sys.argv[0]).split('.')[:-1]) | |
if not self.filename: | |
self.filename = 'console' | |
self.deps = [] | |
self.buffer = '' | |
self.logopt = logopt | |
self.iid = ('Syslog', logopt) | |
self.parentdata = {} | |
self.facility = 'manual' | |
def flush(self): | |
"""D.flush() -> None. Flushes the line buffer.""" | |
if self.buffer: | |
buffer = self.buffer | |
self.buffer = '' | |
toout = [] | |
while buffer: | |
toout.append(buffer[:256]) | |
buffer = buffer[256:] | |
for buffer in toout: | |
self.counter += 1 | |
openlog(self.filename, self.logopt) | |
syslog(self.logopt, '%08d %s: %s' % (self.counter, self.facility, \ | |
buffer)) | |
closelog() | |
class File(PseudoFile): | |
openfile = sys.stderr | |
def __init__(self, file=sys.stderr, flags='a', bufsize=1, longlog=0): | |
"""D = File(file, flags, bufsize, longlog) -> file-like object. | |
Logs to a file or file-like object. | |
Keyword arguments: | |
file -- File to log to. Can be a string filename or a file-like object. | |
flags -- Flags to open the file with if string passed as file argument. | |
bufsize -- Buffer size if string passed as file argument. 0 to disable | |
buffering, 1 for line buffering, any other number to specify buffer | |
size. This applies to the file only. This object itself will always | |
behave as if it is line buffered. | |
longlog -- 1 for a verbose line format (full date/filename/facility), | |
0 for a short line format (short date, default). | |
""" | |
self.filename = '.'.join(os.path.basename(sys.argv[0]).split('.')[:-1]) | |
if not self.filename: | |
self.filename = 'console' | |
self.deps = [] | |
self.buffer = '' | |
self.iid = ('File', file) | |
self.parentdata = {} | |
self.facility = 'manual' | |
self.longlog = longlog | |
if type(file) == types.FileType: | |
self.openfile = file | |
else: | |
self.openfile = open(file, flags, bufsize) | |
def __del__(self): | |
"""del(D) -> None. Severs dependencies and closes the file.""" | |
self.openfile.close() | |
PseudoFile.__del__(self) | |
def flush(self): | |
"""D.flush() -> None. Flushes the line buffer.""" | |
if self.buffer: | |
if self.longlog: | |
print >> self.openfile, \ | |
"%s (%s/%s): %s" % \ | |
(time.ctime(), \ | |
self.filename, self.facility, self.buffer) | |
else: | |
itime = time.localtime() | |
print >> self.openfile, "%02d:%02d:%02d %s" % \ | |
(itime[3], itime[4], itime[5], self.buffer) | |
self.buffer = '' | |
def test(): | |
"""test() -> None. Tests logger capability.""" | |
log = LogObj() | |
log.nothing = Nolog() | |
log.debug = File(file='debuglog.txt', flags='w', bufsize=0, longlog=1) | |
log.info = File(sys.stdout) | |
log.info.deps.append('debug') | |
log.error = Syslog(LOG_LOCAL5 | LOG_ERR) | |
log.error.deps.append('info') | |
log.crit = File(sys.stderr, longlog=1) | |
log.crit.deps.append('debug') | |
print >> log.error, 'This will go to syslog, stdout, and debuglog.txt.' | |
log.debug.write('This won\'t get flushed until the line is complete, ') | |
log.debug.write('or the buffer is flushed.') | |
log.debug.flush() | |
log.nothing('The file object is also callable, ') | |
log.nothing('but since this is a Nolog object, nothing will be done here.\n') | |
print >> log.crit, 'This is a call to stderr with no dependencies.' | |
if __name__ == "__main__": | |
test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment