pvl.syslog: simplify SyslogSource, and implement better handling for sys.stdin using tail.Tail
--- a/pvl/syslog/args.py Fri Jan 04 18:43:18 2013 +0200
+++ b/pvl/syslog/args.py Fri Jan 04 18:44:58 2013 +0200
@@ -23,7 +23,7 @@
syslog.add_option('--syslog-file', metavar='FILE',
help="Read syslog messages from given file")
- syslog.add_option('--syslog-tail', type='float', metavar='POLL',
+ syslog.add_option('--syslog-tail', type='float', metavar='TIMEOUT',
help="Continuously poll file")
syslog.add_option('--syslog-raw', action='store_true',
@@ -42,29 +42,32 @@
"""
if options.syslog_fifo :
- # read fifo
+ # fifo pipe
source = fifo.Fifo(options.syslog_fifo)
- poll = True
+ poll = True # select(source)
elif options.syslog_tail :
# tail file
- source = tail.TailFile(options.syslog_file, skip=True)
- poll = options.syslog_tail # float
+ source = tail.TailFile(options.syslog_file)
+ poll = options.syslog_tail # select(float)
elif options.syslog_file :
# read file
- source = open(options.syslog_file)
- poll = False
+ source = tail.Tail(open(options.syslog_file))
+ poll = False # do not loop, just read up to EOF
elif optional :
- log.debug("No --syslog source given")
return None
else :
# from stdin
- source = sys.stdin
- poll = False # XXX: SyslogSource can't handle normal file EOF
-
+ if sys.stdin.isatty() :
+ log.warning("Reading syslog messages from TTY?")
+
+ source = tail.Tail(sys.stdin)
+ poll = False # XXX: tty vs pipe vs file?
+
+ # options
parser = SyslogParser(
raw = options.syslog_raw,
)
@@ -73,9 +76,8 @@
prog = options.syslog_prog,
)
- # build
- return SyslogSource(source, parser,
- filter = filter,
- poll = poll,
- )
+ # chain iterables
+ syslog = filter(parser(source))
+ # polling
+ return SyslogSource(syslog, source, poll)
--- a/pvl/syslog/syslog.py Fri Jan 04 18:43:18 2013 +0200
+++ b/pvl/syslog/syslog.py Fri Jan 04 18:44:58 2013 +0200
@@ -8,116 +8,91 @@
class SyslogSource (object) :
"""
- A source of syslog items.
+ Process syslog input from a given source.
+
+ Implements an iterable mainloop doing continuous polling on the source, using either a timeout or
+ select():able source.
"""
- def __init__ (self, source, parser, filter=None, poll=None) :
+ def __init__ (self, syslog, source, poll=None) :
"""
Using given underlying line source.
-
- source - file-like object with poll() and iter()
- parser - process() bytes -> items
- filter - filter items
- poll - using given polling style:
- True - select() source
- float - given interval
- None/False - single-shot
+
+ syslog - iterable
+ source - source to select() if poll=True
+ poll - polling behaviour
"""
-
+
+ self.syslog = syslog
self.source = source
- self.parser = parser
- self.filter = filter
-
- log.debug("source: %s", source)
-
self._poll = poll
- def __iter__ (self) :
- """
- Read syslog messages from source.
+ def poll (self, poll=None) :
"""
-
- # directly iter across source
- iter = self.source
-
- # parse
- iter = self.parser(iter)
-
- # filter ?
- if self.filter :
- iter = self.filter(iter)
-
- return iter
-
- def poll (self, timeout=None) :
- """
- Poll source for input, with given timeout in seconds (float).
-
- A timeout of None indicates to block forever, False indicates to never block.
+ Poll our source for input, with given polling behaviour:
+ True - select() on source
+ False - peek on source
+ float - timeout in seconds
Returns True if we have input waiting, False on timeout with no input. None on indeterminate.
"""
- read = write = ()
+ reading = writing = ()
- if timeout is True :
- timeout = None
- read += (self.source, )
+ if poll is True :
+ timeout = None # block
+ reading += (self.source, ) # file-like object with fileno()
- elif timeout is False :
- timeout = 0.0
+ elif not poll :
+ timeout = 0.0 # do not block
+
+ else :
+ timeout = float(poll)
log.debug("%s", timeout)
# select
- read, write, ex = select.select(read, write, [], timeout)
+ readable, writeable, ex = select.select(reading, writing, [], timeout)
- if read :
+ if readable :
return True
- else :
+ elif reading :
# timeout
return False
- def loop (self) :
+ else :
+ # unknown
+ return None
+
+ def main (self, poll=None) :
"""
- Continuously read items from syslog source, blocking as suitable.
+ yield items from syslog source, polling as given.
Returns once no more lines are available.
- XXX: reconnect?
+ XXX: reconnect? or source takes care of that..
+ TODO: SIGINT -> finish iteration and return?
"""
+
+ if poll is None :
+ poll = self._poll
# mainloop
while True :
- log.debug("tick")
-
# pull in messages
- for item in self :
+ for item in self.syslog :
log.debug("%s", item)
yield item
# poll
- if self._poll :
+ if poll :
# wait
- self.poll(self._poll)
+ self.poll(poll)
else :
# done
break
log.debug("exit")
-
- def stop (self) :
- """
- Stop loop after current iteration.
- """
- self._poll = False
-
- def main (self) :
- """
- Mainloop.
- """
-
- # TODO: SIGINT -> stop()
- return loop()
+ __iter__ = main