Skip to content

Commit 4b70e42

Browse files
committed
feat(instrumentation-undici): add custom metric labels
Fixes #2597 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/open-telemetry/opentelemetry-js-contrib/issues/2597?shareId=XXXX-XXXX-XXXX-XXXX).
1 parent 91c9089 commit 4b70e42

File tree

3 files changed

+25
-3
lines changed

3 files changed

+25
-3
lines changed

plugins/node/instrumentation-undici/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Undici instrumentation has few options available to choose from. You can set the
5757
| [`startSpanHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/instrumentation-undici/src/types.ts#L79) | `StartSpanHookFunction` | Function for adding custom attributes before a span is started. |
5858
| [`requireParentforSpans`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/instrumentation-undici/src/types.ts#L81) | `Boolean` | Require a parent span is present to create new span for outgoing requests. |
5959
| [`headersToSpanAttributes`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/instrumentation-undici/src/types.ts#L83) | `Object` | List of case insensitive HTTP headers to convert to span attributes. Headers will be converted to span attributes in the form of `http.{request\|response}.header.header-name` where the name is only lowercased, e.g. `http.response.header.content-length` |
60+
| [`requestMetricAttributesHook`](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/instrumentation-undici/src/types.ts#L85) | `RequestMetricAttributesHookFunction` | Function for adding custom attributes to metrics based on request. |
6061

6162
### Observations
6263

plugins/node/instrumentation-undici/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ export interface StartSpanHookFunction<T = UndiciRequest> {
6363
(request: T): Attributes;
6464
}
6565

66+
export interface RequestMetricAttributesHookFunction<
67+
RequestType = UndiciRequest
68+
> {
69+
(request: RequestType): Attributes;
70+
}
71+
6672
// This package will instrument HTTP requests made through `undici` or `fetch` global API
6773
// so it seems logical to have similar options than the HTTP instrumentation
6874
export interface UndiciInstrumentationConfig<
@@ -84,4 +90,6 @@ export interface UndiciInstrumentationConfig<
8490
requestHeaders?: string[];
8591
responseHeaders?: string[];
8692
};
93+
/** Function for adding custom attributes to metrics based on request */
94+
requestMetricAttributesHook?: RequestMetricAttributesHookFunction<RequestType>;
8795
}

plugins/node/instrumentation-undici/src/undici.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
430430
this._recordFromReq.delete(request);
431431

432432
// Record metrics
433-
this.recordRequestDuration(attributes, startTime);
433+
this.recordRequestDuration(attributes, startTime, request);
434434
}
435435

436436
// This is the event we get when something is wrong in the request like
@@ -464,10 +464,10 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
464464

465465
// Record metrics (with the error)
466466
attributes[SemanticAttributes.ERROR_TYPE] = error.message;
467-
this.recordRequestDuration(attributes, startTime);
467+
this.recordRequestDuration(attributes, startTime, request);
468468
}
469469

470-
private recordRequestDuration(attributes: Attributes, startTime: HrTime) {
470+
private recordRequestDuration(attributes: Attributes, startTime: HrTime, request: UndiciRequest) {
471471
// Time to record metrics
472472
const metricsAttributes: Attributes = {};
473473
// Get the attribs already in span attributes
@@ -485,6 +485,19 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
485485
}
486486
});
487487

488+
// Apply custom attributes from the requestMetricAttributesHook if defined
489+
const config = this.getConfig();
490+
const customAttributes = safeExecuteInTheMiddle(
491+
() => config.requestMetricAttributesHook?.(request),
492+
e => e && this._diag.error('caught requestMetricAttributesHook error: ', e),
493+
true
494+
);
495+
if (customAttributes) {
496+
Object.entries(customAttributes).forEach(([key, val]) => {
497+
metricsAttributes[key] = val;
498+
});
499+
}
500+
488501
// Take the duration and record it
489502
const durationSeconds =
490503
hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000;

0 commit comments

Comments
 (0)