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
52
53
import org .springframework .kafka .core .KafkaAdmin ;
53
54
import org .springframework .kafka .core .KafkaTemplate ;
54
55
import org .springframework .kafka .core .ProducerFactory ;
55
- import org .springframework .kafka .listener .AbstractMessageListenerContainer ;
56
56
import org .springframework .kafka .support .micrometer .KafkaListenerObservation .DefaultKafkaListenerObservationConvention ;
57
57
import org .springframework .kafka .support .micrometer .KafkaTemplateObservation .DefaultKafkaTemplateObservationConvention ;
58
58
import org .springframework .kafka .test .EmbeddedKafkaBroker ;
83
83
/**
84
84
* @author Gary Russell
85
85
* @author Artem Bilan
86
+ * @author Wang Zhiyang
86
87
*
87
88
* @since 3.0
88
89
*/
89
90
@ SpringJUnitConfig
90
- @ EmbeddedKafka (topics = { "observation.testT1" , "observation.testT2" , "ObservationTests.testT3" })
91
+ @ EmbeddedKafka (topics = { "observation.testT1" , "observation.testT2" , "observation.testT3" ,
92
+ ObservationTests .OBSERVATION_RUNTIME_EXCEPTION , ObservationTests .OBSERVATION_ERROR })
91
93
@ DirtiesContext
92
94
public class ObservationTests {
93
95
96
+ public final static String OBSERVATION_RUNTIME_EXCEPTION = "observation.runtime-exception" ;
97
+
98
+ public final static String OBSERVATION_ERROR = "observation.error" ;
99
+
94
100
@ Test
95
101
void endToEnd (@ Autowired Listener listener , @ Autowired KafkaTemplate <Integer , String > template ,
96
102
@ Autowired SimpleTracer tracer , @ Autowired KafkaListenerEndpointRegistry rler ,
@@ -103,8 +109,8 @@ void endToEnd(@Autowired Listener listener, @Autowired KafkaTemplate<Integer, St
103
109
assertThat (listener .latch1 .await (10 , TimeUnit .SECONDS )).isTrue ();
104
110
assertThat (listener .record ).isNotNull ();
105
111
Headers headers = listener .record .headers ();
106
- assertThat (headers .lastHeader ("foo" )).extracting (hdr -> hdr . value () ).isEqualTo ("some foo value" .getBytes ());
107
- assertThat (headers .lastHeader ("bar" )).extracting (hdr -> hdr . value () ).isEqualTo ("some bar value" .getBytes ());
112
+ assertThat (headers .lastHeader ("foo" )).extracting (Header :: value ).isEqualTo ("some foo value" .getBytes ());
113
+ assertThat (headers .lastHeader ("bar" )).extracting (Header :: value ).isEqualTo ("some bar value" .getBytes ());
108
114
Deque <SimpleSpan > spans = tracer .getSpans ();
109
115
assertThat (spans ).hasSize (4 );
110
116
SimpleSpan span = spans .poll ();
@@ -145,14 +151,15 @@ public KeyValues getLowCardinalityKeyValues(KafkaRecordReceiverContext context)
145
151
}
146
152
147
153
});
154
+
148
155
rler .getListenerContainer ("obs1" ).stop ();
149
156
rler .getListenerContainer ("obs1" ).start ();
150
157
template .send ("observation.testT1" , "test" ).get (10 , TimeUnit .SECONDS );
151
158
assertThat (listener .latch2 .await (10 , TimeUnit .SECONDS )).isTrue ();
152
159
assertThat (listener .record ).isNotNull ();
153
160
headers = listener .record .headers ();
154
- assertThat (headers .lastHeader ("foo" )).extracting (hdr -> hdr . value () ).isEqualTo ("some foo value" .getBytes ());
155
- assertThat (headers .lastHeader ("bar" )).extracting (hdr -> hdr . value () ).isEqualTo ("some bar value" .getBytes ());
161
+ assertThat (headers .lastHeader ("foo" )).extracting (Header :: value ).isEqualTo ("some foo value" .getBytes ());
162
+ assertThat (headers .lastHeader ("bar" )).extracting (Header :: value ).isEqualTo ("some bar value" .getBytes ());
156
163
assertThat (spans ).hasSize (4 );
157
164
span = spans .poll ();
158
165
assertThat (span .getTags ()).containsEntry ("spring.kafka.template.name" , "template" );
@@ -227,6 +234,48 @@ public KeyValues getLowCardinalityKeyValues(KafkaRecordReceiverContext context)
227
234
.doesNotHaveMeterWithNameAndTags ("spring.kafka.template" , KeyValues .of ("error" , "KafkaException" ));
228
235
}
229
236
237
+ @ Test
238
+ void observationRuntimeException (@ Autowired ExceptionListener listener , @ Autowired SimpleTracer tracer ,
239
+ @ Autowired @ Qualifier ("throwableTemplate" ) KafkaTemplate <Integer , String > runtimeExceptionTemplate ,
240
+ @ Autowired KafkaListenerEndpointRegistry endpointRegistry )
241
+ throws ExecutionException , InterruptedException , TimeoutException {
242
+
243
+ runtimeExceptionTemplate .send (OBSERVATION_RUNTIME_EXCEPTION , "testRuntimeException" ).get (10 , TimeUnit .SECONDS );
244
+ assertThat (listener .latch4 .await (10 , TimeUnit .SECONDS )).isTrue ();
245
+ endpointRegistry .getListenerContainer ("obs4" ).stop ();
246
+
247
+ Deque <SimpleSpan > spans = tracer .getSpans ();
248
+ assertThat (spans ).hasSize (2 );
249
+ SimpleSpan span = spans .poll ();
250
+ assertThat (span .getTags ().get ("spring.kafka.template.name" )).isEqualTo ("throwableTemplate" );
251
+ span = spans .poll ();
252
+ assertThat (span .getTags ().get ("spring.kafka.listener.id" )).isEqualTo ("obs4-0" );
253
+ assertThat (span .getError ().getCause ())
254
+ .isInstanceOf (IllegalStateException .class )
255
+ .hasMessage ("obs4 run time exception" );
256
+ }
257
+
258
+ @ Test
259
+ void observationErrorException (@ Autowired ExceptionListener listener , @ Autowired SimpleTracer tracer ,
260
+ @ Autowired @ Qualifier ("throwableTemplate" ) KafkaTemplate <Integer , String > errorTemplate ,
261
+ @ Autowired KafkaListenerEndpointRegistry endpointRegistry )
262
+ throws ExecutionException , InterruptedException , TimeoutException {
263
+
264
+ errorTemplate .send (OBSERVATION_ERROR , "testError" ).get (10 , TimeUnit .SECONDS );
265
+ assertThat (listener .latch5 .await (10 , TimeUnit .SECONDS )).isTrue ();
266
+ endpointRegistry .getListenerContainer ("obs5" ).stop ();
267
+
268
+ Deque <SimpleSpan > spans = tracer .getSpans ();
269
+ assertThat (spans ).hasSize (2 );
270
+ SimpleSpan span = spans .poll ();
271
+ assertThat (span .getTags ().get ("spring.kafka.template.name" )).isEqualTo ("throwableTemplate" );
272
+ span = spans .poll ();
273
+ assertThat (span .getTags ().get ("spring.kafka.listener.id" )).isEqualTo ("obs5-0" );
274
+ assertThat (span .getError ())
275
+ .isInstanceOf (Error .class )
276
+ .hasMessage ("obs5 error" );
277
+ }
278
+
230
279
@ Configuration
231
280
@ EnableKafka
232
281
public static class Config {
@@ -272,6 +321,13 @@ KafkaTemplate<Integer, String> customTemplate(ProducerFactory<Integer, String> p
272
321
return template ;
273
322
}
274
323
324
+ @ Bean
325
+ KafkaTemplate <Integer , String > throwableTemplate (ProducerFactory <Integer , String > pf ) {
326
+ KafkaTemplate <Integer , String > template = new KafkaTemplate <>(pf );
327
+ template .setObservationEnabled (true );
328
+ return template ;
329
+ }
330
+
275
331
@ Bean
276
332
ConcurrentKafkaListenerContainerFactory <Integer , String > kafkaListenerContainerFactory (
277
333
ConsumerFactory <Integer , String > cf ) {
@@ -282,7 +338,7 @@ ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerF
282
338
factory .getContainerProperties ().setObservationEnabled (true );
283
339
factory .setContainerCustomizer (container -> {
284
340
if (container .getListenerId ().equals ("obs3" )) {
285
- (( AbstractMessageListenerContainer < Integer , String >) container ) .setKafkaAdmin (this .mockAdmin );
341
+ container .setKafkaAdmin (this .mockAdmin );
286
342
}
287
343
});
288
344
return factory ;
@@ -348,6 +404,11 @@ Listener listener(KafkaTemplate<Integer, String> template) {
348
404
return new Listener (template );
349
405
}
350
406
407
+ @ Bean
408
+ ExceptionListener exceptionListener () {
409
+ return new ExceptionListener ();
410
+ }
411
+
351
412
}
352
413
353
414
public static class Listener {
@@ -383,4 +444,24 @@ void listen3(ConsumerRecord<Integer, String> in) {
383
444
384
445
}
385
446
447
+ public static class ExceptionListener {
448
+
449
+ final CountDownLatch latch4 = new CountDownLatch (1 );
450
+
451
+ final CountDownLatch latch5 = new CountDownLatch (1 );
452
+
453
+ @ KafkaListener (id = "obs4" , topics = OBSERVATION_RUNTIME_EXCEPTION )
454
+ void listenRuntimeException (ConsumerRecord <Integer , String > in ) {
455
+ this .latch4 .countDown ();
456
+ throw new IllegalStateException ("obs4 run time exception" );
457
+ }
458
+
459
+ @ KafkaListener (id = "obs5" , topics = OBSERVATION_ERROR )
460
+ void listenError (ConsumerRecord <Integer , String > in ) {
461
+ this .latch5 .countDown ();
462
+ throw new Error ("obs5 error" );
463
+ }
464
+
465
+ }
466
+
386
467
}
0 commit comments