{"version":3,"sources":["../../src/tracing/instrumentation.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 {\n  ROOT_CONTEXT,\n  SpanOptions,\n  SpanStatusCode,\n  trace,\n  type Span as ApiSpan,\n  type Link,\n} from '@opentelemetry/api';\nimport { performance } from 'node:perf_hooks';\nimport { getAsyncContext } from '../async-context.mjs';\nimport type { HasRegistry, Registry } from '../registry.mjs';\nimport { ensureBasicTelemetryInstrumentation } from '../tracing.mjs';\nimport type { PathMetadata, SpanMetadata, TraceMetadata } from './types.mjs';\n\nexport const spanMetadataAlsKey = 'core.tracing.instrumentation.span';\n\nexport const ATTR_PREFIX = 'genkit';\n/** @hidden */\nexport const SPAN_TYPE_ATTR = ATTR_PREFIX + ':type';\nconst TRACER_NAME = 'genkit-tracer';\nconst TRACER_VERSION = 'v1';\n\ntype SpanContext = {\n  metadata: SpanMetadata;\n  labels?: Record<string, string>;\n  spanId?: string;\n} & TraceMetadata;\n\ninterface RunInNewSpanOpts {\n  metadata: SpanMetadata;\n  labels?: Record<string, string>;\n  links?: Link[];\n}\n\ntype RunInNewSpanFn<T> = (\n  metadata: SpanMetadata,\n  otSpan: ApiSpan,\n  isRoot: boolean\n) => Promise<T>;\n\n/**\n * Runs the provided function in a new span.\n * @deprecated\n * @hidden\n */\nexport async function runInNewSpan<T>(\n  registry: Registry | HasRegistry,\n  opts: RunInNewSpanOpts,\n  fn: RunInNewSpanFn<T>\n): Promise<T>;\n\n/**\n * Runs the provided function in a new span.\n * @hidden\n */\nexport async function runInNewSpan<T>(\n  opts: RunInNewSpanOpts,\n  fn: RunInNewSpanFn<T>\n): Promise<T>;\n\n/**\n * Runs the provided function in a new span.\n * @hidden\n */\nexport async function runInNewSpan<T>(\n  registryOrOprs: Registry | HasRegistry | RunInNewSpanOpts,\n  optsOrFn: RunInNewSpanOpts | RunInNewSpanFn<T>,\n  fnMaybe?: RunInNewSpanFn<T>\n): Promise<T> {\n  let opts: RunInNewSpanOpts;\n  let fn: RunInNewSpanFn<T>;\n  if (arguments.length === 3) {\n    opts = optsOrFn as RunInNewSpanOpts;\n    fn = fnMaybe as RunInNewSpanFn<T>;\n  } else {\n    opts = registryOrOprs as RunInNewSpanOpts;\n    fn = optsOrFn as RunInNewSpanFn<T>;\n  }\n  await ensureBasicTelemetryInstrumentation();\n  const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION);\n  const parentStep =\n    getAsyncContext().getStore<SpanContext>(spanMetadataAlsKey);\n  const isInRoot = parentStep?.metadata?.isRoot === true;\n  if (!parentStep) opts.metadata.isRoot ||= true;\n\n  const spanOptions: SpanOptions = {\n    links: opts.links,\n    attributes: opts.labels,\n  };\n  if (!isDisableRootSpanDetection()) {\n    spanOptions.root = opts.metadata.isRoot;\n  }\n\n  return await tracer.startActiveSpan(\n    opts.metadata.name,\n    spanOptions,\n    async (otSpan) => {\n      const spanContext = {\n        ...parentStep,\n        metadata: opts.metadata,\n        labels: opts.labels,\n      } as SpanContext;\n      try {\n        opts.metadata.path = buildPath(\n          opts.metadata.name,\n          parentStep?.metadata?.path || '',\n          opts.labels\n        );\n\n        const isGenkitSpan = !!opts.labels?.[SPAN_TYPE_ATTR];\n        if (isGenkitSpan && parentStep) {\n          const parentIsGenkit = !!parentStep.labels?.[SPAN_TYPE_ATTR];\n          if (!parentIsGenkit && parentStep.spanId) {\n            otSpan.setAttribute(\n              ATTR_PREFIX + ':lastKnownParentSpanId',\n              parentStep.spanId\n            );\n          }\n        }\n\n        // Store spanId in context for Genkit spans so nested spans can reference it\n        if (isGenkitSpan) {\n          spanContext.spanId = otSpan.spanContext().spanId;\n        }\n\n        const output = await getAsyncContext().run(\n          spanMetadataAlsKey,\n          spanContext,\n          () => fn(opts.metadata, otSpan, isInRoot)\n        );\n        if (opts.metadata.state !== 'error') {\n          opts.metadata.state = 'success';\n        }\n\n        recordPath(opts.metadata, spanContext);\n        return output;\n      } catch (e) {\n        recordPath(opts.metadata, spanContext, e);\n        opts.metadata.state = 'error';\n        otSpan.setStatus({\n          code: SpanStatusCode.ERROR,\n          message: getErrorMessage(e),\n        });\n        if (e instanceof Error) {\n          otSpan.recordException(e);\n        }\n\n        // Mark the first failing span as the source of failure. Prevent parent\n        // spans that catch re-thrown exceptions from also claiming to be the\n        // source.\n        if (typeof e === 'object') {\n          if (!(e as any).ignoreFailedSpan) {\n            opts.metadata.isFailureSource = true;\n          }\n          (e as any).ignoreFailedSpan = true;\n        }\n\n        throw e;\n      } finally {\n        otSpan.setAttributes(metadataToAttributes(opts.metadata));\n        otSpan.end();\n      }\n    }\n  );\n}\n\n/**\n * Creates a new child span and attaches it to a previously created trace. This\n * is useful, for example, for adding deferred user engagement metadata.\n *\n * @hidden\n */\nexport async function appendSpan(\n  traceId: string,\n  parentSpanId: string,\n  metadata: SpanMetadata,\n  labels?: Record<string, string>\n) {\n  await ensureBasicTelemetryInstrumentation();\n\n  const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION);\n\n  const spanContext = trace.setSpanContext(ROOT_CONTEXT, {\n    traceId: traceId,\n    traceFlags: 1, // sampled\n    spanId: parentSpanId,\n  });\n\n  // TODO(abrook): add explicit start time to align with parent\n  const span = tracer.startSpan(metadata.name, {}, spanContext);\n  span.setAttributes(metadataToAttributes(metadata));\n  if (labels) {\n    span.setAttributes(labels);\n  }\n  span.end();\n}\n\nfunction getErrorMessage(e: any): string {\n  if (e instanceof Error) {\n    return e.message;\n  }\n  return `${e}`;\n}\n\nfunction metadataToAttributes(metadata: SpanMetadata): Record<string, string> {\n  const out = {} as Record<string, string>;\n  Object.keys(metadata).forEach((key) => {\n    if (\n      key === 'metadata' &&\n      typeof metadata[key] === 'object' &&\n      metadata.metadata\n    ) {\n      Object.entries(metadata.metadata).forEach(([metaKey, value]) => {\n        out[ATTR_PREFIX + ':metadata:' + metaKey] = value;\n      });\n    } else if (key === 'input' || typeof metadata[key] === 'object') {\n      out[ATTR_PREFIX + ':' + key] = JSON.stringify(metadata[key]);\n    } else {\n      out[ATTR_PREFIX + ':' + key] = metadata[key];\n    }\n  });\n  return out;\n}\n\n/**\n * Sets provided attribute value in the current span.\n *\n * @hidden\n */\nexport function setCustomMetadataAttribute(key: string, value: string) {\n  const currentStep = getCurrentSpan();\n  if (!currentStep) {\n    return;\n  }\n  if (!currentStep.metadata) {\n    currentStep.metadata = {};\n  }\n  currentStep.metadata[key] = value;\n}\n\n/**\n * Sets provided attribute values in the current span.\n *\n * @hidden\n */\nexport function setCustomMetadataAttributes(values: Record<string, string>) {\n  const currentStep = getCurrentSpan();\n  if (!currentStep) {\n    return;\n  }\n  if (!currentStep.metadata) {\n    currentStep.metadata = {};\n  }\n  for (const [key, value] of Object.entries(values)) {\n    currentStep.metadata[key] = value;\n  }\n}\n\n/**\n * Converts a fully annotated path to a friendly display version for logs\n *\n * @hidden\n */\nexport function toDisplayPath(path: string): string {\n  const pathPartRegex = /\\{([^\\,}]+),[^\\}]+\\}/g;\n  return Array.from(path.matchAll(pathPartRegex), (m) => m[1]).join(' > ');\n}\n\nfunction getCurrentSpan(): SpanMetadata {\n  const step = getAsyncContext().getStore<SpanContext>(spanMetadataAlsKey);\n  if (!step) {\n    throw new Error('running outside step context');\n  }\n  return step.metadata;\n}\n\nfunction buildPath(\n  name: string,\n  parentPath: string,\n  labels?: Record<string, string>\n) {\n  const stepType =\n    labels && labels['genkit:type']\n      ? `,t:${labels['genkit:metadata:subtype'] === 'flow' ? 'flow' : labels['genkit:type']}`\n      : '';\n  return parentPath + `/{${name}${stepType}}`;\n}\n\nfunction recordPath(\n  spanMeta: SpanMetadata,\n  spanContext: SpanContext,\n  err?: any\n) {\n  const path = spanMeta.path || '';\n  const decoratedPath = decoratePathWithSubtype(spanMeta);\n  // Only add the path if a child has not already been added. In the event that\n  // an error is rethrown, we don't want to add each step in the unwind.\n  const paths = Array.from(spanContext?.paths || new Set<PathMetadata>());\n  const status = err ? 'failure' : 'success';\n  if (!paths.some((p) => p.path.startsWith(path) && p.status === status)) {\n    const now = performance.now();\n    const start = spanContext?.timestamp || now;\n    spanContext?.paths?.add({\n      path: decoratedPath,\n      error: err?.name,\n      latency: now - start,\n      status,\n    });\n  }\n  spanMeta.path = decoratedPath;\n}\n\nfunction decoratePathWithSubtype(metadata: SpanMetadata): string {\n  if (!metadata.path) {\n    return '';\n  }\n\n  const pathComponents = metadata.path.split('}/{');\n\n  if (pathComponents.length == 1) {\n    return metadata.path;\n  }\n\n  const stepSubtype =\n    metadata.metadata && metadata.metadata['subtype']\n      ? `,s:${metadata.metadata['subtype']}`\n      : '';\n  const root = `${pathComponents.slice(0, -1).join('}/{')}}/`;\n  const decoratedStep = `{${pathComponents.at(-1)?.slice(0, -1)}${stepSubtype}}`;\n  return root + decoratedStep;\n}\n\nconst rootSpanDetectionKey = '__genkit_disableRootSpanDetection';\n\nfunction isDisableRootSpanDetection(): boolean {\n  return global[rootSpanDetectionKey] === true;\n}\n\n/**\n * Disables Genkit's custom root span detection and leaves default Otel root span.\n *\n * This function attempts to control Genkit's internal OTel instrumentation behaviour,\n * since internal implementation details are subject to change at any time consider\n * this function \"unstable\" and subject to breaking changes as well.\n *\n * @hidden\n */\nexport function disableOTelRootSpanDetection() {\n  global[rootSpanDetectionKey] = true;\n}\n"],"mappings":"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","names":[]}