Menulis Plugin Telemetry Genkit

OpenTelemetry mendukung pengumpulan trace, metrik, dan log. Firebase Genkit dapat diperluas untuk mengekspor semua data telemetri ke sistem apa pun yang mendukung OpenTelemetry dengan menulis plugin telemetri yang mengonfigurasi Node.js SDK.

Konfigurasi

Untuk mengontrol ekspor telemetri, PluginOptions plugin harus menyediakan Objek telemetry yang sesuai dengan blok telemetry dalam konfigurasi Genkit.

export interface InitializedPlugin {
  ...
  telemetry?: {
    instrumentation?: Provider<TelemetryConfig>;
    logger?: Provider<LoggerConfig>;
  };
}

Objek ini dapat menyediakan dua konfigurasi terpisah:

  • instrumentation: menyediakan konfigurasi OpenTelemetry untuk Traces dan Metrics.
  • logger: menyediakan pencatat log dasar yang digunakan oleh Genkit untuk menulis data log terstruktur, termasuk input dan output alur Genkit.

Pemisahan ini saat ini diperlukan karena fungsi logging untuk Node.js OpenTelemetry SDK masih dalam pengembangan. Logging disediakan secara terpisah sehingga plugin dapat mengontrol lokasi data secara eksplisit.

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;

Dengan blok kode di atas, plugin Anda sekarang akan menyediakan telemetri ke Genkit konfigurasi yang dapat digunakan oleh pengembang.

Instrumentasi

Untuk mengontrol ekspor trace dan metrik, plugin Anda harus menyediakan Properti instrumentation pada objek telemetry yang sesuai dengan Antarmuka TelemetryConfig:

interface TelemetryConfig {
  getConfig(): Partial<NodeSDKConfiguration>;
}

Ini menyediakan Partial<NodeSDKConfiguration> yang akan digunakan oleh framework Genkit untuk memulai NodeSDK. Ini memberikan plugin kontrol penuh tentang bagaimana integrasi OpenTelemetry digunakan oleh Genkit.

Misalnya, konfigurasi telemetri berikut menyediakan pengekspor metrik dan trace dalam memori yang sederhana:

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

Untuk mengontrol logger yang digunakan framework Genkit untuk menulis data log terstruktur, plugin harus menyediakan properti logger pada objek telemetry yang sesuai dengan Antarmuka LoggerConfig:

interface LoggerConfig {
  getLogger(env: string): any;
}
{
  debug(...args: any);
  info(...args: any);
  warn(...args: any);
  error(...args: any);
  level: string;
}

Sebagian besar framework logging populer sesuai dengan fungsi ini. Salah satu kerangka kerja tersebut adalah winston, yang memungkinkan konfigurasi yang dapat langsung mendorong data log ke lokasi pilihan Anda.

Misalnya, untuk menyediakan {i>winston logger<i} yang menulis data log ke konsol, Anda dapat memperbarui logger plugin untuk menggunakan hal berikut:

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}`;
      }),
    });
  }
};

Menautkan log dan Trace

Sering kali lebih baik untuk memiliki laporan log Anda berkorelasi dengan Rekaman aktivitas OpenTelemetry yang diekspor oleh plugin Anda. Karena laporan log tidak diekspor oleh kerangka kerja OpenTelemetry secara langsung. Hal ini tidak terjadi di luar . Untungnya, OpenTelemetry mendukung instrumentasi yang akan menyalin jejak dan menempelkan ID ke laporan log untuk framework logging populer seperti winston dan pino. Dengan menggunakan paket @opentelemetry/auto-instrumentations-node, Anda dapat mengatur instrumentasi ini (dan yang lainnya) secara otomatis dikonfigurasi, tetapi dalam dalam beberapa kasus, Anda mungkin perlu mengontrol nama dan nilai {i>field <i}untuk jejak dan span. Untuk melakukan ini, Anda harus menyediakan instrumentasi LogHook kustom untuk konfigurasi NodeSDK yang disediakan oleh TelemetryConfig Anda:

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;
      },
    }),
  ]);

Contoh ini mengaktifkan semua instrumentasi otomatis untuk NodeSDK OpenTelemetry, lalu menyediakan WinstonInstrumentation kustom yang menulis rekaman aktivitas dan yang menjangkau ID ke kolom khusus pada pesan log.

Framework Genkit akan menjamin bahwa TelemetryConfig plugin Anda akan menginisialisasi sebelum LoggerConfig plugin, tetapi Anda harus berhati-hati memastikan bahwa pencatat log yang mendasarinya tidak diimpor sampai LoggerConfig diinisialisasi. Misalnya, loggingConfig di atas dapat diubah sebagai berikut:

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}`;
      }),
    });
  },
};

Contoh Lengkap

Berikut adalah contoh lengkap plugin telemetri yang dibuat di atas. Sebagai contoh nyata, lihat plugin @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;

Pemecahan masalah

Jika Anda mengalami kesulitan mendapatkan data untuk muncul di tempat yang Anda harapkan, OpenTelemetry menyediakan Alat diagnostik yang membantu menemukan sumber masalahnya.