OpenTelemetry概念之检测库

了解如何将本机检测添加到您的库中。

        OpenTelemetry 为许多库提供检测库,这通常是通过库钩子或补丁修补库代码完成的。

        使用 OpenTelemetry 的本机库检测为用户提供了更好的可观察性和开发人员体验,消除了库公开和文档记录的需要:

  • 自定义日志钩子可以被通用的、简单易用的OpenTelemetry APIS所替换,用户只需要和OpenTelemetry交互
  • 来自库和应用程序代码的跟踪、日志、指标是相关且连贯的

  • 通用协议允许用户在同一技术、跨库或者跨语言获得相似且一致的检查

  • 遥测信号可以使用各种有据可查的 OpenTelemetry 扩展点针对各种消费场景进行微调(过滤、处理、聚合)

语义约定

        查看涵盖 Web 框架、RPC 客户端、数据库、消息传递客户端、基础设施等的可用语义约定

        如果你的库是其中之一 - 遵循约定,它们是事实的主要来源,并告诉哪些信息应该包含在 span 中。约定使检测一致:使用遥测技术的用户不必学习库细节,可观察性供应商可以为各种技术构建经验 (e.g. databases or messaging systems)。当库遵循约定时,许多场景可以在没有用户输入或配置的情况下开箱即用。

        如果您有任何反馈或想添加新约定 - 请前来贡献! Instrumentation SlackSpecification repo 是一个很好的起点!

什么时候不检测

        一些库是包装网络调用的瘦客户端,碰巧OpenTelemetry 可能有一个用于底层 RPC 客户端的检测库(查看组件库),在这种情况下,可能不需要检测包装器库。

        以下情况不要检测:

  • 你的库是文档化或不言自明的 API 之上的瘦代理

  • 并且 OpenTelemetry 具有用于底层网络调用的工具

  • 并且没有您的库应该遵循的规范来丰富遥测

        如果您有疑问 - 不要使用检测 - 您可以在以后看到需要时随时使用。

        如果您选择不检测,提供一种为内部 RPC 客户端实例配置 OpenTelemetry 处理程序的方法可能仍然有用,它在不支持全自动检测的语言中是必不可少的,并且在其他语言中仍然有用。

        如果你决定使用,那么剩下的文档将为你怎么做检测提供指导。

OpenTelemetry API

第一步是依赖 OpenTelemetry API 包。

        OpenTelemetry 有两个主要模块——API 和 SDK,OpenTelemetry API 是一组抽象和非操作性实现。除非您的应用程序导入 OpenTelemetry SDK,否则,您的检测什么都不做,也不会影响应用程序性能。

        库应该只使用 OpenTelemetry API。

        您可能会担心添加新的依赖项,这里有一些注意事项可帮助您决定如何最大程度地减少依赖项苦境:

  • OpenTelemetry Trace API 在 2021 年初达到稳定,它遵循语义版本控制 2.0,我们非常重视 API 稳定性。

  • 获取依赖项时,请使用最早稳定的 OpenTelemetry API (1.0.*) 并避免更新它,除非您必须使用新功能。

  • 当您的仪器稳定时,请考虑将其作为独立的包提供,这样就不会给不使用它的用户带来问题。您可以将其保留在您的代码库中,或将其贡献到 OpenTelemetry库,这样它将与其他检测包一起提供。

  • 语义约定是稳定的,但会发生变化:虽然这不会导致任何功能问题,但您可能需要每隔一段时间更新一次检测代码,将它放在预览插件或 OpenTelemetry contrib repo 中可能有助于使约定保持最新,而不会被用户的破坏更改。

Getting a tracer

        通过 Tracer API,所有应用程序配置都对您的库隐藏。默认情况下,库应从全局 TracerProvider 获取跟踪器。

private static final Tracer tracer = GlobalOpenTelemetry.getTracer("demo-db-client", "0.1.0-beta1");

        对于库来说,拥有一个允许应用程序显式传递 TracerProvider 实例的 API 是很有用的,这样可以实现更好的依赖注入并简化测试。

        获取跟踪器时,请提供您的库(或跟踪插件)名称和版本 - 它们会显示在可观测数据中并帮助用户处理和过滤可观测数据、了解它的来源以及调试/报告任何检测到的问题。

检测什么?

Public APIs

公共 API 是很适合使用跟踪的:为公共 API 调用创建的跨度允许用户将遥测映射到应用程序代码,了解调用的持续时间和结果。跟踪哪些调用:

  • 在内部进行网络调用的公共方法或花费大量时间并可能失败的本地操作(例如 IO)

  • 处理请求或消息的处理程序

检测例子:

private static final Tracer tracer = GlobalOpenTelemetry.getTracer("demo-db-client", "0.1.0-beta1");

private Response selectWithTracing(Query query) {
    // check out conventions for guidance on span names and attributes
    Span span = tracer.spanBuilder(String.format("SELECT %s.%s", dbName, collectionName))
            .setSpanKind(SpanKind.CLIENT)
            .setAttribute("db.name", dbName)
            ...
            .startSpan();

    // makes span active and allows correlating logs and nest spans
    try (Scope unused = span.makeCurrent()) {
        Response response = query.runWithRetries();
        if (response.isSuccessful()) {
            span.setStatus(StatusCode.OK);
        }

        if (span.isRecording()) {
           // populate response attributes for response codes and other information
        }
    } catch (Exception e) {
        span.recordException(e);
        span.setStatus(StatusCode.ERROR, e.getClass().getSimpleName());
        throw e;
    } finally {
        span.end();
    }
}

按照约定填充属性!如果没有适用的,请查看一般约定

Nested network and other spans

网络调用通常通过使用 OpenTelemetry 自动检测的客户端实现进行跟踪。

如果 OpenTelemetry 不支持跟踪您的网络客户端,运用你最好的判断力,以下是一些可以提供帮助的注意事项:

  • 跟踪网络调用是否会提高用户的可观察性或您支持他们的能力?

  • 您的库是公开的、文档化的RPC API之上的包装器吗?如果出现问题,用户是否需要从底层服务获得支持?

    • 检测库并确保跟踪单个网络尝试。

  • 使用 span 跟踪这些调用会非常多余吗?还是会显着影响性能?

    • 使用详细日志或跨度事件:日志可以关联到父级(公共 API 调用),而跨度事件应该在公共 API 跨度上设置。

    • 如果它们必须是跨度的(携带和传播唯一的跟踪上下文),将它们放在配置选项后面并默认禁用它们。

如果 OpenTelemetry 已经支持跟踪您的网络调用,您可能不想复制它。可能有一些例外:

  • 支持没有自动检测的用户(在某些环境中可能无法工作,或者用户可能担心补丁)。

  • 使用底层服务启用自定义关联和上下文传播协议。

  • 丰富那些没有被自动检测覆盖的非常重要的库或者服务的RPC跨度的关键信息。

WARNING:避免重复的通用解决方案正在建设中。

Events

        Traces是您的应用程序可以发出的一种信号。Events(or logs)和traces相互补充,而不是重复。每当你有一些应该冗长的东西时,logs 比 traces是一个更好的选择。

        很可能您的应用程序已经使用了日志记录或一些类似的模块。您的模块可能已经集成了 OpenTelemetry——要查找,请查看注册表。集成通常在所有日志上标记活动跟踪上下文,以便用户可以将它们关联起来。

        如果您的语言和生态系统没有通用的日志记录支持,使用span events来分享额外的应用细节。如果您还想添加属性,Events可能会更方便。

        根据经验,使用事件或日志来获取详细数据而不是跨度。始终将事件附加到您的检测创建的​​跨度实例。如果可以,请避免使用active span,因为您无法控制它所指的内容。

上下文传递

提取上下文

        如果你工作在一个接收上游调用的库或者是服务中,例如一个web框架或者是一个消息消费者,你应该从请求或者消息中提取上下文。OpenTelemetry 提供了 Propagator API,它隐藏了特定的传播标准并从链路中读取跟踪上下文。在单个响应的情况下,线路上只有一个上下文,它成为库创建的新跨度的父级。

        创建跨度后,您应该通过激活跨度将新的跟踪上下文传递给应用程序代码(回调或处理程序);如果可能,您应该明确地这样做。

// extract the context
Context extractedContext = propagator.extract(Context.current(), httpExchange, getter);
Span span = tracer.spanBuilder("receive")
            .setSpanKind(SpanKind.SERVER)
            .setParent(extractedContext)
            .startSpan();

// make span active so any nested telemetry is correlated
try (Scope unused = span.makeCurrent()) {
  userCode();
} catch (Exception e) {
  span.recordException(e);
  span.setStatus(StatusCode.ERROR);
  throw e;
} finally {
  span.end();
}

以下是 Java 中上下文提取的完整示例,请查看您的语言的 OpenTelemetry 文档。

        在消息系统的情况下,您可能会同时收到多条消息。收到的消息成为您创建的跨度上的链接。有关详细信息,请参阅消息传递约定(WARNING:消息传递约定正在建设中)。

注入上下文

        当你调用外部系统的时候,您通常希望将上下文传递给下游服务。在这种情况下,您应该创建一个新的 span 来跟踪传出调用并使用 Propagator API 将上下文注入到消息中。在其他情况下,您可能想要注入上下文,例如在为异步处理创建消息时。

Span span = tracer.spanBuilder("send")
            .setSpanKind(SpanKind.CLIENT)
            .startSpan();

// make span active so any nested telemetry is correlated
// even network calls might have nested layers of spans, logs or events
try (Scope unused = span.makeCurrent()) {
  // inject the context
  propagator.inject(Context.current(), transportLayer, setter);
  send();
} catch (Exception e) {
  span.recordException(e);
  span.setStatus(StatusCode.ERROR);
  throw e;
} finally {
  span.end();
}

这里是完整的使用Java注入上下文的例子

        这里可能有一些例外:

  • 下游服务不支持元数据或禁止未知字段

  • 下游服务没有定义关联协议,是否有可能某些未来的服务版本将支持兼容的上下文传播?注入它!

  • 下游服务支持自定义关联协议。

    • 自定义传播者的最大努力:如果兼容,请使用 OpenTelemetry 跟踪上下文。

    • 或在跨度上生成并标记自定义相关 ID。

进行中

  • 使您的跨度活跃(又名当前):它支持将跨度与日志和任何嵌套的自动检测相关联。

  • 如果库有上下文的概念,除了活动跨度之外,还支持可选的显式跟踪上下文传播

    • 将库创建的跨度(跟踪上下文)明确地放在上下文中,记录如何访问它。

    • 允许用户在您的上下文中传递跟踪上下文

  • 在库中,显式传播跟踪上下文 - 活动跨度可能会在回调期间发生变化!

    • 尽快从公共 API 表面上的用户捕获活动上下文,将其用作跨度的父上下文

    • 传递上下文并在显式传播的实例上标记属性、异常和事件

    • 如果您显式启动线程、执行后台处理或其他由于您的语言中的异步上下文流限制而可能中断的事情,这是必不可少的

其他杂项

检测仓库

        请将您的检测库添加到 OpenTelemetry 仓库,这样用户就可以找到它。

表现

        当应用程序中没有 SDK 时,OpenTelemetry API 是空操作并且性能非常好。配置 OpenTelemetry SDK 时,它会消耗绑定资源

        现实生活中的应用程序,尤其是大规模应用程序,通常会配置基于头部的采样。采样出的跨度很便宜,您可以检查跨度是否正在记录,以避免在填充属性时进行额外的分配和潜在的昂贵计算。

// some attributes are important for sampling, they should be provided at creation time
Span span = tracer.spanBuilder(String.format("SELECT %s.%s", dbName, collectionName))
        .setSpanKind(SpanKind.CLIENT)
        .setAttribute("db.name", dbName)
        ...
        .startSpan();

// other attributes, especially those that are expensive to calculate
// should be added if span is recording
if (span.isRecording()) {
    span.setAttribute("db.statement", sanitize(query.statement()))
}

异常处理

        OpenTelemetry API 在运行时是宽容的,不会因无效参数而失败,从不抛出和吞下异常。这样,检测问题就不会影响应用程序逻辑。测试检测以注意 OpenTelemetry 在运行时隐藏的问题。

测试

        由于 OpenTelemetry 具有多种自动检测,尝试测试您的检测如何与其他遥测交互是很有用的:传入请求、传出请求、日志等。使用典型的应用程序,使用流行的框架和库,并在试用您的仪器时启用所有跟踪。看与您的库相似的库是如何出现的。对于单元测试,您通常可以模拟或伪造 SpanProcessor 和 SpanExporter。

@Test
public void checkInstrumentation() {
  SpanExporter exporter = new TestExporter();

  Tracer tracer = OpenTelemetrySdk.builder()
           .setTracerProvider(SdkTracerProvider.builder()
              .addSpanProcessor(SimpleSpanProcessor.create(exporter)).build()).build()
           .getTracer("test");
  // run test ...

  validateSpans(exporter.exportedSpans);
}

class TestExporter implements SpanExporter {
  public final List<SpanData> exportedSpans = Collections.synchronizedList(new ArrayList<>());

  @Override
  public CompletableResultCode export(Collection<SpanData> spans) {
    exportedSpans.addAll(spans);
    return CompletableResultCode.ofSuccess();
  }
  ...
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值