使用 OpenTelemetry api 自订义内容

Resources

在 OpenTelemetry 中,服务由资源描述,资源是在应用程序启动期间初始化 OpenTelemetry SDK 时设置的。详细规范可点击此链结

Attribute Description Example Required
service.name 给予服务的额外名称。对於水平扩展服务的所有实例,必须相同 oteldemo Y
service.version 定义服务 API 或服务版本 aip:1.2.3 N
host.hostname 主机名或以域名表示 oteldemo.com.tw N

目前只能使用环境变数方式进行设置 OTEL_RESOURCE_ATTRIBUTES。这些的设定都是为了让後端能够进一步的识别分析。

Span

Span 是组成 Tracer 的单位。

SpanContext

为了要有完整 Tracer 的过程,每个 Span 要将 SpanContext 传递至子 Span。

SpanContext 透过 span_ID 告知子 span 谁是父,以及它属於什麽 Tracer(trace_ID)。

Attributes

Key-value 组成,提供 Span 详细的资讯。可以查询、分组或以其他方式分析 tracespan

Status

可将其设定为 OKCancelledPermission Denied

SpanKind

SpanKindTracer 中提供有用讯息,此 Span 是否为远端系统? SpanKind 值可以为 CLIENTSERVERPRODUCERCONSUMERINTERNAL。详细可参考官方链结

Events

包含名称、时间戳和可选的 Attributes 集合等。其表示在 Span 工作负载内的特定时间发生所发生的事件。可能如下

events: 
t:3
name:log
message: "retrieved 400 records"
...

程序码实现概念

Annotation @WithSpan

每次调用带注释的方法时,它会在当前 Trace 中创建一个子 `Span,并记录所有引发的异常。

也可以透过 exclude,排除一些不要的场景。

Accessing the tracer

如果要与 Tracer 进行交互,需要先获取 Tracer 的实例。Tracer 以它们要检测的组件命名。通常是一个 library、一个 package或一个 class

Tracer tracer = GlobalOpenTelemetry.getTracer(instrumentationName, instrumentationVersion);

Span 属性使用

  • SpansetAttribute 是用於细分数据的索引。可能想要新增帐户 ID 之类,以了解请求瓶颈和错误是否特定於某个帐户或是否影响每个帐户。
  • SpanaddEvent 可将细节做记录,就是日志的概念。这可自动查找与特定事务关联的所有日志,而不必进行大量的搜索和过滤。随着服务的扩展这将是一个很好的追踪过程

Accessing the current span

Span 是在应用程序框架中被创建和管理。每个请求自动创建一个 Tracer,且应用程序程序码已经被包装在一个 Span,可用於新增特定於应用程序的属性(setAttribute)和事件(addEvent)。

上述的前提是框架要被支援

Span span = Span.current()

Create own spans

当创建自己的 Span。这些范围将自动成为当前范围的子,同时新增至 Tracer 中。Span 管理包括三个步骤:开始 Span、设置为当前 Span 和结束 Span。当前如果存在 SpanOpenTelemetry 会将其创建为当前 Span 的子。会在 Tracer 上调用 spanBuilder 方法以触发一个新的 Tracer。创建新的 Span 後,使用 Scope 建立一个新的代码区块,当中会包含子 Span。在该范围内对 Span.current() 的任何调用都将回传该子 Span,而不是请求的父 Span。都完成後,需呼叫 end() 方法来关闭 Span,否则会有泄漏问题。

Error Handling

OpenTelemetry 中,异常记录为事件。但是,为确保异常的格式正确,应使用 span.recordException(error) 方法而非 addEvent

childSpan.recordException(new RuntimeException("oops"));
childSpan.setStatus(StatusCode.ERROR);

OpenTelemetry 中,错误表示整体操作未完成。当异常被处理并不表示着整个操作都无法完成。为了示意操作失败,需调用 span.setStatus() 并传入错误代码,该代码可让分析工具来自动触发警报、测量错误率等。

实作

使用 Annotation 方式,此方式会建立一个 Span,而此 Span 会是触发该 Span 的子 Span,我们这边设置了属性(setAttribute)和事件(event)这会以 Log 方式呈现。

    @WithSpan
    public static void requestValueSpan(Note note) {
        Span span = Span.current();
        span.setAttribute("id", note.getId());
        span.addEvent("Get.note.from.id", Attributes.of(AttributeKey.stringKey("content"), note.getContent(),AttributeKey.stringKey("title"), note.getTitle()));
    }

下面算是实作 @WithSpan 的方式,只不过需要定义一个 Tracer,在建立一个 Span(spanBuilder)。

private final Tracer tracer = GlobalOpenTelemetry.getTracer(NoteServiceImp.class.getSimpleName());
    @Override
    public void saveNote(Note note) {
        // TODO Auto-generated method stub
        Span span = tracer.spanBuilder("saveNote").startSpan();
        try (Scope scope = span.makeCurrent()) {
            this.noteRepository.save(note);
            span.setAttribute("CreateTime", note.getCreatedAt().toString());
            span.addEvent("Request.query", Attributes.of(AttributeKey.stringKey("content"), note.getContent(),AttributeKey.stringKey("title"), note.getTitle()));
        } catch (Throwable t) {
            span.setStatus(StatusCode.ERROR, "Change it to your error message");
        } finally {
            span.end(); // closing the scope does not end the span, this has to be done manually
        }
    }

建立父子关系的 Span,在 child 方法中呼叫 setParent

    private static void parent() {
        Span parentSpan = tracer.spanBuilder("parent").startSpan();
        try {
            parentSpan.addEvent("parent", Attributes.of(AttributeKey.stringKey("parent"), "parent"));
            child(parentSpan);
        } finally {
            parentSpan.end();
        }
    }

    private static void child(Span span) {
        Span childSpan = tracer.spanBuilder("child").setParent(Context.current().with(span)).startSpan();
        try (Scope scope = childSpan.makeCurrent()) {
            childSpan.setAttribute("name", "child");
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            childSpan.end();
        }
    }

设定 optntelemtry agent

架设开发环境在 VSCODE,需在 lunch.json 中设定 vmArgs,定义与前几章 docker-compose 中几乎相同的参数,-javaagent:opentelemetry-javaagent-all.jar 导入 opentelemetry,定义 endpoint。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch Current File",
            "request": "launch",
            "mainClass": "${file}",
            "vmArgs": "-javaagent:opentelemetry-javaagent-all.jar -Dotel.resource.attributes=service.name=oteldemo -Dotel.exporter.otlp.endpoint=http://172.17.10.105:4317 -Dotel.exporter.otlp.traces.endpoint=http://172.17.10.105:4317 -Dotel.exporter.otlp.metrics.endpoint=http://172.17.10.105:4317"
        },
        {
            "type": "java",
            "name": "Launch OteldemoApplication",
            "request": "launch",
            "mainClass": "com.otel.example.oteldemo.OteldemoApplication",
            "projectName": "oteldemo"
        }
    ]
}

上述的范例可参考 github


<<:  Day26:Dynamic Programming(DP) - 动态规划(下)

>>:  Day 14 : 程序码日志与品质

Day 13 | 同步与非同步- Thread类别与runOnUiThread()方法

Thread Thread是Java的原生类别,当需要执行绪处理费时任务时,就可以新增该类别执行Ta...

[进阶指南] 深入 JSX( Day25 )

基本上,JSX 单纯只是 React.createElement(component, props...

《瞬间爆击或者持续伤害》

在长尾效应跟爆款理论中,实际上是边际效应的正反两面。 网路减少了边际效应的成本,让原本的长尾需求被收...

[笔记]vue-cli i18n 多语系应用练习

参考文章: https://medium.com/easons-murmuring/%E5%9C%A...

C#入门之文本处理(下)

前面我们将了,如果通过 C# 从文本中获取内容,并进行一些简单的处理。今天我们来看看,怎么将获取的内...