1
1
/*
2
- * Copyright 2022-2023 the original author or authors.
2
+ * Copyright 2022-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
35
35
import org .apache .kafka .clients .consumer .ConsumerRecord ;
36
36
import org .apache .kafka .clients .producer .ProducerConfig ;
37
37
import org .apache .kafka .common .errors .InvalidTopicException ;
38
+ import org .apache .kafka .common .header .Header ;
38
39
import org .apache .kafka .common .header .Headers ;
39
40
import org .junit .jupiter .api .Test ;
40
41
54
55
import org .springframework .kafka .core .KafkaAdmin ;
55
56
import org .springframework .kafka .core .KafkaTemplate ;
56
57
import org .springframework .kafka .core .ProducerFactory ;
57
- import org .springframework .kafka .listener .AbstractMessageListenerContainer ;
58
58
import org .springframework .kafka .support .micrometer .KafkaListenerObservation .DefaultKafkaListenerObservationConvention ;
59
59
import org .springframework .kafka .support .micrometer .KafkaTemplateObservation .DefaultKafkaTemplateObservationConvention ;
60
60
import org .springframework .kafka .test .EmbeddedKafkaBroker ;
85
85
/**
86
86
* @author Gary Russell
87
87
* @author Artem Bilan
88
+ * @author Wang Zhiyang
88
89
*
89
90
* @since 3.0
90
91
*/
91
92
@ SpringJUnitConfig
92
- @ EmbeddedKafka (topics = { "observation.testT1" , "observation.testT2" , "ObservationTests.testT3" })
93
+ @ EmbeddedKafka (topics = { "observation.testT1" , "observation.testT2" , "observation.testT3" ,
94
+ ObservationTests .OBSERVATION_RUNTIME_EXCEPTION , ObservationTests .OBSERVATION_ERROR })
93
95
@ DirtiesContext
94
96
public class ObservationTests {
95
97
98
+ public final static String OBSERVATION_RUNTIME_EXCEPTION = "observation.runtime-exception" ;
99
+
100
+ public final static String OBSERVATION_ERROR = "observation.error" ;
101
+
96
102
@ Test
97
103
void endToEnd (@ Autowired Listener listener , @ Autowired KafkaTemplate <Integer , String > template ,
98
104
@ Autowired SimpleTracer tracer , @ Autowired KafkaListenerEndpointRegistry rler ,
@@ -106,8 +112,8 @@ void endToEnd(@Autowired Listener listener, @Autowired KafkaTemplate<Integer, St
106
112
assertThat (listener .latch1 .await (10 , TimeUnit .SECONDS )).isTrue ();
107
113
assertThat (listener .record ).isNotNull ();
108
114
Headers headers = listener .record .headers ();
109
- assertThat (headers .lastHeader ("foo" )).extracting (hdr -> hdr . value () ).isEqualTo ("some foo value" .getBytes ());
110
- assertThat (headers .lastHeader ("bar" )).extracting (hdr -> hdr . value () ).isEqualTo ("some bar value" .getBytes ());
115
+ assertThat (headers .lastHeader ("foo" )).extracting (Header :: value ).isEqualTo ("some foo value" .getBytes ());
116
+ assertThat (headers .lastHeader ("bar" )).extracting (Header :: value ).isEqualTo ("some bar value" .getBytes ());
111
117
Deque <SimpleSpan > spans = tracer .getSpans ();
112
118
assertThat (spans ).hasSize (4 );
113
119
SimpleSpan span = spans .poll ();
@@ -148,14 +154,15 @@ public KeyValues getLowCardinalityKeyValues(KafkaRecordReceiverContext context)
148
154
}
149
155
150
156
});
157
+
151
158
rler .getListenerContainer ("obs1" ).stop ();
152
159
rler .getListenerContainer ("obs1" ).start ();
153
160
template .send ("observation.testT1" , "test" ).get (10 , TimeUnit .SECONDS );
154
161
assertThat (listener .latch2 .await (10 , TimeUnit .SECONDS )).isTrue ();
155
162
assertThat (listener .record ).isNotNull ();
156
163
headers = listener .record .headers ();
157
- assertThat (headers .lastHeader ("foo" )).extracting (hdr -> hdr . value () ).isEqualTo ("some foo value" .getBytes ());
158
- assertThat (headers .lastHeader ("bar" )).extracting (hdr -> hdr . value () ).isEqualTo ("some bar value" .getBytes ());
164
+ assertThat (headers .lastHeader ("foo" )).extracting (Header :: value ).isEqualTo ("some foo value" .getBytes ());
165
+ assertThat (headers .lastHeader ("bar" )).extracting (Header :: value ).isEqualTo ("some bar value" .getBytes ());
159
166
assertThat (spans ).hasSize (4 );
160
167
span = spans .poll ();
161
168
assertThat (span .getTags ()).containsEntry ("spring.kafka.template.name" , "template" );
@@ -230,6 +237,48 @@ public KeyValues getLowCardinalityKeyValues(KafkaRecordReceiverContext context)
230
237
.doesNotHaveMeterWithNameAndTags ("spring.kafka.template" , KeyValues .of ("error" , "KafkaException" ));
231
238
}
232
239
240
+ @ Test
241
+ void observationRuntimeException (@ Autowired ExceptionListener listener , @ Autowired SimpleTracer tracer ,
242
+ @ Autowired @ Qualifier ("throwableTemplate" ) KafkaTemplate <Integer , String > runtimeExceptionTemplate ,
243
+ @ Autowired KafkaListenerEndpointRegistry endpointRegistry )
244
+ throws ExecutionException , InterruptedException , TimeoutException {
245
+
246
+ runtimeExceptionTemplate .send (OBSERVATION_RUNTIME_EXCEPTION , "testRuntimeException" ).get (10 , TimeUnit .SECONDS );
247
+ assertThat (listener .latch4 .await (10 , TimeUnit .SECONDS )).isTrue ();
248
+ endpointRegistry .getListenerContainer ("obs4" ).stop ();
249
+
250
+ Deque <SimpleSpan > spans = tracer .getSpans ();
251
+ assertThat (spans ).hasSize (2 );
252
+ SimpleSpan span = spans .poll ();
253
+ assertThat (span .getTags ().get ("spring.kafka.template.name" )).isEqualTo ("throwableTemplate" );
254
+ span = spans .poll ();
255
+ assertThat (span .getTags ().get ("spring.kafka.listener.id" )).isEqualTo ("obs4-0" );
256
+ assertThat (span .getError ().getCause ())
257
+ .isInstanceOf (IllegalStateException .class )
258
+ .hasMessage ("obs4 run time exception" );
259
+ }
260
+
261
+ @ Test
262
+ void observationErrorException (@ Autowired ExceptionListener listener , @ Autowired SimpleTracer tracer ,
263
+ @ Autowired @ Qualifier ("throwableTemplate" ) KafkaTemplate <Integer , String > errorTemplate ,
264
+ @ Autowired KafkaListenerEndpointRegistry endpointRegistry )
265
+ throws ExecutionException , InterruptedException , TimeoutException {
266
+
267
+ errorTemplate .send (OBSERVATION_ERROR , "testError" ).get (10 , TimeUnit .SECONDS );
268
+ assertThat (listener .latch5 .await (10 , TimeUnit .SECONDS )).isTrue ();
269
+ endpointRegistry .getListenerContainer ("obs5" ).stop ();
270
+
271
+ Deque <SimpleSpan > spans = tracer .getSpans ();
272
+ assertThat (spans ).hasSize (2 );
273
+ SimpleSpan span = spans .poll ();
274
+ assertThat (span .getTags ().get ("spring.kafka.template.name" )).isEqualTo ("throwableTemplate" );
275
+ span = spans .poll ();
276
+ assertThat (span .getTags ().get ("spring.kafka.listener.id" )).isEqualTo ("obs5-0" );
277
+ assertThat (span .getError ())
278
+ .isInstanceOf (Error .class )
279
+ .hasMessage ("obs5 error" );
280
+ }
281
+
233
282
@ Configuration
234
283
@ EnableKafka
235
284
public static class Config {
@@ -276,6 +325,13 @@ KafkaTemplate<Integer, String> customTemplate(ProducerFactory<Integer, String> p
276
325
return template ;
277
326
}
278
327
328
+ @ Bean
329
+ KafkaTemplate <Integer , String > throwableTemplate (ProducerFactory <Integer , String > pf ) {
330
+ KafkaTemplate <Integer , String > template = new KafkaTemplate <>(pf );
331
+ template .setObservationEnabled (true );
332
+ return template ;
333
+ }
334
+
279
335
@ Bean
280
336
ConcurrentKafkaListenerContainerFactory <Integer , String > kafkaListenerContainerFactory (
281
337
ConsumerFactory <Integer , String > cf ) {
@@ -286,7 +342,7 @@ ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerF
286
342
factory .getContainerProperties ().setObservationEnabled (true );
287
343
factory .setContainerCustomizer (container -> {
288
344
if (container .getListenerId ().equals ("obs3" )) {
289
- (( AbstractMessageListenerContainer < Integer , String >) container ) .setKafkaAdmin (this .mockAdmin );
345
+ container .setKafkaAdmin (this .mockAdmin );
290
346
}
291
347
});
292
348
return factory ;
@@ -352,6 +408,11 @@ Listener listener(KafkaTemplate<Integer, String> template) {
352
408
return new Listener (template );
353
409
}
354
410
411
+ @ Bean
412
+ ExceptionListener exceptionListener () {
413
+ return new ExceptionListener ();
414
+ }
415
+
355
416
}
356
417
357
418
public static class Listener {
@@ -387,4 +448,24 @@ void listen3(ConsumerRecord<Integer, String> in) {
387
448
388
449
}
389
450
451
+ public static class ExceptionListener {
452
+
453
+ final CountDownLatch latch4 = new CountDownLatch (1 );
454
+
455
+ final CountDownLatch latch5 = new CountDownLatch (1 );
456
+
457
+ @ KafkaListener (id = "obs4" , topics = OBSERVATION_RUNTIME_EXCEPTION )
458
+ void listenRuntimeException (ConsumerRecord <Integer , String > in ) {
459
+ this .latch4 .countDown ();
460
+ throw new IllegalStateException ("obs4 run time exception" );
461
+ }
462
+
463
+ @ KafkaListener (id = "obs5" , topics = OBSERVATION_ERROR )
464
+ void listenError (ConsumerRecord <Integer , String > in ) {
465
+ this .latch5 .countDown ();
466
+ throw new Error ("obs5 error" );
467
+ }
468
+
469
+ }
470
+
390
471
}
0 commit comments