Python Tail-f登录服务器,处理数据,然后通过twisted服务于客户端

Python Tail-f登录服务器,处理数据,然后通过twisted服务于客户端,python,twisted,Python,Twisted,目标:在客户端的wxpythongui中显示来自服务器的数据 新来者扭曲。我有一个在Windows7客户机上运行的wxPython GUI,还有一个在Ubuntu服务器上运行的程序,可以生成日志。我当前的尝试是跟踪日志,将输出通过管道传输到twisted服务器,然后将满足我的正则表达式条件的任何数据提供给客户端。我已经打开了一个隧道,所以我不需要用SSH使事情复杂化。我已经运行了下面的代码块,但它只服务于输入的第一行。我知道我需要不断检查输入的换行符,然后将其写入传输,但我不知道如何在不中断连接

目标:在客户端的wxpythongui中显示来自服务器的数据

新来者扭曲。我有一个在Windows7客户机上运行的wxPython GUI,还有一个在Ubuntu服务器上运行的程序,可以生成日志。我当前的尝试是跟踪日志,将输出通过管道传输到twisted服务器,然后将满足我的正则表达式条件的任何数据提供给客户端。我已经打开了一个隧道,所以我不需要用SSH使事情复杂化。我已经运行了下面的代码块,但它只服务于输入的第一行。我知道我需要不断检查输入的换行符,然后将其写入传输,但我不知道如何在不中断连接的情况下完成这项工作

我无法找到足够的信息来修补完整的解决方案。我也尝试过使用套接字和文件IO的各种其他方法,但我认为Twisted似乎是解决这个问题的一个好工具。我走对了吗?欢迎提出任何建议。谢谢

#! /usr/bin/python

import optparse, os, sys

from twisted.internet.protocol import ServerFactory, Protocol

def parse_args():
    usage = """usage: %prog [options]
"""

    parser = optparse.OptionParser(usage)

    help = "The port to listen on. Default to a random available port."
    parser.add_option('--port', type='int', help=help)

    help = "The interface to listen on. Default is localhost."
    parser.add_option('--iface', help=help, default='localhost')

    options =parser.parse_args()

    return options#, log_file

class LogProtocol(Protocol):
    def connectionMade(self):
        for line in self.factory.log:
            self.transport.write(line)

class LogFactory(ServerFactory):
    protocol = LogProtocol

    def __init__(self,log):
        self.log = log

def main():
    log = sys.stdin.readline()
    options, log_file = parse_args()

    factory = LogFactory(log)

    from twisted.internet import reactor

    port = reactor.listenTCP(options.port or 0, factory,
                             interface=options.iface)

    print 'Serving %s on %s.' % (log_file, port.getHost())

    reactor.run()


if __name__ == '__main__':
    main()
为了回答第一条评论,我还尝试从Python中读取日志,程序挂起。代码如下:

#! /usr/bin/python

import optparse, os, sys, time
from twisted.internet.protocol import ServerFactory, Protocol

def parse_args():
    usage = """ usage: %prog [options]"""

    parser = optparse.OptionParser(usage)

    help = "The port to listen on. Default to a random available port"
    parser.add_option('--port', type='int', help=help, dest="port")

    help = "The logfile to tail and write"
    parser.add_option('--file', help=help, default='log/testgen01.log',dest="logfile")

    options = parser.parse_args()
    return options

class LogProtocol(Protocol):
    def connectionMade(self):
        for line in self.follow():
            self.transport.write(line)
        self.transport.loseConnection()

    def follow(self):
        while True:
            line = self.factory.log.readline()
            if not line:
                time.sleep(0.1)
                continue
            yield line

class LogFactory(ServerFactory):
    protocol = LogProtocol

    def __init__(self,log):
        self.log = log

def main():
    options, log_file = parse_args()
    log = open(options.logfile)
    factory = LogFactory(log)

    from twisted.internet import reactor

    port = reactor.listenTCP(options.port or 0, factory)    #,interface=options.iface)

    print 'Serving %s on %s.' % (options.logfile, port.getHost())

    reactor.run()


if __name__ == '__main__':
    main()

你有几个不同的容易分离的目标,你正试图在这里实现。首先,我将讨论如何查看日志文件

你的发电机有几个问题。其中一个很大,它调用
time.sleep(0.1)
sleep
功能块用于计算传递给它的时间量。当它阻塞时,调用它的线程不能做任何其他事情(毕竟,这大致就是“阻塞”的意思)。您在与
LogProtocol相同的线程中迭代生成器。将在中调用connectionMade
(因为
connectionMade
调用
follow
LogProtocol.connectionMade
在Twisted reactor运行的同一线程中被调用,因为Twisted基本上是单线程的

所以,你用
sleep
调用阻塞了反应器。只要睡眠阻塞了反应器,反应器就不能做任何事情,比如通过套接字发送字节。顺便说一下,阻塞是可传递的。因此,
LogProtocol.connectionMade
是一个更大的问题:它无限期地迭代,睡眠和阅读。所以它会无限期地阻塞反应堆

您需要在不阻塞的情况下从文件中读取行。你可以通过轮询来做到这一点——这是你现在正在采取的有效方法——但要避免睡眠呼叫。使用
reactor.callLater
计划将来对文件的读取:

def follow(fObj):
    line = fObj.readline()
    reactor.callLater(0.1, follow, fObj)

follow(open(filename))
您还可以让
LoopingCall
处理使循环永久运行的部分:

def follow(fObj):
    line = fObj.readline()

from twisted.internet.task import LoopingCall

loop = LoopingCall(follow, open(filename))
loop.start(0.1)
这两种方法都可以让您在不阻塞反应器的情况下从文件中读取新行。当然,他们两人都只是在读完后把绳子扔到地板上。这就引出了第二个问题

您需要对文件中新行的外观作出反应。大概你想把它写在你的连接上。这并不难:“反应”非常简单,它通常只意味着调用函数或方法。在这种情况下,最简单的方法是让
LogProtocol
设置日志跟踪,并提供回调对象来处理出现的行。请考虑上述对 > > <代码>函数的轻微调整:

def follow(fObj, gotLine):
    line = fObj.readline()
    if line:
        gotLine(line)

def printLine(line):
    print line

loop = LoopingCall(follow, open(filename), printLine)
loop.start(0.1)
现在,您可以无阻塞地轮询日志文件中的新行,并了解其中一行何时实际出现。这很容易与
LogProtocol
集成

class LogProtocol(Protocol):
    def connectionMade(self):
        self.loop = LoopingCall(follow, open(filename), self._sendLogLine)
        self.loop.start()

    def _sendLogLine(self, line):
        self.transport.write(line)
最后一个细节是,您可能希望在连接断开时停止监视文件:

    def connectionLost(self, reason):
        self.loop.stop()

因此,此解决方案通过使用
LoopingCall
而不是
time.sleep
来避免阻塞,并在使用简单方法调用时将行推送到协议中。

您是否考虑过从python中读取日志,我们没有通过管道从
tail
输出,而是尝试使用生成器来解决这个问题,程序挂起。在我看来,运输和发电机都在等待另一个完成。上面显示的代码。有没有比使用发电机更好的方法?