Skip to content

feat: add generic network telemetry - #5146

Open
pierrealexandre2 wants to merge 1 commit into
webex:nextfrom
pierrealexandre2:networkTelemetry
Open

feat: add generic network telemetry#5146
pierrealexandre2 wants to merge 1 commit into
webex:nextfrom
pierrealexandre2:networkTelemetry

Conversation

@pierrealexandre2

@pierrealexandre2 pierrealexandre2 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

COMPLETES #SPARK-821340

This pull request addresses

Today, we have nothing in the logs that helps us track volume of requests made by the app and to where they go, we probably want a general request logger to be able to track this information for a session

by making the following changes

Add a generic telemetry on network requests

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI (e.g., refactoring was performed by GAI on manually written code)
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Please Specify
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Make sure to have followed the contributing guidelines before submitting.

@pierrealexandre2
pierrealexandre2 marked this pull request as ready for review August 4, 2026 09:09
@pierrealexandre2
pierrealexandre2 requested review from a team as code owners August 4, 2026 09:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ac771df13

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

* @returns {string}
*/
function getFailureType(reason: RequestFailure, statusCode: number) {
const errorName = typeof reason?.name === 'string' ? reason.name.toLowerCase() : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Classify wrapped timeout errors as timeouts

When a request-level timeout has no HTTP response, the SDK can surface it as a NetworkOrCORSError with statusCode: 0 while the original transport error is wrapped on the response body/code/message (for example the node request wrapper resolves a status-0 response with body: error). Since this only inspects the wrapper name, those timed-out SDK requests are reported as network_error instead of timeout, which undercounts the new timeout dimension; consider checking the wrapped error/code/message or timeout metadata before falling back to network_error.

Useful? React with 👍 / 👎.

name: NETWORK_REQUEST_FAILURE_METRIC,
props: {
type: 'operational',
tags: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about the use of the tags. I did not understand codex answer....

@pierrealexandre2
pierrealexandre2 force-pushed the networkTelemetry branch 2 times, most recently from 540ffd2 to dd62848 Compare August 11, 2026 08:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd62848f06

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return (
toString(wrappedError?.message) ||
toString(typeof reason.body === 'string' ? reason.body : undefined) ||
toString(reason.message) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sanitize fallback HTTP error messages

When a WebexHttpError has no body.message (for example an empty or non-standard 4xx/5xx body), webex-core's WebexHttpError.parse() appends the request URI and WEBEX_TRACKING_ID to reason.message. Falling back to that raw message here means each retry or failed response gets a unique errorMessage, so isSameError() won't aggregate it and tracking IDs bypass the ten-ID cap in trackingIds; please strip the appended request/tracking details or use a bounded fallback before putting it in telemetry.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2f0dd8479b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

}
}

safeSetInterval(submitSummary, NETWORK_TELEMETRY_INTERVAL_MS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear the telemetry interval on logout

When a Webex instance is logged out or discarded, this interval still holds the bound submitMetric callback and keeps calling submitClientMetrics every ten minutes. Because the returned timer handle is discarded, the SDK cannot clear it from an onBeforeLogout or teardown path, so old/logged-out instances can keep submitting empty summaries and stay retained; store the handle and clear it during metrics cleanup/logout.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf4c9bc661

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +506 to +507
telemetry.metricsSummary.totalRecvdResponse += 1;
getRequestMetric(telemetry, options).count_recvdResponse += 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid double-counting replayed auth successes

In expired-token flows, AuthInterceptor.replay() (packages/@webex/webex-core/src/interceptors/auth.js:146-165) calls this.webex.request(options) before the original reversed response chain reaches RequestEventInterceptor (packages/@webex/http-core/src/request/index.js:36-37), so the replay emits its own request:success and then the original chain emits another success for that same replay response. Since this handler increments on every success event, those requests record two received responses but no failed 401 response/error, skewing the new failed-response and error telemetry; consider ignoring the outer replay success or recording handled 401s before auth swallows them.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex recommends fix is to change interceptor ordering in webex-core, not add replay-specific logic to telemetry.
I'll consider it out of scope

* @returns {string}
*/
function normalizeRouteSegment(segment: string) {
if (segment.length > 32 || segment.includes('%')) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questions:

  1. Are you sure this cover all cases?
  2. Why 32?
  3. Which identifier contains %?

Comment on lines +24 to +27
count_sendRequest: number;
count_failedRequest: number;
count_recvdResponse: number;
count_failedResponse: number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: please always use camelCase variable ad property names in JS, eg: countSendRequest

* @param {unknown} value
* @returns {string|undefined}
*/
function toString(value: unknown) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use type annotation only one place. Since other SDK code put all typings into the JSDoc, please do the same. But if you have typing in the docs you do not need to do them in the code

Suggested change
function toString(value: unknown) {
function toString(value) {

same for the rest of the definitions


return (
toString(wrappedError?.message) ||
toString(typeof reason.body === 'string' ? reason.body : undefined) ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toString already have a string typeof check, would it work like this instead:

Suggested change
toString(typeof reason.body === 'string' ? reason.body : undefined) ||
toString(reason.body) ||

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e1a068c55d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

}
}

telemetry.errorMetrics.push(errorMetric);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cap the number of distinct error aggregates

When failures contain distinct messages—for example, a service includes a request ID or user-supplied value in each response—isSameError() treats every occurrence as unique and this unconditional push lets errorMetrics grow without limit for the entire ten-minute window. A busy failing client can therefore consume increasing memory and submit an oversized client-metrics payload that is rejected, losing the whole summary; cap the aggregate count and/or coalesce excess entries into a bounded fallback.

Useful? React with 👍 / 👎.

identifiable information or credentials.

```js
````js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Close the configuration example before the new section

This four-backtick fence remains open until the four backticks after the event-flow example, so the Network request telemetry heading, its scope text, and the nested event-flow fence all render as part of the preceding JavaScript code block rather than as documentation. Close the configuration example after Webex.init(...) and use a separate fence for the event flow.

Useful? React with 👍 / 👎.

@Coread Coread added the validated If the pull request is validated for automation. label Sep 1, 2026
@github-actions github-actions Bot removed the validated If the pull request is validated for automation. label Sep 1, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a33b920384

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +343 to +345
return (
toString(wrappedError?.message) || toString(reason.body) || toString(reason.message) || UNKNOWN
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Truncate error details before aggregation

When a failed request returns a large plain-text body or error message—for example, an HTML proxy error page—this path stores the entire unbounded string in errorMessage and later serializes it into the client-metrics payload. A single oversized response can therefore retain a large value for the whole reporting window and cause the complete network summary to exceed transport limits; cap or normalize error detail strings before adding them to telemetry.

Useful? React with 👍 / 👎.

@Coread Coread added the validated If the pull request is validated for automation. label Sep 1, 2026
* @param options Request options containing the service or API name.
* @returns The normalized service name.
*/
function getService(options: RequestOptions): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: did you find evidence of service names needing normalization?

import {safeSetInterval} from '@webex/common-timers';

export const NETWORK_REQUEST_SUMMARY_METRIC = 'JS_SDK_NETWORK_REQUEST_SUMMARY';
export const NETWORK_TELEMETRY_INTERVAL_MS = 10 * 60 * 1_000;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: A lot of network issues that would be useful to understand occur in the first few minutes of a call. We might not get any telemetry from these as the call will be over before 10 minutes. What is the plan for those calls? This interval is probably better as a configurable default, but also we might want a manual submission path, so consuming applications can submit telemetry when they think it is appropriate e.g. after join failure or before call end.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe that could be an improvement for the next iteration ?

}

const start = options.$timings?.networkStart;
const end = options.$timings?.networkEnd;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: if you are computing latency based on the start and end times, this could be reported in some way. Even if it didn't timeout, high network latency can still be bad

return;
}

this.networkTelemetry = createNetworkTelemetryCollector({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: this whole feature should have a config enable flag which defaults to false

* @returns Nothing.
*/
function stop(): void {
clearInterval(telemetryInterval);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: might want to send the network telemetry on stop, or at least try to. It is set up to stop on before logout, so maybe you can't send it at that point, but if you don't try, you might always lose the last up to 10 minutes of telemetry

@github-actions github-actions Bot removed the validated If the pull request is validated for automation. label Sep 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 300d4a4936

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

* @returns
*/
onBeforeLogout() {
this.stopNetworkTelemetry();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Await the final telemetry flush before logout

When logout occurs with a non-empty partial window, this hook returns immediately even though stop() initiates an asynchronous submitClientMetrics() call. The client-metrics batcher waits up to batcherWait before issuing its request, while WebexCore.logout() proceeds to clear storage and invalidate credentials as soon as this hook resolves, so the documented shutdown summary can be sent after authentication has been torn down and be lost. Return the submission promise through stop() and stopNetworkTelemetry() so logout waits for the flush.

Useful? React with 👍 / 👎.

Comment on lines +247 to +248
metric.totalNetworkDurationMs += duration;
metric.networkDurationsMs.push(duration);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound retained network-duration samples

For a high-volume endpoint, every completed request appends another duration that remains resident for the full ten-minute window, after which the entire array is copied and sorted to calculate each percentile. Unlike the submitted aggregate, memory use and boundary-time CPU therefore grow with raw request volume; a busy SDK can cause a large allocation and event-loop stall every reporting interval. Use a bounded histogram, streaming quantile estimator, or capped reservoir instead of retaining every sample.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants