UNPKG

@genkit-ai/core

Version:

Genkit AI framework core libraries.

1 lines 8.07 kB
{"version":3,"sources":["../../src/tracing/exporter.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { context, SpanKind, type HrTime } from '@opentelemetry/api';\nimport {\n ExportResultCode,\n hrTimeToMilliseconds,\n suppressTracing,\n type ExportResult,\n} from '@opentelemetry/core';\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { logger } from '../logging.js';\nimport { deleteUndefinedProps } from '../utils.js';\nimport type { SpanData, TraceData } from './types.js';\n\nexport let telemetryServerUrl: string | undefined;\n\n/**\n * @hidden\n */\nexport function setTelemetryServerUrl(url: string) {\n telemetryServerUrl = url;\n}\n\n/**\n * Exports collected OpenTelemetetry spans to the telemetry server.\n */\nexport class TraceServerExporter implements SpanExporter {\n /**\n * Export spans.\n * @param spans\n * @param resultCallback\n */\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void\n ): void {\n this._sendSpans(spans, resultCallback);\n }\n\n /**\n * Shutdown the exporter.\n */\n shutdown(): Promise<void> {\n this._sendSpans([]);\n return this.forceFlush();\n }\n\n /**\n * Converts span info into trace store format.\n * @param span\n */\n private _exportInfo(span: ReadableSpan): SpanData {\n const spanData: Partial<SpanData> = {\n spanId: span.spanContext().spanId,\n traceId: span.spanContext().traceId,\n startTime: transformTime(span.startTime),\n endTime: transformTime(span.endTime),\n attributes: { ...span.attributes },\n displayName: span.name,\n links: span.links,\n spanKind: SpanKind[span.kind],\n parentSpanId: span.parentSpanId,\n sameProcessAsParentSpan: { value: !span.spanContext().isRemote },\n status: span.status,\n timeEvents: {\n timeEvent: span.events.map((e) => ({\n time: transformTime(e.time),\n annotation: {\n attributes: e.attributes ?? {},\n description: e.name,\n },\n })),\n },\n };\n if (span.instrumentationLibrary !== undefined) {\n spanData.instrumentationLibrary = {\n name: span.instrumentationLibrary.name,\n };\n if (span.instrumentationLibrary.schemaUrl !== undefined) {\n spanData.instrumentationLibrary.schemaUrl =\n span.instrumentationLibrary.schemaUrl;\n }\n if (span.instrumentationLibrary.version !== undefined) {\n spanData.instrumentationLibrary.version =\n span.instrumentationLibrary.version;\n }\n }\n deleteUndefinedProps(spanData);\n return spanData as SpanData;\n }\n\n /**\n * Exports any pending spans in exporter\n */\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n\n private async _sendSpans(\n spans: ReadableSpan[],\n done?: (result: ExportResult) => void\n ): Promise<void> {\n const traces = {} as Record<string, ReadableSpan[]>;\n for (const span of spans) {\n if (!traces[span.spanContext().traceId]) {\n traces[span.spanContext().traceId] = [];\n }\n traces[span.spanContext().traceId].push(span);\n }\n let error = false;\n for (const traceId of Object.keys(traces)) {\n try {\n await this.save(traceId, traces[traceId]);\n } catch (e) {\n error = true;\n logger.error(`Failed to save trace ${traceId}`, e);\n }\n if (done) {\n return done({\n code: error ? ExportResultCode.FAILED : ExportResultCode.SUCCESS,\n });\n }\n }\n }\n\n private async save(traceId, spans: ReadableSpan[]): Promise<void> {\n if (!telemetryServerUrl) {\n logger.debug(\n `Telemetry server is not configured, trace ${traceId} not saved!`\n );\n return;\n }\n // TODO: add interface for Firestore doc\n const data = {\n traceId,\n spans: {},\n } as TraceData;\n for (const span of spans) {\n const convertedSpan = this._exportInfo(span);\n data.spans[convertedSpan.spanId] = convertedSpan;\n if (!convertedSpan.parentSpanId) {\n data.displayName = convertedSpan.displayName;\n data.startTime = convertedSpan.startTime;\n data.endTime = convertedSpan.endTime;\n }\n }\n // Suppress tracing to prevent infinite loops when auto-instrumentation\n // (e.g., undici) is enabled. Without this, the fetch call would be traced,\n // creating new spans that trigger more exports, causing stack overflow.\n await context.with(suppressTracing(context.active()), () =>\n fetch(`${telemetryServerUrl}/api/traces`, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data),\n })\n );\n }\n}\n\n// Converts an HrTime to milliseconds.\nfunction transformTime(time: HrTime) {\n return hrTimeToMilliseconds(time);\n}\n"],"mappings":"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","names":[]}