Skip to content

Instantly share code, notes, and snippets.

@rfinnie
Created March 11, 2021 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rfinnie/0bdf4ba7306dde9350d2698ca040e72e to your computer and use it in GitHub Desktop.
Save rfinnie/0bdf4ba7306dde9350d2698ca040e72e to your computer and use it in GitHub Desktop.
Python logging precursor, circa 2001
#!/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