1
1
import { Attributes } from "@opentelemetry/api" ;
2
2
3
3
export const NULL_SENTINEL = "$@null((" ;
4
+ export const CIRCULAR_REFERENCE_SENTINEL = "$@circular((" ;
4
5
5
6
export function flattenAttributes (
6
7
obj : Record < string , unknown > | Array < unknown > | string | boolean | number | null | undefined ,
7
- prefix ?: string
8
+ prefix ?: string ,
9
+ seen : WeakSet < object > = new WeakSet ( )
8
10
) : Attributes {
9
11
const result : Attributes = { } ;
10
12
@@ -38,13 +40,25 @@ export function flattenAttributes(
38
40
return result ;
39
41
}
40
42
43
+ // Check for circular reference
44
+ if ( obj !== null && typeof obj === "object" && seen . has ( obj ) ) {
45
+ result [ prefix || "" ] = CIRCULAR_REFERENCE_SENTINEL ;
46
+ return result ;
47
+ }
48
+
49
+ // Add object to seen set
50
+ if ( obj !== null && typeof obj === "object" ) {
51
+ seen . add ( obj ) ;
52
+ }
53
+
54
+
41
55
for ( const [ key , value ] of Object . entries ( obj ) ) {
42
56
const newPrefix = `${ prefix ? `${ prefix } .` : "" } ${ Array . isArray ( obj ) ? `[${ key } ]` : key } ` ;
43
57
if ( Array . isArray ( value ) ) {
44
58
for ( let i = 0 ; i < value . length ; i ++ ) {
45
59
if ( typeof value [ i ] === "object" && value [ i ] !== null ) {
46
60
// update null check here as well
47
- Object . assign ( result , flattenAttributes ( value [ i ] , `${ newPrefix } .[${ i } ]` ) ) ;
61
+ Object . assign ( result , flattenAttributes ( value [ i ] , `${ newPrefix } .[${ i } ]` , seen ) ) ;
48
62
} else {
49
63
if ( value [ i ] === null ) {
50
64
result [ `${ newPrefix } .[${ i } ]` ] = NULL_SENTINEL ;
@@ -55,7 +69,7 @@ export function flattenAttributes(
55
69
}
56
70
} else if ( isRecord ( value ) ) {
57
71
// update null check here
58
- Object . assign ( result , flattenAttributes ( value , newPrefix ) ) ;
72
+ Object . assign ( result , flattenAttributes ( value , newPrefix , seen ) ) ;
59
73
} else {
60
74
if ( typeof value === "number" || typeof value === "string" || typeof value === "boolean" ) {
61
75
result [ newPrefix ] = value ;
@@ -135,8 +149,10 @@ export function unflattenAttributes(
135
149
}
136
150
137
151
const lastPart = parts [ parts . length - 1 ] ;
152
+
138
153
if ( lastPart !== undefined ) {
139
- current [ lastPart ] = rehydrateNull ( value ) ;
154
+ current [ lastPart ] = rehydrateNull ( rehydrateCircular ( value ) ) ;
155
+
140
156
}
141
157
}
142
158
@@ -153,6 +169,13 @@ export function unflattenAttributes(
153
169
return result ;
154
170
}
155
171
172
+ function rehydrateCircular ( value : any ) : any {
173
+ if ( value === CIRCULAR_REFERENCE_SENTINEL ) {
174
+ return "[Circular Reference]" ;
175
+ }
176
+ return value ;
177
+ }
178
+
156
179
export function primitiveValueOrflattenedAttributes (
157
180
obj : Record < string , unknown > | Array < unknown > | string | boolean | number | undefined ,
158
181
prefix : string | undefined
0 commit comments