OpenTracing 工具库
项目描述
早期阶段 WIP + 试验性
为您的 Python 项目添加 OpenTracing 支持的便捷工具。
特性
opentracing-utils 应提供并旨在以下
无外部依赖,仅依赖 opentracing-python。
无线程局部变量。要么使用 tracer scoper 管理器,显式传递 spans 或回退到调用栈帧检查!
上下文无关,因此没有外部 上下文实现 依赖(无 Tornado、Flask、Django 等 ...)。
支持 OpenTracing 2.0 API,带有 scope_manager(opentracing-utils>0.21.0)。
尽量简洁 - 只需添加 @trace 装饰器。
在需要时可以更详细,但无复杂性——只需接受 **kwargs 并通过 @trace(pass_span=True) 将 span 传递给您的跟踪函数。
支持 asyncio/async-await 协程。(停止支持 py2.7)
支持 gevent。
能够将 OpenTracing 支持添加到外部库/框架/客户端。
Django(通过 OpenTracingHttpMiddleware)
Flask(通过 trace_flask())
Requests(通过 trace_requests())
SQLAlchemy(通过 trace_sqlalchemy())
安装
使用 pip(尚未发布到 PyPi)
pip install -U opentracing-utils
或通过克隆仓库
python setup.py install
用法
init_opentracing_tracer
在 OpenTracing 仪表化中,第一步是初始化一个跟踪器。每个供应商都定义了跟踪器初始化的方式。目前支持以下跟踪器
BasicTracer
这是一个基本的空操作跟踪器。它可以与一个记录器(例如 Memory Recorder)一起初始化,这在调试和探索 OpenTracing 概念时可能很有用。
import opentracing
from opentracing_utils import OPENTRACING_BASIC, init_opentracing_tracer
# Initialize upon application start
init_opentracing_tracer(OPENTRACING_BASIC)
# It is possible to pass custom recorder
# init_opentracing_tracer(OPENTRACING_BASIC, recorder=custom_recorder)
# Now use the opentracing.tracer
root_span = opentracing.tracer.start_span(operation_name='root_span')
Instana
配置变量
以下配置变量可以在初始化时作为环境变量使用
- OPENTRACING_INSTANA_SERVICE
服务名称。
import opentracing
from opentracing_utils import OPENTRACING_INSTANA, init_opentracing_tracer
# Initialize upon application start
init_opentracing_tracer(OPENTRACING_INSTANA)
# It is possible to pass args
# init_opentracing_tracer(OPENTRACING_INSTANA, service='python-server')
# Now use the opentracing.tracer
root_span = opentracing.tracer.start_span(operation_name='root_span')
依赖项
将 instana 添加到您项目的 dependencies.txt 文件中。
Jaeger
配置变量
以下配置变量可以在初始化时作为环境变量使用
- OPENTRACING_JAEGER_SERVICE_NAME
服务名称。
import opentracing
from opentracing_utils import OPENTRACING_JAEGER, init_opentracing_tracer
# Initialize upon application start
init_opentracing_tracer(OPENTRACING_JAEGER)
# It is possible to pass args
# init_opentracing_tracer(OPENTRACING_JAEGER, service_name='python-server', config=custom_config_with_sampling)
# Now use the opentracing.tracer
root_span = opentracing.tracer.start_span(operation_name='root_span')
依赖项
将 jaeger_client 添加到您项目的 dependencies.txt 文件中。
LightStep
配置变量
以下配置变量可以在初始化时作为环境变量使用
- OPENTRACING_LIGHTSTEP_COMPONENT_NAME
组件名称。
- OPENTRACING_LIGHTSTEP_ACCESS_TOKEN
LightStep 收集器的访问令牌。
- OPENTRACING_LIGHTSTEP_COLLECTOR_HOST
LightStep 收集器主机。默认:collector.lightstep.com。
- OPENTRACING_LIGHTSTEP_COLLECTOR_PORT
LightStep 收集器端口(整数)。默认:443。
- OPENTRACING_LIGHTSTEP_VERBOSITY
跟踪器的详细程度(整数)。默认:0。
import opentracing
from opentracing_utils import OPENTRACING_LIGHTSTEP, init_opentracing_tracer
# Initialize upon application start
init_opentracing_tracer(OPENTRACING_LIGHTSTEP)
# It is possible to pass args
# init_opentracing_tracer(OPENTRACING_LIGHTSTEP, component_name='python-server', access_token='123', collector_host='production-collector.com')
# Now use the opentracing.tracer
root_span = opentracing.tracer.start_span(operation_name='root_span')
依赖项
将 lightstep 添加到您项目的 dependencies.txt 文件中。
@trace 装饰器
@trace 装饰器支持 OpenTracing scope_manager API(opentracing-utils > 0.21.0 新增)。
检测父 span 的顺序如下
如果存在,则使用 span_extractor。
从传递的 kwargs 中检测。
检测活动的 scope_manager span(opentracing.tracer.active_span)。
使用调用堆栈帧进行检测。
import opentracing
from opentracing_utils import trace, extract_span_from_kwargs
# decorate all your functions that require tracing
# Normal traced function
@trace()
def trace_me():
pass
# Traced function with access to created span in ``kwargs``
@trace(operation_name='user.operation', pass_span=True)
def user_operation(user, op, **kwargs):
current_span = extract_span_from_kwargs(**kwargs)
current_span.set_tag('user.id', user.id)
# Then do stuff ...
# trace_me will have ``current_span`` as its parent.
trace_me()
# Traced function using ``follows_from`` instead of ``child_of`` reference.
@trace(use_follows_from=True)
def trace_me_later():
pass
# Start a fresh trace - any parent spans will be ignored
@trace(operation_name='epoch', ignore_parent_span=True)
def start_fresh():
user = {'id': 1}
# trace decorator will handle trace heirarchy
user_operation(user, 'create')
# trace_me will have ``epoch`` span as its parent.
trace_me()
使用 scope_manager 的 @trace
如果需要始终使用 scope_manager,则可以将 use_scope_manager=True 传递给 @trace。
# ``use_scope_manager=True`` will always use scope_manager API for activating the new span.
@trace(operation_name='traced', use_scope_manager=True)
def trace_me_via_scope_manager():
# @trace will activate the current span using the ``scope_manager``.
current_span = opentracing.tracer.active_span
assert current_span.operation_name == 'traced'
# @trace will detect parent span from the ``scope_manager`` active span and automatically activate the new nested span.
@trace(operation_name='nested')
def trace_and_detect_scope():
nested_span = opentracing.tracer.active_span
assert nested_span.operation_name == 'nested'
trace_and_detect_scope()
# current_span is back to be the active span.
assert current_span == opentracing.tracer.active_span
# If the ``scope_manager`` API is activating the parent span, @trace will detect it and use the ``scope_manager`` for the child span as well.
@trace()
def trace_and_detect_parent_scope():
current_span = opentracing.tracer.active_span
assert current_span.operation_name == 'trace_and_detect_parent_scope'
with opentracing.tracer.start_active_span('top_span', finish_on_close=True):
# the child span will depend on the ``scope_manager`` to detect the ``top_span`` as the parent span for the following function call.
trace_and_detect_parent_scope()
跳过 span
在某些情况下,您可能需要在使用 @trace 装饰器时跳过某些 span。
def skip_this_span(arg1, arg2, **kwargs):
if arg1 == 'special':
# span should be skipped
return True
return False
@trace(skip_span=skip_this_span)
def traced(arg1, arg2):
pass
top_span = opentracing.tracer.start_span(operation_name='top_trace')
with top_span:
# this call will be traced and have a span!
traced('open', 'tracing')
# this call won't be traced and no span to be added!
traced('special', 'tracing')
断开的跟踪
如果您计划断开嵌套跟踪,则建议将 span 传递给跟踪函数。
top_span = opentracing.tracer.start_span(operation_name='top_trace')
with top_span:
# This one gets ``top_span`` as parent span
call_traced()
# Here, we break the trace, since we create a new span with no parents
broken_span = opentracing.tracer.start_span(operation_name='broken_trace')
with broken_span:
# This one gets ``broken_span`` as parent span (not consistent in 2.7 and 3.5)
call_traced()
# pass span as safer/guaranteed trace here
call_traced(span=broken_span)
# ISSUE: Due to stack call inspection, next call will get ``broken_span`` instead of ``top_span``, which is wrong!!
call_traced()
# To get the ``top_span`` as parent span, then pass it to the traced call
call_traced(span=top_span)
多个跟踪
如果您计划使用多个跟踪,则最好始终传递 span,因为它更安全/有保证。
注意:如果使用 scope_manager,则这不应该是一个问题。
first_span = opentracing.tracer.start_span(operation_name='first_trace')
with first_span:
# This one gets ``first_span`` as parent span
call_traced()
second_span = opentracing.tracer.start_span(operation_name='second_trace')
with second_span:
# ISSUE: This one **could** get ``first_span`` as parent span (not consistent among Python versions)
call_traced()
# It is better to pass ``second_span`` explicitly
call_traced(span=second_span)
生成器(yield)
使用生成器可能会变得复杂,并可能导致无效的父 span 检查。建议显式传递 span。
@trace(pass_span=True)
def gen(**kwargs):
s = extract_span_from_kwargs(**kwargs) # noqa
# Extract and pass span to ``f2()`` otherwise it could get ``f1()`` as parent span instead of ``gen()``
f2(span=s)
for i in range(10):
yield i
@trace()
def f2():
pass
@trace()
def f1():
list(gen())
first_span = opentracing.tracer.start_span(operation_name='first_trace')
with first_span:
f1()
外部库和客户端
Django
用于跟踪 Django 应用程序。您可以使用以下内容:
OpenTracingHttpMiddleware:用于跟踪传入的HTTP请求
# In settings.py or equivalent Django config
from opentracing_utils import init_opentracing_tracer
init_opentracing_tracer(YOUR_TRACER) # make sure opentracing.tracer is initialized properly.
MIDDLEWARE = (
'opentracing_utils.OpenTracingHttpMiddleware', # goes first in the list
# ... more middlewares here
)
# Further options
# Add default tags to all incoming HTTP requests spans.
OPENTRACING_UTILS_DEFAULT_TAGS = {'my-default-tag': 'tag-value'}
# Add error tag on 4XX responses (default is ``True``).
OPENTRACING_UTILS_ERROR_4XX = False
# Override span operation_name (default is ``view_func.__name__``).
OPENTRACING_UTILS_OPERATION_NAME_CALLABLE = 'my_app.utils.span_operation_name'
# Use tracer scope manager (default is ``False``).
OPENTRACING_UTILS_USE_SCOPE_MANAGER = True
# Exclude certain requests from OpenTracing
OPENTRACING_UTILS_SKIP_SPAN_CALLABLE = 'my_app.utils.skip_span'
以下是覆盖跨度操作名称和跳过跨度的可调用示例
# my_app/utils.py
def span_operation_name(request, view_func, view_args, view_kwargs):
return 'edge_{}'.format(view_func.__name__)
def skip_span(request, view_func, view_args, view_kwargs):
if view_func.__name__.startswith('no_trace_'):
return True
return False
为了在视图中跟踪跟踪,您可以使用 extract_span_from_django_request 工具函数。
# my_app/views.py
from opentracing_utils import trace, extract_span_from_django_request
@trace(span_extractor=extract_span_from_django_request, operation_name='custom_view')
def my_traced_view(request):
...
Flask
用于跟踪 Flask 应用程序。此实用函数添加了一个中间件,用于处理Flask应用程序的所有传入请求。
from opentracing_utils import trace_flask, extract_span_from_flask_request
from flask import Flask
app = Flask(__name__)
trace_flask(app)
# You can use the ``scope_manager`` for managing all spans.
trace_flask(app, use_scope_manager=True)
# You can add default_tags or optionally treat 4xx responses as not an error (i.e no error tag in span)
# trace_flask(app, default_tags={'always-there': True}, error_on_4xx=False)
# Extract current span from request context
def internal_function():
current_span = extract_span_from_flask_request()
current_span.set_tag('internal', True)
# You can skip requests spans.
def skip_health_checks(request):
return request.path == '/health'
# trace_flask(skip_span=skip_health_checks)
Requests
用于跟踪所有传出请求的 requests 客户端库。
# trace_requests should be called as early as possible, before importing requests
from opentracing_utils import trace_requests
trace_requests() # noqa
# You can use the ``scope_manager`` for managing all spans.
trace_requests(use_scope_manager=True) # noqa
# In case you want to include default span tags to be sent with every outgoing request.
# trace_requests(default_tags={'account_id': '123'}, set_error_tag=False)
# In case you want to keep the URL query args (masked by default in order to avoid leaking auth tokens etc...)
# trace_requests(mask_url_query=False)
# You can also mask URL path parameters (e.g. http://hostname/1 will be http://hostname/??/)
# trace_requests(mask_url_path=True)
# The library patches the requests library send functionality. This causes
# all requests to propagate the span id's in the headers. Sometimes this is
# undesireable so it's also possible to avoid tracing specific URL's or
# endpoints. trace_requests accepts a list of regex patterns and matches the
# request.url against these patterns, ignoring traces if any pattern matches.
# trace_requests(ignore_url_patterns=[r".*hostname/endpoint"])
import requests
def main():
span = opentracing.tracer.start_span(operation_name='main')
with span:
# Following call will be traced as a ``child span`` and propagated via HTTP headers.
requests.get('https://example.org')
SQLAlchemy
用于跟踪所有 SQL 查询的 SQLAlchemy 客户端库。
# trace_sqlalchemy can be used to trace all SQL queries.
# By default, span operation_name will be deduced from the query statement (e.g. select, update, delete).
from opentracing_utils import trace_sqlalchemy
trace_sqlalchemy()
# You can use the ``scope_manager`` for managing all spans.
trace_sqlalchemy(use_scope_manager=True)
# You can customize the span operation_name via supplying a callable
def get_sqlalchemy_span_op_name(conn, cursor, statement, parameters, context, executemany):
# inspect statement and parameters etc...
return 'custom_operation_name'
# trace_sqlalchemy(operation_name=get_sqlalchemy_span_op_name)
# By default, trace_sqlalchemy will not set error tags for SQL errors/exceptions. You can change that via ``set_error_tag`` param.
# trace_sqlalchemy(set_error_tag=True)
# you can skip spans for certain SQL queries.
def skip_inserts(conn, cursor, statement, parameters, context, executemany):
return statement.lower().startswith('insert')
# trace_sqlalchemy(skip_span=skip_inserts)
# you can enrich the span with by supplying an ``enrich_span`` callable.
def enrich_sql_span_parameters(span, conn, cursor, statement, parameters, context, executemany):
span.set_tag('parameters', parameters)
# trace_sqlalchemy(enrich_span=enrich_sql_span_parameters)
许可协议
MIT 许可证 (MIT)
版权所有 (c) 2017 Zalando SE, https://tech.zalando.com
任何人未经许可,不得以任何形式复制、传播、修改、合并、出版、分发、许可和/或出售本软件及其相关文档(“软件”),并允许向软件提供方提供本软件的人从事上述活动,但必须遵守以下条件:
上述版权声明和本许可声明应包含在软件的副本或实质部分中。
软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于适销性、适用于特定目的和不侵犯版权的保证。在任何情况下,作者或版权所有者不对任何索赔、损害或其他责任负责,无论基于合同、侵权或其他原因,是否因软件或其使用或其它方式产生。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。