Genkit 원격 분석 플러그인 작성

OpenTelemetry는 trace, 측정항목, 로그 수집을 지원합니다. Firebase Genkit를 확장하여 모든 원격 분석 데이터를 모든 OpenTelemetry 지원 시스템으로 내보낼 수 있도록 하려면 Node.js 사용할 수 있습니다.

구성

원격 분석 내보내기를 제어하려면 플러그인의 PluginOptions에서 다음을 제공해야 합니다. Genkit 구성의 telemetry 블록을 준수하는 telemetry 객체입니다.

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

이 객체는 별도의 두 구성을 제공할 수 있습니다.

  • instrumentation: Traces 및 OpenTelemetry 구성을 제공합니다. Metrics입니다.
  • 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에 텔레메트리를 제공합니다. 합쳐질 수 있습니다.

계측

trace 및 측정항목 내보내기를 제어하려면 플러그인이 다음을 준수하는 telemetry 객체의 instrumentation 속성 TelemetryConfig 인터페이스:

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

이것은 Partial<NodeSDKConfiguration>을 제공하여 Genkit 프레임워크를 사용하여 NodeSDK 이를 통해 플러그인에서 OpenTelemetry 통합이 사용되는 방식을 완벽하게 제어할 수 있습니다. 제작: Genkit

예를 들어 다음 원격 분석 구성은 간단한 메모리 내 trace 및 측정항목 내보내기 도구를 제공합니다.

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 로거를 제공하려면 다음을 사용하도록 플러그인 로거를 업데이트할 수 있습니다.

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

로그와 Trace 연결

보통 로그 구문을 플러그인에서 내보낸 OpenTelemetry 트레이스입니다. 로그 구문이 OpenTelemetry 프레임워크에서 직접 내보낸 다음 체크박스를 선택합니다. 다행히 OpenTelemetry는 소스 코드를 복사하여 winston과 같이 널리 사용되는 로깅 프레임워크의 로그 구문에 스팬 ID를 추가할 수 있습니다. 그리고 pino입니다. @opentelemetry/auto-instrumentations-node 패키지를 사용하면 이러한 (및 기타) 계측을 자동으로 구성 할 수 있지만 경우에 따라 trace 및 trace에 대한 필드 이름과 값을 제어해야 할 수 있습니다. 있습니다. 이렇게 하려면 커스텀 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는 진단 도구 문제의 원인을 찾는 데 도움이 됩니다