Skip to content

Conversation

@kssenii
Copy link
Contributor

@kssenii kssenii commented Jul 25, 2025

  • new feature PR: "ffi: allow to change event tracing level more than once"

What changes are proposed in this pull request?

#1109

How was this change tested?

Added a new test change_event_tracking_level.

@zachschuermann
Copy link
Member

hi @kssenii just checking on this PR - it's still marked as draft, do you need reviews, etc.?

@kssenii
Copy link
Contributor Author

kssenii commented Aug 12, 2025

@zachschuermann, hi, no, I will undraft when ready for review.

@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch 4 times, most recently from 7e72421 to fdc0610 Compare August 27, 2025 13:02
@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch 2 times, most recently from 8bd28ca to af406cb Compare August 27, 2025 15:35
@github-actions github-actions bot added the breaking-change Change that require a major version bump label Aug 27, 2025
@kssenii kssenii marked this pull request as ready for review August 27, 2025 15:39
@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch 2 times, most recently from f7b8814 to 8ac86aa Compare August 27, 2025 15:55
@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch from 8ac86aa to 007e76c Compare August 27, 2025 15:58
@kssenii kssenii changed the title ffi: allow to change event tracing level more than once ffi: allow to change tracing level and callback more than once Aug 27, 2025
Comment on lines 338 to 351
match set_global_default(dispatch.clone()) {
Ok(()) => {
*locked_state = Some(GlobalTracingState {
dispatch,
reload_handle,
event_callback: Some(event_callback),
log_line_callback: None,
});
Ok(())
}
Err(e) => Err(Error::generic(format!(
"Unable to set default subscriber: {e}"
))),
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
match set_global_default(dispatch.clone()) {
Ok(()) => {
*locked_state = Some(GlobalTracingState {
dispatch,
reload_handle,
event_callback: Some(event_callback),
log_line_callback: None,
});
Ok(())
}
Err(e) => Err(Error::generic(format!(
"Unable to set default subscriber: {e}"
))),
}
set_global_default(dispatch.clone()).map_err(|e| Error::generic(format!(
"Unable to set default subscriber: {e}"
))?;
*locked_state = Some(GlobalTracingState {
dispatch,
reload_handle,
event_callback: Some(event_callback),
log_line_callback: None,
});
Ok(())

static TRACING_STATE: LazyLock<Mutex<Option<GlobalTracingState>>> =
LazyLock::new(|| Mutex::new(None));

fn get_event_dispatcher(
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like this is creating a new one rather than getting an existing one.

Suggested change
fn get_event_dispatcher(
fn create_event_dispatcher(

Comment on lines 323 to 351
if let Some(state) = locked_state.as_mut() {
if let Some(event_callback) = &state.event_callback {
*event_callback.lock().unwrap() = callback;
} else {
state.event_callback = Some(Arc::new(Mutex::new(callback)));
}
return state
.reload_handle
.reload(LevelFilter::from(max_level))
.map_err(|e| {
warn!("Failed to reload tracing level: {e}");
Error::generic(format!("Unable to reload subscriber: {e}"))
});
} else {
let (dispatch, reload_handle, event_callback) = get_event_dispatcher(callback, max_level);
match set_global_default(dispatch.clone()) {
Ok(()) => {
*locked_state = Some(GlobalTracingState {
dispatch,
reload_handle,
event_callback: Some(event_callback),
log_line_callback: None,
});
Ok(())
}
Err(e) => Err(Error::generic(format!(
"Unable to set default subscriber: {e}"
))),
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're duplicating the logic to do the following in both setup_event_subscriber and setup_log_line_subscriber:

  1. Get state
  2. If it hasn't been initialized, then initialized it using get_.*_dispatcher. Update state.
  3. If it has been initialized, set callback, perform a reload.

We're also manipulating GlobalTracingState a bunch here. I'd propose to:

  • Make this functionality part of GlobalTracingState. GlobalTracingState.register_new_callback(event_callback, max_level)
  • I'd prefer if possible to make GlobalTracingState internally handle the Option. ie:
static TRACING_STATE: LazyLock<Mutex<GlobalTracingState>> =
    LazyLock::new(|| Mutex::new(GlobalTracingState::uninitiaziled()));

This way, all the tracing handling is done in GlobalTracingState, and we can track where the global state is being modified.

let ok = expected_log_lines.is_empty()
|| expected_log_lines.iter().any(|expected_log_line| {
let res = line_str.ends_with(expected_log_line);
//println!("Line: {line_str}, expected: {expected_log_line}, res: {res}");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
//println!("Line: {line_str}, expected: {expected_log_line}, res: {res}");


// ensure we can setup again with a new callback and a new tracing level
unsafe {
enable_event_tracing(event_callback_2, Level::DEBUG);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we do the following?

enable_event_tracing()
enable_log_line_tracing()

From what I gather, this isn't possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, not possible (or at least I did not manage to make it work though I tried), I also added related comment here

#[test]
#[ignore] // We cannot run this test if test_enable_log_line_tracing was run before - see comment there, however this test works if run individually.
fn test_enable_event_tracing() {

@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch from 3ccd287 to 00aab70 Compare September 24, 2025 14:17
@kssenii kssenii force-pushed the allow-to-change-event-tracing-level branch from 00aab70 to 9e7b4f0 Compare September 24, 2025 14:26
@OussamaSaoudi OussamaSaoudi removed the request for review from zachschuermann September 29, 2025 22:40
@codecov
Copy link

codecov bot commented Sep 29, 2025

Codecov Report

❌ Patch coverage is 65.23438% with 89 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.64%. Comparing base (1bb3365) to head (ad7b29a).

Files with missing lines Patch % Lines
ffi/src/ffi_tracing.rs 65.23% 79 Missing and 10 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1111      +/-   ##
==========================================
- Coverage   84.86%   84.64%   -0.22%     
==========================================
  Files         119      119              
  Lines       30880    31075     +195     
  Branches    30880    31075     +195     
==========================================
+ Hits        26206    26304      +98     
- Misses       3395     3485      +90     
- Partials     1279     1286       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@nicklan nicklan left a comment

Choose a reason for hiding this comment

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

thanks! took an initial pass and had a few comments. will look more deeply in the next day or so

use tracing::warn;
use tracing::trace;
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

Suggested change
use tracing::warn;
use tracing::trace;
use tracing::{trace, warn};

file: kernel_string_slice!(file),
};
(self.callback)(event);
let cb = self.callback.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should not call unwrap as it panics and can crash the engine. This is a gross situation because we can't really inform the engine that something went wrong :) Maybe we can just error! log in that case and hope that something else on the rust side is logging.

}

if let (Some(reload), Some(event_cb)) = (&self.reload_handle, &self.event_callback) {
*event_cb.lock().unwrap() = callback;
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto on unwrap here

let message = String::from_utf8_lossy(&buf);
let message = kernel_string_slice!(message);
(self.callback)(message);
let cb = self.callback.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

unwrap

let message = "INTERNAL KERNEL ERROR: Could not lock message buffer.";
let message = kernel_string_slice!(message);
(self.callback)(message);
let cb = self.callback.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

unwrap

@kssenii kssenii requested a review from nicklan October 8, 2025 15:33
Copy link
Collaborator

@OussamaSaoudi OussamaSaoudi left a comment

Choose a reason for hiding this comment

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

Getting close!

Comment on lines 626 to 639
if let Some(ref msgs) = *lock {
assert_eq!(msgs.len(), expected_lines.len());
for (got, expect) in msgs.iter().zip(expected_lines) {
println!("Got: {got}");
assert!(got.ends_with(expect));
assert!(got.contains(expected_level_str));
assert!(got.contains("delta_kernel_ffi::ffi_tracing::tests"));
if let Some(ref tstr) = expected_time_str {
assert!(got.contains(tstr));
}
}
} else {
panic!("Messages wasn't Some");
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if let Some(ref msgs) = *lock {
assert_eq!(msgs.len(), expected_lines.len());
for (got, expect) in msgs.iter().zip(expected_lines) {
println!("Got: {got}");
assert!(got.ends_with(expect));
assert!(got.contains(expected_level_str));
assert!(got.contains("delta_kernel_ffi::ffi_tracing::tests"));
if let Some(ref tstr) = expected_time_str {
assert!(got.contains(tstr));
}
}
} else {
panic!("Messages wasn't Some");
}
let Some(ref msgs) = *lock else {
panic!("Messages wasn't Some");
}
assert_eq!(msgs.len(), expected_lines.len());
for (got, expect) in msgs.iter().zip(expected_lines) {
println!("Got: {got}");
assert!(got.ends_with(expect));
assert!(got.contains(expected_level_str));
assert!(got.contains("delta_kernel_ffi::ffi_tracing::tests"));
if let Some(ref tstr) = expected_time_str {
assert!(got.contains(tstr));
}
}

}
let dispatch = get_event_dispatcher(callback, max_level);
set_global_default(dispatch)
let mut state = TRACING_STATE.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let mut state = TRACING_STATE.lock().unwrap();
let mut state = TRACING_STATE.lock().map_err(|e| Error::generic("Poisoned mutex while setting up event subscriber");

return Err(Error::generic("max_level out of range"));
}
let dispatch = get_log_line_dispatch(
let mut state = TRACING_STATE.lock().unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let mut state = TRACING_STATE.lock().unwrap();
let mut state = TRACING_STATE.lock().map_err(|e| Error::generic("Poisoned mutex while setting up log_line_subscriber")?;

Comment on lines 311 to 315
if let Ok(mut event_cb) = event_cb.lock() {
*event_cb = callback;
} else {
error!("Failed to acquire lock for event callback (mutex poisoned).");
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let the caller know something went wrong

Suggested change
if let Ok(mut event_cb) = event_cb.lock() {
*event_cb = callback;
} else {
error!("Failed to acquire lock for event callback (mutex poisoned).");
}
let (mut event_cb) = event_cb.lock().map_err(|e| Error::generic("Failed to acquire lock for event callback (mutex poisoned).")?;
*event_cb = callback;

Comment on lines 346 to 350
if let Ok(mut log_cb) = log_cb.lock() {
*log_cb = callback;
} else {
error!("Failed to acquire lock for log callback (mutex poisoned).");
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if let Ok(mut log_cb) = log_cb.lock() {
*log_cb = callback;
} else {
error!("Failed to acquire lock for log callback (mutex poisoned).");
}
let mut log_cb = log_cb.lock().map_err(|e| Error::generic("Failed to acquire lock for log callback (mutex poisoned)."))?;
*log_cb = callback;

@kssenii kssenii requested a review from OussamaSaoudi October 27, 2025 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Change that require a major version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants