自动化仪表化示例

本页面演示如何在OpenTelemetry中使用Python自动仪表化。该示例基于[OpenTracing示例][]而制作。您可以从opentelemetry-python存储库中下载或查看在本页面中使用的[源文件][]。

该示例使用了三个不同的脚本。它们之间的主要区别在于它们的仪表化方式:

  1. server_manual.py是通过_手动_方式进行仪表化的。
  2. server_automatic.py是通过_自动化_方式进行仪表化的。
  3. server_programmatic.py是通过_编程_方式进行仪表化的。

_编程式_仪表化是一种需要将最少的仪表化代码添加到应用程序中的仪表化方法。只有一些仪表化库提供了额外的功能,使您在使用编程式仪表化时对仪表化过程具有更大的控制权。

在不使用自动化仪表化代理的情况下运行第一个脚本,然后使用代理运行第二个脚本。它们应该产生完全相同的结果,以证明自动化仪表化代理所做的事情与手动仪表化完全相同。

自动化仪表化利用[Monkey Patching][]在运行时动态重写方法和类,通过仪表化库来减少将OpenTelemetry集成到应用程序代码中所需的工作量。下面将展示手动、自动和程序化仪表化的Flask路由之间的区别。

手动仪表化服务器

server_manual.py

@app.route("/server_request")
def server_request():
    with tracer.start_as_current_span(
        "server_request",
        context=extract(request.headers),
        kind=trace.SpanKind.SERVER,
        attributes=collect_request_attributes(request.environ),
    ):
        print(request.args.get("param"))
        return "served"

自动化仪表化服务器

server_automatic.py

@app.route("/server_request")
def server_request():
    print(request.args.get("param"))
    return "served"

程序化仪表化服务器

server_programmatic.py

instrumentor = FlaskInstrumentor()

app = Flask(__name__)

instrumentor.instrument_app(app)
# instrumentor.instrument_app(app, excluded_urls="/server_request")
@app.route("/server_request")
def server_request():
    print(request.args.get("param"))
    return "served"

准备

在单独的虚拟环境中执行以下示例。运行以下命令准备进行自动化仪表化:

mkdir auto_instrumentation
virtualenv auto_instrumentation
source auto_instrumentation/bin/activate

安装

运行以下命令以安装适当的软件包。opentelemetry-distro包依赖于其他一些软件包,例如opentelemetry-sdk用于自定义仪表化您自己的代码,opentelemetry-instrumentation提供了几个帮助自动仪表化程序的命令。

pip install opentelemetry-distro
pip install opentelemetry-instrumentation-flask
pip install flask
pip install requests

后续示例将将仪表化结果发送到控制台。了解更多关于安装和配置 OpenTelemetry Distro将遥测数据发送到其他目标(如OpenTelemetry Collector)的信息。

注意:要通过opentelemetry-instrument使用自动仪表化,必须通过环境变量或命令行进行配置。代理创建了一个无法通过其他方式修改的遥测管道。如果您需要对遥测管道进行更多自定义,则需要放弃代理,并将OpenTelemetry SDK和仪表化库导入到您的代码中并在那里进行配置。您还可以通过导入OpenTelemetry API来扩展自动仪表化。有关详细信息,请参阅[API参考][]。

执行

本节将指导您完成手动仪表化服务器以及执行自动化仪表化服务器的过程。

执行手动仪表化服务器

在两个单独的控制台中执行服务器,一个用于运行此示例的每个脚本:

source auto_instrumentation/bin/activate
python server_manual.py
source auto_instrumentation/bin/activate
python client.py testing

运行server_manual.py的控制台将以JSON格式显示仪表化生成的跨度。跨度应该类似于以下示例:

{
  "name": "server_request",
  "context": {
    "trace_id": "0xfa002aad260b5f7110db674a9ddfcd23",
    "span_id": "0x8b8bbaf3ca9c5131",
    "trace_state": "{}"
  },
  "kind": "SpanKind.SERVER",
  "parent_id": null,
  "start_time": "2020-04-30T17:28:57.886397Z",
  "end_time": "2020-04-30T17:28:57.886490Z",
  "status": {
    "status_code": "OK"
  },
  "attributes": {
    "http.method": "GET",
    "http.server_name": "127.0.0.1",
    "http.scheme": "http",
    "host.port": 8082,
    "http.host": "localhost:8082",
    "http.target": "/server_request?param=testing",
    "net.peer.ip": "127.0.0.1",
    "net.peer.port": 52872,
    "http.flavor": "1.1"
  },
  "events": [],
  "links": [],
  "resource": {
    "telemetry.sdk.language": "python",
    "telemetry.sdk.name": "opentelemetry",
    "telemetry.sdk.version": "0.16b1"
  }
}

执行自动化仪表化服务器

通过按下Control+C停止执行server_manual.py,然后运行以下命令:

opentelemetry-instrument --traces_exporter console --metrics_exporter none python server_automatic.py

在之前运行client.py的控制台中再次运行以下命令:

python client.py testing

运行server_automatic.py的控制台将以JSON格式显示仪表化生成的跨度。跨度应该类似于以下示例:

{
  "name": "server_request",
  "context": {
    "trace_id": "0x9f528e0b76189f539d9c21b1a7a2fc24",
    "span_id": "0xd79760685cd4c269",
    "trace_state": "{}"
  },
  "kind": "SpanKind.SERVER",
  "parent_id": "0xb4fb7eee22ef78e4",
  "start_time": "2020-04-30T17:10:02.400604Z",
  "end_time": "2020-04-30T17:10:02.401858Z",
  "status": {
    "status_code": "OK"
  },
  "attributes": {
    "http.method": "GET",
    "http.server_name": "127.0.0.1",
    "http.scheme": "http",
    "host.port": 8082,
    "http.host": "localhost:8082",
    "http.target": "/server_request?param=testing",
    "net.peer.ip": "127.0.0.1",
    "net.peer.port": 48240,
    "http.flavor": "1.1",
    "http.route": "/server_request",
    "http.status_text": "OK",
    "http.status_code": 200
  },
  "events": [],
  "links": [],
  "resource": {
    "telemetry.sdk.language": "python",
    "telemetry.sdk.name": "opentelemetry",
    "telemetry.sdk.version": "0.16b1",
    "service.name": ""
  }
}

可以看到两个输出是相同的,因为自动化仪表化做的事情与手动仪表化完全相同。

执行程序化仪表化服务器

还可以仅使用仪表化库(例如opentelemetry-instrumentation-flask)本身,这可能具有自定义选项的优势。但是,通过选择这样做意味着您放弃使用自动仪表化,而是使用opentelemetry-instrument启动应用程序,因为二者是互斥的。

像执行手动仪表化一样,在两个单独的控制台中执行服务器,一个用于运行此示例的每个脚本:

source auto_instrumentation/bin/activate
python server_programmatic.py
source auto_instrumentation/bin/activate
python client.py testing

结果应该与手动仪表化运行时相同。

使用程序化仪表化功能

一些仪表化库包括一些功能,允许在进行程序化仪表化时更精确地控制。Flask的仪表化库就是其中之一。

此示例有一行被注释掉了的代码,请将其更改为以下内容:

# instrumentor.instrument_app(app)
instrumentor.instrument_app(app, excluded_urls="/server_request")

再次运行示例后,服务器端不应显示任何仪表化。这是因为通过传递给instrument_appexcluded_urls选项,有效地阻止了server_request函数的仪表化,因为其URL与传递给excluded_urls的正则表达式匹配。

在调试时进行仪表化

可以像这样在Flask应用程序中启用调试模式:

if __name__ == "__main__":
    app.run(port=8082, debug=True)

调试模式可能会中断仪表化的发生,因为它启用了重新加载器。要在启用调试模式的情况下运行仪表化,请将use_reloader选项设置为False

if __name__ == "__main__":
    app.run(port=8082, debug=True, use_reloader=False)

配置

自动仪表化可以利用环境变量中的配置。

捕获HTTP请求和响应头

您可以根据语义约定捕获预定义的HTTP头作为跨度属性。

要定义要捕获的HTTP头,通过环境变量 OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUESTOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,提供一个逗号分隔的HTTP头名称列表,例如:

export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="Accept-Encoding,User-Agent,Referer"
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="Last-Modified,Content-Type"
opentelemetry-instrument --traces_exporter console --metrics_exporter none python app.py

以下HTTP仪表化支持这些配置选项:

  • Django
  • Falcon
  • FastAPI
  • Pyramid
  • Starlette
  • Tornado
  • WSGI

如果这些头可用,它们将包含在您的跨度中:

{
  "attributes": {
    "http.request.header.user-agent": [
      "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)"
    ],
    "http.request.header.accept_encoding": ["gzip, deflate, br"],
    "http.response.header.last_modified": ["2022-04-20 17:07:13.075765"],
    "http.response.header.content_type": ["text/html; charset=utf-8"]
  }
}
最后修改 December 10, 2023: translate (a4350d6e)