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 ( 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 ( 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 ;
@@ -125,7 +139,7 @@ export function unflattenAttributes(
125
139
}
126
140
const lastPart = parts [ parts . length - 1 ] ;
127
141
if ( lastPart ) {
128
- current [ lastPart ] = rehydrateNull ( value ) ;
142
+ current [ lastPart ] = rehydrateNull ( rehydrateCircular ( value ) ) ;
129
143
}
130
144
}
131
145
@@ -142,6 +156,13 @@ export function unflattenAttributes(
142
156
return result ;
143
157
}
144
158
159
+ function rehydrateCircular ( value : any ) : any {
160
+ if ( value === CIRCULAR_REFERENCE_SENTINEL ) {
161
+ return "[Circular Reference]" ;
162
+ }
163
+ return value ;
164
+ }
165
+
145
166
export function primitiveValueOrflattenedAttributes (
146
167
obj : Record < string , unknown > | Array < unknown > | string | boolean | number | undefined ,
147
168
prefix : string | undefined
0 commit comments