Skip to content

Commit

Permalink
feat(instrumentation-undici): add custom metric labels
Browse files Browse the repository at this point in the history
  • Loading branch information
lionskape committed Dec 10, 2024
1 parent 91c9089 commit 4b70e42
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 3 deletions.
1 change: 1 addition & 0 deletions plugins/node/instrumentation-undici/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Undici instrumentation has few options available to choose from. You can set the
| [`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. |
| [`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. |
| [`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` |
| [`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. |

### Observations

Expand Down
8 changes: 8 additions & 0 deletions plugins/node/instrumentation-undici/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export interface StartSpanHookFunction<T = UndiciRequest> {
(request: T): Attributes;
}

export interface RequestMetricAttributesHookFunction<
RequestType = UndiciRequest
> {
(request: RequestType): Attributes;
}

// This package will instrument HTTP requests made through `undici` or `fetch` global API
// so it seems logical to have similar options than the HTTP instrumentation
export interface UndiciInstrumentationConfig<
Expand All @@ -84,4 +90,6 @@ export interface UndiciInstrumentationConfig<
requestHeaders?: string[];
responseHeaders?: string[];
};
/** Function for adding custom attributes to metrics based on request */
requestMetricAttributesHook?: RequestMetricAttributesHookFunction<RequestType>;
}
19 changes: 16 additions & 3 deletions plugins/node/instrumentation-undici/src/undici.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ export class UndiciInstrumentation extends InstrumentationBase<UndiciInstrumenta
this._recordFromReq.delete(request);

// Record metrics
this.recordRequestDuration(attributes, startTime);
this.recordRequestDuration(attributes, startTime, request);
}

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

// Record metrics (with the error)
attributes[SemanticAttributes.ERROR_TYPE] = error.message;
this.recordRequestDuration(attributes, startTime);
this.recordRequestDuration(attributes, startTime, request);
}

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

// Apply custom attributes from the requestMetricAttributesHook if defined
const config = this.getConfig();
const customAttributes = safeExecuteInTheMiddle(
() => config.requestMetricAttributesHook?.(request),
e => e && this._diag.error('caught requestMetricAttributesHook error: ', e),
true
);
if (customAttributes) {
Object.entries(customAttributes).forEach(([key, val]) => {
metricsAttributes[key] = val;
});
}

// Take the duration and record it
const durationSeconds =
hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000;
Expand Down

0 comments on commit 4b70e42

Please sign in to comment.