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" }
proc-macro2 = "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]
proc-macro = true

@ -10,6 +10,8 @@ use syn::{
use aya_log_common::DisplayHint;
use aya_log_parser::{parse, Fragment};
use crate::{LevelFilter, MAX_LEVEL};
pub(crate) struct LogArgs {
pub(crate) ctx: 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 },
None => quote! { module_path!() },
};
let lvl: TokenStream = if let Some(l) = level {
l
} else if let Some(l) = args.level {
quote! { #l }
let level: TokenStream = if let Some(level) = level {
// Level was chosen by calling concrete macros like error!, warn!, etc.
level
} 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 {
return Err(Error::new(
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(
&mut buf.buf,
#target,
#lvl,
#level,
module_path!(),
file!(),
line!(),

@ -3,6 +3,44 @@ use syn::parse_macro_input;
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]
pub fn log(args: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as expand::LogArgs);
@ -13,6 +51,9 @@ pub fn log(args: TokenStream) -> TokenStream {
#[proc_macro]
pub fn error(args: TokenStream) -> TokenStream {
if LevelFilter::Error > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs);
expand::error(args)
.unwrap_or_else(|err| err.to_compile_error())
@ -21,6 +62,9 @@ pub fn error(args: TokenStream) -> TokenStream {
#[proc_macro]
pub fn warn(args: TokenStream) -> TokenStream {
if LevelFilter::Warn > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs);
expand::warn(args)
.unwrap_or_else(|err| err.to_compile_error())
@ -29,6 +73,9 @@ pub fn warn(args: TokenStream) -> TokenStream {
#[proc_macro]
pub fn info(args: TokenStream) -> TokenStream {
if LevelFilter::Info > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs);
expand::info(args)
.unwrap_or_else(|err| err.to_compile_error())
@ -37,6 +84,9 @@ pub fn info(args: TokenStream) -> TokenStream {
#[proc_macro]
pub fn debug(args: TokenStream) -> TokenStream {
if LevelFilter::Debug > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs);
expand::debug(args)
.unwrap_or_else(|err| err.to_compile_error())
@ -45,6 +95,9 @@ pub fn debug(args: TokenStream) -> TokenStream {
#[proc_macro]
pub fn trace(args: TokenStream) -> TokenStream {
if LevelFilter::Trace > MAX_LEVEL {
return TokenStream::new();
}
let args = parse_macro_input!(args as expand::LogArgs);
expand::trace(args)
.unwrap_or_else(|err| err.to_compile_error())

@ -22,5 +22,20 @@ tokio = { version = "1.2.0" }
env_logger = "0.9"
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]
path = "src/lib.rs"

@ -8,5 +8,20 @@ aya-bpf = { path = "../aya-bpf" }
aya-log-common = { path = "../../aya-log-common" }
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]
path = "src/lib.rs"

Loading…
Cancel
Save