Skip to content

Commit

Permalink
feat: support OTel span.addLink, span.addLinks; add similar APIs to t…
Browse files Browse the repository at this point in the history
…he APM agent API

This also updates a few places for the new otel/api v1.9.0.

Refs: #4071 (the dependabot update doesn't get everything)
Refs: #4070
Refs: #4069
Refs: #4077 (a separate issue for this other new feature in otel/[email protected])
  • Loading branch information
trentm committed Jun 10, 2024
1 parent 63da687 commit 6f7f4c7
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 211 deletions.
30 changes: 30 additions & 0 deletions docs/span-api.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,33 @@ If false-y values (e.g. `null`) are given for both `type` and `name`, then `serv
If this method is not called, the service target values are inferred from other span fields (https://github.com/elastic/apm/blob/main/specs/agents/tracing-spans-service-target.md#field-values[spec]).

`service.target.*` fields are ignored for APM Server before v8.3.

[[span-addlink]]
==== `span.addLink(link)`

[small]#Added in: REPLACEME#

* `link` +{type-link}+

A span can refer to zero or more other transactions or spans (separate
from its parent). Span links will be shown in the Kibana APM app trace view. The
`link` argument is an object with a single "context" field that is a
`Transaction`, `Span`, OpenTelemetry `SpanContext` object, or W3C trace-context
'traceparent' string.
For example: `span.addLink({ context: anotherSpan })`.

[[span-addlinks]]
==== `span.addLinks([links])`

[small]#Added in: REPLACEME#

* `links` +{type-array}+ Span links.

Add span links to this span.

A span can refer to zero or more other transactions or spans (separate
from its parent). Span links will be shown in the Kibana APM app trace view. The
`link` argument is an object with a single "context" field that is a
`Transaction`, `Span`, OpenTelemetry `SpanContext` object, or W3C trace-context
'traceparent' string.
For example: `span.addLinks([{ context: anotherSpan }])`.
2 changes: 1 addition & 1 deletion docs/supported-technologies.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Metrics API and Metrics SDK to allow
[options="header"]
|=======================================================================
| Framework | Version
| <<opentelemetry-bridge,@opentelemetry/api>> | >=1.0.0 <1.9.0
| <<opentelemetry-bridge,@opentelemetry/api>> | >=1.0.0 <1.10.0
| https://www.npmjs.com/package/@opentelemetry/sdk-metrics[@opentelemetry/sdk-metrics] | >=1.11.0 <2
|=======================================================================

Expand Down
30 changes: 30 additions & 0 deletions docs/transaction-api.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,33 @@ Non-HTTP transactions will begin with an outcome of `unknown`.
* `outcome` +{type-string}+

The `setOutcome` method allows an end user to override the Node.js agent's default setting of a transaction's `outcome` property. The `setOutcome` method accepts a string of either `success`, `failure`, or `unknown`, and will force the agent to report this value for a specific span.

[[transaction-addlink]]
==== `transaction.addLink(link)`

[small]#Added in: REPLACEME#

* `link` +{type-link}+

A transaction can refer to zero or more other transactions or spans (separate
from its parent). Span links will be shown in the Kibana APM app trace view. The
`link` argument is an object with a single "context" field that is a
`Transaction`, `Span`, OpenTelemetry `SpanContext` object, or W3C trace-context
'traceparent' string.
For example: `transaction.addLink({ context: anotherSpan })`.

[[transaction-addlinks]]
==== `transaction.addLinks([links])`

[small]#Added in: REPLACEME#

* `links` +{type-array}+ Span links.

Add span links to this transaction.

A transaction can refer to zero or more other transactions or spans (separate
from its parent). Span links will be shown in the Kibana APM app trace view. The
`link` argument is an object with a single "context" field that is a
`Transaction`, `Span`, OpenTelemetry `SpanContext` object, or W3C trace-context
'traceparent' string.
For example: `transaction.addLinks([{ context: anotherSpan }])`.
8 changes: 6 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ declare namespace apm {
setLabel (name: string, value: LabelValue, stringify?: boolean): boolean;
addLabels (labels: Labels, stringify?: boolean): boolean;
setOutcome(outcome: Outcome): void;
addLink (link: Link): void;
addLinks (links: Link[]): void;

startSpan(
name?: string | null,
Expand Down Expand Up @@ -201,6 +203,8 @@ declare namespace apm {
addLabels (labels: Labels, stringify?: boolean): boolean;
setOutcome(outcome: Outcome): void;
setServiceTarget(type?: string | null, name?: string | null): void;
addLink (link: Link): void;
addLinks (links: Link[]): void;
end (endTime?: number): void;
}

Expand Down Expand Up @@ -349,8 +353,8 @@ declare namespace apm {
// equivalent APIs in "opentelemetry-js-api/src/trace/link.ts". Currently
// span link attributes are not supported.
export interface Link {
/** A W3C trace-context 'traceparent' string, Transaction, or Span. */
context: Transaction | Span | string; // This is a SpanContext in OTel.
/** A W3C trace-context 'traceparent' string, Transaction, Span, or OTel SpanContext. */
context: Transaction | Span | {traceId: string, spanId: string} | string;
}

export interface TransactionOptions {
Expand Down
45 changes: 27 additions & 18 deletions lib/instrumentation/generic-span.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,28 +170,31 @@ GenericSpan.prototype.addLabels = function (labels, stringify) {
return true;
};

// This method is private because the APM agents spec says that (for OTel
// compat), adding links after span creation should not be allowed.
// https://github.com/elastic/apm/blob/main/specs/agents/span-links.md
//
// To support adding span links for SQS ReceiveMessage and equivalent, the
// message data isn't known until the *response*, after the span has been
// created.
// Add span links.
//
// @param {Array} links - An array of objects with a `context` property that is
// a Transaction, Span, or TraceParent instance, or a W3C trace-context
// 'traceparent' string.
GenericSpan.prototype._addLinks = function (links) {
// a Transaction, Span, or TraceParent instance; an OTel SpanContext object;
// or a W3C trace-context 'traceparent' string.
GenericSpan.prototype.addLinks = function (links) {
if (links) {
for (let i = 0; i < links.length; i++) {
const link = linkFromLinkArg(links[i]);
if (link) {
this._links.push(link);
}
this.addLink(links[i]);
}
}
};

// Add a span link.
//
// @param {Link} link - An object with a `context` property that is
// a Transaction, Span, or TraceParent instance; an OTel SpanContext object;
// or a W3C trace-context 'traceparent' string.
GenericSpan.prototype.addLink = function (linkArg) {
const link = linkFromLinkArg(linkArg);
if (link) {
this._links.push(link);
}
};

GenericSpan.prototype._freezeOutcome = function () {
this._isOutcomeFrozen = true;
};
Expand Down Expand Up @@ -278,9 +281,9 @@ GenericSpan.prototype._serializeOTel = function (payload) {
// span link as it will be serialized and sent to APM server. If the linkArg is
// invalid, this will return null.
//
// @param {Object} linkArg - An object with a `context` property that is a
// Transaction, Span, or TraceParent instance, or a W3C trace-context
// 'traceparent' string.
// @param {Array} linkArg - An object with a `context` property that is
// a Transaction, Span, or TraceParent instance; an OTel SpanContext object;
// or a W3C trace-context 'traceparent' string.
function linkFromLinkArg(linkArg) {
if (!linkArg || !linkArg.context) {
return null;
Expand All @@ -290,7 +293,13 @@ function linkFromLinkArg(linkArg) {
let traceId;
let spanId;

if (ctx._context instanceof TraceContext) {
if (ctx.traceId && ctx.spanId) {
// Duck-typing for an OTel SpanContext. APM intake v2 only supports the
// trace id and span id fields for span links, so we only need care about
// those attributes.
traceId = ctx.traceId;
spanId = ctx.spanId;
} else if (ctx._context instanceof TraceContext) {
// Transaction or Span
traceId = ctx._context.traceparent.traceId;
spanId = ctx._context.traceparent.id;
Expand Down
8 changes: 8 additions & 0 deletions lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ class OTelBridgeNonRecordingSpan {
return this;
}

addLink(_link) {
return this;
}

addLinks(_links) {
return this;
}

end(_endTime) {}

// isRecording always returns false for NonRecordingSpan.
Expand Down
8 changes: 8 additions & 0 deletions lib/opentelemetry-bridge/OTelSpan.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ class OTelSpan {
return this;
}

addLink(link) {
this._span.addLink(link);
}

addLinks(links) {
this._span.addLinks(links);
}

end(otelEndTime) {
oblog.apicall('%s.end(endTime=%s)', this, otelEndTime);
const endTime =
Expand Down
Loading

0 comments on commit 6f7f4c7

Please sign in to comment.