扭曲.如何为每个请求的日志写一个唯一的前缀

mas*_*tak 2 logging twisted twistd

我有扭曲的服务器.它与插件一起运行.我想根据请求为每个条目写独特的前缀.

这意味着当user1发出请求时,它将生成一个唯一的字符串,该字符串将以日志记录为前缀(仅适用于此请求).当user2发出请求时,它将成为日志记录的另一个唯一前缀.

我认为它应该是log observer logger,但是如何在用户请求之间分组记录?

Jea*_*one 8

这个问题有几个部分.

首先,简单的部分 - 如何为日志事件提供自定义前缀?当然,有几种方法可以解决这个问题.你可以做一些非常非常简单的事情:

from twisted.python.log import msg

def myLog(message):
    msg("my prefix" + message)
Run Code Online (Sandbox Code Playgroud)

也就是说,只需在日志消息前加上所需的前缀.这可能不是你真正想要的,因为它会破坏你的日志消息并将前缀文本与日志消息以难以逆转的方式混合(考虑一些记录的消息可能来自不同的系统而没有任何前缀 - 但是你怎么说?)

因此,您可以在日志事件中使用另一个键,而不是使用前缀:

from twisted.python.log import msg

def myLog(message):
    msg(message, system="my prefix")
Run Code Online (Sandbox Code Playgroud)

这将导致发出的日志事件看起来像(不完全,足够接近),如:

{"message": message, "system": "my prefix"}
Run Code Online (Sandbox Code Playgroud)

而"系统"键恰好是一个特殊的键.它由Twisted中的默认文件日志观察器识别,其内容最终在写入文件的日志消息中:

2013-07-23 06:25:35-0400 [my prefix] message
Run Code Online (Sandbox Code Playgroud)

您可能不希望为基于用户的信息选择"系统"密钥.毕竟,系统可能与HTTP服务器有关.用户不是系统.

您可以传递msg任何其他您想要的关键字参数:

from twisted.python.log import msg

def myLog(message):
    msg(message, userIdentifier="alice")
Run Code Online (Sandbox Code Playgroud)

userIdentifier将被Twisted附带的文件日志观察者忽略,但您可以编写自己的观察者来注意它.例如:

from twisted.python.log import FileLogObserver, textFromEventDict, addObserver

class MyObserver(FileLogObserver):
    def emit(self, event):
        text = textFromEventDict(event)
        if text and event.get("userIdentifier"):
            adjusted = event.copy()
            adjusted["message"] = "(%s) %s" % (event["userIdentifier"], text)
            FileLogObserver.emit(self, adjusted)

addObserver(MyObserver(...).emit)
Run Code Online (Sandbox Code Playgroud)

这为您提供了一个观察者,它会注意到您的特殊日志事件,并在将文本发送到普通文件写入逻辑之前修改它们的文本.我刚刚在这里做了一些简单的文本格式化,但你可以做更好的事情,比如将每个用户的事件写入他们自己的专用日志文件或使用更容易解析的结构化日志格式.

我希望到目前为止这一切都很有用,但它实际上并没有帮助您将正确的用户标识符转换为特定的日志事件.到目前为止,每个用户都是"爱丽丝".

首先,让我们看看我们如何能够改变用户.一种方法是制作userIdentifier一个参数:

from twisted.python.log import msg

def myLog(message, userIdentifier):
    msg(message, userIdentifier=userIdentifier)
Run Code Online (Sandbox Code Playgroud)

当然,myLog现在看起来有点傻.你可以打电话msg.也许你想要一些比这更自动的东西.您可以将用户标识符包装在callable中,这样您就不必继续传入它:

from functools import partial

from twisted.python.log import msg

aliceLog = partial(msg, userIdentifier="alice")
Run Code Online (Sandbox Code Playgroud)

现在,您只需执行操作即可为alice记录事件aliceLog("login").您可能不希望在顶层定义此项,因为您还不知道用户名,并且可能您将拥有多个用户.幸运的是,你可以随时轻松制作这些.

让我们暂时搁置一边,考虑一下如何识别您的用户.

有很多方法,所以我只是假设这request.getUser()是合适的.用你真正想要使用的其他机制替换它.

现在您需要跟踪这些信息.一种简单的方法是将其作为参数传递给任何想要记录与用户相关的事件的代码.我希望这实际上不是一个负担,因为任何想要记录与用户相关的事件的代码可能已经需要知道它正在代表哪个用户.

这进入了一个有点毛茸茸的Twisted Web区域.遍历系统(即getChild)非常灵活,根据您的使用方式,将信息传递给最终消费者的方式可能差别很大.

一种方法是使用透明的IResource包装器.这里的想法是将IResource插入到不使用任何段的层次结构中,只检查请求并创建一些有用的状态,例如用户标识符.例如,

from twisted.web.resource import IResource
from twisted.python.components import proxyForInterface

class GiveChildrenUserInfo(proxyForInterface(IResource)):
    def getChild(self, request, segment):
        child = self.original.getChild(request, segment)
        child.setUser(request.getUser())
        return GiveChildrenUserInfo(child)

rootResource = GiveChildrenUserInfo(actualRootResource)
...
Run Code Online (Sandbox Code Playgroud)

请注意两件事.首先,我发明了一种setUser方法,您现在必须实现所有资源.它将在用户从请求中获取的每个资源上调用.现在这些资源可以使用该用户信息 - 例如,msg(message, userIdentifier=user)使用partial如上所示调用或定义这样的日志助手.

我还没有涉及到很多与日志相关的问题,但我希望这足以让你开始并展示一些可能的方向.