@opentelemetry/core編
こんにちは、@sugar235711です。
この記事は「ひとりで気になるOSSのソースコード全部読んで何かする Advent Calendar 2025」9日目の記事です。
前日の記事はこちら:@opentelemetry/api編
概要
This package provides default implementations of the OpenTelemetry API for trace and metrics. It's intended for use both on the server and in the browser.
かっこいいですね。サーバーおよびブラウザどちらでも使えるトレースとメトリクスのデフォルト実装が入っているようです。
https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core
主な実装は以下の通りです:
Propagators: W3C Trace Context / W3C Baggage 準拠の伝搬実装
Time utilities: HrTime(高精度時間)の変換・計算
Attributes: 属性値のバリデーション・サニタイズ
Error handling: グローバルエラーハンドラ
Platform abstraction: Node.js/Browser両対応の環境抽象化
Utils: タイムアウト、マージ、コールバック制御など
Built-in Propagators
Readmeでは3つのPropagatorsが提供されていると書いています。
W3CTraceContextPropagator Propagator
Composite Propagator
Baggage Propagator
実際にドキュメントにもあるようにプラットフォームによってはOpenTelemetryではout-of-the-boxとして事前にPropagatorが事前構成され、デフォルトでこれらのPropagatorが設定されているべきであると書いてあります。
Platforms such as ASP.NET may pre-configure out-of-the-box propagators. If pre-configured, Propagators SHOULD default to a composite Propagator containing the W3C Trace Context Propagator and the Baggage Propagator specified in the Baggage API. These platforms MUST also allow pre-configured propagators to be disabled or overridden.
https://opentelemetry.io/docs/specs/otel/context/api-propagators/
W3CTraceContextPropagator Propagator
ご存知のようにtraceparentとtracestateのヘッダーが解析され伝播される必要があります。
https://opentelemetry.io/docs/specs/otel/context/api-propagators/#w3c-trace-context-requirements
inject
SpanContextに対してtraceparentヘッダーを構築してSetter経由でCarrierに設定しています。仕様通りです。
https://opentelemetry.io/docs/specs/otel/context/api-propagators/#inject
code:ts
const traceParent = `${VERSION}-${spanContext.traceId}-${
spanContext.spanId
}-0${Number(spanContext.traceFlags || TraceFlags.NONE).toString(16)}`;
https://github.com/open-telemetry/opentelemetry-js/blob/6321f483a95a494684508738673e8f423de34f51/packages/opentelemetry-core/src/trace/W3CTraceContextPropagator.ts#L75-L96
extract
SpanContextから取得したTraceparent Headerがパースできない場合は例外を返さず元のContextをそのまま返すようになっています。(MUST NOT throw an exception)
https://github.com/open-telemetry/opentelemetry-js/blob/6321f483a95a494684508738673e8f423de34f51/packages/opentelemetry-core/src/trace/W3CTraceContextPropagator.ts#L99-L121
parseをする関数に面白いことが書いてあります。
https://github.com/open-telemetry/opentelemetry-js/blob/6321f483a95a494684508738673e8f423de34f51/packages/opentelemetry-core/src/trace/W3CTraceContextPropagator.ts#L52
W3C仕様にバージョニングルールがあるそうです。
with future versions. If there are more parts, we only reject it if it's using version 00
https://www.w3.org/TR/trace-context/#versioning-of-traceparent
そもそもtraceparentがどんな構造になっているかというと↓のような感じになっています。
code:md
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
─┬ ────────────────┬─────────────── ────────┬─────── ─┬
│ │ │ │
version(00) trace-id(32桁hex) parent-id(16桁) flags
正規表現では↓になっており00以外のversionが新しい場合のみ拡張できるような実装になっています。
code:ts
const TRACE_PARENT_REGEX = new RegExp(
^ + // 文字列の開始
\\s? + // オプショナルな空白
(${VERSION_PART}) + // ← match1: version(2桁hex、ffは除外)
- + // ハイフン区切り
(${TRACE_ID_PART}) + // ← match2: trace-id(32桁hex、全ゼロ除外)
- + // ハイフン区切り
(${PARENT_ID_PART}) + // ← match3: parent-id(16桁hex、全ゼロ除外)
- + // ハイフン区切り
(${FLAGS_PART}) + // ← match4: flags(2桁hex)★固定で存在
(-.*)? + // ← match5: 拡張パート(オプショナル)★将来の拡張用
\\s? + // オプショナルな空白
$ // 文字列の終了
);
拡張できるだけで現状の実装では無視されているのでparse結果からは除外されています。
CompositePropagator
複数のPropagatorを1つにまとめて使うためのものです。
https://opentelemetry.io/docs/specs/otel/context/api-propagators/#composite-propagator
code:ts
const propagator = new CompositePropagator({
propagators: [
new W3CTraceContextPropagator(), // traceparent, tracestate
new W3CBaggagePropagator(), // baggage
new B3Propagator(), // x-b3-* ヘッダー
],
});
https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/src/propagation/composite.ts
内部的にはTextMapPropagatorをinterfaceを満たすPropagator実装ではなんでも渡せるようになっています。
Baggage Propagator
サービス間で任意のキー/バリューペアを伝搬するための仕組みです。トレースIDやSpanIDとは別に、ビジネスロジックで使うデータを渡せます。
https://www.w3.org/TR/baggage/
code:md
baggage: key1=value1,key2=value2;metadata,key3=value3
─────┬───── ──────────┬──────── ─────┬─────
│ │ │
エントリ1 エントリ2 エントリ3
(メタデータ付き)
inject
特殊文字等に対応するためにencodeURIComponentが使われています。またメタデータは別に扱うために新たにentryが構成されています。
code:ts
export function getKeyPairs(baggage: Baggage): string[] {
return baggage.getAllEntries().map((key, value) => {
let entry = ${encodeURIComponent(key)}=${encodeURIComponent(value.value)};
// include opaque metadata if provided
// NOTE: we intentionally don't URI-encode the metadata - that responsibility falls on the metadata implementation
if (value.metadata !== undefined) {
entry += BAGGAGE_PROPERTIES_SEPARATOR + value.metadata.toString();
}
return entry;
});
}
ヘッダーに載せるためにkeyparがシリアライズされます。
code:ts
export function serializeKeyPairs(keyPairs: string[]): string {
return keyPairs.reduce((hValue: string, current: string) => {
const value = `${hValue}${
hValue !== '' ? BAGGAGE_ITEMS_SEPARATOR : ''
}${current}`;
return value.length > BAGGAGE_MAX_TOTAL_LENGTH ? hValue : value;
}, '');
}
code:md
keyPairs = "a=1", "b=2", "c=3", ..., "huge=..." (超長い)
reduce処理:
"" + "a=1" = "a=1" (5文字 < 8192) → OK
"a=1" + ",b=2" = "a=1,b=2" (9文字 < 8192) → OK
"a=1,b=2" + ",c=3" = "a=1,b=2,c=3" (13文字 < 8192) → OK
...
"a=1,b=2,..." + ",huge=..." = 8300文字 > 8192 → 追加せず前の状態を返す
extract
extract側でも同様にkeypairとmetadataをデコードしパースしています。
AnchoredClockに関して
システム時刻が変更されると、Spanの開始時刻より終了時刻が前になる可能性があるのでそれに対応するために、モノトニック時計が使われます。
https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/src/common/anchored-clock.ts
BindOnceFuture
シャットダウン処理等のPromise処理を一度のみ行うような設計になっています。(2回目以降は同じ結果のPromiseが返る)
https://github.com/open-telemetry/opentelemetry-js/blob/6321f483a95a494684508738673e8f423de34f51/packages/opentelemetry-core/src/utils/callback.ts#L22
まとめ
coreの実装を見ていきました、Built inのPropagatorの他に他のパッケージで使えるutil関数などたくさん定義されていました。