Span.prototype._inferServiceTargetAndDestinationService = function()

in lib/instrumentation/span.js [185:273]


Span.prototype._inferServiceTargetAndDestinationService = function () {
  // `context.service.target.*` must be set for exit spans. There is a public
  // `span.setServiceTarget(...)` for users to manually set this, but typically
  // it is inferred from other span fields here.
  // https://github.com/elastic/apm/blob/main/specs/agents/tracing-spans-service-target.md#field-values
  if (this._excludeServiceTarget || !this._exitSpan) {
    this._serviceTarget = null;
  } else {
    if (!this._serviceTarget) {
      this._serviceTarget = {};
    }
    if (!('type' in this._serviceTarget)) {
      this._serviceTarget.type =
        this.subtype || this.type || constants.DEFAULT_SPAN_TYPE;
    }
    if (!('name' in this._serviceTarget)) {
      if (this._db) {
        if (this._db.instance) {
          this._serviceTarget.name = this._db.instance;
        }
      } else if (this._message) {
        if (this._message.queue && this._message.queue.name) {
          this._serviceTarget.name = this._message.queue.name;
        }
      } else if (this._http && this._http.url) {
        try {
          const defaultPorts = { 'https:': '443', 'http:': '80' };
          const url = new URL(this._http.url);
          if (!url.port && defaultPorts[url.protocol]) {
            this._serviceTarget.name = `${url.host}:${
              defaultPorts[url.protocol]
            }`;
          } else {
            this._serviceTarget.name = url.host;
          }
        } catch (invalidUrlErr) {
          this._agent.logger.debug(
            'cannot set "service.target.name": %s (ignoring)',
            invalidUrlErr,
          );
        }
      }
    }

    // `destination.service.*` is deprecated, but still required for older
    // APM servers.
    if (!this._destination) {
      this._destination = {};
    }
    if (!this._destination.service) {
      this._destination.service = {};
    }
    // - `destination.service.{type,name}` could be skipped if the upstream APM server is known to be >=7.14.
    this._destination.service.type = '';
    this._destination.service.name = '';
    // - Infer the now deprecated `context.destination.service.resource` value.
    //   https://github.com/elastic/apm/blob/main/specs/agents/tracing-spans-destination.md#destination-resource
    if (!this._destination.service.resource) {
      if (!this._serviceTarget.name) {
        // If we only have `.type`, then use that.
        this._destination.service.resource = this._serviceTarget.type;
      } else if (!this._serviceTarget.type) {
        // If we only have `.name`, then use that.
        this._destination.service.resource = this._serviceTarget.name;
      } else if (this.type === 'external') {
        // Typically the "resource" value would now be "$type/$name", e.g.
        // "mysql/customers". However, we want a special case for some spans (to
        // have the same value as historically?) where we do NOT use the
        // "$type/" prefix. One example is HTTP spans. Another is gRPC spans
        // and, I infer from otel_bridge.feature, any OTel "rpc.system"-usage
        // spans as well
        // (https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/rpc/).
        // Options to infer this from other span data:
        // - Use the presence of "http" context, but without "db" and "message"
        //   context. This is a little brittle, and requires more complete OTel
        //   bridge compatibility mapping of OTel attributes than is currently
        //   being done.
        //      } else if (!this._db && !this._message && this._http && this._http.url) {
        // - Use `span.subtype`: "http", "grpc", ... add others if/when they are
        //   used.
        // - Use `span.type === "external"`. This, at least currently corresponds.
        //   Let's use this one.
        this._destination.service.resource = this._serviceTarget.name;
      } else {
        this._destination.service.resource = `${this._serviceTarget.type}/${this._serviceTarget.name}`;
      }
    }
  }
};