Skip to main content

profile_bee_aya_log/
lib.rs

1//! A logging framework for eBPF programs.
2//!
3//! This is the user space side of the [Aya] logging framework. For the eBPF
4//! side, see the `aya-log-ebpf` crate.
5//!
6//! `aya-log` provides functions which read log records created by
7//! `aya-log-ebpf` and log them using the [log] crate. Any logger that
8//! implements the [Log] trait can be used with this crate.
9//!
10//! # Example:
11//!
12//! This example uses the [env_logger] crate to log messages to the terminal.
13//!
14//! ```no_run
15//! # let mut bpf = aya::Ebpf::load(&[]).unwrap();
16//! use aya_log::EbpfLogger;
17//!
18//! // initialize env_logger as the default logger
19//! env_logger::init();
20//!
21//! // start reading aya-log records and log them using the default logger
22//! let logger = EbpfLogger::init(&mut bpf).unwrap();
23//! let mut logger = tokio::io::unix::AsyncFd::with_interest(logger, tokio::io::Interest::READABLE).unwrap();
24//! tokio::task::spawn(async move {
25//!     loop {
26//!         let mut guard = logger.readable_mut().await.unwrap();
27//!         guard.get_inner_mut().flush();
28//!         guard.clear_ready();
29//!     }
30//! });
31//! ```
32//!
33//! With the following eBPF code:
34//!
35//! ```ignore
36//! # let ctx = ();
37//! use aya_log_ebpf::{debug, error, info, trace, warn};
38//!
39//! error!(&ctx, "this is an error message 🚨");
40//! warn!(&ctx, "this is a warning message âš ī¸");
41//! info!(&ctx, "this is an info message â„šī¸");
42//! debug!(&ctx, "this is a debug message ī¸đŸ");
43//! trace!(&ctx, "this is a trace message 🔍");
44//! ```
45//! Outputs:
46//!
47//! ```text
48//! 21:58:55 [ERROR] xxx: [src/main.rs:35] this is an error message 🚨
49//! 21:58:55 [WARN] xxx: [src/main.rs:36] this is a warning message âš ī¸
50//! 21:58:55 [INFO] xxx: [src/main.rs:37] this is an info message â„šī¸
51//! 21:58:55 [DEBUG] (7) xxx: [src/main.rs:38] this is a debug message ī¸đŸ
52//! 21:58:55 [TRACE] (7) xxx: [src/main.rs:39] this is a trace message 🔍
53//! ```
54//!
55//! [Aya]: https://docs.rs/aya
56//! [env_logger]: https://docs.rs/env_logger
57//! [Log]: https://docs.rs/log/0.4.14/log/trait.Log.html
58//! [log]: https://docs.rs/log
59
60#![cfg_attr(test, expect(unused_crate_dependencies, reason = "used in doctests"))]
61
62use std::{
63    fmt::{LowerHex, UpperHex},
64    net::{Ipv4Addr, Ipv6Addr},
65    os::fd::{AsFd, AsRawFd},
66    ptr, str,
67};
68
69const MAP_NAME: &str = "AYA_LOGS";
70
71pub const LEVEL: &str = "AYA_LOG_LEVEL";
72use aya::{
73    Ebpf, Pod,
74    maps::{Map, MapData, MapError, MapInfo, RingBuf},
75    programs::{ProgramError, loaded_programs},
76};
77pub use aya_log_common::Level;
78use aya_log_common::{ArgumentKind, DisplayHint, LogValueLength, RecordFieldKind};
79use log::{Log, Record, error};
80use thiserror::Error;
81
82#[derive(Copy, Clone)]
83#[repr(transparent)]
84struct RecordFieldWrapper(RecordFieldKind);
85#[derive(Copy, Clone)]
86#[repr(transparent)]
87struct ArgumentWrapper(ArgumentKind);
88#[derive(Copy, Clone)]
89#[repr(transparent)]
90struct DisplayHintWrapper(DisplayHint);
91
92unsafe impl Pod for RecordFieldWrapper {}
93unsafe impl Pod for ArgumentWrapper {}
94unsafe impl Pod for DisplayHintWrapper {}
95
96/// Log messages generated by `aya_log_ebpf` using the [log] crate.
97///
98/// For more details see the [module level documentation](crate).
99#[must_use = "Dropping the logger will close the map FD and cause program loading failure."]
100pub struct EbpfLogger<T> {
101    ring_buf: RingBuf<MapData>,
102    logger: T,
103}
104
105impl<T> AsFd for EbpfLogger<T> {
106    fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
107        let Self {
108            ring_buf,
109            logger: _,
110        } = self;
111        ring_buf.as_fd()
112    }
113}
114
115impl<T> AsRawFd for EbpfLogger<T> {
116    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
117        let Self {
118            ring_buf,
119            logger: _,
120        } = self;
121        ring_buf.as_raw_fd()
122    }
123}
124
125/// Log messages generated by `aya_log_ebpf` using the [log] crate.
126#[deprecated(since = "0.2.1", note = "Use `aya_log::EbpfLogger` instead")]
127pub type BpfLogger<T> = EbpfLogger<T>;
128
129impl EbpfLogger<&'static dyn Log> {
130    /// Starts reading log records created with `aya-log-ebpf` and logs them
131    /// with the default logger. See [`log::logger`].
132    pub fn init(bpf: &mut Ebpf) -> Result<Self, Error> {
133        Self::init_with_logger(bpf, log::logger())
134    }
135
136    /// Attaches to an existing `aya-log-ebpf` instance.
137    ///
138    /// Attaches to the logs produced by `program_id`. Can be used to read logs
139    /// generated by a pinned program. The log records will be written to the
140    /// default logger. See [`log::logger`].
141    pub fn init_from_id(program_id: u32) -> Result<Self, Error> {
142        Self::init_from_id_with_logger(program_id, log::logger())
143    }
144}
145
146impl<T: Log> EbpfLogger<T> {
147    /// Starts reading log records created with `aya-log-ebpf` and logs them
148    /// with the given logger.
149    pub fn init_with_logger(bpf: &mut Ebpf, logger: T) -> Result<Self, Error> {
150        let map = bpf.take_map(MAP_NAME).ok_or(Error::MapNotFound)?;
151        Self::new(map, logger)
152    }
153
154    /// Attaches to an existing `aya-log-ebpf` instance and logs with the given logger.
155    ///
156    /// Attaches to the logs produced by `program_id`. Can be used to read logs generated by a
157    /// pinned program. The log records will be written to the given logger.
158    pub fn init_from_id_with_logger(program_id: u32, logger: T) -> Result<Self, Error> {
159        let program_info = loaded_programs()
160            .filter_map(Result::ok)
161            .find(|info| info.id() == program_id)
162            .ok_or(Error::ProgramNotFound)?;
163
164        let map = program_info
165            .map_ids()?
166            .ok_or_else(|| Error::MapNotFound)?
167            .iter()
168            .filter_map(|id| MapInfo::from_id(*id).ok())
169            .find(|map_info| map_info.name_as_str() == Some(MAP_NAME))
170            .ok_or(Error::MapNotFound)?;
171        let map = MapData::from_id(map.id())?;
172
173        Self::new(Map::RingBuf(map), logger)
174    }
175
176    fn new(map: Map, logger: T) -> Result<Self, Error> {
177        let ring_buf: RingBuf<_> = map.try_into()?;
178
179        Ok(Self { ring_buf, logger })
180    }
181
182    /// Reads log records from eBPF and writes them to the logger.
183    pub fn flush(&mut self) {
184        let Self { ring_buf, logger } = self;
185        while let Some(buf) = ring_buf.next() {
186            log_buf(buf.as_ref(), logger).unwrap();
187        }
188    }
189}
190
191pub trait Formatter<T> {
192    fn format(v: T) -> String;
193}
194
195pub struct DefaultFormatter;
196impl<T> Formatter<T> for DefaultFormatter
197where
198    T: ToString,
199{
200    fn format(v: T) -> String {
201        v.to_string()
202    }
203}
204
205pub struct LowerHexFormatter;
206impl<T> Formatter<T> for LowerHexFormatter
207where
208    T: LowerHex,
209{
210    fn format(v: T) -> String {
211        format!("{v:x}")
212    }
213}
214
215pub struct LowerHexBytesFormatter;
216impl Formatter<&[u8]> for LowerHexBytesFormatter {
217    fn format(v: &[u8]) -> String {
218        let mut s = String::new();
219        for v in v {
220            let () = core::fmt::write(&mut s, format_args!("{v:02x}")).unwrap();
221        }
222        s
223    }
224}
225
226pub struct UpperHexFormatter;
227impl<T> Formatter<T> for UpperHexFormatter
228where
229    T: UpperHex,
230{
231    fn format(v: T) -> String {
232        format!("{v:X}")
233    }
234}
235
236pub struct UpperHexBytesFormatter;
237impl Formatter<&[u8]> for UpperHexBytesFormatter {
238    fn format(v: &[u8]) -> String {
239        let mut s = String::new();
240        for v in v {
241            let () = core::fmt::write(&mut s, format_args!("{v:02X}")).unwrap();
242        }
243        s
244    }
245}
246
247pub struct Ipv4Formatter;
248impl<T> Formatter<T> for Ipv4Formatter
249where
250    T: Into<Ipv4Addr>,
251{
252    fn format(v: T) -> String {
253        v.into().to_string()
254    }
255}
256
257pub struct Ipv6Formatter;
258impl<T> Formatter<T> for Ipv6Formatter
259where
260    T: Into<Ipv6Addr>,
261{
262    fn format(v: T) -> String {
263        v.into().to_string()
264    }
265}
266
267pub struct LowerMacFormatter;
268impl Formatter<[u8; 6]> for LowerMacFormatter {
269    fn format(v: [u8; 6]) -> String {
270        format!(
271            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
272            v[0], v[1], v[2], v[3], v[4], v[5]
273        )
274    }
275}
276
277pub struct UpperMacFormatter;
278impl Formatter<[u8; 6]> for UpperMacFormatter {
279    fn format(v: [u8; 6]) -> String {
280        format!(
281            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
282            v[0], v[1], v[2], v[3], v[4], v[5]
283        )
284    }
285}
286
287pub struct PointerFormatter;
288
289impl<T> Formatter<*const T> for PointerFormatter {
290    #[expect(clippy::pointer_format, reason = "that's the point")]
291    fn format(v: *const T) -> String {
292        format!("{v:p}")
293    }
294}
295
296impl<T> Formatter<*mut T> for PointerFormatter {
297    #[expect(clippy::pointer_format, reason = "that's the point")]
298    fn format(v: *mut T) -> String {
299        format!("{v:p}")
300    }
301}
302
303trait Format {
304    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()>;
305}
306
307impl Format for &[u8] {
308    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
309        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
310            Some(DisplayHint::LowerHex) => Ok(LowerHexBytesFormatter::format(self)),
311            Some(DisplayHint::UpperHex) => Ok(UpperHexBytesFormatter::format(self)),
312            _ => Err(()),
313        }
314    }
315}
316
317impl Format for u32 {
318    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
319        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
320            Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
321            Some(DisplayHint::LowerHex) => Ok(LowerHexFormatter::format(self)),
322            Some(DisplayHint::UpperHex) => Ok(UpperHexFormatter::format(self)),
323            Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
324            Some(DisplayHint::LowerMac) => Err(()),
325            Some(DisplayHint::UpperMac) => Err(()),
326            Some(DisplayHint::Pointer) => Err(()),
327            None => Ok(DefaultFormatter::format(self)),
328        }
329    }
330}
331
332impl Format for Ipv4Addr {
333    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
334        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
335            Some(DisplayHint::Default) => Ok(Ipv4Formatter::format(*self)),
336            Some(DisplayHint::LowerHex) => Err(()),
337            Some(DisplayHint::UpperHex) => Err(()),
338            Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
339            Some(DisplayHint::LowerMac) => Err(()),
340            Some(DisplayHint::UpperMac) => Err(()),
341            Some(DisplayHint::Pointer) => Err(()),
342            None => Ok(Ipv4Formatter::format(*self)),
343        }
344    }
345}
346
347impl Format for Ipv6Addr {
348    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
349        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
350            Some(DisplayHint::Default) => Ok(Ipv6Formatter::format(*self)),
351            Some(DisplayHint::LowerHex) => Err(()),
352            Some(DisplayHint::UpperHex) => Err(()),
353            Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
354            Some(DisplayHint::LowerMac) => Err(()),
355            Some(DisplayHint::UpperMac) => Err(()),
356            Some(DisplayHint::Pointer) => Err(()),
357            None => Ok(Ipv6Formatter::format(*self)),
358        }
359    }
360}
361
362impl Format for [u8; 4] {
363    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
364        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
365            Some(DisplayHint::Default) => Ok(Ipv4Formatter::format(*self)),
366            Some(DisplayHint::LowerHex) => Err(()),
367            Some(DisplayHint::UpperHex) => Err(()),
368            Some(DisplayHint::Ip) => Ok(Ipv4Formatter::format(*self)),
369            Some(DisplayHint::LowerMac) => Err(()),
370            Some(DisplayHint::UpperMac) => Err(()),
371            Some(DisplayHint::Pointer) => Err(()),
372            None => Ok(Ipv4Formatter::format(*self)),
373        }
374    }
375}
376
377impl Format for [u8; 6] {
378    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
379        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
380            Some(DisplayHint::Default) => Err(()),
381            Some(DisplayHint::LowerHex) => Err(()),
382            Some(DisplayHint::UpperHex) => Err(()),
383            Some(DisplayHint::Ip) => Err(()),
384            Some(DisplayHint::LowerMac) => Ok(LowerMacFormatter::format(*self)),
385            Some(DisplayHint::UpperMac) => Ok(UpperMacFormatter::format(*self)),
386            Some(DisplayHint::Pointer) => Err(()),
387            None => Err(()),
388        }
389    }
390}
391
392impl Format for [u8; 16] {
393    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
394        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
395            Some(DisplayHint::Default) => Err(()),
396            Some(DisplayHint::LowerHex) => Err(()),
397            Some(DisplayHint::UpperHex) => Err(()),
398            Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
399            Some(DisplayHint::LowerMac) => Err(()),
400            Some(DisplayHint::UpperMac) => Err(()),
401            Some(DisplayHint::Pointer) => Err(()),
402            None => Err(()),
403        }
404    }
405}
406
407impl Format for [u16; 8] {
408    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
409        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
410            Some(DisplayHint::Default) => Err(()),
411            Some(DisplayHint::LowerHex) => Err(()),
412            Some(DisplayHint::UpperHex) => Err(()),
413            Some(DisplayHint::Ip) => Ok(Ipv6Formatter::format(*self)),
414            Some(DisplayHint::LowerMac) => Err(()),
415            Some(DisplayHint::UpperMac) => Err(()),
416            Some(DisplayHint::Pointer) => Err(()),
417            None => Err(()),
418        }
419    }
420}
421
422macro_rules! impl_format {
423    ($type:ident) => {
424        impl Format for $type {
425            fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
426                match last_hint.map(|DisplayHintWrapper(dh)| dh) {
427                    Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
428                    Some(DisplayHint::LowerHex) => Ok(LowerHexFormatter::format(self)),
429                    Some(DisplayHint::UpperHex) => Ok(UpperHexFormatter::format(self)),
430                    Some(DisplayHint::Ip) => Err(()),
431                    Some(DisplayHint::LowerMac) => Err(()),
432                    Some(DisplayHint::UpperMac) => Err(()),
433                    Some(DisplayHint::Pointer) => Err(()),
434                    None => Ok(DefaultFormatter::format(self)),
435                }
436            }
437        }
438    };
439}
440
441impl_format!(i8);
442impl_format!(i16);
443impl_format!(i32);
444impl_format!(i64);
445impl_format!(isize);
446
447impl_format!(u8);
448impl_format!(u16);
449impl_format!(u64);
450impl_format!(usize);
451
452macro_rules! impl_format_float {
453    ($type:ident) => {
454        impl Format for $type {
455            fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
456                match last_hint.map(|DisplayHintWrapper(dh)| dh) {
457                    Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
458                    Some(DisplayHint::LowerHex) => Err(()),
459                    Some(DisplayHint::UpperHex) => Err(()),
460                    Some(DisplayHint::Ip) => Err(()),
461                    Some(DisplayHint::LowerMac) => Err(()),
462                    Some(DisplayHint::UpperMac) => Err(()),
463                    Some(DisplayHint::Pointer) => Err(()),
464                    None => Ok(DefaultFormatter::format(self)),
465                }
466            }
467        }
468    };
469}
470
471impl_format_float!(f32);
472impl_format_float!(f64);
473
474impl<T> Format for *const T {
475    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
476        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
477            Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)),
478            _ => Err(()),
479        }
480    }
481}
482
483impl<T> Format for *mut T {
484    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
485        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
486            Some(DisplayHint::Pointer) => Ok(PointerFormatter::format(*self)),
487            _ => Err(()),
488        }
489    }
490}
491
492#[derive(Error, Debug)]
493pub enum Error {
494    #[error("{} not found", MAP_NAME)]
495    MapNotFound,
496
497    #[error(transparent)]
498    MapError(#[from] MapError),
499
500    #[error("program not found")]
501    ProgramNotFound,
502
503    #[error(transparent)]
504    ProgramError(#[from] ProgramError),
505}
506
507fn log_buf<T: ?Sized + Log>(mut buf: &[u8], logger: &T) -> Result<(), ()> {
508    let size = LogValueLength::from_ne_bytes(
509        buf.get(..size_of::<LogValueLength>())
510            .ok_or(())?
511            .try_into()
512            .map_err(|std::array::TryFromSliceError { .. }| ())?,
513    )
514    .into();
515    buf = buf.get(size_of::<LogValueLength>()..size).ok_or(())?;
516
517    let mut target = None;
518    let mut level = None;
519    let mut module = None;
520    let mut file = None;
521    let mut line = None;
522    let mut num_args = None;
523
524    while target.is_none()
525        || level.is_none()
526        || module.is_none()
527        || file.is_none()
528        || line.is_none()
529        || num_args.is_none()
530    {
531        let (RecordFieldWrapper(tag), value, rest) = try_read(buf)?;
532
533        match tag {
534            RecordFieldKind::Target => {
535                let target =
536                    target.replace(str::from_utf8(value).map_err(|str::Utf8Error { .. }| ())?);
537                if target.is_some() {
538                    return Err(());
539                }
540            }
541            RecordFieldKind::Level => {
542                let level = level.replace({
543                    let level = unsafe { ptr::read_unaligned(value.as_ptr().cast()) };
544                    match level {
545                        Level::Error => log::Level::Error,
546                        Level::Warn => log::Level::Warn,
547                        Level::Info => log::Level::Info,
548                        Level::Debug => log::Level::Debug,
549                        Level::Trace => log::Level::Trace,
550                    }
551                });
552                if level.is_some() {
553                    return Err(());
554                }
555            }
556            RecordFieldKind::Module => {
557                let module =
558                    module.replace(str::from_utf8(value).map_err(|str::Utf8Error { .. }| ())?);
559                if module.is_some() {
560                    return Err(());
561                }
562            }
563            RecordFieldKind::File => {
564                let file = file.replace(str::from_utf8(value).map_err(|str::Utf8Error { .. }| ())?);
565                if file.is_some() {
566                    return Err(());
567                }
568            }
569            RecordFieldKind::Line => {
570                let line = line.replace(u32::from_ne_bytes(
571                    value
572                        .try_into()
573                        .map_err(|std::array::TryFromSliceError { .. }| ())?,
574                ));
575                if line.is_some() {
576                    return Err(());
577                }
578            }
579            RecordFieldKind::NumArgs => {
580                let num_args = num_args.replace(u32::from_ne_bytes(
581                    value
582                        .try_into()
583                        .map_err(|std::array::TryFromSliceError { .. }| ())?,
584                ));
585                if num_args.is_some() {
586                    return Err(());
587                }
588            }
589        }
590
591        buf = rest;
592    }
593
594    let mut full_log_msg = String::new();
595    let mut last_hint: Option<DisplayHintWrapper> = None;
596    let num_args = num_args.ok_or(()).and_then(|num_args| {
597        usize::try_from(num_args).map_err(|std::num::TryFromIntError { .. }| ())
598    })?;
599    for () in std::iter::repeat_n((), num_args) {
600        let (ArgumentWrapper(tag), value, rest) = try_read(buf)?;
601
602        match tag {
603            ArgumentKind::DisplayHint => {
604                last_hint = Some(unsafe { ptr::read_unaligned(value.as_ptr().cast()) });
605            }
606            ArgumentKind::I8 => {
607                full_log_msg.push_str(
608                    &i8::from_ne_bytes(
609                        value
610                            .try_into()
611                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
612                    )
613                    .format(last_hint.take())?,
614                );
615            }
616            ArgumentKind::I16 => {
617                full_log_msg.push_str(
618                    &i16::from_ne_bytes(
619                        value
620                            .try_into()
621                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
622                    )
623                    .format(last_hint.take())?,
624                );
625            }
626            ArgumentKind::I32 => {
627                full_log_msg.push_str(
628                    &i32::from_ne_bytes(
629                        value
630                            .try_into()
631                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
632                    )
633                    .format(last_hint.take())?,
634                );
635            }
636            ArgumentKind::I64 => {
637                full_log_msg.push_str(
638                    &i64::from_ne_bytes(
639                        value
640                            .try_into()
641                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
642                    )
643                    .format(last_hint.take())?,
644                );
645            }
646            ArgumentKind::Isize => {
647                full_log_msg.push_str(
648                    &isize::from_ne_bytes(
649                        value
650                            .try_into()
651                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
652                    )
653                    .format(last_hint.take())?,
654                );
655            }
656            ArgumentKind::U8 => {
657                full_log_msg.push_str(
658                    &u8::from_ne_bytes(
659                        value
660                            .try_into()
661                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
662                    )
663                    .format(last_hint.take())?,
664                );
665            }
666            ArgumentKind::U16 => {
667                full_log_msg.push_str(
668                    &u16::from_ne_bytes(
669                        value
670                            .try_into()
671                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
672                    )
673                    .format(last_hint.take())?,
674                );
675            }
676            ArgumentKind::U32 => {
677                full_log_msg.push_str(
678                    &u32::from_ne_bytes(
679                        value
680                            .try_into()
681                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
682                    )
683                    .format(last_hint.take())?,
684                );
685            }
686            ArgumentKind::U64 => {
687                full_log_msg.push_str(
688                    &u64::from_ne_bytes(
689                        value
690                            .try_into()
691                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
692                    )
693                    .format(last_hint.take())?,
694                );
695            }
696            ArgumentKind::Usize => {
697                full_log_msg.push_str(
698                    &usize::from_ne_bytes(
699                        value
700                            .try_into()
701                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
702                    )
703                    .format(last_hint.take())?,
704                );
705            }
706            ArgumentKind::F32 => {
707                full_log_msg.push_str(
708                    &f32::from_ne_bytes(
709                        value
710                            .try_into()
711                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
712                    )
713                    .format(last_hint.take())?,
714                );
715            }
716            ArgumentKind::F64 => {
717                full_log_msg.push_str(
718                    &f64::from_ne_bytes(
719                        value
720                            .try_into()
721                            .map_err(|std::array::TryFromSliceError { .. }| ())?,
722                    )
723                    .format(last_hint.take())?,
724                );
725            }
726            ArgumentKind::Ipv4Addr => {
727                let value: [u8; 4] = value
728                    .try_into()
729                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
730                let value = Ipv4Addr::from(value);
731                full_log_msg.push_str(&value.format(last_hint.take())?)
732            }
733            ArgumentKind::Ipv6Addr => {
734                let value: [u8; 16] = value
735                    .try_into()
736                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
737                let value = Ipv6Addr::from(value);
738                full_log_msg.push_str(&value.format(last_hint.take())?)
739            }
740            ArgumentKind::ArrU8Len4 => {
741                let value: [u8; 4] = value
742                    .try_into()
743                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
744                full_log_msg.push_str(&value.format(last_hint.take())?);
745            }
746            ArgumentKind::ArrU8Len6 => {
747                let value: [u8; 6] = value
748                    .try_into()
749                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
750                full_log_msg.push_str(&value.format(last_hint.take())?);
751            }
752            ArgumentKind::ArrU8Len16 => {
753                let value: [u8; 16] = value
754                    .try_into()
755                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
756                full_log_msg.push_str(&value.format(last_hint.take())?);
757            }
758            ArgumentKind::ArrU16Len8 => {
759                let data: [u8; 16] = value
760                    .try_into()
761                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
762                let mut value: [u16; 8] = Default::default();
763                for (i, s) in data.chunks_exact(2).enumerate() {
764                    value[i] = (u16::from(s[1]) << 8) | u16::from(s[0]);
765                }
766                full_log_msg.push_str(&value.format(last_hint.take())?);
767            }
768            ArgumentKind::Bytes => {
769                full_log_msg.push_str(&value.format(last_hint.take())?);
770            }
771            ArgumentKind::Str => match str::from_utf8(value) {
772                Ok(v) => {
773                    full_log_msg.push_str(v);
774                }
775                Err(e) => error!("received invalid utf8 string: {e}"),
776            },
777            ArgumentKind::Pointer => {
778                let value = value
779                    .try_into()
780                    .map_err(|std::array::TryFromSliceError { .. }| ())?;
781                let ptr = usize::from_ne_bytes(value) as *const ();
782                full_log_msg.push_str(&ptr.format(last_hint.take())?);
783            }
784        }
785
786        buf = rest;
787    }
788
789    logger.log(
790        &Record::builder()
791            .args(format_args!("{full_log_msg}"))
792            .target(target.ok_or(())?)
793            .level(level.ok_or(())?)
794            .module_path(module)
795            .file(file)
796            .line(line)
797            .build(),
798    );
799    logger.flush();
800    Ok(())
801}
802
803fn try_read<T: Pod>(mut buf: &[u8]) -> Result<(T, &[u8], &[u8]), ()> {
804    if buf.len() < size_of::<T>() + size_of::<LogValueLength>() {
805        return Err(());
806    }
807
808    let tag = unsafe { ptr::read_unaligned(buf.as_ptr().cast::<T>()) };
809    buf = &buf[size_of::<T>()..];
810
811    let len = LogValueLength::from_ne_bytes(buf[..size_of::<LogValueLength>()].try_into().unwrap());
812    buf = &buf[size_of::<LogValueLength>()..];
813
814    let len: usize = len.into();
815    if buf.len() < len {
816        return Err(());
817    }
818
819    let (value, rest) = buf.split_at(len);
820    Ok((tag, value, rest))
821}
822
823#[cfg(test)]
824mod test {
825    use std::{net::IpAddr, num::NonZeroUsize};
826
827    use aya_log_common::{Argument, Field, Header};
828    use log::{Level, logger};
829
830    trait WriteToBuf {
831        fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize>;
832    }
833
834    impl<T: Argument> WriteToBuf for T {
835        fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
836            let (kind, value) = self.as_argument();
837            let field = Field::new(kind, value)?;
838            let mut size = 0;
839            let mut op = |slice: &[u8]| {
840                let buf = buf.get_mut(size..)?;
841                let buf = buf.get_mut(..slice.len())?;
842                buf.copy_from_slice(slice);
843                size += slice.len();
844                Some(())
845            };
846            field.with_bytes(&mut op)?;
847            NonZeroUsize::new(size)
848        }
849    }
850
851    use super::*;
852
853    fn write_record_header(
854        buf: &mut [u8],
855        target: &str,
856        level: aya_log_common::Level,
857        module: &str,
858        file: &str,
859        line: u32,
860        num_args: u32,
861    ) -> Option<NonZeroUsize> {
862        let header = Header::new(target, level, module, file, line, num_args)?;
863        let mut size = 0;
864        let mut op = |slice: &[u8]| {
865            let buf = buf.get_mut(size..)?;
866            let buf = buf.get_mut(..slice.len())?;
867            buf.copy_from_slice(slice);
868            size += slice.len();
869            Some(())
870        };
871        header.with_bytes(&mut op)?;
872        NonZeroUsize::new(size)
873    }
874
875    fn new_log(args: u32) -> Option<(usize, Vec<u8>)> {
876        let mut buf = vec![0; 8192];
877        let len = write_record_header(
878            &mut buf,
879            "test",
880            aya_log_common::Level::Info,
881            "test",
882            "test.rs",
883            123,
884            args,
885        )?;
886        Some((len.get(), buf))
887    }
888
889    #[test]
890    fn test_str() {
891        testing_logger::setup();
892        let (mut len, mut input) = new_log(1).unwrap();
893
894        len += "test".write(&mut input[len..]).unwrap().get();
895
896        let len = u16::try_from(len).unwrap();
897        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
898
899        let logger = logger();
900        let () = log_buf(&input, logger).unwrap();
901        testing_logger::validate(|captured_logs| {
902            assert_eq!(captured_logs.len(), 1);
903            assert_eq!(captured_logs[0].body, "test");
904            assert_eq!(captured_logs[0].level, Level::Info);
905        });
906    }
907
908    #[test]
909    fn test_str_with_args() {
910        testing_logger::setup();
911        let (mut len, mut input) = new_log(2).unwrap();
912
913        len += "hello ".write(&mut input[len..]).unwrap().get();
914        len += "test".write(&mut input[len..]).unwrap().get();
915
916        let len = u16::try_from(len).unwrap();
917        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
918
919        let logger = logger();
920        let () = log_buf(&input, logger).unwrap();
921        testing_logger::validate(|captured_logs| {
922            assert_eq!(captured_logs.len(), 1);
923            assert_eq!(captured_logs[0].body, "hello test");
924            assert_eq!(captured_logs[0].level, Level::Info);
925        });
926    }
927
928    #[test]
929    fn test_bytes() {
930        testing_logger::setup();
931        let (mut len, mut input) = new_log(2).unwrap();
932
933        len += DisplayHint::LowerHex
934            .write(&mut input[len..])
935            .unwrap()
936            .get();
937        len += [0xde, 0xad].write(&mut input[len..]).unwrap().get();
938
939        let len = u16::try_from(len).unwrap();
940        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
941
942        let logger = logger();
943        let () = log_buf(&input, logger).unwrap();
944        testing_logger::validate(|captured_logs| {
945            assert_eq!(captured_logs.len(), 1);
946            assert_eq!(captured_logs[0].body, "dead");
947            assert_eq!(captured_logs[0].level, Level::Info);
948        });
949    }
950
951    #[test]
952    fn test_bytes_with_args() {
953        testing_logger::setup();
954        let (mut len, mut input) = new_log(5).unwrap();
955
956        len += DisplayHint::LowerHex
957            .write(&mut input[len..])
958            .unwrap()
959            .get();
960        len += [0xde, 0xad].write(&mut input[len..]).unwrap().get();
961
962        len += " ".write(&mut input[len..]).unwrap().get();
963
964        len += DisplayHint::UpperHex
965            .write(&mut input[len..])
966            .unwrap()
967            .get();
968        len += [0xbe, 0xef].write(&mut input[len..]).unwrap().get();
969
970        let len = u16::try_from(len).unwrap();
971        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
972
973        let logger = logger();
974        let () = log_buf(&input, logger).unwrap();
975        testing_logger::validate(|captured_logs| {
976            assert_eq!(captured_logs.len(), 1);
977            assert_eq!(captured_logs[0].body, "dead BEEF");
978            assert_eq!(captured_logs[0].level, Level::Info);
979        });
980    }
981
982    #[test]
983    fn test_bytes_unambiguous() {
984        testing_logger::setup();
985        let (mut len, mut input) = new_log(5).unwrap();
986
987        len += DisplayHint::LowerHex
988            .write(&mut input[len..])
989            .unwrap()
990            .get();
991        len += [0x01, 0x02].write(&mut input[len..]).unwrap().get();
992
993        len += " ".write(&mut input[len..]).unwrap().get();
994
995        len += DisplayHint::LowerHex
996            .write(&mut input[len..])
997            .unwrap()
998            .get();
999        len += [0x12].write(&mut input[len..]).unwrap().get();
1000
1001        let len = u16::try_from(len).unwrap();
1002        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1003
1004        let logger = logger();
1005        let () = log_buf(&input, logger).unwrap();
1006        testing_logger::validate(|captured_logs| {
1007            assert_eq!(captured_logs.len(), 1);
1008            assert_eq!(captured_logs[0].body, "0102 12");
1009            assert_eq!(captured_logs[0].level, Level::Info);
1010        });
1011    }
1012
1013    #[test]
1014    fn test_display_hint_default() {
1015        testing_logger::setup();
1016        let (mut len, mut input) = new_log(3).unwrap();
1017
1018        len += "default hint: ".write(&mut input[len..]).unwrap().get();
1019        len += DisplayHint::Default.write(&mut input[len..]).unwrap().get();
1020        len += 14.write(&mut input[len..]).unwrap().get();
1021
1022        let len = u16::try_from(len).unwrap();
1023        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1024
1025        let logger = logger();
1026        let () = log_buf(&input, logger).unwrap();
1027        testing_logger::validate(|captured_logs| {
1028            assert_eq!(captured_logs.len(), 1);
1029            assert_eq!(captured_logs[0].body, "default hint: 14");
1030            assert_eq!(captured_logs[0].level, Level::Info);
1031        });
1032    }
1033
1034    #[test]
1035    fn test_display_hint_lower_hex() {
1036        testing_logger::setup();
1037        let (mut len, mut input) = new_log(3).unwrap();
1038
1039        len += "lower hex: ".write(&mut input[len..]).unwrap().get();
1040        len += DisplayHint::LowerHex
1041            .write(&mut input[len..])
1042            .unwrap()
1043            .get();
1044        len += 200.write(&mut input[len..]).unwrap().get();
1045
1046        let len = u16::try_from(len).unwrap();
1047        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1048
1049        let logger = logger();
1050        let () = log_buf(&input, logger).unwrap();
1051        testing_logger::validate(|captured_logs| {
1052            assert_eq!(captured_logs.len(), 1);
1053            assert_eq!(captured_logs[0].body, "lower hex: c8");
1054            assert_eq!(captured_logs[0].level, Level::Info);
1055        });
1056    }
1057
1058    #[test]
1059    fn test_display_hint_upper_hex() {
1060        testing_logger::setup();
1061        let (mut len, mut input) = new_log(3).unwrap();
1062
1063        len += "upper hex: ".write(&mut input[len..]).unwrap().get();
1064        len += DisplayHint::UpperHex
1065            .write(&mut input[len..])
1066            .unwrap()
1067            .get();
1068        len += 200.write(&mut input[len..]).unwrap().get();
1069
1070        let len = u16::try_from(len).unwrap();
1071        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1072
1073        let logger = logger();
1074        let () = log_buf(&input, logger).unwrap();
1075        testing_logger::validate(|captured_logs| {
1076            assert_eq!(captured_logs.len(), 1);
1077            assert_eq!(captured_logs[0].body, "upper hex: C8");
1078            assert_eq!(captured_logs[0].level, Level::Info);
1079        });
1080    }
1081
1082    #[test]
1083    fn test_display_hint_ipv4() {
1084        testing_logger::setup();
1085        let (mut len, mut input) = new_log(3).unwrap();
1086
1087        len += "ipv4: ".write(&mut input[len..]).unwrap().get();
1088        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1089        len += Ipv4Addr::new(10, 0, 0, 1)
1090            .write(&mut input[len..])
1091            .unwrap()
1092            .get();
1093
1094        let len = u16::try_from(len).unwrap();
1095        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1096
1097        let logger = logger();
1098        let () = log_buf(&input, logger).unwrap();
1099        testing_logger::validate(|captured_logs| {
1100            assert_eq!(captured_logs.len(), 1);
1101            assert_eq!(captured_logs[0].body, "ipv4: 10.0.0.1");
1102            assert_eq!(captured_logs[0].level, Level::Info);
1103        });
1104    }
1105
1106    #[test]
1107    fn test_display_hint_ip_ipv4() {
1108        testing_logger::setup();
1109        let (mut len, mut input) = new_log(3).unwrap();
1110
1111        len += "ipv4: ".write(&mut input[len..]).unwrap().get();
1112        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1113        len += IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))
1114            .write(&mut input[len..])
1115            .unwrap()
1116            .get();
1117
1118        let len = u16::try_from(len).unwrap();
1119        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1120
1121        let logger = logger();
1122        let () = log_buf(&input, logger).unwrap();
1123        testing_logger::validate(|captured_logs| {
1124            assert_eq!(captured_logs.len(), 1);
1125            assert_eq!(captured_logs[0].body, "ipv4: 10.0.0.1");
1126            assert_eq!(captured_logs[0].level, Level::Info);
1127        });
1128    }
1129
1130    #[test]
1131    fn test_display_hint_ipv4_u32() {
1132        testing_logger::setup();
1133        let (mut len, mut input) = new_log(3).unwrap();
1134
1135        len += "ipv4: ".write(&mut input[len..]).unwrap().get();
1136        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1137        // 10.0.0.1 as u32
1138        len += 167772161u32.write(&mut input[len..]).unwrap().get();
1139
1140        let len = u16::try_from(len).unwrap();
1141        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1142
1143        let logger = logger();
1144        let () = log_buf(&input, logger).unwrap();
1145        testing_logger::validate(|captured_logs| {
1146            assert_eq!(captured_logs.len(), 1);
1147            assert_eq!(captured_logs[0].body, "ipv4: 10.0.0.1");
1148            assert_eq!(captured_logs[0].level, Level::Info);
1149        });
1150    }
1151
1152    #[test]
1153    fn test_display_hint_ipv6() {
1154        testing_logger::setup();
1155        let (mut len, mut input) = new_log(3).unwrap();
1156
1157        len += "ipv6: ".write(&mut input[len..]).unwrap().get();
1158        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1159        len += Ipv6Addr::new(
1160            0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
1161        )
1162        .write(&mut input[len..])
1163        .unwrap()
1164        .get();
1165
1166        let len = u16::try_from(len).unwrap();
1167        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1168
1169        let logger = logger();
1170        let () = log_buf(&input, logger).unwrap();
1171        testing_logger::validate(|captured_logs| {
1172            assert_eq!(captured_logs.len(), 1);
1173            assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
1174            assert_eq!(captured_logs[0].level, Level::Info);
1175        });
1176    }
1177
1178    #[test]
1179    fn test_display_hint_ip_ipv6() {
1180        testing_logger::setup();
1181        let (mut len, mut input) = new_log(3).unwrap();
1182
1183        len += "ipv6: ".write(&mut input[len..]).unwrap().get();
1184        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1185        len += IpAddr::V6(Ipv6Addr::new(
1186            0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
1187        ))
1188        .write(&mut input[len..])
1189        .unwrap()
1190        .get();
1191
1192        let len = u16::try_from(len).unwrap();
1193        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1194
1195        let logger = logger();
1196        let () = log_buf(&input, logger).unwrap();
1197        testing_logger::validate(|captured_logs| {
1198            assert_eq!(captured_logs.len(), 1);
1199            assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
1200            assert_eq!(captured_logs[0].level, Level::Info);
1201        });
1202    }
1203
1204    #[test]
1205    fn test_display_hint_ipv6_arr_u8_len_16() {
1206        testing_logger::setup();
1207        let (mut len, mut input) = new_log(3).unwrap();
1208
1209        len += "ipv6: ".write(&mut input[len..]).unwrap().get();
1210        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1211        // 2001:db8::1:1 as byte array
1212        let ipv6_arr: [u8; 16] = [
1213            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1214            0x00, 0x01,
1215        ];
1216        len += ipv6_arr.write(&mut input[len..]).unwrap().get();
1217
1218        let len = u16::try_from(len).unwrap();
1219        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1220
1221        let logger = logger();
1222        let () = log_buf(&input, logger).unwrap();
1223        testing_logger::validate(|captured_logs| {
1224            assert_eq!(captured_logs.len(), 1);
1225            assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
1226            assert_eq!(captured_logs[0].level, Level::Info);
1227        });
1228    }
1229
1230    #[test]
1231    fn test_display_hint_ipv6_arr_u16_len_8() {
1232        testing_logger::setup();
1233        let (mut len, mut input) = new_log(3).unwrap();
1234
1235        len += "ipv6: ".write(&mut input[len..]).unwrap().get();
1236        len += DisplayHint::Ip.write(&mut input[len..]).unwrap().get();
1237
1238        let ipv6 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0x1, 0x1);
1239        let ipv6_arr = ipv6.octets();
1240        len += ipv6_arr.write(&mut input[len..]).unwrap().get();
1241
1242        let len = u16::try_from(len).unwrap();
1243        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1244
1245        let logger = logger();
1246        let () = log_buf(&input, logger).unwrap();
1247        testing_logger::validate(|captured_logs| {
1248            assert_eq!(captured_logs.len(), 1);
1249            assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
1250            assert_eq!(captured_logs[0].level, Level::Info);
1251        });
1252    }
1253
1254    #[test]
1255    fn test_display_hint_lower_mac() {
1256        testing_logger::setup();
1257        let (mut len, mut input) = new_log(3).unwrap();
1258
1259        len += "mac: ".write(&mut input[len..]).unwrap().get();
1260        len += DisplayHint::LowerMac
1261            .write(&mut input[len..])
1262            .unwrap()
1263            .get();
1264        // 00:00:5e:00:53:af as byte array
1265        let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
1266        len += mac_arr.write(&mut input[len..]).unwrap().get();
1267
1268        let len = u16::try_from(len).unwrap();
1269        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1270
1271        let logger = logger();
1272        let () = log_buf(&input, logger).unwrap();
1273        testing_logger::validate(|captured_logs| {
1274            assert_eq!(captured_logs.len(), 1);
1275            assert_eq!(captured_logs[0].body, "mac: 00:00:5e:00:53:af");
1276            assert_eq!(captured_logs[0].level, Level::Info);
1277        });
1278    }
1279
1280    #[test]
1281    fn test_display_hint_upper_mac() {
1282        testing_logger::setup();
1283        let (mut len, mut input) = new_log(3).unwrap();
1284
1285        len += "mac: ".write(&mut input[len..]).unwrap().get();
1286        len += DisplayHint::UpperMac
1287            .write(&mut input[len..])
1288            .unwrap()
1289            .get();
1290        // 00:00:5E:00:53:AF as byte array
1291        let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
1292        len += mac_arr.write(&mut input[len..]).unwrap().get();
1293
1294        let len = u16::try_from(len).unwrap();
1295        input.splice(0..0, (len + 2).to_ne_bytes().iter().copied());
1296
1297        let logger = logger();
1298        let () = log_buf(&input, logger).unwrap();
1299        testing_logger::validate(|captured_logs| {
1300            assert_eq!(captured_logs.len(), 1);
1301            assert_eq!(captured_logs[0].body, "mac: 00:00:5E:00:53:AF");
1302            assert_eq!(captured_logs[0].level, Level::Info);
1303        });
1304    }
1305}