Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ceph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ pub fn connect_to_ceph(user_id: &str, config_file: &str) -> RadosResult<Rados> {
if ret_code < 0 {
return Err(ret_code.into());
}
// Tracing integration needs to be enabled after we connected for some reason
crate::tracing_integration::enable_tracing_integration(rados.rados)?;
Ok(rados)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub mod utils;
mod ceph_client;
mod ceph_version;
mod mon_command;
mod tracing_integration;

pub use crate::ceph_client::CephClient;
pub use crate::ceph_version::CephVersion;
Expand Down
115 changes: 115 additions & 0 deletions src/tracing_integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::error::RadosResult;
use crate::rados::*;

use libc::*;
use std::ffi::CStr;
use tracing::Level;

/// For tracing integration to function properly, logging (tracing handler) must be enabled before
/// connecting to ceph: if tracing is not enabled, we don't set a callback, which avoids the
/// corresponding overhead.
pub(crate) fn enable_tracing_integration(rados: rados_t) -> RadosResult<()> {
let (level_str, opt_log_callback): (&[u8], rados_log_callback_t) = {
if event_enabled!(target: "librados", Level::ERROR) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I assume you're writing this way to minimize the runtime cost of additional checks. However, the enable API itself is usually not a heavy call. How about expanding them into one place within the loop?

for example:

type RadosLogCallback = rados_log_callback_t;

fn librados_log_level() -> (&'static [u8], Option<RadosLogCallback>) {
    const CANDIDATES: &[(Level, &[u8])] = &[
        (Level::DEBUG, b"debug\0"),
        (Level::INFO,  b"info\0"),
        (Level::WARN,  b"warn\0"),
        (Level::ERROR, b"error\0"),
    ];

    for &(lvl, cstr) in CANDIDATES {
        if event_enabled!(target: "librados", lvl) {
            return (cstr, Some(log_callback));
        }
    }

    (b"\0", None)
}

Copy link
Contributor Author

@Ten0 Ten0 Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is not possible because these macros require const level, like below.

if event_enabled!(target: "librados", Level::WARN) {
if event_enabled!(target: "librados", Level::INFO) {
if event_enabled!(target: "librados", Level::DEBUG) {
(b"debug\0", Some(log_callback))
} else {
(b"info\0", Some(log_callback))
}
} else {
(b"warn\0", Some(log_callback))
}
} else {
(b"error\0", Some(log_callback))
}
} else {
(b"\0", None)
}
};

let level_cstr =
CStr::from_bytes_with_nul(level_str).expect("Constructed with trailing nul byte above");
unsafe {
let ret_code = rados_monitor_log(
rados,
level_cstr.as_ptr(),
opt_log_callback,
0 as *mut c_void,
);
if ret_code < 0 {
return Err(ret_code.into());
}
}

Ok(())
}

extern "C" fn log_callback(
_arg: *mut ::std::os::raw::c_void,
line: *const ::libc::c_char,
who: *const ::libc::c_char,
_sec: u64,
_nsec: u64,
_seq: u64,
level: *const ::libc::c_char,
msg: *const ::libc::c_char,
) {
macro_rules! level {
($($l: expr => $n: ident,)*) => {
match unsafe { *((level as *const u8).add(1)) } {
$(
$l => {
if event_enabled!(target: "librados", Level::$n) {
Level::$n
} else {
return;
}
}
)*
_ => {
let s = unsafe { CStr::from_ptr(level) };
let level = s.to_string_lossy();
event!(target: "librados", Level::WARN, "Unknown log level: {level} - please report issue at github.com/ceph/ceph-rust");
Level::DEBUG
},
}
};
}
let level = level!(
b'E' => ERROR,
b'W' => WARN,
b'I' => INFO,
b'D' => DEBUG,
);

// We need to log, build the things
let who = unsafe { CStr::from_ptr(who) };
let who = who.to_string_lossy();
let line = unsafe { CStr::from_ptr(line) };
let line: Option<usize> = line.to_str().ok().and_then(|l| l.parse().ok());
let msg = unsafe { CStr::from_ptr(msg) };
let msg = msg.to_string_lossy();

// Macro is necessary because tracing::event! requires level to be const
// https://github.com/tokio-rs/tracing/issues/2730
macro_rules! build_and_capture_event {
($($lvl: ident),*) => {
match level {
$(
Level::$lvl => {
if let Some(line) = line {
event!(target: "librados", Level::$lvl, %who, %line, "{msg}");
} else {
event!(target: "librados", Level::$lvl, %who, "{msg}");
}
},
)*
_ => unreachable!(),
}

};
}
build_and_capture_event!(DEBUG, INFO, WARN, ERROR);
}