Перейти к содержанию

🚀 Где задеплоить проект?

Партнёрская ссылка. Дата-центры в РФ, оплата картой.

Канал диагностики

Стабильность: 2 – Стабильная

АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.

Модуль node:diagnostics_channel предоставляет API для создания именованных каналов передачи произвольных данных в целях диагностики.

Подключение:

1
import diagnostics_channel from 'node:diagnostics_channel';
1
const diagnostics_channel = require('node:diagnostics_channel');

Обычно автор модуля, которому нужны диагностические сообщения, создаёт один или несколько каналов верхнего уровня. Каналы можно получать и во время выполнения, но это нежелательно из‑за накладных расходов. Каналы можно экспортировать для удобства; если известно имя, канал можно получить откуда угодно.

Если модуль должен отдавать диагностические данные потребителям, задокументируйте используемые имена каналов и форму сообщений. В имена обычно включают имя модуля, чтобы избежать коллизий.

Публичный API

Обзор

Краткий обзор публичного API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import diagnostics_channel from 'node:diagnostics_channel';

// Получить переиспользуемый объект канала
const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Полученные данные
}

// Подписка на канал
diagnostics_channel.subscribe('my-channel', onMessage);

// Проверка активных подписчиков
if (channel.hasSubscribers) {
  // Публикация в канал
  channel.publish({
    some: 'data',
  });
}

// Отписка
diagnostics_channel.unsubscribe('my-channel', onMessage);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Полученные данные
}

diagnostics_channel.subscribe('my-channel', onMessage);

if (channel.hasSubscribers) {
  channel.publish({
    some: 'data',
  });
}

diagnostics_channel.unsubscribe('my-channel', onMessage);

diagnostics_channel.hasSubscribers(name)

  • name <string> | <symbol> имя канала
  • Возвращает: <boolean> есть ли активные подписчики

Проверяет наличие активных подписчиков у именованного канала. Полезно, если подготовка сообщения может быть дорогой.

API необязательно, но удобно при публикации из кода, критичного к производительности.

1
2
3
4
5
import diagnostics_channel from 'node:diagnostics_channel';

if (diagnostics_channel.hasSubscribers('my-channel')) {
  // Есть подписчики — подготовить и опубликовать сообщение
}
1
2
3
4
5
const diagnostics_channel = require('node:diagnostics_channel');

if (diagnostics_channel.hasSubscribers('my-channel')) {
  // Есть подписчики — подготовить и опубликовать сообщение
}

diagnostics_channel.channel(name)

  • name <string> | <symbol> имя канала
  • Возвращает: <Channel> объект именованного канала

Основная точка входа для публикации в именованный канал. Возвращает объект канала, оптимизированный для минимальных накладных расходов при публикации.

1
2
3
import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');
1
2
3
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

diagnostics_channel.subscribe(name, onMessage)

Регистрирует обработчик подписки на канал. Обработчик вызывается синхронно при каждой публикации. Ошибки в обработчике приводят к 'uncaughtException'.

1
2
3
4
5
import diagnostics_channel from 'node:diagnostics_channel';

diagnostics_channel.subscribe('my-channel', (message, name) => {
  // Полученные данные
});
1
2
3
4
5
const diagnostics_channel = require('node:diagnostics_channel');

diagnostics_channel.subscribe('my-channel', (message, name) => {
  // Полученные данные
});

diagnostics_channel.unsubscribe(name, onMessage)

  • name <string> | <symbol> имя канала
  • onMessage <Function> ранее зарегистрированный обработчик для удаления
  • Возвращает: <boolean> true, если обработчик найден, иначе false.

Удаляет обработчик, ранее зарегистрированный через diagnostics_channel.subscribe(name, onMessage).

1
2
3
4
5
6
7
8
9
import diagnostics_channel from 'node:diagnostics_channel';

function onMessage(message, name) {
  // Полученные данные
}

diagnostics_channel.subscribe('my-channel', onMessage);

diagnostics_channel.unsubscribe('my-channel', onMessage);
1
2
3
4
5
6
7
8
9
const diagnostics_channel = require('node:diagnostics_channel');

function onMessage(message, name) {
  // Полученные данные
}

diagnostics_channel.subscribe('my-channel', onMessage);

diagnostics_channel.unsubscribe('my-channel', onMessage);

diagnostics_channel.tracingChannel(nameOrChannels)

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Создаёт обёртку TracingChannel для заданных каналов TracingChannel. Если передано имя, соответствующие каналы трассировки создаются в виде tracing:${name}:${eventType}, где eventType соответствует типам каналов TracingChannel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import diagnostics_channel from 'node:diagnostics_channel';

const channelsByName = diagnostics_channel.tracingChannel('my-channel');

// или...

const channelsByCollection = diagnostics_channel.tracingChannel({
  start: diagnostics_channel.channel('tracing:my-channel:start'),
  end: diagnostics_channel.channel('tracing:my-channel:end'),
  asyncStart: diagnostics_channel.channel('tracing:my-channel:asyncStart'),
  asyncEnd: diagnostics_channel.channel('tracing:my-channel:asyncEnd'),
  error: diagnostics_channel.channel('tracing:my-channel:error'),
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const diagnostics_channel = require('node:diagnostics_channel');

const channelsByName = diagnostics_channel.tracingChannel('my-channel');

// или...

const channelsByCollection = diagnostics_channel.tracingChannel({
  start: diagnostics_channel.channel('tracing:my-channel:start'),
  end: diagnostics_channel.channel('tracing:my-channel:end'),
  asyncStart: diagnostics_channel.channel('tracing:my-channel:asyncStart'),
  asyncEnd: diagnostics_channel.channel('tracing:my-channel:asyncEnd'),
  error: diagnostics_channel.channel('tracing:my-channel:error'),
});

diagnostics_channel.boundedChannel(nameOrChannels)

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Создаёт обёртку BoundedChannel для заданных каналов. Если передано имя, каналы создаются в виде tracing:${name}:${eventType}, где eventTypestart или end.

BoundedChannel — упрощённый вариант TracingChannel: трассируются только синхронные операции; есть только события start и end без asyncStart, asyncEnd и error, что подходит для операций без асинхронных продолжений и отдельной обработки ошибок.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import { boundedChannel, channel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

// или...

const wc2 = boundedChannel({
  start: channel('tracing:my-operation:start'),
  end: channel('tracing:my-operation:end'),
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const { boundedChannel, channel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

// или...

const wc2 = boundedChannel({
  start: channel('tracing:my-operation:start'),
  end: channel('tracing:my-operation:end'),
});

Класс: Channel

Класс Channel представляет отдельный именованный канал в конвейере данных. Отслеживает подписчиков и публикует сообщения при их наличии. Отдельный объект нужен, чтобы не выполнять поиск канала при публикации — это даёт высокую скорость публикации и низкую стоимость при активном использовании. Каналы создаются через diagnostics_channel.channel(name); прямой вызов new Channel(name) не поддерживается.

channel.hasSubscribers

  • Возвращает: <boolean> есть ли активные подписчики

Проверяет наличие активных подписчиков у этого канала. Полезно, если подготовка сообщения может быть дорогой.

API необязательно, но удобно при публикации из кода, критичного к производительности.

1
2
3
4
5
6
7
import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

if (channel.hasSubscribers) {
  // Есть подписчики — подготовить и опубликовать сообщение
}
1
2
3
4
5
6
7
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

if (channel.hasSubscribers) {
  // Есть подписчики — подготовить и опубликовать сообщение
}

channel.publish(message)

  • message <any> сообщение для подписчиков канала

Публикует сообщение всем подписчикам канала. Обработчики вызываются синхронно в том же контексте.

1
2
3
4
5
6
7
import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

channel.publish({
  some: 'message',
});
1
2
3
4
5
6
7
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

channel.publish({
  some: 'message',
});

channel.subscribe(onMessage)

  • onMessage <Function> обработчик сообщений канала

Регистрирует обработчик подписки на этот канал. Обработчик выполняется синхронно при каждой публикации. Ошибки в обработчике приводят к 'uncaughtException'.

1
2
3
4
5
6
7
import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

channel.subscribe((message, name) => {
  // Полученные данные
});
1
2
3
4
5
6
7
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

channel.subscribe((message, name) => {
  // Полученные данные
});

channel.unsubscribe(onMessage)

  • onMessage <Function> ранее зарегистрированный обработчик для удаления
  • Возвращает: <boolean> true, если обработчик найден, иначе false.

Удаляет обработчик, ранее зарегистрированный через channel.subscribe(onMessage).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Полученные данные
}

channel.subscribe(onMessage);

channel.unsubscribe(onMessage);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Полученные данные
}

channel.subscribe(onMessage);

channel.unsubscribe(onMessage);

channel.bindStore(store[, transform])

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

  • store <AsyncLocalStorage> хранилище для привязки контекста
  • transform <Function> преобразование данных контекста перед установкой в хранилище

При вызове channel.runStores(context, ...) указанные данные контекста применяются ко всем хранилищам, привязанным к каналу. Если хранилище уже было привязано, предыдущая функция transform заменяется новой. Функцию transform можно опустить — тогда данные контекста задают контекст хранилища напрямую.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (data) => {
  return { data };
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (data) => {
  return { data };
});

channel.unbindStore(store)

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

  • store <AsyncLocalStorage> хранилище для отвязки от канала
  • Возвращает: <boolean> true, если хранилище найдено, иначе false.

Удаляет привязку хранилища, ранее созданную channel.bindStore(store).

1
2
3
4
5
6
7
8
9
import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store);
channel.unbindStore(store);
1
2
3
4
5
6
7
8
9
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store);
channel.unbindStore(store);

channel.runStores(context, fn[, thisArg[, ...args]])

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

  • context <any> сообщение для подписчиков и привязки к хранилищам
  • fn <Function> функция, выполняемая во введённом контексте хранилища
  • thisArg <any> значение this для вызова функции
  • ...args <any> необязательные аргументы функции

Применяет данные ко всем экземплярам AsyncLocalStorage, привязанным к каналу, на время выполнения fn, затем публикует в канал в области, где данные применены к хранилищам.

Если в channel.bindStore(store) задана функция преобразования, она применяется к данным сообщения до того, как они станут контекстом хранилища. Предыдущий контекст хранилища доступен внутри transform, если нужна связка контекстов.

Контекст хранилища должен быть доступен в асинхронном коде, продолжающем выполнение, начатое в fn; однако возможна потеря контекста.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (message) => {
  const parent = store.getStore();
  return new Span(message, parent);
});
channel.runStores({ some: 'message' }, () => {
  store.getStore(); // Span({ some: 'message' })
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (message) => {
  const parent = store.getStore();
  return new Span(message, parent);
});
channel.runStores({ some: 'message' }, () => {
  store.getStore(); // Span({ some: 'message' })
});

channel.withStoreScope(data)

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

  • data <any> данные для привязки к хранилищам
  • Возвращает: <RunStoresScope> объект области видимости с Disposable

Создаёт область с автоматическим освобождением: привязывает данные к экземплярам AsyncLocalStorage, привязанным к каналу, и публикует их подписчикам. При освобождении восстанавливает предыдущие контексты хранилищ.

Позволяет использовать явное управление ресурсами в JavaScript (синтаксис using и Symbol.dispose) без обёртки в замыкание.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { channel } from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();
const ch = channel('my-channel');

ch.bindStore(store, (message) => {
  return { ...message, timestamp: Date.now() };
});

{
  using scope = ch.withStoreScope({ request: 'data' });
  // хранилище введено, данные опубликованы
  console.log(store.getStore()); // { request: 'data', timestamp: ... }
}
// при выходе из области хранилище автоматически восстанавливается
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const { channel } = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();
const ch = channel('my-channel');

ch.bindStore(store, (message) => {
  return { ...message, timestamp: Date.now() };
});

{
  using scope = ch.withStoreScope({ request: 'data' });
  // хранилище введено, данные опубликованы
  console.log(store.getStore()); // { request: 'data', timestamp: ... }
}
// при выходе из области хранилище автоматически восстанавливается

Класс: RunStoresScope

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Класс RunStoresScope — область с Disposable, создаваемая channel.withStoreScope(data). Управляет жизненным циклом контекстов хранилищ и восстанавливает их при выходе из области.

Область нужно использовать с синтаксисом using, чтобы гарантировать освобождение.

Класс: TracingChannel

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Класс TracingChannel объединяет каналы TracingChannel, описывающие одно трассируемое действие. Формализует и упрощает генерацию событий для трассировки потока выполнения. Экземпляр создаётся через diagnostics_channel.tracingChannel(). Как и для Channel, рекомендуется создавать один TracingChannel на уровне модуля и переиспользовать, а не создавать динамически.

tracingChannel.subscribe(subscribers)

Вспомогательный метод подписки набора функций на соответствующие каналы. Эквивалентно вызову channel.subscribe(onMessage) для каждого канала по отдельности.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.subscribe({
  start(message) {
    // обработка сообщения start
  },
  end(message) {
    // обработка сообщения end
  },
  asyncStart(message) {
    // обработка сообщения asyncStart
  },
  asyncEnd(message) {
    // обработка сообщения asyncEnd
  },
  error(message) {
    // обработка сообщения error
  },
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.subscribe({
  start(message) {
    // обработка сообщения start
  },
  end(message) {
    // обработка сообщения end
  },
  asyncStart(message) {
    // обработка сообщения asyncStart
  },
  asyncEnd(message) {
    // обработка сообщения asyncEnd
  },
  error(message) {
    // обработка сообщения error
  },
});

tracingChannel.unsubscribe(subscribers)

Отписка набора функций от соответствующих каналов. Эквивалентно channel.unsubscribe(onMessage) на каждом канале отдельно.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.unsubscribe({
  start(message) {
    // обработка сообщения start
  },
  end(message) {
    // обработка сообщения end
  },
  asyncStart(message) {
    // обработка сообщения asyncStart
  },
  asyncEnd(message) {
    // обработка сообщения asyncEnd
  },
  error(message) {
    // обработка сообщения error
  },
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.unsubscribe({
  start(message) {
    // обработка сообщения start
  },
  end(message) {
    // обработка сообщения end
  },
  asyncStart(message) {
    // обработка сообщения asyncStart
  },
  asyncEnd(message) {
    // обработка сообщения asyncEnd
  },
  error(message) {
    // обработка сообщения error
  },
});

tracingChannel.traceSync(fn[, context[, thisArg[, ...args]]])

  • fn <Function> функция для обёртки трассировкой
  • context <Object> общий объект для корреляции событий
  • thisArg <any> значение this для вызова
  • ...args <any> необязательные аргументы функции
  • Возвращает: <any> результат вызова fn

Трассирует синхронный вызов: всегда генерируются событие start и событие end вокруг выполнения и при необходимости событие error, если функция выбросила ошибку. Функция выполняется через channel.runStores(context, ...) на канале start, чтобы привязанные хранилища соответствовали контексту трассировки.

События публикуются только если подписчики есть до начала трассировки; подписки после старта не получат события этой трассировки.

1
2
3
4
5
6
7
8
9
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceSync(() => {
  // полезная работа
}, {
  some: 'thing',
});
1
2
3
4
5
6
7
8
9
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceSync(() => {
  // полезная работа
}, {
  some: 'thing',
});

tracingChannel.tracePromise(fn[, context[, thisArg[, ...args]]])

  • fn <Function> функция для обёртки трассировкой
  • context <Object> общий объект для корреляции событий трассировки
  • thisArg <any> значение this для вызова
  • ...args <any> необязательные аргументы функции
  • Возвращает: <any> результат fn или результат .then(...), если у канала трассировки есть активные подписчики. Если значение не Promise и не thenable, оно возвращается как есть и выводится предупреждение.

Трассирует асинхронный вызов, возвращающий Promise или thenable-объект. Всегда генерируются событие start и событие end вокруг синхронной части; при разрешении или отклонении промиса — событие asyncStart и событие asyncEnd. Возможен событие error, если функция выбросила ошибку или промис отклонён. Выполнение идёт через channel.runStores(context, ...) на канале start.

Если fn вернула не промис и не thenable, значение возвращается с предупреждением, без событий asyncStart и asyncEnd.

События публикуются только если подписчики есть до начала трассировки.

1
2
3
4
5
6
7
8
9
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.tracePromise(async () => {
  // полезная работа
}, {
  some: 'thing',
});
1
2
3
4
5
6
7
8
9
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.tracePromise(async () => {
  // полезная работа
}, {
  some: 'thing',
});

tracingChannel.traceCallback(fn[, position[, context[, thisArg[, ...args]]]])

  • fn <Function> функция, принимающая колбэк, для обёртки трассировкой
  • position <number> индекс (с нуля) аргумента с ожидаемым колбэком (по умолчанию — последний аргумент, если передан undefined)
  • context <Object> общий объект корреляции (по умолчанию {}, если undefined)
  • thisArg <any> значение this для вызова
  • ...args <any> аргументы вызова (должен включать колбэк)
  • Возвращает: <any> результат вызова fn

Трассирует вызов функции с колбэком в типичной конвенции «ошибка первым аргументом». Всегда даёт событие start и событие end вокруг синхронной части и событие asyncStart с событие asyncEnd вокруг выполнения колбэка. Возможен событие error, если функция выбросила ошибку или в колбэк передан первый аргумент (ошибка). Выполнение через channel.runStores(context, ...) на канале start.

События публикуются только если подписчики есть до начала трассировки.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceCallback((arg1, callback) => {
  // полезная работа
  callback(null, 'result');
}, 1, {
  some: 'thing',
}, thisArg, arg1, callback);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceCallback((arg1, callback) => {
  // полезная работа
  callback(null, 'result');
}, 1, {
  some: 'thing',
}, thisArg, arg1, callback);

Колбэк также выполняется внутри channel.runStores(context, ...), что в ряде случаев помогает восстановить контекст после потери контекста.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const channels = diagnostics_channel.tracingChannel('my-channel');
const myStore = new AsyncLocalStorage();

// Канал start задаёт начальные данные хранилища и сохраняет значение в объекте контекста трассировки
channels.start.bindStore(myStore, (data) => {
  const span = new Span(data);
  data.span = span;
  return span;
});

// asyncStart может восстановить контекст из ранее сохранённых данных
channels.asyncStart.bindStore(myStore, (data) => {
  return data.span;
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const channels = diagnostics_channel.tracingChannel('my-channel');
const myStore = new AsyncLocalStorage();

channels.start.bindStore(myStore, (data) => {
  const span = new Span(data);
  data.span = span;
  return span;
});

channels.asyncStart.bindStore(myStore, (data) => {
  return data.span;
});

tracingChannel.hasSubscribers

  • Возвращает: <boolean> true, если хотя бы у одного из каналов есть подписчик, иначе false.

Вспомогательное свойство экземпляра TracingChannel: есть ли подписчики у любого из каналов TracingChannel.

1
2
3
4
5
6
7
import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

if (channels.hasSubscribers) {
  // полезная работа
}
1
2
3
4
5
6
7
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

if (channels.hasSubscribers) {
  // полезная работа
}

Класс: BoundedChannel

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Класс BoundedChannel — упрощённый TracingChannel: трассируются только синхронные операции; два канала (start и end) вместо пяти, без asyncStart, asyncEnd и error. Подходит для операций без асинхронных продолжений и отдельной обработки ошибок.

Как и для TracingChannel, рекомендуется один экземпляр на уровне модуля.

boundedChannel.hasSubscribers

  • Возвращает: <boolean> true, если у одного из каналов есть подписчик, иначе false.

Проверяет наличие подписчиков у каналов start или end.

1
2
3
4
5
6
7
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

if (wc.hasSubscribers) {
  // Есть подписчики — выполнить трассируемую операцию
}
1
2
3
4
5
6
7
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

if (wc.hasSubscribers) {
  // Есть подписчики — выполнить трассируемую операцию
}

boundedChannel.subscribe(handlers)

  • handlers <Object> подписчики каналов
    • start <Function> подписчик события start
    • end <Function> подписчик события end

Подписка на события bounded-канала. Эквивалентно channel.subscribe(onMessage) для каждого канала.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

wc.subscribe({
  start(message) {
    // обработка start
  },
  end(message) {
    // обработка end
  },
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

wc.subscribe({
  start(message) {
    // обработка start
  },
  end(message) {
    // обработка end
  },
});

boundedChannel.unsubscribe(handlers)

  • handlers <Object> подписчики каналов
    • start <Function> подписчик события start
    • end <Function> подписчик события end
  • Возвращает: <boolean> true, если все обработчики сняты, иначе false.

Отписка от событий bounded-канала. Эквивалентно channel.unsubscribe(onMessage) на каждом канале.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const handlers = {
  start(message) {},
  end(message) {},
};

wc.subscribe(handlers);
wc.unsubscribe(handlers);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const handlers = {
  start(message) {},
  end(message) {},
};

wc.subscribe(handlers);
wc.unsubscribe(handlers);

boundedChannel.run(context, fn[, thisArg[, ...args]])

  • context <Object> общий объект корреляции событий
  • fn <Function> функция для обёртки трассировкой
  • thisArg <any> значение this для вызова
  • ...args <any> необязательные аргументы функции
  • Возвращает: <any> результат вызова fn

Трассирует синхронный вызов: события start и end вокруг выполнения. Функция выполняется через channel.runStores(context, ...) на канале start.

1
2
3
4
5
6
7
8
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const result = wc.run({ operationId: '123' }, () => {
  // выполнение операции
  return 42;
});
1
2
3
4
5
6
7
8
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const result = wc.run({ operationId: '123' }, () => {
  // выполнение операции
  return 42;
});

boundedChannel.withScope([context])

  • context <Object> общий объект корреляции событий
  • Возвращает: <BoundedChannelScope> объект области с Disposable

Создаёт область для трассировки синхронной операции с явным управлением ресурсами (синтаксис using). Публикует события start и end, входит в привязанные хранилища и выполняет очистку при освобождении.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const context = { operationId: '123' };
{
  using scope = wc.withScope(context);
  // хранилища введены, опубликовано событие start

  // работа и запись результата в context
  context.result = 42;
}
// публикуется событие end, хранилища автоматически восстанавливаются
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const context = { operationId: '123' };
{
  using scope = wc.withScope(context);
  // хранилища введены, опубликовано событие start

  // работа и запись результата в context
  context.result = 42;
}
// публикуется событие end, хранилища автоматически восстанавливаются

Класс: BoundedChannelScope

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Класс BoundedChannelScope — область с Disposable, создаваемая boundedChannel.withScope(context). Управляет жизненным циклом трассируемой операции, публикует события и контексты хранилищ.

Использовать только с синтаксисом using.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const context = {};
{
  using scope = wc.withScope(context);
  // публикуется событие start, вводятся хранилища
  context.result = performOperation();
  // событие end автоматически публикуется в конце блока
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const context = {};
{
  using scope = wc.withScope(context);
  // публикуется событие start, вводятся хранилища
  context.result = performOperation();
  // событие end автоматически публикуется в конце блока
}

Каналы BoundedChannel

BoundedChannel состоит из двух каналов диагностики, описывающих жизненный цикл области, созданной синтаксисом using:

  • tracing:${name}:start — публикуется при выполнении оператора using (создание области)
  • tracing:${name}:end — публикуется при выходе из блока (освобождение области)

При использовании using с [boundedChannel.withScope([context])][] событие start публикуется сразу при входе в оператор, а end — автоматически при освобождении в конце блока. Все события разделяют один объект контекста, который при выполнении области можно дополнять полями вроде result.

Каналы TracingChannel

TracingChannel — набор нескольких diagnostics_channel, соответствующих этапам жизненного цикла одного трассируемого действия. Поведение разбито на пять каналов: start, end, asyncStart, asyncEnd и error. Одно трассируемое действие использует один и тот же объект события во всех точках — это удобно для корреляции (например через WeakMap).

При «завершении» задачи объект события дополняется полями result или error. Для синхронной задачи result — возвращаемое значение, error — исключение из функции. Для асинхронных функций с колбэком result — второй аргумент колбэка, а error — либо исключение, видимое в событии end, либо первый аргумент колбэка в событиях asyncStart или asyncEnd.

Чтобы граф трассировки был корректным, события следует публиковать только если подписчики уже есть до начала трассировки. Подписки, добавленные после старта, не получат события текущей трассировки — только последующих.

Имена каналов трассировки рекомендуется задавать по шаблону:

  • tracing:module.class.method:start или tracing:module.function:start
  • tracing:module.class.method:end или tracing:module.function:end
  • tracing:module.class.method:asyncStart или tracing:module.function:asyncStart
  • tracing:module.class.method:asyncEnd или tracing:module.function:asyncEnd
  • tracing:module.class.method:error или tracing:module.function:error

start(event)

  • Имя: tracing:${name}:start

Событие start — момент вызова функции. В данных события могут быть аргументы функции или любая информация, доступная в самом начале выполнения.

end(event)

  • Имя: tracing:${name}:end

Событие end — момент возврата значения из вызова функции. Для асинхронной функции это момент возврата промиса, а не внутреннего return в теле. Если трассируемая функция синхронна, поле result содержит возвращаемое значение; при ошибке может быть поле error.

Рекомендуется отдельно слушать событие error: одно трассируемое действие может породить несколько ошибок (например внутренняя асинхронная задача завершилась ошибкой до того, как синхронная часть выбросила исключение).

asyncStart(event)

  • Имя: tracing:${name}:asyncStart

Событие asyncStart — достижение колбэка или продолжения трассируемой функции. Здесь доступны аргументы колбэка и др., описывающие «результат» действия.

Для функций с колбэком первый аргумент присваивается полю error, если он не undefined и не null, второй — полю result.

Для промисов аргумент resolve попадает в result, аргумент reject — в error.

Снова рекомендуется слушать error отдельно по тем же причинам, что и для end.

asyncEnd(event)

  • Имя: tracing:${name}:asyncEnd

Событие asyncEnd — завершение колбэка асинхронной функции. Данные после asyncStart обычно не меняются, но полезно зафиксировать момент окончания колбэка.

error(event)

  • Имя: tracing:${name}:error

Событие error — любая ошибка трассируемой функции, синхронная или асинхронная. Исключение в синхронной части попадает в поле error и вызывает событие error. Ошибка из колбэка или отклонение промиса также попадают в error и вызывают событие.

Один вызов может сгенерировать несколько событий error — учитывайте это при обработке. Например, внутренняя асинхронная задача упала, затем синхронная часть выбросила исключение — будет два события error.

Встроенные каналы

Консоль

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'console.log'

Генерируется при вызове console.log(). Передаётся массив аргументов вызова console.log().

Событие: 'console.info'

Генерируется при вызове console.info(). Передаётся массив аргументов вызова console.info().

Событие: 'console.debug'

Генерируется при вызове console.debug(). Передаётся массив аргументов вызова console.debug().

Событие: 'console.warn'

Генерируется при вызове console.warn(). Передаётся массив аргументов вызова console.warn().

Событие: 'console.error'

Генерируется при вызове console.error(). Передаётся массив аргументов вызова console.error().

HTTP

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'http.client.request.created'

Генерируется, когда клиент создаёт объект запроса. В отличие от http.client.request.start, событие до отправки запроса.

Событие: 'http.client.request.start'

Генерируется, когда клиент начинает запрос.

Событие: 'http.client.request.error'

Генерируется при ошибке клиентского запроса.

Событие: 'http.client.response.finish'

Генерируется, когда клиент получил ответ.

Событие: 'http.server.request.start'

Генерируется, когда сервер получил запрос.

Событие: 'http.server.response.created'

Генерируется, когда сервер создал объект ответа. Событие до отправки ответа.

Событие: 'http.server.response.finish'

Генерируется, когда сервер отправил ответ.

HTTP/2

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'http2.client.stream.created'

Генерируется при создании потока на клиенте.

Событие: 'http2.client.stream.start'

Генерируется при старте потока на клиенте.

Событие: 'http2.client.stream.error'

Генерируется при ошибке обработки потока на клиенте.

Событие: 'http2.client.stream.finish'

Генерируется при получении потока на клиенте.

Событие: 'http2.client.stream.bodyChunkSent'

Генерируется при отправке фрагмента тела потока клиента.

Событие: 'http2.client.stream.bodySent'

Генерируется после полной отправки тела потока клиента.

Событие: 'http2.client.stream.close'

Генерируется при закрытии потока на клиенте. Код ошибки HTTP/2 при закрытии доступен в stream.rstCode.

Событие: 'http2.server.stream.created'

Генерируется при создании потока на сервере.

Событие: 'http2.server.stream.start'

Генерируется при старте потока на сервере.

Событие: 'http2.server.stream.error'

Генерируется при ошибке обработки потока на сервере.

Событие: 'http2.server.stream.finish'

Генерируется при отправке потока с сервера.

Событие: 'http2.server.stream.close'

Генерируется при закрытии потока на сервере. Код ошибки HTTP/2 при закрытии доступен в stream.rstCode.

Модули

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'module.require.start'
  • event <Object> со свойствами:
    • id — аргумент require(), имя модуля.
    • parentFilename — файл модуля, вызвавшего require(id).

Генерируется при выполнении require(). См. событие start.

Событие: 'module.require.end'
  • event <Object> со свойствами:
    • id — аргумент require(), имя модуля.
    • parentFilename — файл модуля, вызвавшего require(id).

Генерируется при возврате из require(). См. событие end.

Событие: 'module.require.error'
  • event <Object> со свойствами:
    • id — аргумент require(), имя модуля.
    • parentFilename — файл модуля, вызвавшего require(id).
  • error <Error>

Генерируется при ошибке require(). См. событие error.

Событие: 'module.import.asyncStart'
  • event <Object> со свойствами:
    • id — аргумент import(), имя модуля.
    • parentURL — URL модуля, вызвавшего import(id).

Генерируется при вызове import(). См. событие asyncStart.

Событие: 'module.import.asyncEnd'
  • event <Object> со свойствами:
    • id — аргумент import(), имя модуля.
    • parentURL — URL модуля, вызвавшего import(id).

Генерируется по завершении import(). См. событие asyncEnd.

Событие: 'module.import.error'
  • event <Object> со свойствами:
    • id — аргумент import(), имя модуля.
    • parentURL — URL модуля, вызвавшего import(id).
  • error <Error>

Генерируется при ошибке import(). См. событие error.

Сеть

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'net.client.socket'

Генерируется при создании нового клиентского TCP- или pipe-сокета.

Событие: 'net.server.socket'

Генерируется при приёме нового TCP- или pipe-подключения.

Событие: 'tracing:net.server.listen:asyncStart'

Генерируется при вызове net.Server.listen(), до настройки порта или pipe.

Событие: 'tracing:net.server.listen:asyncEnd'

Генерируется после завершения net.Server.listen() — сервер готов принимать соединения.

Событие: 'tracing:net.server.listen:error'

Генерируется при ошибке net.Server.listen().

UDP

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'udp.socket'

Генерируется при создании нового UDP-сокета.

Процесс

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'child_process'

Генерируется при создании нового дочернего процесса.

tracing:child_process.spawn:start

Генерируется при вызове child_process.spawn(), до фактического запуска процесса.

tracing:child_process.spawn:end

Генерируется после успешного завершения child_process.spawn() — процесс создан.

tracing:child_process.spawn:error

Генерируется при ошибке child_process.spawn().

Событие: 'execve'

Генерируется при вызове process.execve().

Веб-блокировки

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Эти каналы генерируются при каждом вызове locks.request(). Подробнее о механизме веб-блокировок — worker_threads.locks.

Событие: 'locks.request.start'
  • name <string> имя ресурса блокировки
  • mode <string> режим: 'exclusive' или 'shared'

Генерируется при инициации запроса блокировки, до её выдачи.

Событие: 'locks.request.grant'
  • name <string> имя ресурса блокировки
  • mode <string> режим: 'exclusive' или 'shared'

Генерируется при успешной выдаче блокировки, непосредственно перед запуском колбэка.

Событие: 'locks.request.miss'
  • name <string> имя ресурса блокировки
  • mode <string> режим: 'exclusive' или 'shared'

Генерируется, если ifAvailable равен true, блокировка сразу недоступна и колбэк вызывается с null вместо объекта Lock.

Событие: 'locks.request.end'
  • name <string> имя ресурса блокировки
  • mode <string> режим: 'exclusive' или 'shared'
  • steal <boolean> используется ли семантика steal
  • ifAvailable <boolean> используется ли семантика ifAvailable
  • error <Error> | undefined ошибка из колбэка, если была

Генерируется по завершении запроса блокировки: успех колбэка, исключение или украденная блокировка.

Поток Worker

Стабильность: 1 – Экспериментальная

Фича изменяется и не допускается флагом командной строки. Может быть изменена или удалена в последующих версиях.

Событие: 'worker_threads'

Генерируется при создании нового потока worker.

Комментарии