4
4
*/
5
5
6
6
'use strict'
7
- const SegmentSynthesizer = require ( './segment-synthesis' )
8
- const { otelSynthesis } = require ( '../symbols' )
7
+
9
8
const { hrTimeToMilliseconds } = require ( '@opentelemetry/core' )
10
9
const { SpanKind } = require ( '@opentelemetry/api' )
11
- const urltils = require ( '../util/urltils' )
10
+
11
+ const AttributeReconciler = require ( './attr-reconciler' )
12
+ const SegmentSynthesizer = require ( './segment-synthesis' )
13
+ const { otelSynthesis } = require ( '../symbols' )
14
+
12
15
const {
13
16
ATTR_DB_NAME ,
14
17
ATTR_DB_STATEMENT ,
@@ -33,10 +36,14 @@ const {
33
36
const { DESTINATIONS } = require ( '../config/attribute-filter' )
34
37
35
38
module . exports = class NrSpanProcessor {
39
+ #reconciler
40
+
36
41
constructor ( agent ) {
37
42
this . agent = agent
38
43
this . synthesizer = new SegmentSynthesizer ( agent )
39
44
this . tracer = agent . tracer
45
+
46
+ this . #reconciler = new AttributeReconciler ( { agent } )
40
47
}
41
48
42
49
/**
@@ -93,49 +100,27 @@ module.exports = class NrSpanProcessor {
93
100
* @param {Transaction } params.transaction The NR transaction to attach
94
101
* the found attributes to.
95
102
*/
96
- reconcileConsumerAttributes ( { span, transaction } ) { // eslint-disable-line sonarjs/cognitive-complexity
103
+ reconcileConsumerAttributes ( { span, transaction } ) {
97
104
const baseSegment = transaction . baseSegment
98
105
const trace = transaction . trace
99
106
const isHighSecurity = this . agent . config . high_security ?? false
100
107
101
- for ( const [ key , value ] of Object . entries ( span . attributes ) ) {
102
- switch ( key ) {
103
- case ATTR_SERVER_ADDRESS : {
104
- if ( value ) {
105
- let serverAddress = value
106
- if ( urltils . isLocalhost ( value ) ) {
107
- serverAddress = this . agent . config . getHostnameSafe ( value )
108
- }
109
- baseSegment . addAttribute ( 'host' , serverAddress )
110
- }
111
- break
112
- }
113
-
114
- case ATTR_SERVER_PORT : {
115
- baseSegment . addAttribute ( 'port' , value )
116
- break
117
- }
118
-
119
- case ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY : {
120
- if ( isHighSecurity === true || ! value ) break
121
- trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'message.routingKey' , value )
122
- baseSegment . addAttribute ( 'message.routingKey' , value )
123
- break
124
- }
125
-
126
- case ATTR_MESSAGING_DESTINATION_NAME :
127
- case ATTR_MESSAGING_DESTINATION : {
128
- if ( isHighSecurity === true || ! value ) break
129
- trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'message.queueName' , value )
130
- baseSegment . addAttribute ( 'message.queueName' , value )
131
- break
132
- }
133
-
134
- default : {
135
- baseSegment . addAttribute ( key , value )
136
- }
137
- }
108
+ const queueNameMapper = ( value ) => {
109
+ if ( isHighSecurity === true ) return
110
+ trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'message.queueName' , value )
111
+ baseSegment . addAttribute ( 'message.queueName' , value )
112
+ }
113
+ const mapper = {
114
+ [ ATTR_SERVER_ADDRESS ] : ( value ) => baseSegment . addAttribute ( 'host' , value ) ,
115
+ [ ATTR_SERVER_PORT ] : ( value ) => baseSegment . addAttribute ( 'port' , value ) ,
116
+ [ ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ] : ( value ) => {
117
+ if ( isHighSecurity === true ) return
118
+ trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'message.routingKey' , value )
119
+ } ,
120
+ [ ATTR_MESSAGING_DESTINATION_NAME ] : queueNameMapper ,
121
+ [ ATTR_MESSAGING_DESTINATION ] : queueNameMapper
138
122
}
123
+ this . #reconciler. reconcile ( { segment : baseSegment , otelSpan : span , mapper } )
139
124
140
125
transaction . end ( )
141
126
}
@@ -157,98 +142,83 @@ module.exports = class NrSpanProcessor {
157
142
}
158
143
159
144
reconcileHttpAttributes ( { segment, span, transaction } ) {
160
- for ( const [ prop , value ] of Object . entries ( span . attributes ) ) {
161
- let key = prop
162
- let sanitized = value
163
- if ( key === ATTR_HTTP_ROUTE ) {
164
- // TODO: can we get the route params?
165
- transaction . nameState . appendPath ( sanitized )
166
- } else if ( key === ATTR_HTTP_STATUS_CODE || key === ATTR_HTTP_RES_STATUS_CODE ) {
167
- key = 'http.statusCode'
168
- transaction . statusCode = sanitized
169
- transaction . trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , key , sanitized )
170
- // Not using const as it is not in semantic-conventions
171
- } else if ( key === ATTR_HTTP_STATUS_TEXT ) {
172
- key = 'http.statusText'
173
- transaction . trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , key , sanitized )
174
- } else if ( key === ATTR_SERVER_PORT || key === ATTR_NET_HOST_PORT ) {
175
- key = 'port'
176
- } else if ( key === ATTR_SERVER_ADDRESS || key === ATTR_NET_HOST_NAME ) {
177
- key = 'host'
178
- if ( urltils . isLocalhost ( sanitized ) ) {
179
- sanitized = this . agent . config . getHostnameSafe ( sanitized )
180
- }
181
- }
182
-
183
- // TODO: otel instrumentation does not collect headers
184
- // a customer can specify which ones, we also specify this
185
- // so i think we'd have to cross reference our list
186
- // it also looks like we add all headers to the trace
187
- // this isn't doing that
188
- segment . addAttribute ( key , sanitized )
145
+ const status = ( value ) => {
146
+ transaction . statusCode = value
147
+ transaction . trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'http.statusCode' , value )
148
+ }
149
+ const port = ( value ) => segment . addAttribute ( 'port' , value )
150
+ const host = ( value ) => segment . addAttribute ( 'host' , value )
151
+ const mapper = {
152
+ // TODO: if route params are available, assign them as well
153
+ [ ATTR_HTTP_ROUTE ] : ( value ) => {
154
+ transaction . nameState . appendPath ( value )
155
+ segment . addAttribute ( 'http.route' , value )
156
+ } ,
157
+ [ ATTR_HTTP_STATUS_CODE ] : status ,
158
+ [ ATTR_HTTP_RES_STATUS_CODE ] : status ,
159
+ [ ATTR_HTTP_STATUS_TEXT ] : ( value ) => {
160
+ transaction . trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'http.statusText' , value )
161
+ } ,
162
+ [ ATTR_SERVER_PORT ] : port ,
163
+ [ ATTR_NET_HOST_PORT ] : port ,
164
+ [ ATTR_SERVER_ADDRESS ] : host ,
165
+ [ ATTR_NET_HOST_NAME ] : host
189
166
}
167
+ this . #reconciler. reconcile ( { segment, otelSpan : span , mapper } )
168
+
169
+ // TODO: otel instrumentation does not collect headers
170
+ // a customer can specify which ones, we also specify this
171
+ // so i think we'd have to cross reference our list
172
+ // it also looks like we add all headers to the trace
173
+ // this isn't doing that
190
174
}
191
175
192
176
// TODO: our grpc instrumentation handles errors when the status code is not 0
193
177
// we should prob do this here too
194
178
reconcileRpcAttributes ( { segment, span, transaction } ) {
195
- for ( const [ prop , value ] of Object . entries ( span . attributes ) ) {
196
- if ( prop === ATTR_GRPC_STATUS_CODE ) {
179
+ const mapper = {
180
+ [ ATTR_GRPC_STATUS_CODE ] : ( value ) => {
197
181
transaction . trace . attributes . addAttribute ( DESTINATIONS . TRANS_COMMON , 'response.status' , value )
182
+ segment . addAttribute ( ATTR_GRPC_STATUS_CODE , value )
198
183
}
199
- segment . addAttribute ( prop , value )
200
184
}
185
+ this . #reconciler. reconcile ( { segment, otelSpan : span , mapper } )
201
186
}
202
187
203
188
reconcileDbAttributes ( { segment, span } ) {
204
- for ( const [ prop , value ] of Object . entries ( span . attributes ) ) {
205
- let key = prop
206
- let sanitized = value
207
- if ( key === ATTR_NET_PEER_PORT ) {
208
- key = 'port_path_or_id'
209
- } else if ( prop === ATTR_NET_PEER_NAME ) {
210
- key = 'host'
211
- if ( urltils . isLocalhost ( sanitized ) ) {
212
- sanitized = this . agent . config . getHostnameSafe ( sanitized )
213
- }
214
- } else if ( prop === ATTR_DB_NAME ) {
215
- key = 'database_name'
216
- } else if ( prop === ATTR_DB_SYSTEM ) {
217
- key = 'product'
218
- /**
219
- * This attribute was collected in `onStart`
220
- * and was passed to `ParsedStatement`. It adds
221
- * this segment attribute as `sql` or `sql_obfuscated`
222
- * and then when the span is built from segment
223
- * re-assigns to `db.statement`. This needs
224
- * to be skipped because it will be the raw value.
225
- */
226
- } else if ( prop === ATTR_DB_STATEMENT ) {
227
- continue
228
- }
229
- segment . addAttribute ( key , sanitized )
189
+ const mapper = {
190
+ [ ATTR_NET_PEER_PORT ] : ( value ) => {
191
+ segment . addAttribute ( 'port_path_or_id' , value )
192
+ } ,
193
+ [ ATTR_NET_PEER_NAME ] : ( value ) => {
194
+ segment . addAttribute ( 'host' , value )
195
+ } ,
196
+ [ ATTR_DB_NAME ] : ( value ) => {
197
+ segment . addAttribute ( 'database_name' , value )
198
+ } ,
199
+ [ ATTR_DB_SYSTEM ] : ( value ) => {
200
+ segment . addAttribute ( 'product' , value )
201
+ /*
202
+ * This attribute was collected in `onStart`
203
+ * and was passed to `ParsedStatement`. It adds
204
+ * this segment attribute as `sql` or `sql_obfuscated`
205
+ * and then when the span is built from segment
206
+ * re-assigns to `db.statement`. This needs
207
+ * to be skipped because it will be the raw value.
208
+ */
209
+ } ,
210
+ [ ATTR_DB_STATEMENT ] : ( ) => { }
230
211
}
212
+ this . #reconciler. reconcile ( { segment, otelSpan : span , mapper } )
231
213
}
232
214
233
215
reconcileProducerAttributes ( { segment, span } ) {
234
- for ( const [ prop , value ] of Object . entries ( span . attributes ) ) {
235
- let key = prop
236
- let sanitized = value
237
-
238
- if ( prop === ATTR_SERVER_ADDRESS ) {
239
- key = 'host'
240
- if ( urltils . isLocalhost ( sanitized ) ) {
241
- sanitized = this . agent . config . getHostnameSafe ( sanitized )
242
- }
243
- } else if ( prop === ATTR_SERVER_PORT ) {
244
- key = 'port'
245
- } else if ( prop === ATTR_MESSAGING_MESSAGE_CONVERSATION_ID ) {
246
- key = 'correlation_id'
247
- } else if ( prop === ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ) {
248
- key = 'routing_key'
249
- }
250
-
251
- segment . addAttribute ( key , sanitized )
216
+ const mapper = {
217
+ [ ATTR_SERVER_ADDRESS ] : ( value ) => segment . addAttribute ( 'host' , value ) ,
218
+ [ ATTR_SERVER_PORT ] : ( value ) => segment . addAttribute ( 'port' , value ) ,
219
+ [ ATTR_MESSAGING_MESSAGE_CONVERSATION_ID ] : ( value ) => segment . addAttribute ( 'correlation_id' , value ) ,
220
+ [ ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ] : ( value ) => segment . addAttribute ( 'routing_key' , value )
252
221
}
222
+ this . #reconciler. reconcile ( { segment, otelSpan : span , mapper } )
253
223
}
254
224
}
0 commit comments