OpenTelemetry поддерживает сбор трассировок, метрик и журналов. Firebase Genkit можно расширить для экспорта всех данных телеметрии в любую систему с поддержкой OpenTelemetry, написав плагин телеметрии, который настраивает Node.js SDK.
Конфигурация
Чтобы управлять экспортом телеметрии, PluginOptions
вашего плагина должен предоставить объект telemetry
, соответствующий блоку telemetry
в конфигурации Genkit.
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
Этот объект может предоставлять две отдельные конфигурации:
-
instrumentation
: обеспечивает конфигурацию OpenTelemetry дляTraces
иMetrics
. -
logger
: предоставляет базовый регистратор, используемый Genkit для записи структурированных данных журнала, включая входные и выходные данные потоков Genkit.
Такое разделение в настоящее время необходимо, поскольку функция ведения журналов для Node.js OpenTelemetry SDK все еще находится в стадии разработки . Ведение журнала осуществляется отдельно, чтобы плагин мог явно контролировать, куда записываются данные.
import { genkitPlugin, Plugin } from '@genkit-ai/core';
...
export interface MyPluginOptions {
// [Optional] Your plugin options
}
export const myPlugin: Plugin<[MyPluginOptions] | []> = genkitPlugin(
'myPlugin',
async (options?: MyPluginOptions) => {
return {
telemetry: {
instrumentation: {
id: 'myPlugin',
value: myTelemetryConfig,
},
logger: {
id: 'myPlugin',
value: myLogger,
},
},
};
}
);
export default myPlugin;
Благодаря приведенному выше блоку кода ваш плагин теперь предоставит Genkit конфигурацию телеметрии, которую смогут использовать разработчики.
Инструментарий
Чтобы управлять экспортом трассировок и метрик, ваш плагин должен предоставить свойство instrumentation
объекта telemetry
, соответствующее интерфейсу TelemetryConfig
:
interface TelemetryConfig {
getConfig(): Partial<NodeSDKConfiguration>;
}
Это предоставляет Partial<NodeSDKConfiguration>
, который будет использоваться платформой Genkit для запуска NodeSDK
. Это дает плагину полный контроль над тем, как интеграция OpenTelemetry используется Genkit.
Например, следующая конфигурация телеметрии обеспечивает простой экспортер трассировки и метрик в памяти:
import { AggregationTemporality, InMemoryMetricExporter, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { AlwaysOnSampler, BatchSpanProcessor, InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';
import { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
import { Resource } from '@opentelemetry/resources';
import { TelemetryConfig } from '@genkit-ai/core';
...
const myTelemetryConfig: TelemetryConfig = {
getConfig(): Partial<NodeSDKConfiguration> {
return {
resource: new Resource({}),
spanProcessor: new BatchSpanProcessor(new InMemorySpanExporter()),
sampler: new AlwaysOnSampler(),
instrumentations: myPluginInstrumentations,
metricReader: new PeriodicExportingMetricReader({
exporter: new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE),
}),
};
},
};
Регистратор
Чтобы управлять средством ведения журнала, используемым платформой Genkit для записи структурированных данных журнала, плагин должен предоставить свойство logger
в объекте telemetry
, соответствующее интерфейсу LoggerConfig
:
interface LoggerConfig {
getLogger(env: string): any;
}
{
debug(...args: any);
info(...args: any);
warn(...args: any);
error(...args: any);
level: string;
}
Этому соответствуют большинство популярных платформ ведения журналов. Одной из таких платформ является winston , которая позволяет настраивать транспортеры, которые могут напрямую передавать данные журнала в выбранное вами место.
Например, чтобы предоставить средство ведения журнала Winston, которое записывает данные журнала в консоль, вы можете обновить свой подключаемый модуль ведения журнала, чтобы использовать следующее:
import * as winston from 'winston';
...
const myLogger: LoggerConfig = {
getLogger(env: string) {
return winston.createLogger({
transports: [new winston.transports.Console()],
format: winston.format.printf((info): string => {
return `[${info.level}] ${info.message}`;
}),
});
}
};
Связывание журналов и трассировок
Часто желательно, чтобы ваши операторы журнала коррелировали с трассировками OpenTelemetry, экспортируемыми вашим плагином. Поскольку операторы журнала не экспортируются платформой OpenTelemetry напрямую, это не происходит сразу после установки. К счастью, OpenTelemetry поддерживает инструменты, которые копируют идентификаторы трассировки и диапазона в операторы журнала для популярных платформ ведения журналов, таких как Winston и Pino . Используя пакет @opentelemetry/auto-instrumentations-node
, вы можете настроить эти (и другие) инструменты автоматически, но в некоторых случаях вам может потребоваться контролировать имена и значения полей для трассировок и диапазонов. Для этого вам необходимо предоставить специальный инструментарий LogHook для конфигурации NodeSDK, предоставленной вашим TelemetryConfig
:
import { Instrumentation } from '@opentelemetry/instrumentation';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { WinstonInstrumentation } from '@opentelemetry/instrumentation-winston';
import { Span } from '@opentelemetry/api';
const myPluginInstrumentations: Instrumentation[] =
getNodeAutoInstrumentations().concat([
new WinstonInstrumentation({
logHook: (span: Span, record: any) => {
record['my-trace-id'] = span.spanContext().traceId;
record['my-span-id'] = span.spanContext().spanId;
record['is-trace-sampled'] = span.spanContext().traceFlags;
},
}),
]);
В примере включены все автоматические инструменты для OpenTelemetry NodeSDK
, а затем предоставляется пользовательский WinstonInstrumentation
, который записывает идентификаторы трассировки и диапазона в настраиваемые поля сообщения журнала.
Платформа Genkit гарантирует, что TelemetryConfig
вашего плагина будет инициализирован до LoggerConfig
вашего плагина, но вы должны позаботиться о том, чтобы базовый регистратор не был импортирован до тех пор, пока не будет инициализирован LoggerConfig. Например, приведенный выше loggingConfig можно изменить следующим образом:
const myLogger: LoggerConfig = {
async getLogger(env: string) {
// Do not import winston before calling getLogger so that the NodeSDK
// instrumentations can be registered first.
const winston = await import('winston');
return winston.createLogger({
transports: [new winston.transports.Console()],
format: winston.format.printf((info): string => {
return `[${info.level}] ${info.message}`;
}),
});
},
};
Полный пример
Ниже приведен полный пример плагина телеметрии, созданного выше. В качестве реального примера взгляните на плагин @genkit-ai/google-cloud
.
import {
genkitPlugin,
LoggerConfig,
Plugin,
TelemetryConfig,
} from '@genkit-ai/core';
import { Span } from '@opentelemetry/api';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { Instrumentation } from '@opentelemetry/instrumentation';
import { WinstonInstrumentation } from '@opentelemetry/instrumentation-winston';
import { Resource } from '@opentelemetry/resources';
import {
AggregationTemporality,
InMemoryMetricExporter,
PeriodicExportingMetricReader,
} from '@opentelemetry/sdk-metrics';
import { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
import {
AlwaysOnSampler,
BatchSpanProcessor,
InMemorySpanExporter,
} from '@opentelemetry/sdk-trace-base';
export interface MyPluginOptions {
// [Optional] Your plugin options
}
const myPluginInstrumentations: Instrumentation[] =
getNodeAutoInstrumentations().concat([
new WinstonInstrumentation({
logHook: (span: Span, record: any) => {
record['my-trace-id'] = span.spanContext().traceId;
record['my-span-id'] = span.spanContext().spanId;
record['is-trace-sampled'] = span.spanContext().traceFlags;
},
}),
]);
const myTelemetryConfig: TelemetryConfig = {
getConfig(): Partial<NodeSDKConfiguration> {
return {
resource: new Resource({}),
spanProcessor: new BatchSpanProcessor(new InMemorySpanExporter()),
sampler: new AlwaysOnSampler(),
instrumentations: myPluginInstrumentations,
metricReader: new PeriodicExportingMetricReader({
exporter: new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE),
}),
};
},
};
const myLogger: LoggerConfig = {
async getLogger(env: string) {
// Do not import winston before calling getLogger so that the NodeSDK
// instrumentations can be registered first.
const winston = await import('winston');
return winston.createLogger({
transports: [new winston.transports.Console()],
format: winston.format.printf((info): string => {
return `[${info.level}] ${info.message}`;
}),
});
},
};
export const myPlugin: Plugin<[MyPluginOptions] | []> = genkitPlugin(
'myPlugin',
async (options?: MyPluginOptions) => {
return {
telemetry: {
instrumentation: {
id: 'myPlugin',
value: myTelemetryConfig,
},
logger: {
id: 'myPlugin',
value: myLogger,
},
},
};
}
);
export default myPlugin;
Поиск неисправностей
Если у вас возникли проблемы с отображением данных там, где вы ожидаете, OpenTelemetry предоставляет полезный инструмент диагностики , который помогает найти источник проблемы.