aya-log: Add `*max_level_*` filter features

Add the following features for configuring the maximum log level
available:

* `max_level_off`
* `max_level_error`
* `max_level_warn`
* `max_level_info`
* `max_level_debug`
* `max_level_trace`
* `release_max_level_off`
* `release_max_level_error`
* `release_max_level_warn`
* `release_max_level_info`
* `release_max_level_debug`
* `release_max_level_trace`

Log invocations at disabled level will be skipped, which is especially
beneficial for eBPF programs which are not going to send unneceessary
logs through perf buffers.

Features with `release_` prefix are used only in release builds.

Those features have to applied on the userspace and eBPF crate
separately. The correct thing to do is to set them on the same level. In
case when userspace has higher maximum log level, it's not going to
recieve the logs beyond the filter in eBPF crate. In the opposite
situation, when eBPF crate has higher maximum level, it's going to waste
cycles on sending logs through perf buffer, while they are not going to
be displayed in the userspace.

Signed-off-by: Michal Rostecki <vadorovsky@gmail.com>
pull/399/head
Michal Rostecki 2 years ago
parent a93a975cc6
commit 90faac2a3a

@ -8,7 +8,23 @@ aya-log-common = { path = "../aya-log-common" }
aya-log-parser = { path = "../aya-log-parser" } aya-log-parser = { path = "../aya-log-parser" }
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = "1.0" syn = { version = "1.0", features = ["extra-traits"] }
cfg-if = "1.0"
[features]
max_level_off = []
max_level_error = []
max_level_warn = []
max_level_info = []
max_level_debug = []
max_level_trace = []
release_max_level_off = []
release_max_level_error = []
release_max_level_warn = []
release_max_level_info = []
release_max_level_debug = []
release_max_level_trace = []
[lib] [lib]
proc-macro = true proc-macro = true

@ -10,6 +10,8 @@ use syn::{
use aya_log_common::DisplayHint; use aya_log_common::DisplayHint;
use aya_log_parser::{parse, Fragment}; use aya_log_parser::{parse, Fragment};
use crate::{LevelFilter, MAX_LEVEL};
pub(crate) struct LogArgs { pub(crate) struct LogArgs {
pub(crate) ctx: Expr, pub(crate) ctx: Expr,
pub(crate) target: Option<Expr>, pub(crate) target: Option<Expr>,
@ -90,10 +92,23 @@ pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStre
Some(t) => quote! { #t }, Some(t) => quote! { #t },
None => quote! { module_path!() }, None => quote! { module_path!() },
}; };
let lvl: TokenStream = if let Some(l) = level { let level: TokenStream = if let Some(level) = level {
l // Level was chosen by calling concrete macros like error!, warn!, etc.
} else if let Some(l) = args.level { level
quote! { #l } } else if let Some(level) = args.level {
// Level was chosen by passing an argument to the log! macro.
// We need to apply the max log level filter here.
let level_str = format!("{:?}", level);
if (level_str.contains("Error") && LevelFilter::Error > MAX_LEVEL)
|| (level_str.contains("Warn") && LevelFilter::Warn > MAX_LEVEL)
|| (level_str.contains("Info") && LevelFilter::Info > MAX_LEVEL)
|| (level_str.contains("Debug") && LevelFilter::Debug > MAX_LEVEL)
|| (level_str.contains("Trace") && LevelFilter::Trace > MAX_LEVEL)
{
return Ok(TokenStream::new());
}
quote! { #level }
} else { } else {
return Err(Error::new( return Err(Error::new(
args.format_string.span(), args.format_string.span(),
@ -140,7 +155,7 @@ pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStre
if let Ok(header_len) = ::aya_log_ebpf::write_record_header( if let Ok(header_len) = ::aya_log_ebpf::write_record_header(
&mut buf.buf, &mut buf.buf,
#target, #target,
#lvl, #level,
module_path!(), module_path!(),
file!(), file!(),
line!(), line!(),

@ -3,6 +3,44 @@ use syn::parse_macro_input;
mod expand; mod expand;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum LevelFilter {
_Off,
Error,
Warn,
Info,
Debug,
Trace,
}
cfg_if::cfg_if! {
if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::_Off;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::Error;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::Warn;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::Info;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::Debug;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
const MAX_LEVEL: LevelFilter = LevelFilter::Trace;
} else if #[cfg(feature = "max_level_off")] {
const MAX_LEVEL: LevelFilter = LevelFilter::_Off;
} else if #[cfg(feature = "max_level_error")] {
const MAX_LEVEL: LevelFilter = LevelFilter::Error;
} else if #[cfg(feature = "max_level_warn")] {
const MAX_LEVEL: LevelFilter = LevelFilter::Warn;
} else if #[cfg(feature = "max_level_info")] {
const MAX_LEVEL: LevelFilter = LevelFilter::Info;
} else if #[cfg(feature = "max_level_debug")] {
const MAX_LEVEL: LevelFilter = LevelFilter::Debug;
} else {
const MAX_LEVEL: LevelFilter = LevelFilter::Trace;
}
}
#[proc_macro] #[proc_macro]
pub fn log(args: TokenStream) -> TokenStream { pub fn log(args: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
@ -13,6 +51,9 @@ pub fn log(args: TokenStream) -> TokenStream {
#[proc_macro] #[proc_macro]
pub fn error(args: TokenStream) -> TokenStream { pub fn error(args: TokenStream) -> TokenStream {
if LevelFilter::Error > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
expand::error(args) expand::error(args)
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())
@ -21,6 +62,9 @@ pub fn error(args: TokenStream) -> TokenStream {
#[proc_macro] #[proc_macro]
pub fn warn(args: TokenStream) -> TokenStream { pub fn warn(args: TokenStream) -> TokenStream {
if LevelFilter::Warn > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
expand::warn(args) expand::warn(args)
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())
@ -29,6 +73,9 @@ pub fn warn(args: TokenStream) -> TokenStream {
#[proc_macro] #[proc_macro]
pub fn info(args: TokenStream) -> TokenStream { pub fn info(args: TokenStream) -> TokenStream {
if LevelFilter::Info > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
expand::info(args) expand::info(args)
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())
@ -37,6 +84,9 @@ pub fn info(args: TokenStream) -> TokenStream {
#[proc_macro] #[proc_macro]
pub fn debug(args: TokenStream) -> TokenStream { pub fn debug(args: TokenStream) -> TokenStream {
if LevelFilter::Debug > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
expand::debug(args) expand::debug(args)
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())
@ -45,6 +95,9 @@ pub fn debug(args: TokenStream) -> TokenStream {
#[proc_macro] #[proc_macro]
pub fn trace(args: TokenStream) -> TokenStream { pub fn trace(args: TokenStream) -> TokenStream {
if LevelFilter::Trace > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs); let args = parse_macro_input!(args as expand::LogArgs);
expand::trace(args) expand::trace(args)
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())

@ -22,5 +22,20 @@ tokio = { version = "1.2.0" }
env_logger = "0.9" env_logger = "0.9"
testing_logger = "0.1.1" testing_logger = "0.1.1"
[features]
max_level_off = ["log/max_level_off"]
max_level_error = ["log/max_level_error"]
max_level_warn = ["log/max_level_warn"]
max_level_info = ["log/max_level_info"]
max_level_debug = ["log/max_level_debug"]
max_level_trace = ["log/max_level_trace"]
release_max_level_off = ["log/release_max_level_off"]
release_max_level_error = ["log/release_max_level_error"]
release_max_level_warn = ["log/release_max_level_warn"]
release_max_level_info = ["log/release_max_level_info"]
release_max_level_debug = ["log/release_max_level_debug"]
release_max_level_trace = ["log/release_max_level_trace"]
[lib] [lib]
path = "src/lib.rs" path = "src/lib.rs"

@ -8,5 +8,20 @@ aya-bpf = { path = "../aya-bpf" }
aya-log-common = { path = "../../aya-log-common" } aya-log-common = { path = "../../aya-log-common" }
aya-log-ebpf-macros = { path = "../../aya-log-ebpf-macros" } aya-log-ebpf-macros = { path = "../../aya-log-ebpf-macros" }
[features]
max_level_off = ["aya-log-ebpf-macros/max_level_off"]
max_level_error = ["aya-log-ebpf-macros/max_level_error"]
max_level_warn = ["aya-log-ebpf-macros/max_level_warn"]
max_level_info = ["aya-log-ebpf-macros/max_level_info"]
max_level_debug = ["aya-log-ebpf-macros/max_level_debug"]
max_level_trace = ["aya-log-ebpf-macros/max_level_trace"]
release_max_level_off = ["aya-log-ebpf-macros/release_max_level_off"]
release_max_level_error = ["aya-log-ebpf-macros/release_max_level_error"]
release_max_level_warn = ["aya-log-ebpf-macros/release_max_level_warn"]
release_max_level_info = ["aya-log-ebpf-macros/release_max_level_info"]
release_max_level_debug = ["aya-log-ebpf-macros/release_max_level_debug"]
release_max_level_trace = ["aya-log-ebpf-macros/release_max_level_trace"]
[lib] [lib]
path = "src/lib.rs" path = "src/lib.rs"

Loading…
Cancel
Save