1use super::{CollectItemConsume, CollectItemProduce};
20use crate::reporter::{CollectItem, Report};
21use rdkafka::{
22 config::ClientConfig as RDKafkaClientConfig,
23 producer::{FutureProducer, FutureRecord},
24};
25use std::{
26 collections::HashMap,
27 error,
28 future::{Future, pending},
29 pin::Pin,
30 sync::{
31 Arc,
32 atomic::{AtomicBool, Ordering::Relaxed},
33 },
34 time::Duration,
35};
36use tokio::{select, spawn, sync::mpsc, task::JoinHandle, try_join};
37use tracing::error;
38
39#[derive(Debug, thiserror::Error)]
41pub enum Error {
42 #[error(transparent)]
44 RdKafka(#[from] rdkafka::error::KafkaError),
45
46 #[error("topic not found: {topic}")]
48 TopicNotFound {
49 topic: String,
51 },
52}
53
54#[derive(Debug, Clone, Copy)]
56pub enum LogLevel {
57 Critical,
59 Error,
61 Warning,
63 Notice,
65 Info,
67 Debug,
69}
70
71impl From<LogLevel> for rdkafka::config::RDKafkaLogLevel {
72 fn from(level: LogLevel) -> Self {
73 match level {
74 LogLevel::Critical => rdkafka::config::RDKafkaLogLevel::Critical,
75 LogLevel::Error => rdkafka::config::RDKafkaLogLevel::Error,
76 LogLevel::Warning => rdkafka::config::RDKafkaLogLevel::Warning,
77 LogLevel::Notice => rdkafka::config::RDKafkaLogLevel::Notice,
78 LogLevel::Info => rdkafka::config::RDKafkaLogLevel::Info,
79 LogLevel::Debug => rdkafka::config::RDKafkaLogLevel::Debug,
80 }
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct ClientConfig {
87 params: HashMap<String, String>,
89 log_level: Option<LogLevel>,
91}
92
93impl ClientConfig {
94 pub fn new() -> Self {
96 Self {
97 params: HashMap::new(),
98 log_level: None,
99 }
100 }
101
102 pub fn set<K, V>(&mut self, key: K, value: V) -> &mut Self
104 where
105 K: Into<String>,
106 V: Into<String>,
107 {
108 self.params.insert(key.into(), value.into());
109 self
110 }
111
112 pub fn set_log_level(&mut self, level: LogLevel) -> &mut Self {
114 self.log_level = Some(level);
115 self
116 }
117
118 fn to_rdkafka_config(&self) -> RDKafkaClientConfig {
120 let mut config = RDKafkaClientConfig::new();
121 for (key, value) in &self.params {
122 config.set(key, value);
123 }
124 if let Some(log_level) = self.log_level {
125 config.set_log_level(log_level.into());
126 }
127 config
128 }
129}
130
131impl Default for ClientConfig {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137type DynErrHandler = dyn Fn(&str, &dyn error::Error) + Send + Sync + 'static;
138
139fn default_err_handle(message: &str, err: &dyn error::Error) {
140 error!(?err, "{}", message);
141}
142
143#[derive(Default)]
144struct State {
145 is_closing: AtomicBool,
146}
147
148impl State {
149 fn is_closing(&self) -> bool {
150 self.is_closing.load(Relaxed)
151 }
152}
153
154pub struct KafkaReportBuilder<P, C> {
157 state: Arc<State>,
158 producer: Arc<P>,
159 consumer: C,
160 client_config: ClientConfig,
161 namespace: Option<String>,
162 err_handle: Arc<DynErrHandler>,
163}
164
165impl KafkaReportBuilder<mpsc::UnboundedSender<CollectItem>, mpsc::UnboundedReceiver<CollectItem>> {
166 pub fn new(client_config: ClientConfig) -> Self {
168 let (producer, consumer) = mpsc::unbounded_channel();
169 Self::new_with_pc(client_config, producer, consumer)
170 }
171}
172
173impl<P: CollectItemProduce, C: CollectItemConsume> KafkaReportBuilder<P, C> {
174 pub fn new_with_pc(client_config: ClientConfig, producer: P, consumer: C) -> Self {
177 Self {
178 state: Default::default(),
179 producer: Arc::new(producer),
180 consumer,
181 client_config,
182 namespace: None,
183 err_handle: Arc::new(default_err_handle),
184 }
185 }
186
187 pub fn with_err_handle(
189 mut self,
190 handle: impl Fn(&str, &dyn error::Error) + Send + Sync + 'static,
191 ) -> Self {
192 self.err_handle = Arc::new(handle);
193 self
194 }
195
196 pub fn with_namespace(mut self, namespace: impl Into<String>) -> Self {
199 self.namespace = Some(namespace.into());
200 self
201 }
202
203 pub async fn build(self) -> Result<(KafkaReporter<P>, KafkaReporting<C>), Error> {
206 let kafka_producer = KafkaProducer::new(
207 self.client_config.to_rdkafka_config().create()?,
208 self.err_handle.clone(),
209 self.namespace,
210 )
211 .await?;
212 Ok((
213 KafkaReporter {
214 state: self.state.clone(),
215 producer: self.producer,
216 err_handle: self.err_handle,
217 },
218 KafkaReporting {
219 state: self.state,
220 consumer: self.consumer,
221 kafka_producer,
222 shutdown_signal: Box::pin(pending()),
223 },
224 ))
225 }
226}
227
228pub struct KafkaReporter<P> {
230 state: Arc<State>,
231 producer: Arc<P>,
232 err_handle: Arc<DynErrHandler>,
233}
234
235impl<P> Clone for KafkaReporter<P> {
236 #[inline]
237 fn clone(&self) -> Self {
238 Self {
239 state: self.state.clone(),
240 producer: self.producer.clone(),
241 err_handle: self.err_handle.clone(),
242 }
243 }
244}
245
246impl<P: CollectItemProduce> Report for KafkaReporter<P> {
247 fn report(&self, item: CollectItem) {
248 if !self.state.is_closing() {
249 if let Err(e) = self.producer.produce(item) {
250 (self.err_handle)("report collect item failed", &*e);
251 }
252 }
253 }
254}
255
256pub struct KafkaReporting<C> {
258 state: Arc<State>,
259 consumer: C,
260 kafka_producer: KafkaProducer,
261 shutdown_signal: Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>,
262}
263
264impl<C: CollectItemConsume> KafkaReporting<C> {
265 pub fn with_graceful_shutdown(
269 mut self,
270 shutdown_signal: impl Future<Output = ()> + Send + Sync + 'static,
271 ) -> Self {
272 self.shutdown_signal = Box::pin(shutdown_signal);
273 self
274 }
275
276 pub fn spawn(self) -> ReportingJoinHandle {
278 let handle = spawn(async move {
279 let KafkaReporting {
280 state,
281 mut consumer,
282 mut kafka_producer,
283 shutdown_signal,
284 } = self;
285
286 let (shutdown_tx, mut shutdown_rx) = mpsc::unbounded_channel();
287
288 let work_fut = async move {
289 loop {
290 select! {
291 item = consumer.consume() => {
292 match item {
293 Ok(Some(item)) => {
294 kafka_producer.produce(item).await;
295 }
296 Ok(None) => break,
297 Err(err) => return Err(crate::Error::Other(err)),
298 }
299 }
300 _ = shutdown_rx.recv() => break,
301 }
302 }
303
304 state.is_closing.store(true, Relaxed);
305
306 loop {
308 match consumer.try_consume().await {
309 Ok(Some(item)) => {
310 kafka_producer.produce(item).await;
311 }
312 Ok(None) => break,
313 Err(err) => return Err(err.into()),
314 }
315 }
316
317 Ok::<_, crate::Error>(())
318 };
319
320 let shutdown_fut = async move {
321 shutdown_signal.await;
322 shutdown_tx
323 .send(())
324 .map_err(|e| crate::Error::Other(Box::new(e)))?;
325 Ok(())
326 };
327
328 try_join!(work_fut, shutdown_fut)?;
329
330 Ok(())
331 });
332 ReportingJoinHandle { handle }
333 }
334}
335
336pub struct ReportingJoinHandle {
338 handle: JoinHandle<crate::Result<()>>,
339}
340
341impl Future for ReportingJoinHandle {
342 type Output = crate::Result<()>;
343
344 fn poll(
345 mut self: std::pin::Pin<&mut Self>,
346 cx: &mut std::task::Context<'_>,
347 ) -> std::task::Poll<Self::Output> {
348 Pin::new(&mut self.handle).poll(cx).map(|rs| rs?)
349 }
350}
351
352struct TopicNames {
353 segment: String,
354 meter: String,
355 log: String,
356 #[cfg(feature = "management")]
357 management: String,
358}
359
360impl TopicNames {
361 const TOPIC_LOG: &str = "skywalking-logs";
362 #[cfg(feature = "management")]
363 const TOPIC_MANAGEMENT: &str = "skywalking-managements";
364 const TOPIC_METER: &str = "skywalking-meters";
365 const TOPIC_SEGMENT: &str = "skywalking-segments";
366
367 fn new(namespace: Option<&str>) -> Self {
368 Self {
369 segment: Self::real_topic_name(namespace, Self::TOPIC_SEGMENT),
370 meter: Self::real_topic_name(namespace, Self::TOPIC_METER),
371 log: Self::real_topic_name(namespace, Self::TOPIC_LOG),
372 #[cfg(feature = "management")]
373 management: Self::real_topic_name(namespace, Self::TOPIC_MANAGEMENT),
374 }
375 }
376
377 fn real_topic_name(namespace: Option<&str>, topic_name: &str) -> String {
378 namespace
379 .map(|namespace| format!("{}-{}", namespace, topic_name))
380 .unwrap_or_else(|| topic_name.to_string())
381 }
382}
383
384struct KafkaProducer {
385 topic_names: TopicNames,
386 client: FutureProducer,
387 err_handle: Arc<DynErrHandler>,
388}
389
390impl KafkaProducer {
391 async fn new(
392 client: FutureProducer,
393 err_handle: Arc<DynErrHandler>,
394 namespace: Option<String>,
395 ) -> Result<Self, Error> {
396 let topic_names = TopicNames::new(namespace.as_deref());
397 Ok(Self {
398 client,
399 err_handle,
400 topic_names,
401 })
402 }
403
404 async fn produce(&mut self, item: CollectItem) {
405 let (topic_name, key) = match &item {
406 CollectItem::Trace(item) => (
407 &self.topic_names.segment,
408 item.trace_segment_id.as_bytes().to_vec(),
409 ),
410 CollectItem::Log(item) => (&self.topic_names.log, item.service.as_bytes().to_vec()),
411 CollectItem::Meter(item) => (
412 &self.topic_names.meter,
413 item.service_instance.as_bytes().to_vec(),
414 ),
415 #[cfg(feature = "management")]
416 CollectItem::Instance(item) => (
417 &self.topic_names.management,
418 format!("register-{}", &item.service_instance).into_bytes(),
419 ),
420 #[cfg(feature = "management")]
421 CollectItem::Ping(item) => (
422 &self.topic_names.log,
423 item.service_instance.as_bytes().to_vec(),
424 ),
425 };
426
427 let payload = item.encode_to_vec();
428 let record = FutureRecord::to(topic_name).payload(&payload).key(&key);
429
430 if let Err((err, _)) = self.client.send(record, Duration::from_secs(0)).await {
431 (self.err_handle)("Collect data to kafka failed", &err);
432 }
433 }
434}