@@ -103,7 +103,9 @@ function ecsTransform (info, opts) {
103103 // istanbul ignore else
104104 if ( apm ) {
105105 // Set "service.name" and "event.dataset" from APM conf, if not already set.
106- let serviceName = ecsFields . service && ecsFields . service . name
106+ let serviceName = ( ecsFields . service && ecsFields . service . name && typeof ecsFields . service . name === 'string'
107+ ? ecsFields . service . name
108+ : undefined )
107109 if ( ! serviceName ) {
108110 // https://github.com/elastic/apm-agent-nodejs/pull/1949 is adding
109111 // getServiceName() in v3.11.0. Fallback to private `apm._conf`.
@@ -114,13 +116,27 @@ function ecsTransform (info, opts) {
114116 // A mis-configured APM Agent can be "started" but not have a
115117 // "serviceName".
116118 if ( serviceName ) {
117- ecsFields . service = ecsFields . service || { }
118- ecsFields . service . name = serviceName
119+ if ( ecsFields . service === undefined ) {
120+ ecsFields . service = { name : serviceName }
121+ } else if ( ! isVanillaObject ( ecsFields . service ) ) {
122+ // Warning: "service" type conflicts with ECS spec. Overwriting.
123+ ecsFields . service = { name : serviceName }
124+ } else {
125+ ecsFields . service . name = serviceName
126+ }
119127 }
120128 }
121- if ( serviceName && ! ( ecsFields . event && ecsFields . event . dataset ) ) {
122- ecsFields . event = ecsFields . event || { }
123- ecsFields . event . dataset = serviceName + '.log'
129+ if ( serviceName &&
130+ ! ( ecsFields . event && ecsFields . event . dataset &&
131+ typeof ecsFields . event . dataset === 'string' ) ) {
132+ if ( ecsFields . event === undefined ) {
133+ ecsFields . event = { dataset : serviceName + '.log' }
134+ } else if ( ! isVanillaObject ( ecsFields . event ) ) {
135+ // Warning: "event" type conflicts with ECS spec. Overwriting.
136+ ecsFields . event = { dataset : serviceName + '.log' }
137+ } else {
138+ ecsFields . event . dataset = serviceName + '.log'
139+ }
124140 }
125141
126142 // https://www.elastic.co/guide/en/ecs/current/ecs-tracing.html
@@ -168,4 +184,19 @@ function ecsTransform (info, opts) {
168184 return info
169185}
170186
187+ // Return true if the given arg is a "vanilla" object. Roughly the intent is
188+ // whether this is basic mapping of string keys to values that will serialize
189+ // as a JSON object.
190+ //
191+ // Currently, it excludes Map. The uses above don't really expect a user to:
192+ // service = new Map([["foo", "bar"]])
193+ // log.info({ service }, '...')
194+ //
195+ // There are many ways tackle this. See some attempts and benchmarks at:
196+ // https://gist.github.com/trentm/34131a92eede80fd2109f8febaa56f5a
197+ function isVanillaObject ( o ) {
198+ return ( typeof o === 'object' &&
199+ ( ! o . constructor || o . constructor . name === 'Object' ) )
200+ }
201+
171202module . exports = format ( ecsTransform )
0 commit comments