使用ast(抽象语法树)在代码中植入埋点

摘要:
函数描述在代码执行过程中收集一些日志,但此操作与业务无关。您需要根据运行环境决定是否执行这些操作。一种解决方案是在代码中添加一些收集日志的标记,例如“注释”,这不会改变原始代码的结构,但也会实现功能。入侵相对较低。

功能说明

在代码执行过程中收集一些日志,但是这个操作是与业务无关的,需要根据运行环境来决定
是否要执行这些操作,
一个解决办法:

在代码中加点儿收集日志的标记,比如“注释”,既不会对改变原有代码的结构又能实现功能,
入侵性比较低。

那就需要解释器能识别“注释”并翻译成代码执行了,比如对于以下代码:

# -*- encoding: utf-8 -*-

metrics = {
    "f1": 0.9,
    "acc": 0.8
}
'''@myclient.send_metrics(metrics)'''

希望解释器执行时能把

'''@myclient.send_metrics(metrics)'''

当成代码:

myclient.send_metrics(metrics)

也就是整体代码要翻译成:

# -*- encoding: utf-8 -*-
import myclient

metrics = {
    "f1": 0.9,
    "acc": 0.8
}

myclient.send_metrics(metrics)

简单粗暴的方法替换字符串就可以了,还可以用ast(Abstract Syntax Trees)模块处理这个问题。

代码块有各种类型:

  • 方法定义
  • 变量申明、赋值
  • 计算表达式
  • ...

在python中,语句:

'''@myclient.send_metrics(metrics)'''

表示定义了一个字符串对象,有时候这种形式用于注释,但不是真的注释,解释器不会把它忽略,
只要从语法树中找到这样的代码节点,把它的内容从字符串申明变成方法调用就可以了。

实现

1. 找到埋点在语法树中的代码节点

# -*- encoding: utf-8 -*-
import ast, astunparse

code = 
"""
# -*- encoding: utf-8 -*-

metrics = {
    "f1": 0.9,
    "acc": 0.8
}
'''@myclient.send_metrics(metrics)'''
"""

ast_tree = ast.parse(code)  # 解析成语法树

for node in ast_tree.body:
    if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):  # 字符串定义表达式
        anno_content = node.value.s
        if anno_content.startswith('@myclient.send_metrics'):  # 发现标记
            anno_code = anno_content[1:]  # 去掉@
            print("这个node=%s中带有埋点,埋点代码:
%s" % (str(node), anno_code))


执行结果:

这个node=<_ast.Expr object at 0x120336250>中带有埋点,埋点代码:
myclient.send_metrics(metrics)

2. 把埋点的字符串变成方法调用并生成新代码

# -*- encoding: utf-8 -*-
import ast, astunparse, astor

code = 
"""
# -*- encoding: utf-8 -*-

metrics = {
    "f1": 0.9,
    "acc": 0.8
}
'''@myclient.send_metrics(metrics)'''
"""

ast_tree = ast.parse(code)  # 解析成语法树

for node in ast_tree.body:
    if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):   # 字符串定义表达式
        anno_content = node.value.s
        if anno_content.startswith('@myclient.send_metrics'):  # 发现标记
            anno_code = anno_content[1:]  # 去掉@
            print("这个node=%s中带有埋点,埋点代码:
%s" % (str(node), anno_code))
            anno_ast_tree = ast.parse(anno_code)  # 用"注释"中的代码生成一个新的语法树
            anno_expr = anno_ast_tree.body[0]  # 获取语法树中的表达式,也就是 myclient.send_metrics(metrics)
            anno_call = anno_expr.value  # 获取表达式中的方法调用
            node.value = anno_call  # 把赋值语句变成方法调用语句

# 在第1行导入一下  myclient
import_nni = ast.Import(names=[ast.alias(name='myclient', asname=None)])
nodes = ast_tree.body
nodes.insert(0, import_nni)

print("最终代码: ")
print(astor.to_source(ast_tree))

输出是:

这个node=<_ast.Expr object at 0x1214156d0>中带有埋点,埋点代码:
myclient.send_metrics(metrics)
最终代码: 
import myclient
metrics = {'f1': 0.9, 'acc': 0.8}
myclient.send_metrics(metrics)

3. 执行生成的代码

python 提供的compile、exec、eval 等函数都可以执行。

4. 上述代码的问题:

  1. 只遍历了根节点下的那一层节点,也就是不支持在方法中使用这种特殊注释,这个需要递归遍历所有语法节点
  2. import 是直接加入第一行的,可以遍历代码查看是否有导入,决定添加import语句以及其位置

免责声明:文章转载自《使用ast(抽象语法树)在代码中植入埋点》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇轮船问题(DP基础)JavaWeb学习记录(一)——response响应头之缓存设置与下载功能的实现下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

K8s容器资源限制

在K8s中定义Pod中运行容器有两个维度的限制:1. 资源需求:即运行Pod的节点必须满足运行Pod的最基本需求才能运行Pod。如: Pod运行至少需要2G内存,1核CPU2. 资源限额:即运行Pod期间,可能内存使用量会增加,那最多能使用多少内存,这就是资源限额。 # kubectl describe node node1.zcf.com...

Cloudify基本介绍

介绍 Cloudify 是开源的云编排框架,管理应用的整个生命周期,主要功能包括: 应用编排:blueprint,部署和管理 应用维护:工作流,事件和日志管理 可插拔:使用插件垒起来的 标准:基于TOSCA的DSL(领域描述语言) 术语 blueprint:cloudify 用blueprint 来定义应用,blueprint 是应用的逻辑展示,包括...

虚拟机搭建Kubespere3.0(详细)

前言:不断学习就是程序员的宿命。 相对于rancher,我个人更倾向于kubespere,因为它界面确实比较吸引我,废话不多扯,下面开始吧 1.环境准备 1.前置要求 虚拟机:centos7.6~7.8!!! 以下:https://kuboard.cn/install/install-k8s.html#%E6%A3%80%E6%9F%A5-centos-...

技术基础 | 重要指标和告警

本文节选自DataStax Cassandra文档,点击这里查看更多相关信息。 监控Apache Cassandra®和DataStax Enterprise(DSE)集群是一项非常重要的工作,它帮助您识别集群中的问题并及时地应对并缓解问题。 Apache Cassandra和DSE都公开了用于观察和分析的指标。Cassandra通过使用Java管理扩展...

Prometheus介绍及二进制部署(一)

一、Prometheus是什么 Prometheus是什么Prometheus(普罗米修斯)是一个最初在SoundCloud上构建的监控系统。自2012年成为社区开源项目,拥有非常活跃的开发人员和用户社区。为强调开源及独立维护,Prometheus于2016年加入云原生云计算基金会(CNCF),成为继Kubernetes之后的第二个托管项目。 https:...

监控Spark应用方法简介

监控Spark应用有很多种方法。Web接口每一个SparkContext启动一个web UI用来展示应用相关的一些非常有用的信息,默认在4040端口。这些信息包括:任务和调度状态的列表RDD大小和内存使用的统计信息正在运行的executor的信息环境信息你可以在浏览器中打开http://<driver-node>:4040网址来访问这些信息。如...