use tracing::Level;
#[expect(
clippy::struct_excessive_bools,
reason = "six independent on/off timing flags — one per operation category"
)]
#[derive(Clone, Debug)]
pub struct TracingConfig {
pub(crate) connect_level: Level,
pub(crate) send_level: Level,
pub(crate) receive_level: Level,
pub(crate) call_level: Level,
pub(crate) streaming_level: Level,
pub(crate) close_level: Level,
pub(crate) connect_timing: bool,
pub(crate) send_timing: bool,
pub(crate) receive_timing: bool,
pub(crate) call_timing: bool,
pub(crate) streaming_timing: bool,
pub(crate) close_timing: bool,
}
impl Default for TracingConfig {
fn default() -> Self {
Self {
connect_level: Level::INFO,
send_level: Level::DEBUG,
receive_level: Level::DEBUG,
call_level: Level::DEBUG,
streaming_level: Level::DEBUG,
close_level: Level::INFO,
connect_timing: false,
send_timing: false,
receive_timing: false,
call_timing: false,
streaming_timing: false,
close_timing: false,
}
}
}
impl TracingConfig {
#[must_use]
pub fn with_connect_level(mut self, level: Level) -> Self {
self.connect_level = level;
self
}
#[must_use]
pub fn with_connect_timing(mut self, enabled: bool) -> Self {
self.connect_timing = enabled;
self
}
#[must_use]
pub fn with_send_level(mut self, level: Level) -> Self {
self.send_level = level;
self
}
#[must_use]
pub fn with_send_timing(mut self, enabled: bool) -> Self {
self.send_timing = enabled;
self
}
#[must_use]
pub fn with_receive_level(mut self, level: Level) -> Self {
self.receive_level = level;
self
}
#[must_use]
pub fn with_receive_timing(mut self, enabled: bool) -> Self {
self.receive_timing = enabled;
self
}
#[must_use]
pub fn with_call_level(mut self, level: Level) -> Self {
self.call_level = level;
self
}
#[must_use]
pub fn with_call_timing(mut self, enabled: bool) -> Self {
self.call_timing = enabled;
self
}
#[must_use]
pub fn with_streaming_level(mut self, level: Level) -> Self {
self.streaming_level = level;
self
}
#[must_use]
pub fn with_streaming_timing(mut self, enabled: bool) -> Self {
self.streaming_timing = enabled;
self
}
#[must_use]
pub fn with_close_level(mut self, level: Level) -> Self {
self.close_level = level;
self
}
#[must_use]
pub fn with_close_timing(mut self, enabled: bool) -> Self {
self.close_timing = enabled;
self
}
#[must_use]
pub fn with_all_levels(mut self, level: Level) -> Self {
self.connect_level = level;
self.send_level = level;
self.receive_level = level;
self.call_level = level;
self.streaming_level = level;
self.close_level = level;
self
}
#[must_use]
pub fn with_all_timing(mut self, enabled: bool) -> Self {
self.connect_timing = enabled;
self.send_timing = enabled;
self.receive_timing = enabled;
self.call_timing = enabled;
self.streaming_timing = enabled;
self.close_timing = enabled;
self
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use tracing::Level;
use super::TracingConfig;
fn assert_levels(
cfg: &TracingConfig,
[connect, send, receive, call, streaming, close]: [Level; 6],
) {
assert_eq!(cfg.connect_level, connect, "connect_level");
assert_eq!(cfg.send_level, send, "send_level");
assert_eq!(cfg.receive_level, receive, "receive_level");
assert_eq!(cfg.call_level, call, "call_level");
assert_eq!(cfg.streaming_level, streaming, "streaming_level");
assert_eq!(cfg.close_level, close, "close_level");
}
fn assert_timing_flags(
cfg: &TracingConfig,
[connect, send, receive, call, streaming, close]: [bool; 6],
) {
assert_eq!(cfg.connect_timing, connect, "connect_timing");
assert_eq!(cfg.send_timing, send, "send_timing");
assert_eq!(cfg.receive_timing, receive, "receive_timing");
assert_eq!(cfg.call_timing, call, "call_timing");
assert_eq!(cfg.streaming_timing, streaming, "streaming_timing");
assert_eq!(cfg.close_timing, close, "close_timing");
}
#[test]
fn default_levels_are_info_for_lifecycle_debug_for_data_ops() {
let cfg = TracingConfig::default();
assert_levels(
&cfg,
[
Level::INFO,
Level::DEBUG,
Level::DEBUG,
Level::DEBUG,
Level::DEBUG,
Level::INFO,
],
);
}
#[test]
fn default_timing_flags_are_all_false() {
let cfg = TracingConfig::default();
assert_timing_flags(&cfg, [false; 6]);
}
#[test]
fn with_all_levels_updates_every_level_field() {
let cfg = TracingConfig::default().with_all_levels(Level::TRACE);
assert_levels(&cfg, [Level::TRACE; 6]);
}
#[test]
fn with_all_timing_true_enables_every_flag() {
let cfg = TracingConfig::default().with_all_timing(true);
assert_timing_flags(&cfg, [true; 6]);
}
#[test]
fn with_all_timing_false_disables_every_flag() {
let cfg = TracingConfig::default()
.with_all_timing(true)
.with_all_timing(false);
assert_timing_flags(&cfg, [false; 6]);
}
#[rstest]
#[case::connect(
TracingConfig::default().with_connect_timing(true),
[true, false, false, false, false, false],
)]
#[case::send(
TracingConfig::default().with_send_timing(true),
[false, true, false, false, false, false],
)]
#[case::receive(
TracingConfig::default().with_receive_timing(true),
[false, false, true, false, false, false],
)]
#[case::call(
TracingConfig::default().with_call_timing(true),
[false, false, false, true, false, false],
)]
#[case::streaming(
TracingConfig::default().with_streaming_timing(true),
[false, false, false, false, true, false],
)]
#[case::close(
TracingConfig::default().with_close_timing(true),
[false, false, false, false, false, true],
)]
fn individual_timing_setters_only_affect_their_flag(
#[case] cfg: TracingConfig,
#[case] expected: [bool; 6],
) {
assert_timing_flags(&cfg, expected);
}
#[rstest]
#[case::connect(
TracingConfig::default().with_connect_level(Level::TRACE),
[Level::TRACE, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::INFO],
)]
#[case::send(
TracingConfig::default().with_send_level(Level::TRACE),
[Level::INFO, Level::TRACE, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::INFO],
)]
#[case::receive(
TracingConfig::default().with_receive_level(Level::TRACE),
[Level::INFO, Level::DEBUG, Level::TRACE, Level::DEBUG, Level::DEBUG, Level::INFO],
)]
#[case::call(
TracingConfig::default().with_call_level(Level::TRACE),
[Level::INFO, Level::DEBUG, Level::DEBUG, Level::TRACE, Level::DEBUG, Level::INFO],
)]
#[case::streaming(
TracingConfig::default().with_streaming_level(Level::TRACE),
[Level::INFO, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::TRACE, Level::INFO],
)]
#[case::close(
TracingConfig::default().with_close_level(Level::TRACE),
[Level::INFO, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::DEBUG, Level::TRACE],
)]
fn individual_level_setters_only_affect_their_field(
#[case] cfg: TracingConfig,
#[case] expected: [Level; 6],
) {
assert_levels(&cfg, expected);
}
}