Skip to content

Commit eb341f8

Browse files
aaronlnaAaron Michael Lambmarkkuhn
authored
fix: prevent putDimensions from storing duplicate dimensions (#104)
* fix: prevent putDimensions from storing duplicate dimensions Conditions previously checked for any dimension set which did not match before adding the input dimension set. This would allow different-sized dimension sets from being mistaken as non-duplicate and being added (see unit test for example). New conditions check for any dimension set which does match before skipping adding the input dimension set to the collection. [TESTING] Unit test updated to address edge case; multiple dimension sets of variable size are added and checked. * fix: change putDimensions to update/sort existing dimension sets when duplicate This change ensures new dimension key-values are used for the EMF root node by removing duplicate dimension sets and adding input dimension set to the end of the collection. Example: ``` [ { "keyA": "value1" }, { "keyA": "value2" }, ] // expected EMF target member: { "keyA": "value2 } ``` [TESTING] Updated unit tests to check for this chase wherein putDimensions may be triggered using various dimension set lengths, values, and order. Co-authored-by: Aaron Michael Lamb <[email protected]> Co-authored-by: Mark Kuhn <[email protected]>
1 parent e9f9d4b commit eb341f8

File tree

2 files changed

+59
-41
lines changed

2 files changed

+59
-41
lines changed

src/logger/MetricsContext.ts

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -124,35 +124,23 @@ export class MetricsContext {
124124
* @param dimensions
125125
*/
126126
public putDimensions(incomingDimensionSet: Record<string, string>): void {
127-
MetricsContext.validateDimensionSet(incomingDimensionSet)
127+
MetricsContext.validateDimensionSet(incomingDimensionSet);
128128

129-
if (this.dimensions.length === 0) {
130-
this.dimensions.push(incomingDimensionSet);
131-
return;
132-
}
133-
134-
for (let i = 0; i < this.dimensions.length; i++) {
135-
const existingDimensionSet = this.dimensions[i];
136-
137-
// check for duplicate dimensions when putting
138-
// this is an O(n^2) operation, but since we never expect to have more than
139-
// 10 dimensions, this is acceptable for almost all cases.
140-
// This makes re-using loggers much easier.
129+
// Duplicate dimensions sets are removed before being added to the end of the collection.
130+
// This ensures the latest dimension key-value is used as a target member on the root EMF node.
131+
// This operation is O(n^2), but acceptable given sets are capped at 10 dimensions
132+
const incomingDimensionSetKeys = Object.keys(incomingDimensionSet);
133+
this.dimensions = this.dimensions.filter(existingDimensionSet => {
141134
const existingDimensionSetKeys = Object.keys(existingDimensionSet);
142-
const incomingDimensionSetKeys = Object.keys(incomingDimensionSet);
143135
if (existingDimensionSetKeys.length !== incomingDimensionSetKeys.length) {
144-
this.dimensions.push(incomingDimensionSet);
145-
return;
136+
return true;
146137
}
138+
return !existingDimensionSetKeys.every(existingDimensionSetKey =>
139+
incomingDimensionSetKeys.includes(existingDimensionSetKey),
140+
);
141+
});
147142

148-
for (let j = 0; j < existingDimensionSetKeys.length; j++) {
149-
if (!incomingDimensionSetKeys.includes(existingDimensionSetKeys[j])) {
150-
// we're done now because we know that the dimensions keys are not identical
151-
this.dimensions.push(incomingDimensionSet);
152-
return;
153-
}
154-
}
155-
}
143+
this.dimensions.push(incomingDimensionSet);
156144
}
157145

158146
/**

src/logger/__tests__/MetricsContext.test.ts

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ test('setDimensions allows 30 dimensions', () => {
3131
});
3232

3333
test('putDimension adds key to dimension and sets the dimension as a property', () => {
34+
3435
// arrange
3536
const context = MetricsContext.empty();
3637
const dimension = faker.random.word();
@@ -43,51 +44,80 @@ test('putDimension adds key to dimension and sets the dimension as a property',
4344
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension);
4445
});
4546

46-
test('putDimension will not duplicate dimensions', () => {
47+
test('putDimensions accepts multiple unique dimension sets', () => {
4748
// arrange
4849
const context = MetricsContext.empty();
49-
const dimension = faker.random.word();
50-
const expectedDimension = { dimension };
50+
const expectedDimension1 = { d1: faker.random.word(), d2: faker.random.word() };
51+
const expectedDimension2 = { d2: faker.random.word(), d3: faker.random.word() };
5152

5253
// act
53-
context.putDimensions({ dimension });
54-
context.putDimensions({ dimension });
54+
context.putDimensions(expectedDimension1);
55+
context.putDimensions(expectedDimension2);
5556

5657
// assert
57-
expect(context.getDimensions().length).toBe(1);
58-
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension);
58+
expect(context.getDimensions().length).toBe(2);
59+
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension1);
60+
expect(context.getDimensions()[1]).toStrictEqual(expectedDimension2);
5961
});
6062

61-
test('putDimension will not duplicate dimensions, multiple in different order', () => {
63+
test('putDimensions will not duplicate dimensions', () => {
6264
// arrange
6365
const context = MetricsContext.empty();
6466
const dimension1 = faker.random.word();
6567
const dimension2 = faker.random.word();
66-
const expectedDimension = { dimension1, dimension2 };
68+
const expectedDimension1 = {};
69+
const expectedDimension2 = { dimension1 };
70+
const expectedDimension3 = { dimension2, dimension1 };
71+
const expectedDimension4 = { dimension2 };
6772

6873
// act
74+
context.putDimensions({});
75+
context.putDimensions({ dimension1 });
6976
context.putDimensions({ dimension1, dimension2 });
7077
context.putDimensions({ dimension2, dimension1 });
78+
context.putDimensions({ dimension2 });
79+
context.putDimensions({});
80+
context.putDimensions({ dimension1 });
81+
context.putDimensions({ dimension1, dimension2 });
82+
context.putDimensions({ dimension2, dimension1 });
83+
context.putDimensions({ dimension2 });
7184

7285
// assert
73-
expect(context.getDimensions().length).toBe(1);
74-
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension);
86+
expect(context.getDimensions().length).toBe(4);
87+
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension1);
88+
expect(context.getDimensions()[1]).toStrictEqual(expectedDimension2);
89+
expect(context.getDimensions()[2]).toStrictEqual(expectedDimension3);
90+
expect(context.getDimensions()[3]).toStrictEqual(expectedDimension4);
7591
});
7692

77-
test('putDimension accepts multiple unique dimension sets', () => {
93+
test('putDimensions will sort dimensions correctly', () => {
7894
// arrange
7995
const context = MetricsContext.empty();
80-
const expectedDimension1 = { d1: faker.random.word(), d2: faker.random.word() };
81-
const expectedDimension2 = { d2: faker.random.word(), d3: faker.random.word() };
96+
const dimension1 = faker.random.word();
97+
const dimension2 = faker.random.word();
98+
const expectedDimension1 = { dimension2, dimension1 };
99+
const expectedDimension2 = { dimension2 };
100+
const expectedDimension3 = { dimension1 };
101+
const expectedDimension4 = {};
82102

83103
// act
84-
context.putDimensions(expectedDimension1);
85-
context.putDimensions(expectedDimension2);
104+
context.putDimensions({});
105+
context.putDimensions({ dimension1 });
106+
context.putDimensions({ dimension1, dimension2 });
107+
context.putDimensions({ dimension2, dimension1 });
108+
context.putDimensions({ dimension2 });
109+
context.putDimensions({ dimension1, dimension2 });
110+
context.putDimensions({ dimension2, dimension1 });
111+
context.putDimensions({ dimension2 });
112+
context.putDimensions({ dimension1 });
113+
context.putDimensions({});
86114

87115
// assert
88-
expect(context.getDimensions().length).toBe(2);
116+
expect(context.getDimensions().length).toBe(4);
89117
expect(context.getDimensions()[0]).toStrictEqual(expectedDimension1);
90118
expect(context.getDimensions()[1]).toStrictEqual(expectedDimension2);
119+
expect(context.getDimensions()[2]).toStrictEqual(expectedDimension3);
120+
expect(context.getDimensions()[3]).toStrictEqual(expectedDimension4);
91121
});
92122

93123
test('getDimensions returns default dimensions if custom dimensions not set', () => {

0 commit comments

Comments
 (0)