OpenTelemetry permet de collecter des traces, des métriques et des journaux. Firebase Genkit peut être étendu pour exporter toutes les données de télémétrie vers n'importe quel système compatible avec OpenTelemetry en écrivant un plug-in de télémétrie qui configure le Node.js SDK.
Configuration
Pour contrôler l'exportation de télémétrie, le fichier PluginOptions
de votre plug-in doit fournir un
Objet telemetry
conforme au bloc telemetry
de la configuration de Genkit.
export interface InitializedPlugin {
...
telemetry?: {
instrumentation?: Provider<TelemetryConfig>;
logger?: Provider<LoggerConfig>;
};
}
Cet objet peut fournir deux configurations distinctes:
instrumentation
: fournit la configuration OpenTelemetry pourTraces
etMetrics
logger
: fournit l'enregistreur sous-jacent utilisé par Genkit pour écrire de journaux structurées, y compris les entrées et les sorties des flux Genkit.
Cette séparation est actuellement nécessaire, car la fonctionnalité de journalisation pour Node.js Le SDK OpenTelemetry est encore en cours de développement. Logging est fourni séparément afin qu'un plug-in puisse contrôler l'emplacement des données n'a pas été rédigée explicitement.
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;
Avec le bloc de code ci-dessus, votre plug-in fournira désormais à Genkit une configuration de télémétrie pouvant être utilisée par les développeurs.
Instrumentation
Pour contrôler l'exportation des traces et des métriques, votre plug-in doit fournir un
Propriété instrumentation
de l'objet telemetry
conforme à la
Interface TelemetryConfig
:
interface TelemetryConfig {
getConfig(): Partial<NodeSDKConfiguration>;
}
Cela fournit un Partial<NodeSDKConfiguration>
qui sera utilisé par le framework Genkit pour démarrer NodeSDK
.
Le plug-in peut ainsi contrôler totalement l'utilisation de l'intégration d'OpenTelemetry.
par Genkit.
Par exemple, la configuration de télémétrie suivante fournit un exportateur de trace et de métriques en mémoire simple :
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
Pour contrôler le journal utilisé par le framework Genkit pour écrire des données de journal structurées, le plug-in doit fournir une propriété logger
sur l'objet telemetry
conforme à l'interface LoggerConfig
:
interface LoggerConfig {
getLogger(env: string): any;
}
{
debug(...args: any);
info(...args: any);
warn(...args: any);
error(...args: any);
level: string;
}
La plupart des frameworks de journalisation les plus populaires sont conformes à cette règle. L'un de ces cadres est winston, qui permet de configurer qui peuvent transmettre directement les données du journal à l'emplacement de votre choix.
Par exemple, pour fournir un enregistreur Winston qui écrit les données de journal sur la console, vous pouvez mettre à jour votre enregistreur de plug-in pour utiliser ce qui suit:
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}`;
}),
});
}
};
Associer des journaux et des traces
Il est souvent souhaitable d'établir une corrélation entre vos instructions de journalisation
Traces OpenTelemetry exportées par votre plug-in. Étant donné que les instructions de journalisation ne sont pas directement exportées par le framework OpenTelemetry, cela ne se produit pas immédiatement. Heureusement, OpenTelemetry est compatible avec les instrumentations qui copient les ID de trace et de segment sur les instructions de journalisation pour les frameworks de journalisation populaires tels que winston et pino. En utilisant le package @opentelemetry/auto-instrumentations-node
,
vous pouvez configurer automatiquement
ces instrumentations (et d'autres), mais dans
Dans certains cas, vous devrez peut-être contrôler les noms et les valeurs des champs pour les traces et
segments. Pour ce faire, vous devez fournir une instrumentation LogHook personnalisée
la configuration NodeSDK fournie par votre 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'exemple active toutes les instrumentations automatiques pour le NodeSDK
OpenTelemetry,
puis fournit un WinstonInstrumentation
personnalisé qui écrit la trace et
d'appliquer des ID à des champs personnalisés dans le message de journal.
Le framework Genkit garantit que le TelemetryConfig
de votre plug-in sera initialisé avant le LoggerConfig
de votre plug-in, mais vous devez vous assurer que le journaliseur sous-jacent n'est pas importé tant que le LoggerConfig n'est pas initialisé. Par exemple, l'objet loggingConfig ci-dessus peut être modifié comme suit:
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}`;
}),
});
},
};
Exemple complet
Voici un exemple complet du plug-in de télémétrie créé ci-dessus. Pour un exemple concret, consultez le 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;
Dépannage
Si vous ne parvenez pas à faire apparaître les données là où vous le souhaitez, OpenTelemetry propose une fonctionnalité Outil de diagnostic qui permet de localiser la source du problème.