Skip to content

Commit f515902

Browse files
author
deepshekhardas
committed
fix: escape periods in attribute keys during flatten/unflatten
Object keys containing periods (e.g. 'Key 0.002mm') were incorrectly split on the '.' delimiter during flatten/unflatten, causing data corruption in the dashboard logs view. Escape dots with \$@dot sentinel during flattening and unescape during unflattening. Fixes #1510
1 parent 2dd9f37 commit f515902

2 files changed

Lines changed: 32 additions & 5 deletions

File tree

packages/core/src/v3/utils/flattenAttributes.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Attributes } from "@opentelemetry/api";
22

33
export const NULL_SENTINEL = "$@null((";
44
export const CIRCULAR_REFERENCE_SENTINEL = "$@circular((";
5+
const DOT_ESCAPE = "$@dot";
56

67
const DEFAULT_MAX_DEPTH = 128;
78

@@ -200,7 +201,8 @@ class AttributeFlattener {
200201
break;
201202
}
202203

203-
const newPrefix = `${prefix ? `${prefix}.` : ""}${Array.isArray(obj) ? `[${key}]` : key}`;
204+
const escapedKey = Array.isArray(obj) ? `[${key}]` : key.replace(/\./g, DOT_ESCAPE);
205+
const newPrefix = `${prefix ? `${prefix}.` : ""}${escapedKey}`;
204206

205207
if (Array.isArray(value)) {
206208
for (let i = 0; i < value.length; i++) {
@@ -280,17 +282,18 @@ export function unflattenAttributes(
280282

281283
const parts = key.split(".").reduce(
282284
(acc, part) => {
283-
if (part.startsWith("[") && part.endsWith("]")) {
285+
const unescaped = part.split(DOT_ESCAPE).join(".");
286+
if (unescaped.startsWith("[") && unescaped.endsWith("]")) {
284287
// Handle array indices more precisely
285-
const match = part.match(/^\[(\d+)\]$/);
288+
const match = unescaped.match(/^\[(\d+)\]$/);
286289
if (match && match[1]) {
287290
acc.push(parseInt(match[1]));
288291
} else {
289292
// Remove brackets for non-numeric array keys
290-
acc.push(part.slice(1, -1));
293+
acc.push(unescaped.slice(1, -1));
291294
}
292295
} else {
293-
acc.push(part);
296+
acc.push(unescaped);
294297
}
295298
return acc;
296299
},

packages/core/test/flattenAttributes.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
11
import { flattenAttributes, unflattenAttributes } from "../src/v3/utils/flattenAttributes.js";
22

33
describe("flattenAttributes", () => {
4+
it("escapes periods in object keys", () => {
5+
const obj = { "Key 0.002mm": 31.4 };
6+
const flattened = flattenAttributes(obj);
7+
expect(flattened).toEqual({ "Key 0$@dot002mm": 31.4 });
8+
expect(unflattenAttributes(flattened)).toEqual(obj);
9+
});
10+
11+
it("handles multiple periods in a single key", () => {
12+
const obj = { "a.b.c": "value" };
13+
const flattened = flattenAttributes(obj);
14+
expect(flattened).toEqual({ "a$@dotb$@dotc": "value" });
15+
expect(unflattenAttributes(flattened)).toEqual(obj);
16+
});
17+
18+
it("handles period keys nested inside regular objects", () => {
19+
const obj = { meta: { "Key 0.002mm": 31.4, "version": 2 } };
20+
const flattened = flattenAttributes(obj);
21+
expect(flattened).toEqual({
22+
"meta.Key 0$@dot002mm": 31.4,
23+
"meta.version": 2,
24+
});
25+
expect(unflattenAttributes(flattened)).toEqual(obj);
26+
});
27+
428
it("handles number keys correctly", () => {
529
expect(flattenAttributes({ bar: { "25": "foo" } })).toEqual({ "bar.25": "foo" });
630
expect(unflattenAttributes({ "bar.25": "foo" })).toEqual({ bar: { "25": "foo" } });

0 commit comments

Comments
 (0)