Deno v2.1で導入されたOpenTelemetryサポートを試してみる
はじめに
⚠️ 筆者はOpenTelemetryに詳しくなく、ところどころ一般的な規約やベストプラクティスなどに従えていない箇所があるかもしれません🙏 目的
バージョン
要約
⚠️実験的APIのため、今後、使用方法などが変更される可能性があります
1. Denoの実行時にOTEL_DENO=trueと--unstable-otelを指定することで、fetchやDeno.serveなどのAPIの計装やconsole.*によるLogsの送信が有効化されます 3. 現在、開発が進められているFresh v2向けにもサポートが進められています code:otelcol.yaml
receivers:
otlp:
protocols:
http:
endpoint: localhost:4318
exporters:
debug:
verbosity: detailed
service:
pipelines:
metrics:
traces:
ひとまず疎通の確認をしたいのでdebugエクスポーターを有効化しておきます
code:shell
$ deno fmt otelcol.yaml
下記コマンドで設定内容を確認できます
code:shell
$ otelcol validate --config=otelcol.yaml
code:shell
$ otelcol --config=otelcol.yaml
2. 依存パッケージのダウンロード
必要に応じて依存パッケージをダウンロードします
code:shell
# 1) カスタムのTracesを送信したい場合
$ deno add jsr:@deno/otel npm:@opentelemetry/api
# 2) カスタムのMetricsを送信したい場合
$ deno add npm:@opentelemetry/sdk-metrics
3. Tracesの送信
fetch APIを使ってTracesの送信を試してみます code:main.js
headers: {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
});
--unstable-otel及びOTEL_DENO=true環境変数を指定して上記スクリプトを実行してみます
code:shell
$ OTEL_DENO=true deno run --allow-env --allow-net --unstable-otel main.js
code:shell
YYYY-MM-DDTHH:mm:ss.SSSZZ info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceSpans #0 Resource SchemaURL:
Resource attributes:
-> process.runtime.version: Str(2.1.4+9d315f2)
-> telemetry.sdk.name: Str(deno-opentelemetry)
-> telemetry.sdk.language: Str(deno-rust)
-> service.name: Str(unknown_service)
-> process.runtime.name: Str(deno)
-> telemetry.sdk.version: Str(2.1.4+9d315f2-0.27.0)
ScopeSpans SchemaURL:
InstrumentationScope deno 2.1.4+9d315f2
Trace ID : 828595b8b4a8f6699bd1ec38a47e1eed
Parent ID :
ID : 21c40683a9c261de
Name : GET
Kind : Client
...
Status code : Unset
Status message :
Attributes:
-> http.request.method: Str(GET)
-> url.scheme: Str(https)
-> url.path: Str(/repos/denoland/deno)
-> url.query: Str()
-> http.response.status_code: Str(200)
{"kind": "exporter", "data_type": "traces", "name": "debug"}
4. Logsの送信
console.*で出力したログは自動的にLogsとしてバックエンドへ送信されます
code:main.js
console.info("foo");
このスクリプトを実行してみます
code:shell
$ OTEL_DENO=true deno run --allow-env --unstable-otel main.js
foo
code:shell
YYYY-MM-DDTHH:mm:ss.SSSZZ info Logs {"kind": "exporter", "data_type": "logs", "name": "debug", "resource logs": 1, "log records": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceLog #0 Resource SchemaURL:
Resource attributes:
-> telemetry.sdk.name: Str(deno-opentelemetry)
-> process.runtime.name: Str(deno)
-> process.runtime.version: Str(2.1.4+9d315f2)
-> telemetry.sdk.version: Str(2.1.4+9d315f2-0.27.0)
-> telemetry.sdk.language: Str(deno-rust)
-> service.name: Str(unknown_service)
ScopeLogs SchemaURL:
InstrumentationScope deno
...
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(foo
)
Trace ID:
Span ID:
Flags: 0
{"kind": "exporter", "data_type": "logs", "name": "debug"}
例) OTEL_DENO_CONSOLE=ignoreを指定すると、バックエンドへのLogsの送信が無効化されます
5. カスタムのTracesの送信
code:javascript
import { trace } from "@opentelemetry/api";
import { register } from "@deno/otel";
register();
const tracer = trace.getTracer("example");
function someOperation() {
return new Promise((ok) => setTimeout(ok, 3000));
}
await tracer.startActiveSpan("someOperation", async (span) => {
await someOperation();
span.end();
});
code:shell
YYYY-MM-DDTHH:mm:ss.SSSZZ info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceSpans #0 Resource SchemaURL:
Resource attributes:
-> process.runtime.version: Str(2.1.4+9d315f2)
-> telemetry.sdk.version: Str(2.1.4+9d315f2-0.27.0)
-> process.runtime.name: Str(deno)
-> service.name: Str(unknown_service)
-> telemetry.sdk.name: Str(deno-opentelemetry)
-> telemetry.sdk.language: Str(deno-rust)
ScopeSpans SchemaURL:
InstrumentationScope example
Trace ID : 771ee0a3832c8040b04e2a883516240c
Parent ID :
ID : f289545ad405c65e
Name : someOperation
Kind : Internal
...
Status code : Unset
Status message :
{"kind": "exporter", "data_type": "traces", "name": "debug"}
自前で作成したSpan内でDeno内部で計装されているAPI (fetch)を実行した場合、適切に親子関係の紐づけが行われるようです code:javascript
import { trace } from "@opentelemetry/api";
import { register } from "@deno/otel";
register();
const tracer = trace.getTracer("example");
await tracer.startActiveSpan("makeHTTPRequest", async (span) => {
headers: {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
});
span.end();
});
fetchに対応するSpanにParent IDが設定されており、makeHTTPRequestが参照されています code:shell
YYYY-MM-DDTHH:mm:ss.SSSZZ info Traces {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 2}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceSpans #0 Resource SchemaURL:
Resource attributes:
-> process.runtime.name: Str(deno)
-> telemetry.sdk.version: Str(2.1.4+9d315f2-0.27.0)
-> telemetry.sdk.name: Str(deno-opentelemetry)
-> process.runtime.version: Str(2.1.4+9d315f2)
-> telemetry.sdk.language: Str(deno-rust)
-> service.name: Str(unknown_service)
ScopeSpans SchemaURL:
InstrumentationScope deno 2.1.4+9d315f2
Trace ID : 2a709f3c26099083d36ce9be197c7fd8
Parent ID : f99d09c34da6d3cd
ID : bf8ef320a2e9024a
Name : GET
Kind : Client
...
Status code : Unset
Status message :
Attributes:
-> http.request.method: Str(GET)
-> url.scheme: Str(https)
-> url.path: Str(/repos/denoland/deno)
-> url.query: Str()
-> http.response.status_code: Str(200)
ScopeSpans SchemaURL:
InstrumentationScope example
Trace ID : 2a709f3c26099083d36ce9be197c7fd8
Parent ID :
ID : f99d09c34da6d3cd
Name : makeHTTPRequest
Kind : Internal
...
Status code : Unset
Status message :
{"kind": "exporter", "data_type": "traces", "name": "debug"}
6. カスタムのMetricsの送信
code:javascript
import {
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
const exporter = new Deno.telemetry.MetricExporter();
const meterProvider = new MeterProvider();
meterProvider.addMetricReader(
new PeriodicExportingMetricReader({
exporter,
exportIntervalMillis: 1000,
}),
);
const meter = meterProvider.getMeter("meter");
const views = meter.createCounter("views", {
description: "The total number of views of the page",
});
views.add(1);
await new Promise((ok) => setTimeout(ok, 1500));
views.add(1);
await new Promise((ok) => setTimeout(ok, 1500));
code:shell
YYYY-MM-DDTHH:mm:ss.SSSZZ info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 1, "metrics": 1, "data points": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceMetrics #0 Resource SchemaURL:
ScopeMetrics SchemaURL:
InstrumentationScope meter
Descriptor:
-> Name: views
-> Description: The total number of views of the page
-> Unit:
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: Cumulative
...
Value: 1.000000
{"kind": "exporter", "data_type": "metrics", "name": "debug"}
YYYY-MM-DDTHH:mm:ss.SSSZZ info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 1, "metrics": 1, "data points": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceMetrics #0 Resource SchemaURL:
ScopeMetrics SchemaURL:
InstrumentationScope meter
Descriptor:
-> Name: views
-> Description: The total number of views of the page
-> Unit:
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: Cumulative
...
...
Value: 2.000000
{"kind": "exporter", "data_type": "metrics", "name": "debug"}
YYYY-MM-DDTHH:mm:ss.SSSZZ info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 1, "metrics": 1, "data points": 1}
YYYY-MM-DDTHH:mm:ss.SSSZZ info ResourceMetrics #0 Resource SchemaURL:
ScopeMetrics SchemaURL:
InstrumentationScope meter
Descriptor:
-> Name: views
-> Description: The total number of views of the page
-> Unit:
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: Cumulative
...
Value: 2.000000
{"kind": "exporter", "data_type": "metrics", "name": "debug"}
⚠️ Fresh v2はまだアルファバージョンであり、正式リリースは行われていません おわりに
参考
関連ページ