1- import type { TransactionEvent } from '@sentry/core' ;
1+ import {
2+ MESSAGING_DESTINATION_NAME ,
3+ MESSAGING_OPERATION_NAME ,
4+ MESSAGING_OPERATION_TYPE ,
5+ MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ,
6+ MESSAGING_SYSTEM ,
7+ NETWORK_PROTOCOL_NAME ,
8+ NETWORK_PROTOCOL_VERSION ,
9+ SENTRY_KIND ,
10+ SENTRY_OP ,
11+ SENTRY_ORIGIN ,
12+ SERVER_ADDRESS ,
13+ SERVER_PORT ,
14+ URL_FULL ,
15+ } from '@sentry/conventions/attributes' ;
216import { afterAll , describe , expect } from 'vitest' ;
317import { cleanupChildProcesses , createEsmAndCjsTests , describeWithDockerCompose } from '../../../utils/runner' ;
418
@@ -8,37 +22,35 @@ import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose
822// default (empty) exchange with the queue name as the routing key.
923const expectedProducerSpan = ( routingKey : string ) =>
1024 expect . objectContaining ( {
11- op : 'queue.publish' ,
12- data : expect . objectContaining ( {
13- 'messaging.system' : 'rabbitmq' ,
14- 'messaging.operation.name' : 'send' ,
15- 'messaging.operation.type' : 'send' ,
16- 'messaging.destination.name' : routingKey ,
17- 'messaging.rabbitmq.destination.routing_key' : routingKey ,
18- 'network.protocol.name' : 'AMQP' ,
19- 'network.protocol.version' : '0.9.1' ,
20- 'server.address' : 'localhost' ,
21- 'server.port' : 5672 ,
22- 'url.full' : 'amqp://sentry:***@localhost:5672/' ,
23- 'sentry.kind' : 'producer' ,
24- 'sentry.op' : 'queue.publish' ,
25- 'sentry.origin' : 'auto.amqplib.publisher' ,
25+ attributes : expect . objectContaining ( {
26+ [ MESSAGING_SYSTEM ] : { type : 'string' , value : 'rabbitmq' } ,
27+ [ MESSAGING_OPERATION_NAME ] : { type : 'string' , value : 'send' } ,
28+ [ MESSAGING_OPERATION_TYPE ] : { type : 'string' , value : 'send' } ,
29+ [ MESSAGING_DESTINATION_NAME ] : { type : 'string' , value : routingKey } ,
30+ [ MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ] : { type : 'string' , value : routingKey } ,
31+ [ NETWORK_PROTOCOL_NAME ] : { type : 'string' , value : 'AMQP' } ,
32+ [ NETWORK_PROTOCOL_VERSION ] : { type : 'string' , value : '0.9.1' } ,
33+ [ SERVER_ADDRESS ] : { type : 'string' , value : 'localhost' } ,
34+ [ SERVER_PORT ] : { type : 'integer' , value : 5672 } ,
35+ [ URL_FULL ] : { type : 'string' , value : 'amqp://sentry:***@localhost:5672/' } ,
36+ [ SENTRY_KIND ] : { type : 'string' , value : 'producer' } ,
37+ [ SENTRY_OP ] : { type : 'string' , value : 'queue.publish' } ,
38+ [ SENTRY_ORIGIN ] : { type : 'string' , value : 'auto.amqplib.publisher' } ,
2639 } ) ,
2740 status : 'ok' ,
2841 } ) ;
2942
3043const EXPECTED_MESSAGE_SPAN_CONSUMER = expect . objectContaining ( {
31- op : 'queue.process' ,
32- data : expect . objectContaining ( {
33- 'messaging.system' : 'rabbitmq' ,
44+ attributes : expect . objectContaining ( {
45+ [ MESSAGING_SYSTEM ] : { type : 'string' , value : 'rabbitmq' } ,
3446 // The delivery carries the default exchange (''), so the routing key is the destination.
35- 'messaging.destination.name' : 'queue1' ,
36- 'messaging.rabbitmq.destination.routing_key' : 'queue1' ,
37- 'messaging.operation.name' : 'process' ,
38- 'messaging.operation. type' : 'process' ,
39- 'sentry.kind' : 'consumer' ,
40- 'sentry.op' : 'queue.process' ,
41- 'sentry.origin' : 'auto.amqplib.consumer' ,
47+ [ MESSAGING_DESTINATION_NAME ] : { type : 'string' , value : 'queue1' } ,
48+ [ MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY ] : { type : 'string' , value : 'queue1' } ,
49+ [ MESSAGING_OPERATION_NAME ] : { type : 'string' , value : 'process' } ,
50+ [ MESSAGING_OPERATION_TYPE ] : { type : 'string' , value : 'process' } ,
51+ [ SENTRY_KIND ] : { type : 'string' , value : 'consumer' } ,
52+ [ SENTRY_OP ] : { type : 'string' , value : 'queue.process' } ,
53+ [ SENTRY_ORIGIN ] : { type : 'string' , value : 'auto.amqplib.consumer' } ,
4254 } ) ,
4355 status : 'ok' ,
4456} ) ;
@@ -59,40 +71,27 @@ describeWithDockerCompose('amqplib auto-instrumentation', { workingDirectory: [_
5971 'instrument.mjs' ,
6072 ( createTestRunner , test ) => {
6173 test ( 'should be able to send and receive messages' , { timeout : 60_000 } , async ( ) => {
62- // The producer ('root span') and consumer ('queue1 process') transactions can
63- // arrive in any order, so we collect them and assert after both are received.
64- const receivedTransactions : TransactionEvent [ ] = [ ] ;
65-
6674 await createTestRunner ( )
6775 . expect ( {
68- transaction : ( transaction : TransactionEvent ) => {
69- receivedTransactions . push ( transaction ) ;
70- } ,
71- } )
72- . expect ( {
73- transaction : ( transaction : TransactionEvent ) => {
74- receivedTransactions . push ( transaction ) ;
75-
76- // The producer span is a child of the manually-started 'root span' transaction, so we
77- // identify it by its origin rather than by transaction name. The consumer span is its
78- // own transaction, identified by the origin on its trace context.
79- const producer = receivedTransactions . find ( t =>
80- t . spans ?. some ( s => s . data ?. [ 'sentry.origin' ] === 'auto.amqplib.publisher' ) ,
81- ) ;
82- const consumer = receivedTransactions . find (
83- t => t . contexts ?. trace ?. data ?. [ 'sentry.origin' ] === 'auto.amqplib.consumer' ,
76+ span : container => {
77+ const producer = container . items . find ( span => span . is_segment && span . name === 'root span' ) ;
78+ const consumer = container . items . find (
79+ t => t . attributes [ SENTRY_ORIGIN ] ?. value === 'auto.amqplib.consumer' ,
8480 ) ;
8581
8682 expect ( producer ) . toBeDefined ( ) ;
8783 expect ( consumer ) . toBeDefined ( ) ;
8884
89- expect ( producer ! . transaction ) . toBe ( 'root span' ) ;
90- expect ( consumer ! . transaction ) . toBe ( 'queue1 process' ) ;
85+ expect ( producer ! . name ) . toBe ( 'root span' ) ;
86+ expect ( consumer ! . name ) . toBe ( 'process queue1 ' ) ;
9187
92- const producerSpan = producer ! . spans ?. find ( s => s . data ?. [ 'sentry.origin' ] === 'auto.amqplib.publisher' ) ;
88+ const producerSpan = container . items . find (
89+ s => s . attributes [ SENTRY_ORIGIN ] ?. value === 'auto.amqplib.publisher' ,
90+ ) ;
91+ expect ( producerSpan ?. name ) . toBe ( 'send queue1' ) ;
9392 expect ( producerSpan ) . toMatchObject ( expectedProducerSpan ( 'queue1' ) ) ;
9493
95- expect ( consumer ! . contexts ?. trace ) . toMatchObject ( EXPECTED_MESSAGE_SPAN_CONSUMER ) ;
94+ expect ( consumer ! ) . toMatchObject ( EXPECTED_MESSAGE_SPAN_CONSUMER ) ;
9695 } ,
9796 } )
9897 . start ( )
@@ -108,35 +107,23 @@ describeWithDockerCompose('amqplib auto-instrumentation', { workingDirectory: [_
108107 'instrument.mjs' ,
109108 ( createTestRunner , test ) => {
110109 test ( 'marks the consumer span as errored when the message is rejected' , { timeout : 60_000 } , async ( ) => {
111- // The error scenario emits the producer ('root span') and the rejected consumer
112- // ('queue1 process') transactions in any order, so we collect both and assert on the consumer.
113- const receivedTransactions : TransactionEvent [ ] = [ ] ;
114-
115110 await createTestRunner ( )
116111 . expect ( {
117- transaction : ( transaction : TransactionEvent ) => {
118- receivedTransactions . push ( transaction ) ;
119- } ,
120- } )
121- . expect ( {
122- transaction : ( transaction : TransactionEvent ) => {
123- receivedTransactions . push ( transaction ) ;
124-
125- const consumer = receivedTransactions . find (
126- t => t . contexts ?. trace ?. data ?. [ 'sentry.origin' ] === 'auto.amqplib.consumer' ,
112+ span : container => {
113+ const consumer = container . items . find (
114+ t => t . attributes [ SENTRY_ORIGIN ] ?. value === 'auto.amqplib.consumer' ,
127115 ) ;
128116
129117 expect ( consumer ) . toBeDefined ( ) ;
130- expect ( consumer ! . transaction ) . toBe ( 'queue-error process ' ) ;
131- expect ( consumer ! . contexts ?. trace ) . toMatchObject (
118+ expect ( consumer ! . name ) . toBe ( 'process queue-error' ) ;
119+ expect ( consumer ! ) . toMatchObject (
132120 expect . objectContaining ( {
133- op : 'queue.process' ,
134- status : 'internal_error' ,
135- data : expect . objectContaining ( {
136- 'messaging.system' : 'rabbitmq' ,
137- 'sentry.kind' : 'consumer' ,
138- 'sentry.op' : 'queue.process' ,
139- 'sentry.origin' : 'auto.amqplib.consumer' ,
121+ status : 'error' ,
122+ attributes : expect . objectContaining ( {
123+ [ MESSAGING_SYSTEM ] : { type : 'string' , value : 'rabbitmq' } ,
124+ [ SENTRY_KIND ] : { type : 'string' , value : 'consumer' } ,
125+ [ SENTRY_OP ] : { type : 'string' , value : 'queue.process' } ,
126+ [ SENTRY_ORIGIN ] : { type : 'string' , value : 'auto.amqplib.consumer' } ,
140127 } ) ,
141128 } ) ,
142129 ) ;
@@ -160,11 +147,11 @@ describeWithDockerCompose('amqplib auto-instrumentation', { workingDirectory: [_
160147 async ( ) => {
161148 await createTestRunner ( )
162149 . expect ( {
163- transaction : ( transaction : TransactionEvent ) => {
164- expect ( transaction . transaction ) . toBe ( 'root span' ) ;
150+ span : container => {
151+ expect ( container . items . find ( span => span . is_segment ) ?. name ) . toBe ( 'root span' ) ;
165152
166- const producerSpans = transaction . spans ? .filter (
167- s => s . data ?. [ 'sentry.origin' ] === 'auto.amqplib.publisher' ,
153+ const producerSpans = container . items . filter (
154+ s => s . attributes [ SENTRY_ORIGIN ] ?. value === 'auto.amqplib.publisher' ,
168155 ) ;
169156
170157 // The confirm channel internally calls the base publish; the instrumentation must not
@@ -181,35 +168,4 @@ describeWithDockerCompose('amqplib auto-instrumentation', { workingDirectory: [_
181168 { additionalDependencies } ,
182169 ) ;
183170 } ) ;
184-
185- createEsmAndCjsTests ( __dirname , 'scenario.mjs' , 'instrument-span-streaming.mjs' , ( createTestRunner , test ) => {
186- test ( 'names streamed spans after the messaging conventions' , { timeout : 60_000 } , async ( ) => {
187- await createTestRunner ( )
188- . ignore ( 'event' )
189- . expect ( {
190- span : container => {
191- // `sendToQueue` publishes to the default exchange, which has no name. Its routing key is the
192- // queue name, so it is the destination rather than per-message data.
193- for ( const origin of [ 'auto.amqplib.publisher' , 'auto.amqplib.consumer' ] ) {
194- const span = container . items . find ( item => item . attributes [ 'sentry.origin' ] ?. value === origin ) ;
195- expect ( span ) . toBeDefined ( ) ;
196- expect ( span ! . attributes [ 'messaging.destination.name' ] ?. value ) . toBe ( 'queue1' ) ;
197- expect ( span ! . attributes [ 'messaging.rabbitmq.destination.routing_key' ] ?. value ) . toBe ( 'queue1' ) ;
198- }
199-
200- const producerSpan = container . items . find (
201- span => span . attributes [ 'sentry.origin' ] ?. value === 'auto.amqplib.publisher' ,
202- ) ;
203- expect ( producerSpan ! . name ) . toBe ( 'send queue1' ) ;
204-
205- const consumerSpan = container . items . find (
206- span => span . attributes [ 'sentry.origin' ] ?. value === 'auto.amqplib.consumer' ,
207- ) ;
208- expect ( consumerSpan ! . name ) . toBe ( 'process queue1' ) ;
209- } ,
210- } )
211- . start ( )
212- . completed ( ) ;
213- } ) ;
214- } ) ;
215171} ) ;
0 commit comments