Python装饰器实现代码追踪

在现代软件开发中,代码的追踪和监控是至关重要的。它不仅可以帮助理解代码的执行流程,还可以在出现问题时快速定位问题所在。OpenTelemetry是一个强大的追踪工具,它允许定义代码中的特定部分,称为‘Spans’,并在运行时跟踪它们的执行和依赖关系。然而,为了使这些信息可用,需要在代码中添加大量的追踪代码,这可能会变得相当繁琐。

Python的装饰器为提供了一种优雅的方式来简化这个过程。通过装饰器,可以自动地为函数添加追踪逻辑,而不需要在每个类、模块或函数中手动添加追踪代码。

为什么使用追踪装饰器

使用追踪装饰器的好处是显而易见的。首先,它减少了重复代码。其次,它允许通过约定来命名Span,并且可以轻松地改变这个约定。最后,它使得整个类可以自动地被追踪,而不需要为每个函数单独添加装饰器。

实现基本的追踪装饰器

要实现一个基本的追踪装饰器,首先需要创建一个装饰器函数,它可以自动地为函数添加追踪逻辑。这个装饰器函数会返回一个包装函数,这个包装函数会被解释器用作函数装饰器。

def instrument(_func=None, *, span_name: str = "", record_exception: bool = True, attributes: Dict[str, str] = None, existing_tracer: Tracer = None): def span_decorator(func): tracer = existing_tracer or trace.get_tracer(func.__module__) def _set_attributes(span, attributes_dict): if attributes_dict: for att in attributes_dict: span.set_attribute(att, attributes_dict[att]) @wraps(func) def wrap_with_span(*args, **kwargs): name = span_name or TracingDecoratorOptions.naming_scheme(func) with tracer.start_as_current_span(name, record_exception=record_exception) as span: _set_attributes(span, attributes) return func(*args, **kwargs) return wrap_with_span if _func is None: return span_decorator else: return span_decorator(_func)

在上面的代码中,首先检查是否已经为函数添加了追踪装饰器。如果是的话,就不再添加装饰器。否则,为函数添加装饰器,并返回函数本身。

测试代码

为了确保装饰器工作正常,需要添加一些测试代码。

@classmethod def setup_class(cls): resource = Resource.create(attributes={SERVICE_NAME: "test"}) provider = TracerProvider(resource=resource) trace.set_tracer_provider(provider) @instrument def test_decorated_function_gets_instrumented_automatically_with_span(): assert trace.get_current_span().is_recording() is True

在上面的测试代码中,首先设置OTEL,使得追踪操作会生效。然后,验证在应用了新装饰器的测试方法中,是否有一个活动的追踪Span。

为整个类添加装饰器

如果为每个函数单独添加装饰器,可能会变得有点繁琐和重复。为了解决这个问题,可以修改装饰器,使其遍历类中的每个函数,并为它们添加装饰器,忽略私有函数。

def instrument(_func_or_class=None, *, span_name: str = "", record_exception: bool = True, attributes: Dict[str, str] = None, existing_tracer: Tracer = None, ignore=False): def decorate_class(cls): for name, method in inspect.getmembers(cls, inspect.isfunction): if not name.startswith('_'): setattr(cls, name, instrument(record_exception=record_exception, attributes=attributes, existing_tracer=existing_tracer)(method)) return cls if inspect.isclass(_func_or_class): return decorate_class(_func_or_class) def span_decorator(func_or_class): if inspect.isclass(func_or_class): return decorate_class(func_or_class) ... if _func_or_class is None: return span_decorator else: return span_decorator(_func_or_class)
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485