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}`;
}
}
}
};