11import { Attributes } from "@opentelemetry/api" ;
22
33export const NULL_SENTINEL = "$@null((" ;
4+ export const CIRCULAR_REFERENCE_SENTINEL = "$@circular((" ;
45
56export function flattenAttributes (
67 obj : Record < string , unknown > | Array < unknown > | string | boolean | number | null | undefined ,
7- prefix ?: string
8+ prefix ?: string ,
9+ seen : WeakSet < object > = new WeakSet ( )
810) : Attributes {
911 const result : Attributes = { } ;
1012
@@ -38,13 +40,25 @@ export function flattenAttributes(
3840 return result ;
3941 }
4042
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+
4155 for ( const [ key , value ] of Object . entries ( obj ) ) {
4256 const newPrefix = `${ prefix ? `${ prefix } .` : "" } ${ Array . isArray ( obj ) ? `[${ key } ]` : key } ` ;
4357 if ( Array . isArray ( value ) ) {
4458 for ( let i = 0 ; i < value . length ; i ++ ) {
4559 if ( typeof value [ i ] === "object" && value [ i ] !== null ) {
4660 // 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 ) ) ;
4862 } else {
4963 if ( value [ i ] === null ) {
5064 result [ `${ newPrefix } .[${ i } ]` ] = NULL_SENTINEL ;
@@ -55,7 +69,7 @@ export function flattenAttributes(
5569 }
5670 } else if ( isRecord ( value ) ) {
5771 // update null check here
58- Object . assign ( result , flattenAttributes ( value , newPrefix ) ) ;
72+ Object . assign ( result , flattenAttributes ( value , newPrefix , seen ) ) ;
5973 } else {
6074 if ( typeof value === "number" || typeof value === "string" || typeof value === "boolean" ) {
6175 result [ newPrefix ] = value ;
@@ -125,7 +139,7 @@ export function unflattenAttributes(
125139 }
126140 const lastPart = parts [ parts . length - 1 ] ;
127141 if ( lastPart ) {
128- current [ lastPart ] = rehydrateNull ( value ) ;
142+ current [ lastPart ] = rehydrateNull ( rehydrateCircular ( value ) ) ;
129143 }
130144 }
131145
@@ -142,6 +156,13 @@ export function unflattenAttributes(
142156 return result ;
143157}
144158
159+ function rehydrateCircular ( value : any ) : any {
160+ if ( value === CIRCULAR_REFERENCE_SENTINEL ) {
161+ return "[Circular Reference]" ;
162+ }
163+ return value ;
164+ }
165+
145166export function primitiveValueOrflattenedAttributes (
146167 obj : Record < string , unknown > | Array < unknown > | string | boolean | number | undefined ,
147168 prefix : string | undefined
0 commit comments