2

I am using Brave library https://github.com/openzipkin/brave for tracing and now I would like to use it also for Kafka consumer. I would like to avoid adding Spring Sleuth and leverage just Brave Kafka instrumentation https://github.com/openzipkin/brave/tree/master/instrumentation/kafka-clients.

For the Kafka consumer I use @KafkaListener. The code looks like this:

TestKafkaEndpoint.java

@Service
public class TestKafkaEndpoint {

    @KafkaListener(topics = "myTestTopic", containerFactory = "testKafkaListenerContainerFactory")
    public void procesMyRequest(@Payload final MyRequest request) {
       // do some magic...
    }
}

And configuration class TestKafkaConfig.java


@Configuration
@EnableKafka
@ComponentScan
public class TestKafkaConfig {

    @Bean
    public ConsumerFactory<String, MyRequest> testConsumerFactory() {
        final Map<String, Object> consumerProperties = new HashMap<>();
        consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka01-localhost:9092");
        consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "TestGROUP");
        return new DefaultKafkaConsumerFactory<>(consumerProperties, new StringDeserializer(), new JsonDeserializer<>(MyRequest.class));
    }

    @Bean
    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, MyRequest>> testKafkaListenerContainerFactory() {
        final ConcurrentKafkaListenerContainerFactory<String, MyRequest> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(testConsumerFactory());
        factory.getContainerProperties().setErrorHandler(new LoggingErrorHandler());
        return factory;
    }

But I do not know how to use KafkaConsumer when using Kafka factory or leverage the KafkaTracing. Does anyone has any experience with that and got it working?

Zis
  • 162
  • 16

2 Answers2

2

I'm not familiar with it, but it looks like the TracingConsumer is a simple consumer wrapper: https://github.com/openzipkin/brave/blob/363ceb4c922305ffb4a68ac47dc152e1d15da0fb/instrumentation/kafka-clients/src/main/java/brave/kafka/clients/TracingConsumer.java#L69-L79

You should be able to create a subclass of DefaultKafkaConsumerFactory; override the createConsumer method(s) - the listener container uses...

this.consumer =
        KafkaMessageListenerContainer.this.consumerFactory.createConsumer(
                this.consumerGroupId,
                this.containerProperties.getClientId(),
                KafkaMessageListenerContainer.this.clientIdSuffix,
                consumerProperties);

... call super.createConsumer(...) and wrap it in a TracingConsumer.

If you are using 2.5.3 or later, you can add a ConsumerPostProcessor to the DKCF.

That's how sleuth does it:

https://github.com/spring-cloud/spring-cloud-sleuth/blob/6e306e594d20361483fd19739e0f5f8e82354bf5/spring-cloud-sleuth-brave/src/main/java/org/springframework/cloud/sleuth/brave/instrument/messaging/TraceMessagingAutoConfiguration.java#L263-L285

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Hi Gary, thank you for your splendid comment. I already started looking into subclassing the DefaultKafkaConsumerFactory, which may be the way to go. I'll also have a look to ConsumerPostProcessor which sounds a bit neater solution and will post back my experience, final solution here :) – Zis Nov 19 '20 at 07:43
  • The subclass of DefaultKafkaConsumerFactory helped. Thank you :) – Zis Dec 08 '20 at 11:46
1

For anyone still looking for a way to solve this issue, I managed to implement a solution using a ConsumerPostProcessor.

@Configuration
@EnableKafka
public class Config {

    @Value(value = "${spring.kafka.bootstrap-servers}")
    private String bootstrapAddress;

    @Bean
    ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(KafkaTracing kafkaTracing) {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        props.put(JsonDeserializer.TRUSTED_PACKAGES, "*");
        ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        ConsumerFactory<String, String> consumerFactory = new DefaultKafkaConsumerFactory<>(props);
        consumerFactory.addPostProcessor(kafkaTracing::consumer);
        factory.setConsumerFactory(consumerFactory);
        factory.getContainerProperties().setObservationEnabled(true);
        return factory;
    }

    @Bean
    KafkaTracing kafkaStreamsTracing(Tracing tracing) {
        return KafkaTracing.create(tracing);
    }
}