startSpan()

in lib/opentelemetry-bridge/OTelTracer.js [45:166]


  startSpan(name, otelSpanOptions = {}, otelContext = otel.context.active()) {
    oblog.apicall(
      'OTelTracer.startSpan(name=%s, options=%j, context=%s)',
      name,
      otelSpanOptions,
      otelContext,
    );

    // Get the parent info for the new span.
    // We want to get a core Transaction or Span as a parent, when possible,
    // because that is required to support the span compression feature.
    let parentGenericSpan;
    let parentOTelSpanContext;
    if (otelSpanOptions.root) {
      // Pass through: explicitly want no parent.
    } else if (otelContext instanceof OTelBridgeRunContext) {
      parentGenericSpan =
        otelContext.currSpan() || otelContext.currTransaction();
      if (parentGenericSpan instanceof OTelBridgeNonRecordingSpan) {
        // This isn't a real Transaction we can use. It is a placeholder
        // to propagate its SpanContext. Grab just that.
        parentOTelSpanContext = parentGenericSpan.spanContext();
        parentGenericSpan = null;
      }
    } else {
      // `otelContext` is any object that is meant to satisfy `interface
      // Context`. This may hold an OTel `SpanContext` that should be
      // propagated.
      parentOTelSpanContext = otel.trace.getSpanContext(otelContext);
    }

    const createOpts = {};
    if (otelSpanOptions.links) {
      // Span link *attributes* are not currently supported, they are silently dropped.
      createOpts.links = otelSpanOptions.links
        .filter(
          (otelLink) =>
            otelLink &&
            otelLink.context &&
            otel.isSpanContextValid(otelLink.context),
        )
        .map((otelLink) => {
          return {
            context: traceparentStrFromOTelSpanContext(otelLink.context),
          };
        });
    }

    // Create the new Span/Transaction.
    let newTransOrSpan = null;
    if (parentGenericSpan) {
      // New child span.
      const trans =
        parentGenericSpan instanceof Transaction
          ? parentGenericSpan
          : parentGenericSpan.transaction;
      createOpts.childOf = parentGenericSpan;
      if (otelSpanOptions.startTime) {
        createOpts.startTime = epochMsFromOTelTimeInput(
          otelSpanOptions.startTime,
        );
      }
      if (
        otelSpanOptions.kind === otel.SpanKind.CLIENT ||
        otelSpanOptions.kind === otel.SpanKind.PRODUCER
      ) {
        createOpts.exitSpan = true;
      }
      newTransOrSpan = trans.createSpan(name, createOpts);

      // There might be no span, e.g. if the span is a child of an exit span. We
      // have to return some OTelSpan, and we also want to propagate the
      // parent's trace-context, if any.
      if (!newTransOrSpan) {
        return otel.trace.wrapSpanContext(
          otelSpanContextFromTraceContext(parentGenericSpan._context),
        );
      }
    } else if (
      parentOTelSpanContext &&
      otel.isSpanContextValid(parentOTelSpanContext)
    ) {
      // New continuing transaction.
      // Note: This is *not* using `SpanContext.isRemote`. I am not sure if it
      // is relevant unless using something, like @opentelemetry/core's
      // `W3CTraceContextPropagator`, that sets `isRemote = true`. Nothing in
      // @opentelemetry/api itself sets isRemote.
      createOpts.childOf = traceparentStrFromOTelSpanContext(
        parentOTelSpanContext,
      );
      if (parentOTelSpanContext.traceState) {
        createOpts.tracestate = parentOTelSpanContext.traceState.serialize();
      }
      if (otelSpanOptions.startTime !== undefined) {
        createOpts.startTime = epochMsFromOTelTimeInput(
          otelSpanOptions.startTime,
        );
      }
      newTransOrSpan = this._ins.createTransaction(name, createOpts);
    } else {
      // New root transaction.
      if (otelSpanOptions.startTime !== undefined) {
        createOpts.startTime = epochMsFromOTelTimeInput(
          otelSpanOptions.startTime,
        );
      }
      newTransOrSpan = this._ins.createTransaction(name, createOpts);
    }

    newTransOrSpan._setOTelKind(
      otel.SpanKind[otelSpanOptions.kind || otel.SpanKind.INTERNAL],
    );

    // Explicitly use the higher-priority user outcome API to prevent the agent
    // inferring the outcome from any reported errors or HTTP status code.
    newTransOrSpan.setOutcome(OUTCOME_UNKNOWN);

    const otelSpan = new OTelSpan(newTransOrSpan);
    otelSpan.setAttributes(otelSpanOptions.attributes);

    return otelSpan;
  }