編寫 Genkit 遙測外掛程式

OpenTelemetry 支援收集追蹤記錄、指標和記錄檔。只要編寫遙測外掛程式來設定 Node.js 將機器學習工作流程自動化

設定

如要控制遙測匯出作業,外掛程式的 PluginOptions 必須提供 符合 Genkit 設定 telemetry 區塊的 telemetry 物件。

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

這個物件可提供兩種不同的設定:

  • instrumentation:提供 TracesMetrics
  • 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 提供遙測資料 可由開發人員運用。

檢測

如要控管追蹤記錄和指標的匯出作業,您的外掛程式必須提供 telemetry 物件上的 instrumentation 屬性 (符合 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),
      }),
    };
  },
};

Logger

如要控管 Genkit 架構用來寫入結構化記錄檔資料的記錄器, 外掛程式必須在 telemetry 物件上提供與logger 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 支援可複製追蹤記錄的檢測作業 ,並將 ID 新增至 winston 這類熱門記錄架構的記錄陳述式中 和 pino。使用 @opentelemetry/auto-instrumentations-node 套件 您可以由系統自動設定這些 (和其他) 檢測 在某些情況下,您可能需要控制追蹤記錄和 時距。為此,您需要提供自訂 LogHook 檢測, TelemetryConfig 提供的 NodeSDK 設定:

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 來寫入追蹤記錄 跨越 ID 對應至記錄訊息的自訂欄位。

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 可提供實用的 診斷工具 有助於找出問題來源