skywalking agent在python中的实现原理

225 阅读1分钟

#可观测性

在java体系下,skywalking是借助于jvm提供agent功能实现,中文互联网上介绍原理的内容较多。

既然skywalking支持python,那么在python中,其实现原理是如何?

在python中使用,需要手动启动,用法如下

from skywalking import agent, config

config.init(collector='127.0.0.1:11800', service='your awesome service')
agent.start()

agent.start()函数中,会调用各个组件插件模块,实现代理。以redis插件的代码为例:

插件skywalking\plugins\sw_redis.py

def install():
    from redis.connection import Connection

    _send_command = Connection.send_command

    def _sw_send_command(this: Connection, *args, **kwargs):
        peer = f'{this.host}:{this.port}'

        if len(args) == 1:
            cmd = args[0]
            key = ''
        elif len(args) > 1:
            cmd, key = args[0], args[1]
        else:  # just to be safe
            cmd = key = ''

        if cmd in OPERATIONS_WRITE:
            op = 'write'
        elif cmd in OPERATIONS_READ:
            op = 'read'
        else:
            op = ''

        context = get_context()
        with context.new_exit_span(op=f'Redis/{cmd}' or '/', peer=peer, component=Component.Redis) as span:
            span.layer = Layer.Cache

            res = _send_command(this, *args, **kwargs)
            span.tag(TagCacheType('Redis'))
            span.tag(TagCacheKey(key))
            span.tag(TagCacheCmd(cmd))
            span.tag(TagCacheOp(op))

            return res

    Connection.send_command = _sw_send_command

因为python是弱类型的动态语言,使用过程中不对对象的类型进行限制,且可以在运行过程中随时随地的动态获取或修改任一对象。

而python的模块(用到的库)在被import后,也是以对象的形式存在的,也可以被获取、被修改。而模块中的类定义,也是以对象的形式存在的,也可以被获取、被修改。

所以在业务代码启动运行前,加载redis对应的模块,并使用代理之后的函数替代Connection的send_command方法,在这之后再执行业务代码,此时再创建Connection类的示例,调用其send_command方法时,实际上是执行自定义的_sw_send_command方法,在这个方法中就可以完成trace和记录。

最终实现了 #代理模式