Очередное исправление playwright-labs.
Итак, давно не врывался с обновлениями по playwright-labs: монорепе по полезным утилитам playwright.
Я пропустил, что на той неделе кто-то(некий Александр Сорокин) оставил issue о дочерних span и trace. Только он не уточнил для какой части нужен фикс и я додумал за него: сделал для fixture-otel автоматическую поддержку для функции withSpan
Т.е. раньше (я на самом деле даже не задумавался о таком случае, хотя и звучит логично) такой код test('some test', () => { withSpan('a', (s1) => { withSpan('b', (s2) => { // implementation }) }) })
Порождал вот такое дерево в OTEL/Tempo
[test span] ├── a └── b
Зато после фикса стало так, как и должно быть
[test span] └── a └── b
Вот тут на помощь и пришел AsyncLocalStorage из nodejs. Поскольку функция withSpan возвращает промис, и функция, которая передается тоже является той, которая возвращает промис, то нужно отслеживать ее контекст. Поэтому без AsyncLocalStorage никак, на самом деле реализация функции withSpan доболи простая const _activeSpan = new AsyncLocalStorage();
export async function withSpan(name: string, fn: (span: Span) => Promise | void){ const parent = _activeSpan.getStore(); const span = new Span(name, parent);
try { // вот тут мы запускаем саму функцию внутри "пойманного" activeSpan return await _activeSpan.run(span, () => fn(span)); } catch (error) { span.setStatus( "error", error instanceof Error ? error.message : String(error), ); throw error; } finally { span.end(); } }
Вот таким нехитрым способом используя AsyncLocalStorage мы 1. не теряем контекст во время выполнения переданного fn внутри withSpan 2. добавляем немного накладных расходов, засчет того, что контекст теперь сохраняется
Кстати, эти накладные расходы, связанные с AsyncLocalStorage, меркнут по сравнении с ресурсами, забираемым самим playwright.
#js #ts #pw #playwright #playwright_labs #pw_labs @haradkou_sdet