-
Notifications
You must be signed in to change notification settings - Fork 165
improv(metrics): consistent duplicate dimension handling #4235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a0fdbee
75989d1
277b422
256d9e9
ada1d90
8c1dcf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import { Console } from 'node:console'; | ||
import { | ||
isIntegerNumber, | ||
isNullOrUndefined, | ||
isNumber, | ||
isRecord, | ||
isString, | ||
isStringUndefinedNullEmpty, | ||
Utility, | ||
|
@@ -240,8 +242,10 @@ class Metrics extends Utility implements MetricsInterface { | |
super(); | ||
|
||
this.dimensions = {}; | ||
this.setOptions(options); | ||
this.setEnvConfig(); | ||
this.setConsole(); | ||
this.#logger = options.logger || this.console; | ||
this.setOptions(options); | ||
} | ||
|
||
/** | ||
|
@@ -433,11 +437,6 @@ class Metrics extends Utility implements MetricsInterface { | |
if (!this.getColdStart()) return; | ||
const singleMetric = this.singleMetric(); | ||
|
||
if (this.defaultDimensions.service) { | ||
singleMetric.setDefaultDimensions({ | ||
service: this.defaultDimensions.service, | ||
}); | ||
} | ||
const value = this.functionName?.trim() ?? functionName?.trim(); | ||
if (value && value.length > 0) { | ||
singleMetric.addDimension('function_name', value); | ||
|
@@ -824,13 +823,46 @@ class Metrics extends Utility implements MetricsInterface { | |
* @param dimensions - The dimensions to be added to the default dimensions object | ||
*/ | ||
public setDefaultDimensions(dimensions: Dimensions | undefined): void { | ||
if (isNullOrUndefined(dimensions) || !isRecord(dimensions)) { | ||
return; | ||
} | ||
|
||
const cleanedDimensions: Dimensions = {}; | ||
|
||
for (const [key, value] of Object.entries(dimensions)) { | ||
if ( | ||
isStringUndefinedNullEmpty(key) || | ||
isStringUndefinedNullEmpty(value) | ||
) { | ||
this.#logger.warn( | ||
`The dimension ${key} doesn't meet the requirements and won't be added. Ensure the dimension name and value are non empty strings` | ||
); | ||
continue; | ||
} | ||
|
||
if (Object.hasOwn(this.defaultDimensions, key)) { | ||
const currentValue = this.defaultDimensions[key]; | ||
const suppressOverwriteWarning = | ||
key === 'service' && currentValue === this.defaultServiceName; | ||
if (!suppressOverwriteWarning) { | ||
this.#logger.warn( | ||
`Dimension "${key}" has already been added. The previous value will be overwritten.` | ||
); | ||
} | ||
} | ||
|
||
cleanedDimensions[key] = value; | ||
} | ||
|
||
const targetDimensions = { | ||
...this.defaultDimensions, | ||
...dimensions, | ||
...cleanedDimensions, | ||
}; | ||
if (MAX_DIMENSION_COUNT <= Object.keys(targetDimensions).length) { | ||
|
||
if (Object.keys(targetDimensions).length >= MAX_DIMENSION_COUNT) { | ||
throw new Error('Max dimension count hit'); | ||
} | ||
|
||
this.defaultDimensions = targetDimensions; | ||
} | ||
|
||
|
@@ -1058,8 +1090,6 @@ class Metrics extends Utility implements MetricsInterface { | |
functionName, | ||
} = options; | ||
|
||
this.setEnvConfig(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why were these two removed and extracted in the constructor? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setEnvConfig() and setConsole() calls from setOptions() were moved to the constructor to ensure logger is initialized before any method (eg. setDefaultDimensions) uses it. |
||
this.setConsole(); | ||
this.setCustomConfigService(customConfigService); | ||
this.setDisabled(); | ||
this.setNamespace(namespace); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uttam282005 I tried to run the e2e tests against the PR, and one of the tests is failing. It seems like there's still one warning that gets logged if we use
captureColdStartMetric
.I'm not sure, but I think it has to do with
this.defaultServiceName
. We're doing this in setService to get the service, do we need something similar?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the warning occurs because the
service
dimension is initially set viasetService()
, this value can come from a user-providedserviceName
, an environment variable, or a custom config service. Later, whensetDefaultDimensions()
is called (and includes aservice
key), we get a warning indicating theservice
dimension is being overwritten.In many cases, this warning is correct, it's a user attempting to override a previously set user-defined
service
value, which we want to surface. However, the problem arises whensetDefaultDimensions()
is called internally (e.g., withincaptureColdStartMetric()
or during initialization likesingleMetric()
). In these cases, the overwrite is intentional and should not trigger a warning.To address this, the cleanest solution I can think of is introducing a private boolean flag that marks such internal calls. This flag would let us suppress the warning when
setDefaultDimensions()
is invoked by internal logic rather than user input, preserving the intent of the warning without polluting logs in expected flows.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The root cause is the call to single metric which is emitting the same behaviour that I have written in the note section of the PR.