OpenTelemetry รองรับการรวบรวมการติดตาม เมตริก และบันทึก คุณสามารถขยาย Firebase Genkit เพื่อส่งออกข้อมูลการวัดและส่งข้อมูลทางไกลทั้งหมดไปยังระบบที่ใช้ OpenTelemetry ได้ โดยเขียนปลั๊กอินการวัดและส่งข้อมูลทางไกลที่กำหนดค่า SDK ของ Node.js
การกำหนดค่า
หากต้องการควบคุมการส่งออกการวัดและส่งข้อมูลทางไกล PluginOptions
ของปลั๊กอินต้องมีออบเจ็กต์ telemetry
ที่สอดคล้องกับบล็อก telemetry
ในการกำหนดค่าของ Genkit
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
ออบเจ็กต์นี้มีการกำหนดค่าแยกกันได้ 2 รายการ ดังนี้
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 Logger ที่เขียนข้อมูลบันทึกไปยังคอนโซล คุณสามารถอัปเดตตัวบันทึกปลั๊กอินให้ใช้สิ่งต่อไปนี้ได้
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 รองรับการใช้เครื่องมือที่จะคัดลอกรหัสการติดตามและ Span ไปยังคำสั่งบันทึกสำหรับเฟรมเวิร์กการบันทึกยอดนิยม เช่น 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
ที่กำหนดเองซึ่งเขียนรหัสการติดตามและ Span ไปยังช่องที่กำหนดเองในข้อความบันทึก
เฟรมเวิร์ก 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 มีเครื่องมือวินิจฉัยที่มีประโยชน์ซึ่งจะช่วยค้นหาแหล่งที่มาของปัญหา