OpenTelemetry 支援收集追蹤記錄、指標和記錄檔,您可以編寫設定 Node.js SDK 的遙測外掛程式,讓 Firebase Genkit 將所有遙測資料匯出至任何支援 OpenTelemetry 的系統。
設定
如要控管遙測資料匯出功能,外掛程式的 PluginOptions
必須提供 telemetry
物件,且該物件與 Genkit 設定中的 telemetry
區塊相符。
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
這個物件可提供兩種不同的設定:
instrumentation
:為Traces
和Metrics
提供 OpenTelemetry 設定。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 提供遙測技術,以供開發人員使用。
檢測
如要控制追蹤記錄和指標的匯出作業,外掛程式必須在 telemetry
物件上提供 instrumentation
屬性,該屬性必須符合 TelemetryConfig
介面:
interface TelemetryConfig {
getConfig(): Partial<NodeSDKConfiguration>;
}
這會提供一個 Partial<NodeSDKConfiguration>
,Genkit 架構會用來啟動 NodeSDK
。如此一來,外掛程式就能完全控制 Genkit 如何使用 OpenTelemetry 整合功能。
舉例來說,以下遙測設定提供了簡易的記憶體內追蹤記錄和指標匯出工具:
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),
}),
};
},
};
Logger
如要控管 Genkit 架構用來寫入結構化記錄檔資料的記錄器,外掛程式必須在 telemetry
物件上提供符合 LoggerConfig
介面的 logger
屬性:
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 支援將追蹤記錄和時距 ID 複製到 winston 和 pino 等熱門記錄架構的記錄陳述式。使用 @opentelemetry/auto-instrumentations-node
套件即可自動設定這些 (和其他) 檢測作業,但在某些情況下,您可能需要控制追蹤記錄和時距的欄位名稱和值。為此,您需要為 TelemetryConfig
提供的 NodeSDK 設定提供自訂 LogHook 檢測:
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
,將追蹤記錄和時距 ID 寫入記錄訊息中的自訂欄位。
Genkit 架構將保證外掛程式的 TelemetryConfig
會在外掛程式的 LoggerConfig
之前初始化,但您必須確保在 LoggerConfig 初始化之前,不會匯入基礎記錄器。舉例來說,上述 logConfig 可以修改,如下所示:
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 提供實用的診斷工具,有助於找出問題來源。