@opentelemetry/sdk-trace-web編
@opentelemetry/sdk-trace-webはブラウザ環境向けのTracing SDKです。主に以下の機能を提供しています。
WebTracerProvider: ブラウザ向けTracerProvider
StackContextManager: 同期処理向けのシンプルなContext Manager
Performance API連携ユーティリティ: ネットワークイベント取得、CORS Preflight検出
DOM要素のXPath取得ユーティリティ
依存関係として@opentelemetry/sdk-trace-baseのBasicTracerProviderを継承しており、Web特有の設定をデフォルトで提供します。
WebTracerProvider内ではブラウザ固有のコンテキスト管理方法などが使われています。
たとえばStackContextManagerではAsyncLocalStorageがないため、シンプルなスタック方式で同期処理のみ対応(非同期コールバックではコンテキストが失われる)
code:ts
with<A extends unknown[], F extends (...args: A) => ReturnType<F>>(
context: Context | null,
fn: F,
thisArg?: ThisParameterType<F>,
...args: A
): ReturnType<F> {
const previousContext = this._currentContext;
this._currentContext = context || ROOT_CONTEXT;
try {
return fn.call(thisArg, ...args);
} finally {
this._currentContext = previousContext;
}
また他のパッケージから参照されるブラウザ関連のUtilityが定義されています。
parseUrl
document.baseURIを使用したURL解決
古いブラウザ向けに<a>要素を使ったURLパースのフォールバック
code:ts
export function parseUrl(url: string): URLLike {
if (typeof URL === 'function') {
return new URL(
url,
typeof document !== 'undefined'
? document.baseURI
: typeof location !== 'undefined' // Some JS runtimes (e.g. Deno) don't define this
? location.href
: undefined
);
}
const element = getUrlNormalizingAnchor();
element.href = url;
return element;
}
// ...
// Used to normalize relative URLs
let urlNormalizingAnchor: HTMLAnchorElement | undefined;
function getUrlNormalizingAnchor(): HTMLAnchorElement {
if (!urlNormalizingAnchor) {
urlNormalizingAnchor = document.createElement('a');
}
return urlNormalizingAnchor;
}
addSpanNetworkEventでPerformanceResourceTiming APIからネットワークタイミングを取得しています。
DNS解決、TCP接続、TLSハンドシェイク等の詳細タイミング
code:ts
if (!ignoreNetworkEvents) {
addSpanNetworkEvent(span, PTN.FETCH_START, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.DOMAIN_LOOKUP_START, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.DOMAIN_LOOKUP_END, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.CONNECT_START, resource, ignoreZeros);
addSpanNetworkEvent(
span,
PTN.SECURE_CONNECTION_START,
resource,
ignoreZeros
);
addSpanNetworkEvent(span, PTN.CONNECT_END, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.REQUEST_START, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.RESPONSE_START, resource, ignoreZeros);
addSpanNetworkEvent(span, PTN.RESPONSE_END, resource, ignoreZeros);
}
getElementXPathでDOM XPath取得ができる
DOM要素のnodeType、parentNodeを使用
クリックイベント等のユーザーインタラクショントレース用
code:ts
export function getElementXPath(target: any, optimised?: boolean): string {
if (target.nodeType === Node.DOCUMENT_NODE) {
return '/';
}
const targetValue = getNodeValue(target, optimised);
if (optimised && targetValue.indexOf('@id') > 0) {
return targetValue;
}
let xpath = '';
if (target.parentNode) {
xpath += getElementXPath(target.parentNode, false);
}
xpath += targetValue;
return xpath;
}
ブラウザ環境における非同期コンテキスト処理
デフォルトだとStackContextManagerを使った同期的なContext処理しかできません。ZoneContextManagerを使うことでZone.jsが非同期APIをmonkey patchして、各Zoneにコンテキストを紐づけることができるようになります。zone,js依存が嫌な場合は手動でbindする必要がありますが大変です。
code:md
通常の実行フロー:
┌─────────────────────────────────────────────────┐
│ context.with(ctx, () => { │
│ setTimeout(() => { │
│ // ここではctxが失われる │
│ }, 100); │
│ }); │
└─────────────────────────────────────────────────┘
ZoneContextManager:
┌─────────────────────────────────────────────────┐
│ Zone (ctx を保持) │
│ ┌─────────────────────────────────────────────┐ │
│ │ context.with(ctx, () => { │ │
│ │ setTimeout(() => { ← Zone.jsがpatch │ │
│ │ // Zone経由でctxを取得できる │ │
│ │ }, 100); │ │
│ │ }); │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
code:TS
private _createZone(zoneName: string, context: unknown): Zone {
return Zone.current.fork({
name: zoneName,
properties: {
},
});
}
code:ts
with<A extends unknown[], F extends (...args: A) => ReturnType<F>>(
context: Context | null,
fn: F,
thisArg?: ThisParameterType<F>,
...args: A
): ReturnType<F> {
const zoneName = this._createZoneName();
const newZone = this._createZone(zoneName, context);
return newZone.run(fn, thisArg, args);
}
zone.jsには詳しく触れませんがAngularの一部パッケージとして開発されています。
まとめ
webにおけるTraceProviderのベース実装を見ました。明日はNode.jsのベース実装を見ます。