1use crate::ChannelLogger;
2use crate::MemoryStorageLogger;
3use crate::RecordFilter;
4use crate::buffer_formatter::BufferFormatter;
5use crate::logger::Logger;
6use crate::record::Record;
7use crate::record::RecordKind;
8use std::collections;
9use std::fmt;
10use std::io;
11use std::pin::Pin;
12use std::sync::mpsc;
13use std::task::Context;
14use std::task::Poll;
15use tokio::io as tokio_io;
16
17pub struct LoggedStream<
95 S: 'static,
96 Formatter: 'static,
97 Filter: RecordFilter + 'static,
98 L: Logger + 'static,
99> {
100 inner_stream: S,
101 formatter: Formatter,
102 filter: Filter,
103 logger: L,
104}
105
106impl<S: 'static, Formatter: 'static, Filter: RecordFilter + 'static, L: Logger + 'static>
107 LoggedStream<S, Formatter, Filter, L>
108{
109 pub fn new(stream: S, formatter: Formatter, filter: Filter, logger: L) -> Self {
111 Self {
112 inner_stream: stream,
113 formatter,
114 filter,
115 logger,
116 }
117 }
118
119 pub fn log_open(&mut self, message: impl Into<String>) {
133 self.emit(Record::new(RecordKind::Open, message.into()));
134 }
135
136 #[inline]
141 fn emit(&mut self, record: Record) {
142 if self.filter.check(&record) {
143 self.logger.log(record);
144 }
145 }
146}
147
148impl<S: 'static, Formatter: 'static, Filter: RecordFilter + 'static>
149 LoggedStream<S, Formatter, Filter, MemoryStorageLogger>
150{
151 #[inline]
152 pub fn get_log_records(&self) -> collections::VecDeque<Record> {
153 self.logger.get_log_records()
154 }
155
156 #[inline]
157 pub fn clear_log_records(&mut self) {
158 self.logger.clear_log_records()
159 }
160}
161
162impl<S: 'static, Formatter: 'static, Filter: RecordFilter + 'static>
163 LoggedStream<S, Formatter, Filter, ChannelLogger>
164{
165 #[inline]
166 pub fn take_receiver(&mut self) -> Option<mpsc::Receiver<Record>> {
167 self.logger.take_receiver()
168 }
169
170 #[inline]
171 pub fn take_receiver_unchecked(&mut self) -> mpsc::Receiver<Record> {
172 self.logger.take_receiver_unchecked()
173 }
174}
175
176impl<
177 S: fmt::Debug + 'static,
178 Formatter: fmt::Debug + 'static,
179 Filter: RecordFilter + fmt::Debug + 'static,
180 L: Logger + fmt::Debug + 'static,
181> fmt::Debug for LoggedStream<S, Formatter, Filter, L>
182{
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 f.debug_struct("LoggedStream")
185 .field("inner_stream", &self.inner_stream)
186 .field("formatter", &self.formatter)
187 .field("filter", &self.filter)
188 .field("logger", &self.logger)
189 .finish()
190 }
191}
192
193impl<
194 S: io::Read + 'static,
195 Formatter: BufferFormatter + 'static,
196 Filter: RecordFilter + 'static,
197 L: Logger + 'static,
198> io::Read for LoggedStream<S, Formatter, Filter, L>
199{
200 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
201 let result = self.inner_stream.read(buf);
202
203 match &result {
204 Ok(length) => {
205 let record = Record::new(
206 RecordKind::Read,
207 self.formatter.format_buffer(&buf[0..*length]),
208 );
209 self.emit(record);
210 }
211 Err(e) if matches!(e.kind(), io::ErrorKind::WouldBlock) => {}
212 Err(e) => {
213 self.emit(Record::new(
214 RecordKind::Error,
215 format!("Error during read: {e}"),
216 ));
217 }
218 };
219
220 result
221 }
222}
223
224impl<
225 S: tokio_io::AsyncRead + Unpin + 'static,
226 Formatter: BufferFormatter + Unpin + 'static,
227 Filter: RecordFilter + Unpin + 'static,
228 L: Logger + Unpin + 'static,
229> tokio_io::AsyncRead for LoggedStream<S, Formatter, Filter, L>
230{
231 fn poll_read(
232 self: Pin<&mut Self>,
233 cx: &mut Context<'_>,
234 buf: &mut tokio_io::ReadBuf<'_>,
235 ) -> Poll<io::Result<()>> {
236 let mut_self = self.get_mut();
237 let length_before_read = buf.filled().len();
238 let result = Pin::new(&mut mut_self.inner_stream).poll_read(cx, buf);
239 let length_after_read = buf.filled().len();
240 let diff = length_after_read - length_before_read;
241
242 match &result {
243 Poll::Ready(Ok(())) if diff == 0 => {}
244 Poll::Ready(Ok(())) => {
245 let record = Record::new(
246 RecordKind::Read,
247 mut_self
248 .formatter
249 .format_buffer(&(buf.filled())[length_before_read..length_after_read]),
250 );
251 mut_self.emit(record);
252 }
253 Poll::Ready(Err(e)) => {
254 mut_self.emit(Record::new(
255 RecordKind::Error,
256 format!("Error during async read: {e}"),
257 ));
258 }
259 Poll::Pending => {}
260 }
261
262 result
263 }
264}
265
266impl<
267 S: io::Write + 'static,
268 Formatter: BufferFormatter + 'static,
269 Filter: RecordFilter + 'static,
270 L: Logger + 'static,
271> io::Write for LoggedStream<S, Formatter, Filter, L>
272{
273 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
274 let result = self.inner_stream.write(buf);
275
276 match &result {
277 Ok(length) => {
278 let record = Record::new(
279 RecordKind::Write,
280 self.formatter.format_buffer(&buf[0..*length]),
281 );
282 self.emit(record);
283 }
284 Err(e)
285 if matches!(
286 e.kind(),
287 io::ErrorKind::WriteZero | io::ErrorKind::WouldBlock
288 ) => {}
289 Err(e) => {
290 self.emit(Record::new(
291 RecordKind::Error,
292 format!("Error during write: {e}"),
293 ));
294 }
295 };
296
297 result
298 }
299
300 fn flush(&mut self) -> io::Result<()> {
301 self.inner_stream.flush()
302 }
303}
304
305impl<
306 S: tokio_io::AsyncWrite + Unpin + 'static,
307 Formatter: BufferFormatter + Unpin + 'static,
308 Filter: RecordFilter + Unpin + 'static,
309 L: Logger + Unpin + 'static,
310> tokio_io::AsyncWrite for LoggedStream<S, Formatter, Filter, L>
311{
312 fn poll_write(
313 self: Pin<&mut Self>,
314 cx: &mut Context<'_>,
315 buf: &[u8],
316 ) -> Poll<Result<usize, io::Error>> {
317 let mut_self = self.get_mut();
318 let result = Pin::new(&mut mut_self.inner_stream).poll_write(cx, buf);
319
320 match &result {
321 Poll::Ready(Ok(length)) => {
322 let record = Record::new(
323 RecordKind::Write,
324 mut_self.formatter.format_buffer(&buf[0..*length]),
325 );
326 mut_self.emit(record);
327 }
328 Poll::Ready(Err(e)) => {
329 mut_self.emit(Record::new(
330 RecordKind::Error,
331 format!("Error during async write: {e}"),
332 ));
333 }
334 Poll::Pending => {}
335 }
336
337 result
338 }
339
340 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
341 Pin::new(&mut self.get_mut().inner_stream).poll_flush(cx)
342 }
343
344 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
345 let mut_self = self.get_mut();
346 let result = Pin::new(&mut mut_self.inner_stream).poll_shutdown(cx);
347
348 mut_self.emit(Record::new(
349 RecordKind::Shutdown,
350 String::from("Writer shutdown request."),
351 ));
352
353 result
354 }
355}
356
357impl<S: 'static, Formatter: 'static, Filter: RecordFilter + 'static, L: Logger + 'static> Drop
358 for LoggedStream<S, Formatter, Filter, L>
359{
360 fn drop(&mut self) {
361 self.emit(Record::new(RecordKind::Drop, String::from("Deallocated.")));
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use crate::ChannelLogger;
368 use crate::DecimalFormatter;
369 use crate::DefaultFilter;
370 use crate::LoggedStream;
371 use crate::LowercaseHexadecimalFormatter;
372 use crate::MemoryStorageLogger;
373 use crate::RecordKind;
374 use crate::RecordKindFilter;
375 use crate::UppercaseHexadecimalFormatter;
376 use std::cell::RefCell;
377 use std::io::Cursor;
378 use std::io::ErrorKind;
379 use std::io::Read;
380 use std::io::Write;
381 use std::pin::Pin;
382 use std::rc::Rc;
383 use std::task::Context;
384 use std::task::Poll;
385
386 struct ErrReader(ErrorKind);
392
393 impl Read for ErrReader {
394 fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
395 Err(std::io::Error::new(self.0, "boom"))
396 }
397 }
398
399 struct ErrWriter(ErrorKind);
401
402 impl Write for ErrWriter {
403 fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
404 Err(std::io::Error::new(self.0, "boom"))
405 }
406
407 fn flush(&mut self) -> std::io::Result<()> {
408 Ok(())
409 }
410 }
411
412 struct ErrAsyncReader(ErrorKind);
414
415 impl tokio::io::AsyncRead for ErrAsyncReader {
416 fn poll_read(
417 self: Pin<&mut Self>,
418 _cx: &mut Context<'_>,
419 _buf: &mut tokio::io::ReadBuf<'_>,
420 ) -> Poll<std::io::Result<()>> {
421 Poll::Ready(Err(std::io::Error::new(self.0, "boom")))
422 }
423 }
424
425 struct ErrAsyncWriter(ErrorKind);
427
428 impl tokio::io::AsyncWrite for ErrAsyncWriter {
429 fn poll_write(
430 self: Pin<&mut Self>,
431 _cx: &mut Context<'_>,
432 _buf: &[u8],
433 ) -> Poll<std::io::Result<usize>> {
434 Poll::Ready(Err(std::io::Error::new(self.0, "boom")))
435 }
436
437 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
438 Poll::Ready(Ok(()))
439 }
440
441 fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
442 Poll::Ready(Ok(()))
443 }
444 }
445
446 struct RecordingWriter(Rc<RefCell<Vec<u8>>>);
449
450 impl Write for RecordingWriter {
451 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
452 self.0.borrow_mut().extend_from_slice(buf);
453 Ok(buf.len())
454 }
455
456 fn flush(&mut self) -> std::io::Result<()> {
457 Ok(())
458 }
459 }
460
461 #[test]
466 fn test_read_returns_inner_bytes_unchanged() {
467 let data = vec![0x01, 0x02, 0x03, 0x04];
468 let mut stream = LoggedStream::new(
469 Cursor::new(data.clone()),
470 LowercaseHexadecimalFormatter::new_default(),
471 DefaultFilter,
472 MemoryStorageLogger::new(16),
473 );
474
475 let mut buf = [0u8; 4];
476 stream.read_exact(&mut buf).unwrap();
477
478 assert_eq!(buf.to_vec(), data);
479 }
480
481 #[test]
482 fn test_write_reaches_inner_stream() {
483 let sink = Rc::new(RefCell::new(Vec::new()));
484 let mut stream = LoggedStream::new(
485 RecordingWriter(Rc::clone(&sink)),
486 LowercaseHexadecimalFormatter::new_default(),
487 DefaultFilter,
488 MemoryStorageLogger::new(16),
489 );
490
491 stream.write_all(&[0xde, 0xad, 0xbe, 0xef]).unwrap();
492
493 assert_eq!(*sink.borrow(), vec![0xde, 0xad, 0xbe, 0xef]);
494 }
495
496 #[test]
501 fn test_read_logs_read_record_with_formatted_content() {
502 let mut stream = LoggedStream::new(
503 Cursor::new(vec![0x0a, 0xff]),
504 LowercaseHexadecimalFormatter::new_default(),
505 DefaultFilter,
506 MemoryStorageLogger::new(16),
507 );
508
509 let mut buf = [0u8; 2];
510 stream.read_exact(&mut buf).unwrap();
511
512 let records = stream.get_log_records();
513 assert_eq!(records.len(), 1);
514 assert_eq!(records[0].kind, RecordKind::Read);
515 assert_eq!(records[0].message, "0a:ff");
516 }
517
518 #[test]
519 fn test_read_uses_configured_formatter() {
520 let mut stream = LoggedStream::new(
521 Cursor::new(vec![10, 255]),
522 DecimalFormatter::new_default(),
523 DefaultFilter,
524 MemoryStorageLogger::new(16),
525 );
526
527 let mut buf = [0u8; 2];
528 stream.read_exact(&mut buf).unwrap();
529
530 assert_eq!(stream.get_log_records()[0].message, "10:255");
531 }
532
533 #[test]
534 fn test_multiple_reads_log_in_order() {
535 let mut stream = LoggedStream::new(
536 Cursor::new(vec![0x01, 0x02, 0x03, 0x04]),
537 LowercaseHexadecimalFormatter::new_default(),
538 DefaultFilter,
539 MemoryStorageLogger::new(16),
540 );
541
542 let mut buf = [0u8; 2];
543 stream.read_exact(&mut buf).unwrap();
544 stream.read_exact(&mut buf).unwrap();
545
546 let records = stream.get_log_records();
547 assert_eq!(records.len(), 2);
548 assert_eq!(records[0].message, "01:02");
549 assert_eq!(records[1].message, "03:04");
550 }
551
552 #[test]
557 fn test_write_logs_write_record_with_formatted_content() {
558 let mut stream = LoggedStream::new(
559 Cursor::new(Vec::<u8>::new()),
560 UppercaseHexadecimalFormatter::new_default(),
561 DefaultFilter,
562 MemoryStorageLogger::new(16),
563 );
564
565 stream.write_all(&[0x0a, 0xff]).unwrap();
566
567 let records = stream.get_log_records();
568 assert_eq!(records.len(), 1);
569 assert_eq!(records[0].kind, RecordKind::Write);
570 assert_eq!(records[0].message, "0A:FF");
571 }
572
573 #[test]
574 fn test_flush_does_not_log() {
575 let mut stream = LoggedStream::new(
576 Cursor::new(Vec::<u8>::new()),
577 LowercaseHexadecimalFormatter::new_default(),
578 DefaultFilter,
579 MemoryStorageLogger::new(16),
580 );
581
582 stream.flush().unwrap();
583
584 assert!(stream.get_log_records().is_empty());
585 }
586
587 #[test]
592 fn test_read_record_suppressed_by_filter() {
593 let mut stream = LoggedStream::new(
595 Cursor::new(vec![0x01, 0x02]),
596 LowercaseHexadecimalFormatter::new_default(),
597 RecordKindFilter::new(&[RecordKind::Write]),
598 MemoryStorageLogger::new(16),
599 );
600
601 let mut buf = [0u8; 2];
602 stream.read_exact(&mut buf).unwrap();
603
604 assert!(stream.get_log_records().is_empty());
605 }
606
607 #[test]
608 fn test_write_record_suppressed_by_filter() {
609 let mut stream = LoggedStream::new(
611 Cursor::new(Vec::<u8>::new()),
612 LowercaseHexadecimalFormatter::new_default(),
613 RecordKindFilter::new(&[RecordKind::Read]),
614 MemoryStorageLogger::new(16),
615 );
616
617 stream.write_all(&[0x01, 0x02]).unwrap();
618
619 assert!(stream.get_log_records().is_empty());
620 }
621
622 #[test]
627 fn test_read_error_logs_error_record() {
628 let mut stream = LoggedStream::new(
629 ErrReader(ErrorKind::Other),
630 LowercaseHexadecimalFormatter::new_default(),
631 DefaultFilter,
632 MemoryStorageLogger::new(16),
633 );
634
635 let mut buf = [0u8; 4];
636 let _ = stream.read(&mut buf);
637
638 let records = stream.get_log_records();
639 assert_eq!(records.len(), 1);
640 assert_eq!(records[0].kind, RecordKind::Error);
641 assert!(records[0].message.starts_with("Error during read:"));
642 }
643
644 #[test]
645 fn test_read_error_suppressed_by_filter_without_error() {
646 let mut stream = LoggedStream::new(
647 ErrReader(ErrorKind::Other),
648 LowercaseHexadecimalFormatter::new_default(),
649 RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]),
650 MemoryStorageLogger::new(16),
651 );
652
653 let mut buf = [0u8; 4];
654 let _ = stream.read(&mut buf);
655
656 assert!(stream.get_log_records().is_empty());
657 }
658
659 #[test]
660 fn test_read_would_block_is_not_logged() {
661 let mut stream = LoggedStream::new(
663 ErrReader(ErrorKind::WouldBlock),
664 LowercaseHexadecimalFormatter::new_default(),
665 DefaultFilter,
666 MemoryStorageLogger::new(16),
667 );
668
669 let mut buf = [0u8; 4];
670 let _ = stream.read(&mut buf);
671
672 assert!(stream.get_log_records().is_empty());
673 }
674
675 #[test]
676 fn test_write_error_logs_error_record() {
677 let mut stream = LoggedStream::new(
678 ErrWriter(ErrorKind::Other),
679 LowercaseHexadecimalFormatter::new_default(),
680 DefaultFilter,
681 MemoryStorageLogger::new(16),
682 );
683
684 let _ = stream.write(&[0x01, 0x02]);
685
686 let records = stream.get_log_records();
687 assert_eq!(records.len(), 1);
688 assert_eq!(records[0].kind, RecordKind::Error);
689 assert!(records[0].message.starts_with("Error during write:"));
690 }
691
692 #[test]
693 fn test_write_error_suppressed_by_filter_without_error() {
694 let mut stream = LoggedStream::new(
695 ErrWriter(ErrorKind::Other),
696 LowercaseHexadecimalFormatter::new_default(),
697 RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]),
698 MemoryStorageLogger::new(16),
699 );
700
701 let _ = stream.write(&[0x01, 0x02]);
702
703 assert!(stream.get_log_records().is_empty());
704 }
705
706 #[test]
707 fn test_write_would_block_is_not_logged() {
708 let mut stream = LoggedStream::new(
709 ErrWriter(ErrorKind::WouldBlock),
710 LowercaseHexadecimalFormatter::new_default(),
711 DefaultFilter,
712 MemoryStorageLogger::new(16),
713 );
714
715 let _ = stream.write(&[0x01, 0x02]);
716
717 assert!(stream.get_log_records().is_empty());
718 }
719
720 #[test]
721 fn test_write_write_zero_is_not_logged() {
722 let mut stream = LoggedStream::new(
723 ErrWriter(ErrorKind::WriteZero),
724 LowercaseHexadecimalFormatter::new_default(),
725 DefaultFilter,
726 MemoryStorageLogger::new(16),
727 );
728
729 let _ = stream.write(&[0x01, 0x02]);
730
731 assert!(stream.get_log_records().is_empty());
732 }
733
734 #[test]
739 fn test_log_open_emits_open_record() {
740 let mut stream = LoggedStream::new(
741 Cursor::new(Vec::<u8>::new()),
742 LowercaseHexadecimalFormatter::new_default(),
743 DefaultFilter,
744 MemoryStorageLogger::new(16),
745 );
746 stream.log_open("Established connection with 127.0.0.1:8080");
747
748 let records = stream.get_log_records();
749 assert_eq!(records.len(), 1);
750 assert_eq!(records[0].kind, RecordKind::Open);
751 assert_eq!(
752 records[0].message,
753 "Established connection with 127.0.0.1:8080"
754 );
755 }
756
757 #[test]
758 fn test_log_open_passes_filter_allowing_open() {
759 let mut stream = LoggedStream::new(
760 Cursor::new(Vec::<u8>::new()),
761 LowercaseHexadecimalFormatter::new_default(),
762 RecordKindFilter::new(&[RecordKind::Open]),
763 MemoryStorageLogger::new(16),
764 );
765 stream.log_open("kept");
766
767 let records = stream.get_log_records();
768 assert_eq!(records.len(), 1);
769 assert_eq!(records[0].kind, RecordKind::Open);
770 assert_eq!(records[0].message, "kept");
771 }
772
773 #[test]
774 fn test_log_open_suppressed_by_filter_without_open() {
775 let mut stream = LoggedStream::new(
776 Cursor::new(Vec::<u8>::new()),
777 LowercaseHexadecimalFormatter::new_default(),
778 RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]),
779 MemoryStorageLogger::new(16),
780 );
781 stream.log_open("should be filtered out");
782 assert!(stream.get_log_records().is_empty());
783 }
784
785 #[test]
790 fn test_drop_logs_drop_record() {
791 let mut stream = LoggedStream::new(
793 Cursor::new(Vec::<u8>::new()),
794 LowercaseHexadecimalFormatter::new_default(),
795 DefaultFilter,
796 ChannelLogger::new(),
797 );
798 let receiver = stream.take_receiver_unchecked();
799
800 drop(stream);
801
802 let record = receiver
803 .recv()
804 .expect("dropping the stream should emit a record");
805 assert_eq!(record.kind, RecordKind::Drop);
806 assert_eq!(record.message, "Deallocated.");
807 }
808
809 #[tokio::test]
810 async fn test_async_shutdown_logs_shutdown_record() {
811 use tokio::io::AsyncWriteExt;
812
813 let (client, _server) = tokio::io::duplex(64);
814 let mut stream = LoggedStream::new(
815 client,
816 LowercaseHexadecimalFormatter::new_default(),
817 DefaultFilter,
818 MemoryStorageLogger::new(16),
819 );
820
821 stream.shutdown().await.unwrap();
822
823 let records = stream.get_log_records();
824 assert_eq!(records.len(), 1);
825 assert_eq!(records[0].kind, RecordKind::Shutdown);
826 assert_eq!(records[0].message, "Writer shutdown request.");
827 }
828
829 #[tokio::test]
834 async fn test_async_read_logs_read_record() {
835 use tokio::io::AsyncReadExt;
836 use tokio::io::AsyncWriteExt;
837
838 let (client, mut server) = tokio::io::duplex(64);
839 server.write_all(&[0xaa, 0xbb]).await.unwrap();
840
841 let mut stream = LoggedStream::new(
842 client,
843 LowercaseHexadecimalFormatter::new_default(),
844 DefaultFilter,
845 MemoryStorageLogger::new(16),
846 );
847
848 let mut buf = [0u8; 2];
849 stream.read_exact(&mut buf).await.unwrap();
850
851 assert_eq!(buf, [0xaa, 0xbb]);
852 let records = stream.get_log_records();
853 assert_eq!(records.len(), 1);
854 assert_eq!(records[0].kind, RecordKind::Read);
855 assert_eq!(records[0].message, "aa:bb");
856 }
857
858 #[tokio::test]
859 async fn test_async_write_logs_write_record() {
860 use tokio::io::AsyncWriteExt;
861
862 let (client, _server) = tokio::io::duplex(64);
863 let mut stream = LoggedStream::new(
864 client,
865 LowercaseHexadecimalFormatter::new_default(),
866 DefaultFilter,
867 MemoryStorageLogger::new(16),
868 );
869
870 stream.write_all(&[0x01, 0x02, 0x03, 0x04]).await.unwrap();
871
872 let records = stream.get_log_records();
873 assert_eq!(records.len(), 1);
874 assert_eq!(records[0].kind, RecordKind::Write);
875 assert_eq!(records[0].message, "01:02:03:04");
876 }
877
878 #[tokio::test]
879 async fn test_async_read_error_suppressed_by_filter_without_error() {
880 use tokio::io::AsyncReadExt;
881
882 let mut stream = LoggedStream::new(
883 ErrAsyncReader(ErrorKind::Other),
884 LowercaseHexadecimalFormatter::new_default(),
885 RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]),
886 MemoryStorageLogger::new(16),
887 );
888
889 let mut buf = [0u8; 4];
890 let _ = stream.read(&mut buf).await;
891
892 assert!(stream.get_log_records().is_empty());
893 }
894
895 #[tokio::test]
896 async fn test_async_write_error_suppressed_by_filter_without_error() {
897 use tokio::io::AsyncWriteExt;
898
899 let mut stream = LoggedStream::new(
900 ErrAsyncWriter(ErrorKind::Other),
901 LowercaseHexadecimalFormatter::new_default(),
902 RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]),
903 MemoryStorageLogger::new(16),
904 );
905
906 let _ = stream.write(&[0x01, 0x02]).await;
907
908 assert!(stream.get_log_records().is_empty());
909 }
910
911 #[tokio::test]
912 async fn test_async_read_error_logs_error_record() {
913 use tokio::io::AsyncReadExt;
914
915 let mut stream = LoggedStream::new(
916 ErrAsyncReader(ErrorKind::Other),
917 LowercaseHexadecimalFormatter::new_default(),
918 DefaultFilter,
919 MemoryStorageLogger::new(16),
920 );
921
922 let mut buf = [0u8; 4];
923 let _ = stream.read(&mut buf).await;
924
925 let records = stream.get_log_records();
926 assert_eq!(records.len(), 1);
927 assert_eq!(records[0].kind, RecordKind::Error);
928 assert!(records[0].message.starts_with("Error during async read:"));
929 }
930
931 #[tokio::test]
932 async fn test_async_write_error_logs_error_record() {
933 use tokio::io::AsyncWriteExt;
934
935 let mut stream = LoggedStream::new(
936 ErrAsyncWriter(ErrorKind::Other),
937 LowercaseHexadecimalFormatter::new_default(),
938 DefaultFilter,
939 MemoryStorageLogger::new(16),
940 );
941
942 let _ = stream.write(&[0x01, 0x02]).await;
943
944 let records = stream.get_log_records();
945 assert_eq!(records.len(), 1);
946 assert_eq!(records[0].kind, RecordKind::Error);
947 assert!(records[0].message.starts_with("Error during async write:"));
948 }
949
950 #[test]
955 fn test_memory_storage_get_and_clear() {
956 let mut stream = LoggedStream::new(
957 Cursor::new(vec![0x01, 0x02]),
958 LowercaseHexadecimalFormatter::new_default(),
959 DefaultFilter,
960 MemoryStorageLogger::new(16),
961 );
962
963 let mut buf = [0u8; 2];
964 stream.read_exact(&mut buf).unwrap();
965 assert_eq!(stream.get_log_records().len(), 1);
966
967 stream.clear_log_records();
968 assert!(stream.get_log_records().is_empty());
969 }
970
971 #[test]
972 fn test_channel_logger_delivers_records() {
973 let mut stream = LoggedStream::new(
974 Cursor::new(vec![0x01, 0x02]),
975 LowercaseHexadecimalFormatter::new_default(),
976 DefaultFilter,
977 ChannelLogger::new(),
978 );
979 let receiver = stream.take_receiver_unchecked();
980
981 let mut buf = [0u8; 2];
982 stream.read_exact(&mut buf).unwrap();
983
984 let record = receiver.recv().unwrap();
985 assert_eq!(record.kind, RecordKind::Read);
986 assert_eq!(record.message, "01:02");
987 }
988
989 fn assert_send<T: Send>() {}
994
995 fn assert_unpin<T: Unpin>() {}
996
997 #[test]
998 fn test_send() {
999 assert_send::<
1000 LoggedStream<
1001 Cursor<Vec<u8>>,
1002 LowercaseHexadecimalFormatter,
1003 DefaultFilter,
1004 MemoryStorageLogger,
1005 >,
1006 >();
1007 }
1008
1009 #[test]
1010 fn test_unpin() {
1011 assert_unpin::<
1012 LoggedStream<
1013 Cursor<Vec<u8>>,
1014 LowercaseHexadecimalFormatter,
1015 DefaultFilter,
1016 MemoryStorageLogger,
1017 >,
1018 >();
1019 }
1020
1021 #[test]
1022 fn test_debug() {
1023 let stream = LoggedStream::new(
1024 Cursor::new(Vec::<u8>::new()),
1025 LowercaseHexadecimalFormatter::new_default(),
1026 DefaultFilter,
1027 MemoryStorageLogger::new(4),
1028 );
1029
1030 let debug = format!("{stream:?}");
1031 assert!(debug.contains("LoggedStream"));
1032 assert!(debug.contains("formatter"));
1033 }
1034}