OpenTelemetry unterstützt das Erfassen von Traces, Messwerten und Logs. Firebase Genkit kann erweitert werden, um alle Telemetriedaten in jedes OpenTelemetry-fähige System zu exportieren. Dazu wird ein Telemetrie-Plug-in geschrieben, das die Node.js SDK.
Konfiguration
Zum Steuern des Telemetrieexports muss das PluginOptions
Ihres Plug-ins ein
telemetry
-Objekt, das dem telemetry
-Block in der Genkit-Konfiguration entspricht.
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
Dieses Objekt kann zwei separate Konfigurationen bereitstellen:
instrumentation
: Bietet OpenTelemetry-Konfiguration fürTraces
undMetrics
.logger
: stellt den zugrunde liegenden Logger bereit, der von Genkit zum Schreiben verwendet wird strukturierte Logdaten einschließlich Ein- und Ausgaben von Genkit-Abläufen.
Diese Trennung ist derzeit erforderlich, da die Logging-Funktionen für das Node.js OpenTelemetry SDK noch in der Entwicklung sind. Die Protokollierung wird separat bereitgestellt, damit ein Plug-in steuern kann, wo die Daten gespeichert werden. explizit geschrieben.
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;
Mit dem obigen Codeblock stellt das Plug-in nun Genkit eine Telemetrie zur Verfügung Konfiguration, die von Entwickelnden verwendet werden kann.
Instrumentierung
Um den Export von Traces und Messwerten zu steuern, muss Ihr Plug-in eine
instrumentation
-Eigenschaft für das telemetry
-Objekt fest, das den
TelemetryConfig
-Schnittstelle:
interface TelemetryConfig {
getConfig(): Partial<NodeSDKConfiguration>;
}
Dadurch wird ein Partial<NodeSDKConfiguration>
bereitgestellt, das vom Genkit-Framework zum Starten der NodeSDK
verwendet wird.
So hat das Plug-in die vollständige Kontrolle darüber, wie die OpenTelemetry-Integration von Genkit verwendet wird.
Die folgende Telemetriekonfiguration stellt beispielsweise einen einfachen In-Memory-Trace- und Messwertexporter bereit:
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
Um den Protokoller zu steuern, der vom Genkit-Framework zum Schreiben strukturierter Protokolldaten verwendet wird, muss das Plug-in eine logger
-Property für das telemetry
-Objekt bereitstellen, die der LoggerConfig
-Schnittstelle entspricht:
interface LoggerConfig {
getLogger(env: string): any;
}
{
debug(...args: any);
info(...args: any);
warn(...args: any);
error(...args: any);
level: string;
}
Die meisten gängigen Logging-Frameworks berücksichtigen dies. Ein solches Framework ist winston, mit dem sich Transporter konfigurieren lassen, die die Protokolldaten direkt an einen beliebigen Ort pushen können.
Um beispielsweise einen Winston-Logger bereitzustellen, der Protokolldaten in die Konsole schreibt, können Sie Ihren Plug-in-Logger mit Folgendem aktualisieren:
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}`;
}),
});
}
};
Logs und Traces verknüpfen
Häufig ist es wünschenswert, dass Ihre Protokollanweisungen mit den von Ihrem Plug-in exportierten OpenTelemetry-Spuren korreliert sind. Da die Log-Anweisungen nicht
direkt vom OpenTelemetry-Framework exportiert wird, geschieht nicht
. Glücklicherweise unterstützt OpenTelemetry Instrumentierungen, die Traces
und Span-IDs für Log-Anweisungen für gängige Logging-Frameworks wie winston
und pino. Mit dem Paket @opentelemetry/auto-instrumentations-node
können Sie diese (und andere) Instrumente automatisch konfigurieren lassen.
In einigen Fällen müssen Sie die Feldnamen und -werte für Traces und
Spans. Dazu müssen Sie eine benutzerdefinierte LogHook-Instrumentierung für die NodeSDK-Konfiguration bereitstellen, die von Ihrem TelemetryConfig
bereitgestellt wird:
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;
},
}),
]);
Im Beispiel werden alle automatischen Instrumentierungen für die OpenTelemetry-NodeSDK
aktiviert und dann eine benutzerdefinierte WinstonInstrumentation
bereitgestellt, die die Trace- und Span-IDs in benutzerdefinierte Felder der Protokollnachricht schreibt.
Das Genkit-Framework garantiert, dass das TelemetryConfig
Ihres Plug-ins
vor dem LoggerConfig
des Plug-ins initialisiert wurde.
Stellen Sie sicher, dass der zugrunde liegende Logger erst importiert wird, wenn die Protokollierungskonfiguration aktiviert ist.
initialisiert. Das obige LoggingConfig-Objekt kann beispielsweise so geändert werden:
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}`;
}),
});
},
};
Vollständiges Beispiel
Im Folgenden finden Sie ein vollständiges Beispiel für das oben erstellte Telemetrie-Plug-in. Ein praktisches Beispiel ist das @genkit-ai/google-cloud
-Plug-in.
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;
Fehlerbehebung
Wenn Sie Probleme haben, Daten an der erwarteten Stelle zu sehen, bietet OpenTelemetry ein nützliches Diagnosetool, mit dem Sie die Ursache des Problems ermitteln können.