@genkit-ai/core
Version:
Genkit AI framework core libraries.
1 lines • 13.3 kB
Source Map (JSON)
{"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 {\n LogRecordExporter,\n ReadableLogRecord,\n} from '@opentelemetry/sdk-logs';\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport { logger } from '../logging.mjs';\nimport { deleteUndefinedProps } from '../utils.mjs';\nimport type { SpanData, TraceData } from './types.mjs';\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\n/**\n * Exports collected OpenTelemetetry logs to the telemetry server.\n */\nexport class LogServerExporter implements LogRecordExporter {\n export(\n logs: ReadableLogRecord[],\n resultCallback: (result: ExportResult) => void\n ): void {\n this._sendLogs(logs, resultCallback);\n }\n\n shutdown(): Promise<void> {\n return this.forceFlush();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n\n private async _sendLogs(\n logs: ReadableLogRecord[],\n done?: (result: ExportResult) => void\n ): Promise<void> {\n if (!telemetryServerUrl) {\n if (done) done({ code: ExportResultCode.SUCCESS });\n return;\n }\n\n try {\n const scopeLogsMap = new Map<string, any>();\n for (const log of logs) {\n const scopeName = log.instrumentationScope.name || 'unknown';\n if (!scopeLogsMap.has(scopeName)) {\n scopeLogsMap.set(scopeName, {\n scope: {\n name: scopeName,\n version: log.instrumentationScope.version || '',\n },\n logRecords: [],\n });\n }\n\n // TODO: Handle more complex types (arrays, maps, etc.).\n // See https://opentelemetry.io/docs/specs/otel/common/#anyvalue.\n const attributes: any[] = [];\n for (const [k, v] of Object.entries(log.attributes)) {\n if (typeof v === 'string')\n attributes.push({ key: k, value: { stringValue: v } });\n else if (typeof v === 'number')\n attributes.push({ key: k, value: { intValue: v } });\n else if (typeof v === 'boolean')\n attributes.push({ key: k, value: { boolValue: v } });\n }\n\n let bodyValue;\n if (typeof log.body === 'string') bodyValue = { stringValue: log.body };\n else if (typeof log.body === 'number')\n bodyValue = { intValue: log.body };\n else if (typeof log.body === 'boolean')\n bodyValue = { boolValue: log.body };\n else bodyValue = { stringValue: JSON.stringify(log.body) };\n\n scopeLogsMap.get(scopeName).logRecords.push({\n timeUnixNano: (\n hrTimeToMilliseconds(log.hrTime) * 1_000_000\n ).toString(),\n severityNumber: log.severityNumber,\n severityText: log.severityText,\n body: bodyValue,\n attributes,\n traceId: log.spanContext?.traceId,\n spanId: log.spanContext?.spanId,\n });\n }\n\n const payload = {\n resourceLogs: [\n {\n resource: { attributes: [], droppedAttributesCount: 0 },\n scopeLogs: Array.from(scopeLogsMap.values()),\n },\n ],\n };\n\n await context.with(suppressTracing(context.active()), () =>\n fetch(`${telemetryServerUrl}/api/otlp`, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n })\n );\n if (done) done({ code: ExportResultCode.SUCCESS });\n } catch (e) {\n logger.error('Failed to export logs', e);\n if (done) done({ code: ExportResultCode.FAILED });\n }\n }\n}\n"],"mappings":"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","names":[]}