Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit 1db502b

Browse files
committed
Make logging generic across backends and simplify the code
1 parent d3fe24a commit 1db502b

File tree

7 files changed

+253
-100
lines changed

7 files changed

+253
-100
lines changed

Cargo.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ hipblas = ["whisper-rs-sys/hipblas", "_gpu"]
3232
openblas = ["whisper-rs-sys/openblas"]
3333
metal = ["whisper-rs-sys/metal", "_gpu"]
3434
vulkan = ["whisper-rs-sys/vulkan", "_gpu"]
35+
openmp = ["whisper-rs-sys/openmp"]
3536
_gpu = []
3637
test-with-tiny-model = []
37-
whisper-cpp-log = ["dep:log"]
38-
whisper-cpp-tracing = ["dep:tracing"]
39-
openmp = ["whisper-rs-sys/openmp"]
38+
39+
# Bring logs into Rust via the log crate. *Warning*: not mutually exclusive with tracing_backend,
40+
# will result in duplicate logs if both are enabled and one consumes logs from the other.
41+
log_backend = ["dep:log"]
42+
43+
# Bring logs into Rust via the tracing crate. *Warning*: not mutually exclusive with log_backend,
44+
# will result in duplicate logs if both are enabled and one consumes logs from the other.
45+
tracing_backend = ["dep:tracing"]

src/common_logging.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
macro_rules! generic_error {
2+
($($expr:tt)*) => {
3+
#[cfg(feature = "log_backend")]
4+
log::error!($($expr)*);
5+
#[cfg(feature = "tracing_backend")]
6+
tracing::error!($($expr)*);
7+
};
8+
}
9+
10+
macro_rules! generic_warn {
11+
($($expr:tt)*) => {
12+
#[cfg(feature = "log_backend")]
13+
log::warn!($($expr)*);
14+
#[cfg(feature = "tracing_backend")]
15+
tracing::warn!($($expr)*);
16+
}
17+
}
18+
19+
macro_rules! generic_info {
20+
($($expr:tt)*) => {
21+
#[cfg(feature = "log_backend")]
22+
log::info!($($expr)*);
23+
#[cfg(feature = "tracing_backend")]
24+
tracing::info!($($expr)*);
25+
}
26+
}
27+
28+
macro_rules! generic_debug {
29+
($($expr:tt)*) => {
30+
#[cfg(feature = "log_backend")]
31+
log::debug!($($expr)*);
32+
#[cfg(feature = "tracing_backend")]
33+
tracing::debug!($($expr)*);
34+
}
35+
}
36+
37+
macro_rules! generic_trace {
38+
($($expr:tt)*) => {
39+
#[cfg(feature = "log_backend")]
40+
log::trace!($($expr)*);
41+
#[cfg(feature = "tracing_backend")]
42+
tracing::trace!($($expr)*);
43+
}
44+
}
45+
46+
use whisper_rs_sys::ggml_log_level;
47+
pub(crate) use {generic_debug, generic_error, generic_info, generic_trace, generic_warn};
48+
49+
// Unsigned integer type on most platforms is 32 bit, niche platforms that whisper.cpp
50+
// likely doesn't even support would use 16 bit and would still fit
51+
#[repr(u32)]
52+
pub(crate) enum GGMLLogLevel {
53+
None = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_NONE,
54+
Info = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO,
55+
Warn = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN,
56+
Error = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR,
57+
Debug = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG,
58+
Cont = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_CONT,
59+
Unknown(ggml_log_level),
60+
}
61+
impl From<ggml_log_level> for GGMLLogLevel {
62+
fn from(level: ggml_log_level) -> Self {
63+
match level {
64+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_NONE => GGMLLogLevel::None,
65+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO => GGMLLogLevel::Info,
66+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN => GGMLLogLevel::Warn,
67+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR => GGMLLogLevel::Error,
68+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG => GGMLLogLevel::Debug,
69+
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_CONT => GGMLLogLevel::Cont,
70+
other => GGMLLogLevel::Unknown(other),
71+
}
72+
}
73+
}

src/ggml_logging_hook.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use crate::common_logging::{
2+
generic_debug, generic_error, generic_info, generic_trace, generic_warn, GGMLLogLevel,
3+
};
4+
use core::ffi::{c_char, c_void};
5+
use std::borrow::Cow;
6+
use std::ffi::CStr;
7+
use std::sync::Once;
8+
use whisper_rs_sys::ggml_log_level;
9+
10+
static GGML_LOG_TRAMPOLINE_INSTALL: Once = Once::new();
11+
pub(crate) fn install_ggml_logging_hook() {
12+
GGML_LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
13+
whisper_rs_sys::ggml_log_set(Some(ggml_logging_trampoline), std::ptr::null_mut())
14+
});
15+
}
16+
17+
unsafe extern "C" fn ggml_logging_trampoline(
18+
level: ggml_log_level,
19+
text: *const c_char,
20+
_: *mut c_void, // user_data
21+
) {
22+
if text.is_null() {
23+
generic_error!("ggml_logging_trampoline: text is nullptr");
24+
}
25+
let level = GGMLLogLevel::from(level);
26+
27+
// SAFETY: we must trust ggml that it will not pass us a string that does not satisfy
28+
// from_ptr's requirements.
29+
let log_str = unsafe { CStr::from_ptr(text) }.to_string_lossy();
30+
31+
ggml_logging_trampoline_safe(level, log_str)
32+
}
33+
34+
// this code essentially compiles down to a noop if neither feature is enabled
35+
#[cfg_attr(
36+
not(any(feature = "log_backend", feature = "tracing_backend")),
37+
allow(unused_variables)
38+
)]
39+
fn ggml_logging_trampoline_safe(level: GGMLLogLevel, text: Cow<str>) {
40+
match level {
41+
GGMLLogLevel::None => {
42+
// no clue what to do here, trace it?
43+
generic_trace!("{}", text.trim());
44+
}
45+
GGMLLogLevel::Info => {
46+
generic_info!("{}", text.trim());
47+
}
48+
GGMLLogLevel::Warn => {
49+
generic_warn!("{}", text.trim());
50+
}
51+
GGMLLogLevel::Error => {
52+
generic_error!("{}", text.trim());
53+
}
54+
GGMLLogLevel::Debug => {
55+
generic_debug!("{}", text.trim());
56+
}
57+
GGMLLogLevel::Cont => {
58+
// this means continue previous log
59+
// storing state to do this is a massive pain so it's just a lot easier to not
60+
// plus as far as i can tell it's not actually *used* anywhere
61+
// ggml splits at 128 chars and doesn't actually change the kind of log
62+
// so technically this is unused
63+
generic_trace!("{}", text.trim());
64+
}
65+
GGMLLogLevel::Unknown(level) => {
66+
generic_warn!(
67+
"ggml_logging_trampoline: unknown log level {}: message: {}",
68+
level,
69+
text.trim()
70+
);
71+
}
72+
}
73+
}

src/lib.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
11
#![allow(clippy::uninlined_format_args)]
22
#![cfg_attr(test, feature(test))]
33

4+
mod common_logging;
45
mod error;
6+
mod ggml_logging_hook;
57
mod standalone;
68
mod utilities;
79
mod whisper_ctx;
810
mod whisper_ctx_wrapper;
911
mod whisper_grammar;
12+
mod whisper_logging_hook;
1013
mod whisper_params;
1114
mod whisper_state;
12-
#[cfg(feature = "whisper-cpp-log")]
13-
mod whisper_sys_log;
14-
#[cfg(feature = "whisper-cpp-tracing")]
15-
mod whisper_sys_tracing;
16-
17-
#[cfg(any(feature = "whisper-cpp-log", feature = "whisper-cpp-tracing"))]
18-
static LOG_TRAMPOLINE_INSTALL: Once = Once::new();
1915

2016
pub use error::WhisperError;
2117
pub use standalone::*;
22-
#[cfg(any(feature = "whisper-cpp-log", feature = "whisper-cpp-tracing"))]
23-
use std::sync::Once;
2418
pub use utilities::*;
2519
pub use whisper_ctx::DtwMode;
2620
pub use whisper_ctx::DtwModelPreset;
@@ -33,10 +27,6 @@ pub use whisper_params::{FullParams, SamplingStrategy, SegmentCallbackData};
3327
#[cfg(feature = "raw-api")]
3428
pub use whisper_rs_sys;
3529
pub use whisper_state::WhisperState;
36-
#[cfg(feature = "whisper-cpp-log")]
37-
pub use whisper_sys_log::install_whisper_log_trampoline;
38-
#[cfg(feature = "whisper-cpp-tracing")]
39-
pub use whisper_sys_tracing::install_whisper_tracing_trampoline;
4030

4131
pub type WhisperSysContext = whisper_rs_sys::whisper_context;
4232
pub type WhisperSysState = whisper_rs_sys::whisper_state;
@@ -53,3 +43,25 @@ pub type DtwAhead = whisper_rs_sys::whisper_ahead;
5343

5444
/// The version of whisper.cpp that whisper-rs was linked with.
5545
pub static WHISPER_CPP_VERSION: &str = env!("WHISPER_CPP_VERSION");
46+
47+
/// Redirect all whisper.cpp and GGML logs to logging hooks installed by whisper-rs.
48+
///
49+
/// This will stop most logs from being output to stdout/stderr and will bring them into
50+
/// `log` or `tracing`, if the `log_backend` or `tracing_backend` features, respectively,
51+
/// are enabled. If neither is enabled, this will essentially disable logging, as they won't
52+
/// be output anywhere.
53+
///
54+
/// Note whisper.cpp and GGML do not reliably follow Rust logging conventions.
55+
/// Use your logging crate's configuration to control how these logs will be output.
56+
/// whisper-rs does not currently output any logs, but this may change in the future.
57+
/// You should configure by module path and use `whisper_rs::ggml_logging_hook`,
58+
/// and/or `whisper_rs::whisper_logging_hook`, to avoid possibly ignoring useful
59+
/// `whisper-rs` logs in the future.
60+
///
61+
/// Safe to call multiple times. Only has an effect the first time.
62+
/// (note this means installing your own logging handlers with unsafe functions after this call
63+
/// is permanent and cannot be undone)
64+
pub fn install_logging_hooks() {
65+
crate::whisper_logging_hook::install_whisper_logging_hook();
66+
crate::ggml_logging_hook::install_ggml_logging_hook();
67+
}

src/whisper_logging_hook.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use crate::common_logging::{
2+
generic_debug, generic_error, generic_info, generic_trace, generic_warn, GGMLLogLevel,
3+
};
4+
use core::ffi::{c_char, c_void};
5+
use std::borrow::Cow;
6+
use std::ffi::CStr;
7+
use std::sync::Once;
8+
use whisper_rs_sys::ggml_log_level;
9+
10+
static WHISPER_LOG_TRAMPOLINE_INSTALL: Once = Once::new();
11+
pub(crate) fn install_whisper_logging_hook() {
12+
WHISPER_LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
13+
whisper_rs_sys::whisper_log_set(Some(whisper_logging_trampoline), std::ptr::null_mut())
14+
});
15+
}
16+
17+
unsafe extern "C" fn whisper_logging_trampoline(
18+
level: ggml_log_level,
19+
text: *const c_char,
20+
_: *mut c_void, // user_data
21+
) {
22+
if text.is_null() {
23+
generic_error!("whisper_logging_trampoline: text is nullptr");
24+
}
25+
let level = GGMLLogLevel::from(level);
26+
27+
// SAFETY: we must trust whisper.cpp that it will not pass us a string that does not satisfy
28+
// from_ptr's requirements.
29+
let log_str = unsafe { CStr::from_ptr(text) }.to_string_lossy();
30+
31+
whisper_logging_trampoline_safe(level, log_str)
32+
}
33+
34+
// this code essentially compiles down to a noop if neither feature is enabled
35+
#[cfg_attr(
36+
not(any(feature = "log_backend", feature = "tracing_backend")),
37+
allow(unused_variables)
38+
)]
39+
fn whisper_logging_trampoline_safe(level: GGMLLogLevel, text: Cow<str>) {
40+
match level {
41+
GGMLLogLevel::None => {
42+
// no clue what to do here, trace it?
43+
generic_trace!("{}", text.trim());
44+
}
45+
GGMLLogLevel::Info => {
46+
generic_info!("{}", text.trim());
47+
}
48+
GGMLLogLevel::Warn => {
49+
generic_warn!("{}", text.trim());
50+
}
51+
GGMLLogLevel::Error => {
52+
generic_error!("{}", text.trim());
53+
}
54+
GGMLLogLevel::Debug => {
55+
generic_debug!("{}", text.trim());
56+
}
57+
GGMLLogLevel::Cont => {
58+
// this means continue previous log
59+
// storing state to do this is a massive pain so it's just a lot easier to not
60+
// plus as far as i can tell it's not actually *used* anywhere
61+
// whisper splits at 1024 chars and doesn't actually change the kind
62+
// so technically this is unused
63+
generic_trace!("{}", text.trim());
64+
}
65+
GGMLLogLevel::Unknown(level) => {
66+
generic_warn!(
67+
"whisper_logging_trampoline: unknown log level {}: message: {}",
68+
level,
69+
text.trim()
70+
);
71+
}
72+
}
73+
}

src/whisper_sys_log.rs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/whisper_sys_tracing.rs

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)