Skip to main content

dnet_base/
logging.rs

1//! Logging utilities for `dnet` transports.
2
3use std::{any::type_name, task::Poll};
4
5use tracing::{debug, error, info, trace, trace_span, warn, Span};
6
7/// Trait implemented by `dnet` transports that support logging.
8pub trait Logging {
9    /// Short human-readable type of `dnet` the transport.
10    const KIND: &'static str;
11
12    /// Execute closure with transport's logger.
13    fn with_logger<F, R>(&self, f: F) -> R
14    where
15        F: FnOnce(&Logger) -> R;
16
17    /// Execute closure with transport's logger (mutable).
18    fn with_logger_mut<F, R>(&mut self, f: F) -> R
19    where
20        F: FnOnce(&mut Logger) -> R;
21
22    /// Set transport name (for logging purposed).
23    fn set_logging_name(&mut self, name: &str) {
24        self.with_logger_mut(|logger| logger.set_transport_name(Some(name.to_string())));
25    }
26
27    /// Get transport name (for logging purposed).
28    fn get_logging_name(&mut self) -> Option<String> {
29        self.with_logger_mut(|logger| logger.get_transport_name().map(|name| name.to_string()))
30    }
31
32    /// Enable logging for this transport.
33    fn enable_logging(&mut self) {
34        self.with_logger_mut(|logger| logger.enable());
35    }
36
37    /// Disable logging for this transport.
38    fn disable_logging(&mut self) {
39        self.with_logger_mut(|logger| logger.disable());
40    }
41
42    /// Is logging enabled for this transport.
43    fn is_logging_enabled(&self) -> bool {
44        self.with_logger(|logger| logger.is_enabled())
45    }
46}
47
48/// Logging utility for `dnet` transports.
49pub struct Logger {
50    kind: String,
51    name: Option<String>,
52    enabled: bool,
53}
54
55impl Logger {
56    /// Create new logger for transport.
57    pub fn new<T>() -> Self
58    where
59        T: Logging,
60    {
61        let kind = T::KIND.to_string();
62        let name = None;
63        let enabled = false;
64        Logger {
65            kind,
66            name,
67            enabled,
68        }
69    }
70
71    /// Create new logger for transport that already has a name.
72    ///
73    /// Usually `dnet` transports don't have names, but when they do
74    /// you may use this constructor to make transport's logical name the
75    /// same as the one used for logging purposes.
76    ///
77    /// Note that logging name may still be changed later by the transport user
78    /// and at that point logical name and logging name may be different.
79    pub fn new_for_already_named<T>(transport_name: &str) -> Self
80    where
81        T: Logging,
82    {
83        let kind = T::KIND.to_string();
84        let name = Some(transport_name.to_string());
85        let enabled = false;
86        Logger {
87            kind,
88            name,
89            enabled,
90        }
91    }
92
93    /// Log that the transport was opened successfully.
94    ///
95    /// Note: not all transports emit an "open" event — transports that do
96    /// not wait for an explicit open should skip calling this.
97    #[inline]
98    pub fn log_open_success(&self) {
99        if self.enabled {
100            let span = self.span();
101            let _guard = span.enter();
102            info!("transport open");
103        }
104    }
105
106    /// Log that opening the transport failed.
107    ///
108    /// Useful for transports that report an explicit open failure to the
109    /// application. Transports that don't wait for opening can ignore this.
110    #[inline]
111    pub fn log_open_failure(&self, error: impl std::error::Error) {
112        if self.enabled {
113            let span = self.span();
114            let _guard = span.enter();
115            error!("opening transport failed: {error}");
116        }
117    }
118
119    /// Helper method for logging readiness based on result.
120    ///
121    /// See [log_ready_success](Logger::log_ready_success) and
122    /// [log_ready_failure](Logger::log_ready_failure) for more details.
123    #[inline]
124    pub fn log_ready<S, F>(&self, result: &Poll<Result<S, F>>)
125    where
126        F: std::error::Error,
127    {
128        match result {
129            Poll::Ready(Ok(_)) => self.log_ready_success(),
130            Poll::Ready(Err(error)) => self.log_ready_failure(error),
131            _ => {} // ignore
132        }
133    }
134
135    /// Log that the transport is ready for sending messages.
136    ///
137    /// Currently this does not log anything to keep the log less verbose, but it may be changed in the future.
138    #[inline]
139    pub fn log_ready_success(&self) {
140        // ignore
141    }
142
143    /// Log that the transport failed to become ready for sending messages.
144    #[inline]
145    pub fn log_ready_failure(&self, error: impl std::error::Error) {
146        if self.enabled {
147            let span = self.span();
148            let _guard = span.enter();
149            error!("failed to become ready for sending: {error}");
150        }
151    }
152
153    /// Helper method for logging message staging based on result.
154    ///
155    /// See [log_message_staging_success](Logger::log_message_staging_success) and
156    /// [log_message_staging_failure](Logger::log_message_staging_failure) for more
157    /// info.
158    #[inline]
159    pub fn log_message_staging<O, S, F>(&self, result: &Result<S, F>)
160    where
161        F: std::error::Error,
162    {
163        match result {
164            Ok(_) => {
165                self.log_message_staging_success::<O>();
166            }
167            Err(error) => self.log_message_staging_failure(error),
168        }
169    }
170
171    /// Log successful staging of an outgoing message for sending.
172    ///
173    /// Used by transports that stage outgoing messages by putting them into buffer
174    /// before encoding and sending.
175    /// Not every transport stages messages before sending - this method may be unused.
176    #[inline]
177    pub fn log_message_staging_success<O>(&self) {
178        if self.enabled {
179            let span = self.span();
180            let _guard = span.enter();
181            trace!(
182                message_type = type_name::<O>(),
183                "staged message for sending"
184            );
185        }
186    }
187
188    /// Log failure while staging an outgoing message for sending.
189    ///
190    /// Used by transports that stage outgoing messages by putting them into buffer
191    /// before encoding and sending.
192    /// Not every transport stages messages before sending - this method may be unused.
193    #[inline]
194    pub fn log_message_staging_failure(&self, error: impl std::error::Error) {
195        if self.enabled {
196            let span = self.span();
197            let _guard = span.enter();
198            error!("failed to stage message for sending: {error}");
199        }
200    }
201
202    /// Helper method for logging message preparation based on result.
203    ///
204    /// See [log_message_preparation_success](Logger::log_message_preparation_success) and
205    /// [log_message_preparation_failure](Logger::log_message_preparation_failure) for more
206    /// info.
207    #[inline]
208    pub fn log_message_preparation<O, S, F>(
209        &self,
210        send_buffer_len_before: usize,
211        send_buffer_len_after: usize,
212        result: &Result<S, F>,
213    ) where
214        F: std::error::Error,
215    {
216        match result {
217            Ok(_) => {
218                let message_size = send_buffer_len_after - send_buffer_len_before;
219                self.log_message_preparation_success::<O>(Some(message_size));
220            }
221            Err(error) => self.log_message_preparation_failure(error),
222        }
223    }
224
225    /// Log successful preparation of an outgoing message.
226    ///
227    /// Used by transports that buffer or prepare messages before sending (flushing).
228    /// Not every transport buffers messages before sending.
229    #[inline]
230    pub fn log_message_preparation_success<O>(&self, size: Option<usize>) {
231        if self.enabled {
232            let span = self.span();
233            let _guard = span.enter();
234            if let Some(size) = size {
235                trace!(
236                    message_type = type_name::<O>(),
237                    size_in_bytes = size,
238                    "prepared message for sending"
239                );
240            } else {
241                trace!(
242                    message_type = type_name::<O>(),
243                    "prepared message for sending"
244                );
245            }
246        }
247    }
248
249    /// Log failure while preparing an outgoing message.
250    ///
251    /// Used by buffered transports that perform message preparation steps
252    /// before sending (flushing); may be unused by simpler transports.
253    #[inline]
254    pub fn log_message_preparation_failure(&self, error: impl std::error::Error) {
255        if self.enabled {
256            let span = self.span();
257            let _guard = span.enter();
258            error!("failed to prepare message for sending: {error}");
259        }
260    }
261
262    /// Helper method for logging message sending based on result.
263    ///
264    /// See [log_sending_success](Logger::log_sending_success) and
265    /// [log_sending_failure](Logger::log_sending_failure) for more
266    /// info.
267    #[inline]
268    pub fn log_sending<O, F>(&self, result: &Result<(), F>, size: Option<usize>)
269    where
270        F: std::error::Error,
271    {
272        match result {
273            Ok(_) => self.log_sending_success::<O>(size),
274            Err(error) => self.log_sending_failure(error),
275        }
276    }
277
278    /// Log that a message was sent successfully.
279    #[inline]
280    pub fn log_sending_success<O>(&self, size: Option<usize>) {
281        if self.enabled {
282            let span = self.span();
283            let _guard = span.enter();
284            if let Some(size) = size {
285                debug!(
286                    message_type = type_name::<O>(),
287                    size_in_bytes = size,
288                    "message sent"
289                );
290            } else {
291                debug!(message_type = type_name::<O>(), "message sent");
292            }
293        }
294    }
295
296    /// Log that sending a message failed.
297    #[inline]
298    pub fn log_sending_failure(&self, error: impl std::error::Error) {
299        if self.enabled {
300            let span = self.span();
301            let _guard = span.enter();
302            error!("failed to send message: {error}");
303        }
304    }
305
306    /// Log that a message data arrived (was received and enqueued).
307    ///
308    /// This is used by transports that receive messages data in the background
309    /// and place them into a buffer for later deserialization and
310    /// consumption; not all transports will use arrival logs.
311    ///
312    /// It is called "unknown" because at this point we don't know
313    /// if message deserialization will succeed or not.
314    #[inline]
315    pub fn log_message_arrived_unknown(&self, size: Option<usize>) {
316        if self.enabled {
317            let span = self.span();
318            let _guard = span.enter();
319            if let Some(size) = size {
320                trace!(size_in_bytes = size, "new message arrived");
321            } else {
322                trace!("new message arrived");
323            }
324        }
325    }
326
327    /// Log that a message arrived (was received and enqueued).
328    ///
329    /// This is used by transports that receive messages in the background
330    /// and place them into a buffer for later consumption; not all transports
331    /// will use arrival logs.
332    #[inline]
333    pub fn log_message_arrived_success<I>(&self, _item: &I, size: Option<usize>) {
334        if self.enabled {
335            let span = self.span();
336            let _guard = span.enter();
337            if let Some(size) = size {
338                trace!(
339                    message_type = type_name::<I>(),
340                    size_in_bytes = size,
341                    "new message arrived"
342                );
343            } else {
344                trace!(message_type = type_name::<I>(), "new message arrived");
345            }
346        }
347    }
348
349    /// Log failure during background arrival/enqueue of a received message.
350    ///
351    /// Relevant for transports that buffer incoming messages in the background;
352    /// may be unused by transports that only receive message while precessing
353    /// `next` method.
354    #[inline]
355    pub fn log_message_arrived_failure(&self, error: impl std::error::Error) {
356        if self.enabled {
357            let span = self.span();
358            let _guard = span.enter();
359            error!("failed to process arriving message: {error}");
360        }
361    }
362
363    /// Helper method for logging receiving based on result.
364    ///
365    /// See [log_receiving_success](Logger::log_receiving_success),
366    /// [log_receiving_failure](Logger::log_receiving_failure) and
367    /// [log_remote_closed](Logger::log_remote_closed) for more info.
368    #[inline]
369    pub fn log_receiving<I, F>(
370        &self,
371        result: &Poll<Option<Result<I, F>>>,
372        message_length: Option<usize>,
373    ) where
374        F: std::error::Error,
375    {
376        match result {
377            Poll::Ready(Some(Ok(item))) => self.log_receiving_success(item, message_length),
378            Poll::Ready(Some(Err(error))) => self.log_receiving_failure(error),
379            Poll::Ready(None) => self.log_remote_closed(),
380            _ => {} // ignore
381        }
382    }
383
384    /// Log that a message was successfully received.
385    #[inline]
386    pub fn log_receiving_success<I>(&self, _item: &I, size: Option<usize>) {
387        if self.enabled {
388            let span = self.span();
389            let _guard = span.enter();
390            if let Some(size) = size {
391                debug!(
392                    message_type = type_name::<I>(),
393                    size_in_bytes = size,
394                    "received message"
395                );
396            } else {
397                debug!(message_type = type_name::<I>(), "received message");
398            }
399        }
400    }
401
402    /// Log that receiving a message failed.
403    #[inline]
404    pub fn log_receiving_failure(&self, error: impl std::error::Error) {
405        if self.enabled {
406            let span = self.span();
407            let _guard = span.enter();
408            error!("failed to receive message: {error}");
409        }
410    }
411
412    /// Log that an attempt to receive from a `Void` transport was made.
413    pub fn log_receive_from_void(&self) {
414        if self.enabled {
415            let span = self.span();
416            let _guard = span.enter();
417            warn!("attempted to receive from void");
418        }
419    }
420
421    /// Helper method for logging flush.
422    ///
423    /// See [log_flush_success](Logger::log_flush_success) and
424    /// [log_flush_failure](Logger::log_flush_failure) for more
425    /// info.
426    #[inline]
427    pub fn log_flush<S, F>(&self, result: &Poll<Result<S, F>>)
428    where
429        F: std::error::Error,
430    {
431        match result {
432            Poll::Ready(Ok(_)) => self.log_flush_success(),
433            Poll::Ready(Err(error)) => self.log_flush_failure(error),
434            _ => {} // ignore
435        }
436    }
437
438    /// Log that buffered messages were flushed successfully.
439    #[inline]
440    pub fn log_flush_success(&self) {
441        if self.enabled {
442            let span = self.span();
443            let _guard = span.enter();
444            debug!("outgoing messages flushed");
445        }
446    }
447
448    /// Log that flushing buffered messages failed.
449    #[inline]
450    pub fn log_flush_failure(&self, error: impl std::error::Error) {
451        if self.enabled {
452            let span = self.span();
453            let _guard = span.enter();
454            error!("failed to flush outgoing messages: {error}");
455        }
456    }
457
458    /// Helper method for logging closing of the transport based on result.
459    ///
460    /// See [log_closed_success](Logger::log_closed_success),
461    /// [log_closed_failure](Logger::log_closed_failure) for more info.
462    #[inline]
463    pub fn log_close<S, F>(&self, result: &Poll<Result<S, F>>)
464    where
465        F: std::error::Error,
466    {
467        match result {
468            Poll::Ready(Ok(_)) => self.log_closed_success(),
469            Poll::Ready(Err(error)) => self.log_closed_failure(error),
470            _ => {} // ignore
471        }
472    }
473
474    /// Log that the transport was closed locally successfully.
475    #[inline]
476    pub fn log_closed_success(&self) {
477        if self.enabled {
478            let span = self.span();
479            let _guard = span.enter();
480            info!("transport closed");
481        }
482    }
483
484    /// Log that closing the transport locally failed.
485    #[inline]
486    pub fn log_closed_failure(&self, error: impl std::error::Error) {
487        if self.enabled {
488            let span = self.span();
489            let _guard = span.enter();
490            error!("failed to close transport: {error}");
491        }
492    }
493
494    /// Log that the remote peer closed the transport.
495    #[inline]
496    pub fn log_remote_closed(&self) {
497        if self.enabled {
498            let span = self.span();
499            let _guard = span.enter();
500            info!("remote transport closed");
501        }
502    }
503
504    /// Log that an incoming message was filtered out.
505    #[inline]
506    pub fn log_incoming_filtered_out<I>(&self) {
507        if self.enabled {
508            let span = self.span();
509            let _guard = span.enter();
510            trace!(
511                message_type = type_name::<I>(),
512                "incoming message filtered out"
513            );
514        }
515    }
516
517    /// Log that an outgoing message was filtered out.
518    #[inline]
519    pub fn log_outgoing_filtered_out<I>(&self) {
520        if self.enabled {
521            let span = self.span();
522            let _guard = span.enter();
523            trace!(
524                message_type = type_name::<I>(),
525                "outgoing message filtered out"
526            );
527        }
528    }
529
530    /// Set transport name for logging purposes.
531    pub fn set_transport_name(&mut self, name: Option<String>) {
532        self.name = name;
533    }
534
535    /// Get transport name for logging purposes.
536    pub fn get_transport_name(&self) -> Option<&str> {
537        self.name.as_deref()
538    }
539
540    /// Enable logging.
541    pub fn enable(&mut self) {
542        self.enabled = true;
543    }
544
545    /// Disable logging.
546    pub fn disable(&mut self) {
547        self.enabled = false;
548    }
549
550    /// Is logging enabled.
551    pub fn is_enabled(&self) -> bool {
552        self.enabled
553    }
554
555    /// Override existing logger kind with another transport type kind.
556    pub fn override_kind<T>(&mut self)
557    where
558        T: Logging,
559    {
560        self.kind = T::KIND.to_string();
561    }
562
563    /// Override existing logger kind with provided string.
564    pub fn override_kind_with_str(&mut self, kind: &str) {
565        self.kind = kind.to_string();
566    }
567
568    /// Override existing logger kind with another (buffered) transport type kind.
569    pub fn override_kind_buffered<T>(&mut self)
570    where
571        T: Logging,
572    {
573        self.kind = format!("{}(buffered)", T::KIND);
574    }
575
576    /// Override existing logger kind with another (part) transport type kind.
577    pub fn override_kind_part<T>(&mut self, variant: usize)
578    where
579        T: Logging,
580    {
581        self.kind = format!("{}({variant})", T::KIND);
582    }
583
584    /// Create a tracing span for this transport.
585    pub fn span(&self) -> Span {
586        if let Some(name) = &self.name {
587            trace_span!("dnet", transport = self.kind, name = name.as_str())
588        } else {
589            trace_span!("dnet", transport = self.kind)
590        }
591    }
592}