OpenTelemetry supporta la raccolta di tracce, metriche e log. Firebase Genkit può essere esteso per esportare tutti i dati di telemetria in qualsiasi sistema che supporta OpenTelemetry scrivendo un plug-in di telemetria che configuri Node.js l'SDK.
Configurazione
Per controllare l'esportazione della telemetria, il parametro PluginOptions
del plug-in deve fornire un
Oggetto telemetry
conforme al blocco telemetry
nella configurazione di Genkit.
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
Questo oggetto può fornire due configurazioni distinte:
instrumentation
: fornisce la configurazione di OpenTelemetry perTraces
eMetrics
.logger
: fornisce il logger di base utilizzato da Genkit per scrivere dati dei log strutturati, inclusi input e output dei flussi Genkit.
Questa separazione è attualmente necessaria perché la funzionalità di logging per Node.js L'SDK OpenTelemetry è ancora in fase di sviluppo. Il logging è fornito separatamente in modo che un plug-in possa controllare la posizione dei dati scritte in modo esplicito.
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;
Con il blocco di codice riportato sopra, il plug-in fornirà a Genkit una configurazione della telemetria che può essere utilizzata dagli sviluppatori.
Strumentazione
Per controllare l'esportazione di tracce e metriche, il plug-in deve fornire una proprietà instrumentation
nell'oggetto telemetry
conforme all'interfaccia TelemetryConfig
:
interface TelemetryConfig {
getConfig(): Partial<NodeSDKConfiguration>;
}
In questo modo viene fornito un valore Partial<NodeSDKConfiguration>
che verrà utilizzato
il framework Genkit per avviare
NodeSDK
In questo modo, il plug-in ha il controllo completo sull'utilizzo dell'integrazione di OpenTelemetry da parte di Genkit.
Ad esempio, la seguente configurazione di telemetria fornisce una semplice traccia in memoria ed esportatore di metriche:
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
Per controllare il logger utilizzato dal framework Genkit per scrivere dati di log strutturati,
il plug-in deve fornire una proprietà logger
sull'oggetto telemetry
conforme alle
Interfaccia LoggerConfig
:
interface LoggerConfig {
getLogger(env: string): any;
}
{
debug(...args: any);
info(...args: any);
warn(...args: any);
error(...args: any);
level: string;
}
La maggior parte dei framework di logging è conforme a questo standard. Uno di questi framework è winston, che consente di configurare i trasportatori che possono inviare direttamente i dati dei log in una posizione a tua scelta.
Ad esempio, per fornire un logger Winston che scrive i dati di log nella console, puoi aggiornare il tuo logger dei plug-in in modo da utilizzare quanto segue:
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}`;
}),
});
}
};
Collegamento di log e tracce
Spesso è opportuno correlare le istruzioni di log con le tracce OpenTelemetry esportate dal plug-in. Poiché le istruzioni di log non sono
direttamente dal framework OpenTelemetry, ciò non avviene
. Fortunatamente, OpenTelemetry supporta le misurazioni che copiano gli ID traccia e span nelle istruzioni di log per i framework di logging più diffusi come winston e pino. Utilizzando il pacchetto @opentelemetry/auto-instrumentations-node
,
puoi configurare automaticamente queste e altre misurazioni, ma in alcuni casi potresti dover controllare i nomi e i valori dei campi per le tracce e gli span. Per farlo, dovrai fornire un'instrumentazione LogHook personalizzata alla configurazione NodeSDK fornita dal tuo 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;
},
}),
]);
L'esempio attiva tutte le ispezioni automatiche per il NodeSDK
OpenTelemetry,
quindi fornisce un WinstonInstrumentation
personalizzato che scrive gli ID traccia e
WinstonInstrumentation
nei campi personalizzati del messaggio di log.
Il framework Genkit garantirà che il valore TelemetryConfig
del plug-in venga
inizializzato prima del plug-in LoggerConfig
, ma devi prestare attenzione
assicura che il logger sottostante non venga importato finché il valore
inizializzato. Ad esempio, il loggingConfig sopra può essere modificato come segue:
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}`;
}),
});
},
};
Esempio completo
Di seguito è riportato un esempio completo del plug-in di telemetria creato sopra. Per
un esempio reale, dai un'occhiata al plug-in @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;
Risoluzione dei problemi
Se hai difficoltà a visualizzare i dati dove ti aspetti, OpenTelemetry fornisce un utile strumento di diagnostica che aiuta a individuare l'origine del problema.