4
4
*/
5
5
6
6
'use strict'
7
+
7
8
const assert = require ( 'node:assert' )
8
9
const test = require ( 'node:test' )
9
- const helper = require ( '../../lib/agent_helper' )
10
10
const otel = require ( '@opentelemetry/api' )
11
11
const { hrTimeToMilliseconds } = require ( '@opentelemetry/core' )
12
+
13
+ const helper = require ( '../../lib/agent_helper' )
12
14
const { otelSynthesis } = require ( '../../../lib/symbols' )
13
- const { SEMATTRS_HTTP_HOST , SEMATTRS_HTTP_METHOD , SEMATTRS_DB_NAME , SEMATTRS_DB_STATEMENT , SEMATTRS_DB_SYSTEM , SEMATTRS_NET_PEER_PORT , SEMATTRS_NET_PEER_NAME , DbSystemValues } = require ( '@opentelemetry/semantic-conventions' )
15
+
16
+ const {
17
+ ATTR_DB_NAME ,
18
+ ATTR_DB_STATEMENT ,
19
+ ATTR_DB_SYSTEM ,
20
+ ATTR_HTTP_HOST ,
21
+ ATTR_HTTP_METHOD ,
22
+ ATTR_HTTP_REQUEST_METHOD ,
23
+ ATTR_HTTP_ROUTE ,
24
+ ATTR_NET_PEER_NAME ,
25
+ ATTR_NET_PEER_PORT ,
26
+ ATTR_RPC_METHOD ,
27
+ ATTR_RPC_SERVICE ,
28
+ ATTR_RPC_SYSTEM ,
29
+ ATTR_SERVER_ADDRESS ,
30
+ ATTR_URL_PATH ,
31
+ ATTR_URL_SCHEME ,
32
+ DB_SYSTEM_VALUES
33
+ } = require ( '../../../lib/otel/constants.js' )
14
34
15
35
test . beforeEach ( ( ctx ) => {
16
36
const agent = helper . instrumentMockedAgent ( {
@@ -85,7 +105,7 @@ test('Otel http external span test', (t, end) => {
85
105
const { agent, tracer } = t . nr
86
106
helper . runInTransaction ( agent , ( tx ) => {
87
107
tx . name = 'http-external-test'
88
- tracer . startActiveSpan ( 'http-outbound' , { kind : otel . SpanKind . CLIENT , attributes : { [ SEMATTRS_HTTP_HOST ] : 'newrelic.com' , [ SEMATTRS_HTTP_METHOD ] : 'GET' } } , ( span ) => {
108
+ tracer . startActiveSpan ( 'http-outbound' , { kind : otel . SpanKind . CLIENT , attributes : { [ ATTR_HTTP_HOST ] : 'newrelic.com' , [ ATTR_HTTP_METHOD ] : 'GET' } } , ( span ) => {
89
109
const segment = agent . tracer . getSegment ( )
90
110
assert . equal ( segment . name , 'External/newrelic.com' )
91
111
span . end ( )
@@ -107,11 +127,11 @@ test('Otel http external span test', (t, end) => {
107
127
test ( 'Otel db client span statement test' , ( t , end ) => {
108
128
const { agent, tracer } = t . nr
109
129
const attributes = {
110
- [ SEMATTRS_DB_NAME ] : 'test-db' ,
111
- [ SEMATTRS_DB_SYSTEM ] : 'postgresql' ,
112
- [ SEMATTRS_DB_STATEMENT ] : "select foo from test where foo = 'bar';" ,
113
- [ SEMATTRS_NET_PEER_PORT ] : 5436 ,
114
- [ SEMATTRS_NET_PEER_NAME ] : '127.0.0.1'
130
+ [ ATTR_DB_NAME ] : 'test-db' ,
131
+ [ ATTR_DB_SYSTEM ] : 'postgresql' ,
132
+ [ ATTR_DB_STATEMENT ] : "select foo from test where foo = 'bar';" ,
133
+ [ ATTR_NET_PEER_PORT ] : 5436 ,
134
+ [ ATTR_NET_PEER_NAME ] : '127.0.0.1'
115
135
}
116
136
const expectedHost = agent . config . getHostnameSafe ( '127.0.0.1' )
117
137
helper . runInTransaction ( agent , ( tx ) => {
@@ -152,10 +172,10 @@ test('Otel db client span statement test', (t, end) => {
152
172
test ( 'Otel db client span operation test' , ( t , end ) => {
153
173
const { agent, tracer } = t . nr
154
174
const attributes = {
155
- [ SEMATTRS_DB_SYSTEM ] : DbSystemValues . REDIS ,
156
- [ SEMATTRS_DB_STATEMENT ] : 'hset has random random' ,
157
- [ SEMATTRS_NET_PEER_PORT ] : 5436 ,
158
- [ SEMATTRS_NET_PEER_NAME ] : '127.0.0.1'
175
+ [ ATTR_DB_SYSTEM ] : DB_SYSTEM_VALUES . REDIS ,
176
+ [ ATTR_DB_STATEMENT ] : 'hset has random random' ,
177
+ [ ATTR_NET_PEER_PORT ] : 5436 ,
178
+ [ ATTR_NET_PEER_NAME ] : '127.0.0.1'
159
179
}
160
180
const expectedHost = agent . config . getHostnameSafe ( '127.0.0.1' )
161
181
helper . runInTransaction ( agent , ( tx ) => {
@@ -189,3 +209,147 @@ test('Otel db client span operation test', (t, end) => {
189
209
} )
190
210
} )
191
211
} )
212
+
213
+ test ( 'http metrics are bridged correctly' , ( t , end ) => {
214
+ const { agent, tracer } = t . nr
215
+
216
+ // Required span attributes for incoming HTTP server spans as defined by:
217
+ // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-server-semantic-conventions
218
+ const attributes = {
219
+ [ ATTR_URL_SCHEME ] : 'http' ,
220
+ [ ATTR_SERVER_ADDRESS ] : 'newrelic.com' ,
221
+ [ ATTR_HTTP_REQUEST_METHOD ] : 'GET' ,
222
+ [ ATTR_URL_PATH ] : '/foo/bar' ,
223
+ [ ATTR_HTTP_ROUTE ] : '/foo/:param'
224
+ }
225
+
226
+ tracer . startActiveSpan ( 'http-test' , { kind : otel . SpanKind . SERVER , attributes } , ( span ) => {
227
+ const tx = agent . getTransaction ( )
228
+ const segment = agent . tracer . getSegment ( )
229
+ assert . equal ( segment . name , 'WebTransaction/Nodejs/GET//foo/:param' )
230
+ span . end ( )
231
+
232
+ const duration = hrTimeToMilliseconds ( span . duration )
233
+ assert . equal ( duration , segment . getDurationInMillis ( ) )
234
+ tx . end ( )
235
+
236
+ const attrs = segment . getAttributes ( )
237
+ assert . equal ( attrs . host , 'newrelic.com' )
238
+ assert . equal ( attrs [ 'http.request.method' ] , 'GET' )
239
+ assert . equal ( attrs [ 'http.route' ] , '/foo/:param' )
240
+ assert . equal ( attrs [ 'url.path' ] , '/foo/bar' )
241
+ assert . equal ( attrs [ 'url.scheme' ] , 'http' )
242
+ assert . equal ( attrs . nr_exclusive_duration_millis , duration )
243
+
244
+ const unscopedMetrics = tx . metrics . unscoped
245
+ const expectedMetrics = [
246
+ 'HttpDispatcher' ,
247
+ 'WebTransaction' ,
248
+ 'WebTransaction/Nodejs/GET//foo/:param' ,
249
+ 'WebTransactionTotalTime' ,
250
+ 'WebTransactionTotalTime/null' ,
251
+ segment . name
252
+ ]
253
+ for ( const expectedMetric of expectedMetrics ) {
254
+ assert . equal ( unscopedMetrics [ expectedMetric ] . callCount , 1 , `${ expectedMetric } has correct callCount` )
255
+ }
256
+ assert . equal ( unscopedMetrics . Apdex . apdexT , 0.1 )
257
+ assert . equal ( unscopedMetrics [ 'Apdex/null' ] . apdexT , 0.1 )
258
+
259
+ end ( )
260
+ } )
261
+ } )
262
+
263
+ test ( 'rpc server metrics are bridged correctly' , ( t , end ) => {
264
+ const { agent, tracer } = t . nr
265
+
266
+ // Required span attributes for incoming HTTP server spans as defined by:
267
+ // https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans/#client-attributes
268
+ const attributes = {
269
+ [ ATTR_RPC_SYSTEM ] : 'foo' ,
270
+ [ ATTR_RPC_METHOD ] : 'getData' ,
271
+ [ ATTR_RPC_SERVICE ] : 'test.service' ,
272
+ [ ATTR_SERVER_ADDRESS ] : 'newrelic.com' ,
273
+ [ ATTR_URL_PATH ] : '/foo/bar'
274
+ }
275
+
276
+ tracer . startActiveSpan ( 'http-test' , { kind : otel . SpanKind . SERVER , attributes } , ( span ) => {
277
+ const tx = agent . getTransaction ( )
278
+ const segment = agent . tracer . getSegment ( )
279
+ assert . equal ( segment . name , 'WebTransaction/WebFrameworkUri/foo/test.service.getData' )
280
+ span . end ( )
281
+
282
+ const duration = hrTimeToMilliseconds ( span . duration )
283
+ assert . equal ( duration , segment . getDurationInMillis ( ) )
284
+ tx . end ( )
285
+
286
+ const attrs = segment . getAttributes ( )
287
+ assert . equal ( attrs . host , 'newrelic.com' )
288
+ assert . equal ( attrs [ 'rpc.system' ] , 'foo' )
289
+ assert . equal ( attrs [ 'rpc.method' ] , 'getData' )
290
+ assert . equal ( attrs [ 'rpc.service' ] , 'test.service' )
291
+ assert . equal ( attrs [ 'url.path' ] , '/foo/bar' )
292
+ assert . equal ( attrs . nr_exclusive_duration_millis , duration )
293
+
294
+ const unscopedMetrics = tx . metrics . unscoped
295
+ const expectedMetrics = [
296
+ 'HttpDispatcher' ,
297
+ 'WebTransaction' ,
298
+ 'WebTransaction/WebFrameworkUri/foo/test.service.getData' ,
299
+ 'WebTransactionTotalTime' ,
300
+ 'WebTransactionTotalTime/null' ,
301
+ segment . name
302
+ ]
303
+ for ( const expectedMetric of expectedMetrics ) {
304
+ assert . equal ( unscopedMetrics [ expectedMetric ] . callCount , 1 , `${ expectedMetric } has correct callCount` )
305
+ }
306
+ assert . equal ( unscopedMetrics . Apdex . apdexT , 0.1 )
307
+ assert . equal ( unscopedMetrics [ 'Apdex/null' ] . apdexT , 0.1 )
308
+
309
+ end ( )
310
+ } )
311
+ } )
312
+
313
+ test ( 'fallback metrics are bridged correctly' , ( t , end ) => {
314
+ const { agent, tracer } = t . nr
315
+
316
+ const attributes = {
317
+ [ ATTR_URL_SCHEME ] : 'gopher' ,
318
+ [ ATTR_SERVER_ADDRESS ] : 'newrelic.com' ,
319
+ [ ATTR_URL_PATH ] : '/foo/bar' ,
320
+ }
321
+
322
+ tracer . startActiveSpan ( 'http-test' , { kind : otel . SpanKind . SERVER , attributes } , ( span ) => {
323
+ const tx = agent . getTransaction ( )
324
+ const segment = agent . tracer . getSegment ( )
325
+ assert . equal ( segment . name , 'WebTransaction/NormalizedUri/*' )
326
+ span . end ( )
327
+
328
+ const duration = hrTimeToMilliseconds ( span . duration )
329
+ assert . equal ( duration , segment . getDurationInMillis ( ) )
330
+ tx . end ( )
331
+
332
+ const attrs = segment . getAttributes ( )
333
+ assert . equal ( attrs . host , 'newrelic.com' )
334
+ assert . equal ( attrs [ 'url.path' ] , '/foo/bar' )
335
+ assert . equal ( attrs [ 'url.scheme' ] , 'gopher' )
336
+ assert . equal ( attrs . nr_exclusive_duration_millis , duration )
337
+
338
+ const unscopedMetrics = tx . metrics . unscoped
339
+ const expectedMetrics = [
340
+ 'HttpDispatcher' ,
341
+ 'WebTransaction' ,
342
+ 'WebTransaction/NormalizedUri/*' ,
343
+ 'WebTransactionTotalTime' ,
344
+ 'WebTransactionTotalTime/null' ,
345
+ segment . name
346
+ ]
347
+ for ( const expectedMetric of expectedMetrics ) {
348
+ assert . equal ( unscopedMetrics [ expectedMetric ] . callCount , 1 , `${ expectedMetric } has correct callCount` )
349
+ }
350
+ assert . equal ( unscopedMetrics . Apdex . apdexT , 0.1 )
351
+ assert . equal ( unscopedMetrics [ 'Apdex/null' ] . apdexT , 0.1 )
352
+
353
+ end ( )
354
+ } )
355
+ } )
0 commit comments