根据 Twisted 客户端应用程序中的连接打印到不同的日志文件

Printing to different log file based on connection in Twisted client application

我的 Twisted 应用程序中有以下 TCP 客户端代码:

from twisted.internet import reactor, protocol
from twisted.python import log
import time

class EchoClient(protocol.Protocol):
    def connectionMade(self):
        if(self.factory.server_name == 'server1'):
          log.startLogging(open('server1.log','w'))
        else:
          log.startLogging(open('server2.log','w'))
        log.msg("Connected to "+self.factory.server_name)

    def connectionLost(self,reason):
        log.msg("Connection is lost " + reason.getErrorMessage())

    def dataReceived(self,data):
        log.msg("Server said: " + data)

class EchoFactory(protocol.ClientFactory):
    def __init__(self,server_name):
        self.server_name = server_name

    def buildProtocol(self, addr):
        client = EchoClient()
        client.factory = self
        return client

    def clientConnectionFailed(self, connector, reason):
        reactor.stop()

    def clientConnectionLost(self, connector, reason):
        time.sleep(10)
        connector.connect()

conn1 = EchoFactory('server1')
conn2 = EchoFactory('server2')

reactor.connectTCP("localhost",8000,conn1)
reactor.connectTCP("localhost",8001,conn2)

reactor.run()

现在,当我 运行 程序时,我看到 server1.logConnected to server1Connected to server2 行,而server2.log只有Connected to server2.

我该如何解决这个问题?谢谢

你在这里遇到的问题是 log.startLogging 本来就是 每个进程生命周期调用 一次,以打开单个日志文件。如果你认为 关于它,Twisted 应该如何知道特定的日志文件 log.msg 在你的例子中应该调用?您没有传递日志文件或任何 有关使用哪一个的信息。

你的例子中还有很多地方用得非常好 老式的 Twisted 习语,所以我要彻底修改这个例子 使用较新的 Twisted API:

  1. 与其使用旧的和损坏的 twisted.python.log 模块,我要 使用 new logging hotnesstwisted.logger.
  2. 而不是使用旧的和破坏的 IReactorTCP.connectTCP,我打算 采用 Endpoints 有很多优点。
  3. 与其直接导入反应堆 运行,不如使用 twisted.internet.task.react 所以我依赖于全球反应堆(这是 bad).

还有一些你正在做的事情总是很糟糕 Twisted 中的想法:

  1. 永远不要在任何 Twisted 程序中使用 time.sleep()。它阻止 整个反应堆,这意味着你的 Twisted 中所有未完成的任务 程序(线程中的程序除外)在您等待时停止。 有异步API,例如 deferLater, 你可以用它来处理时间的流逝。
  2. 切勿使用字符串格式将日志消息放在一起。 twisted.logger 使这更容易一些并且具有更好的语法,但即使 twisted.python.log一直支持日志中的结构化数据 消息。
  3. 你在几个地方有 2-space 个缩进。羞耻:).

我将使用 Twisted 日志系统来回答你的问题,但是 仅从您的连接向专门的观察​​者发送日志。原因 我这样做是因为 intercept 记录消息有点棘手 用于全局日志并说服现有的全局日志记录 不应该 将这些消息写入主日志文件的基础设施。 这是设计使然:一旦设置了日志记录,它通常用作万能的 捕获导致错误的消息所必需的。另一个潜力 答案是简单地从每个连接打开一个文本文件而不涉及 Twisted 的日志系统。

这是一个如何执行此操作的示例:

import io

from twisted.internet import protocol
from twisted.logger import Logger, jsonFileLogObserver

class EchoClient(protocol.Protocol):
    log = Logger()

    def connectionMade(self):
        log = self.log
        log.observer = jsonFileLogObserver(
            io.open(self.factory.server_name + ".json.log", "tw")
        )
        self.log = log

        self.log.info("Connected to {log_source.factory.server_name}")

    def dataReceived(self, data):
        self.log.info("server said: {data!r}",
                      data=data)

    def connectionLost(self, reason):
        self.log.info("Connection is lost: {reason}",
                      reason=reason)

class EchoFactory(protocol.Factory):
    protocol = EchoClient
    def __init__(self, server_name):
        self.server_name = server_name

from twisted.internet.task import react
from twisted.internet.endpoints import HostnameEndpoint
from twisted.internet.defer import Deferred

def main(reactor):
    for portNumber in 8001, 8002:
        endpoint = HostnameEndpoint(reactor, "localhost", portNumber)
        endpoint.connect(
            EchoFactory("server_" + str(portNumber - 8000))
        )
    return Deferred()

react(main)

请注意,在 connectionMade 中,我们使用 self.log 获得 Logger 实例, 但是由于 class 属性 EchoClient.log 是一个描述符,它正在创建一个 每次都是新实例。确保我们修改的observer属性状态 持久化后,我们将 self.log 重新分配给结果对象。

这将产生一些包含丰富结构化信息的日志文件,但是 人类阅读可能有些困难。如果你真正关心的是 供人们阅读的文字文本日志文件,而您不想做任何日志 分析,你可以只使用 textFileLogObserver 而不是 jsonFileLogObserver;但是,只需一点点代码,您就可以获得最好的 两个世界。这是一个非常短的程序,它将读取其中一个日志 此处生成的文件并发出人们可以阅读或通过管道传输的 classic 日志文本 grep:

import io
from twisted.logger import eventsFromJSONLogFile, formatEventAsClassicLogText

for event in eventsFromJSONLogFile(io.open("server_1.json.log", "rt")):
    print(formatEventAsClassicLogText(event))