Skip to content

feat(core): add OpenCV-style logging facade (cv::utils::logging equivalent) #80

Description

@kalwalt

Is your feature request related to a problem? Please describe.

purecv already depends on the log facade and exposes thin set_log_level / get_log_level wrappers in core::utils, but it has no structured logging API comparable to OpenCV's cv::utils::logging (the CV_LOG_* family in opencv2/core/utils/logger.hpp).

Two concrete gaps:

  1. Porting friction. OpenCV C/C++ sources are littered with CV_LOG_INFO(tag, ...), CV_LOG_WARNING(tag, ...), CV_LOG_ONCE_*, CV_LOG_IF_*, etc. Without 1:1 counterparts, every ported site has to be rewritten by hand, which is error-prone and loses the per-subsystem tag.
  2. No subsystem tagging. There is currently no consistent way to attribute log messages to a module (core, imgproc, features2d, ...) so users can filter them.
    Describe the solution you'd like

Add a dedicated core::logging module that mirrors cv::utils::logging, built on top of the existing log facade (so the output backend stays the application's choice — env_logger / fern / tracing / console_log on WASM). The OpenCV tag maps onto the log target:, so downstream filtering uses standard tooling, e.g. RUST_LOG=purecv::imgproc=debug,purecv::core=warn.

Proposed surface:

  • A LogLevel enum mirroring cv::utils::logging::LogLevel (Silent, Fatal, Error, Warning, Info, Debug, Verbose). Since log has five levels, Fatal collapses onto Error and Verbose onto Trace (documented).
  • set_log_level(LogLevel) -> LogLevel (returns the previous level, matching setLogLevel) and get_log_level() -> LogLevel.
  • A tags submodule with one &'static str per subsystem, following the module_path!() convention ("purecv", "purecv::core", "purecv::imgproc", ...).
  • #[macro_export] macros providing OpenCV parity:
    • levels: cv_log_fatal!, cv_log_error!, cv_log_warning!, cv_log_info!, cv_log_debug!, cv_log_verbose!(tag, v, ...)
    • once-per-call-site: cv_log_once_warning!, cv_log_once_info! (implemented with a core::sync::atomic::AtomicBool, no_std-friendly)
    • conditional: cv_log_if_error!, cv_log_if_warning!, cv_log_if_info!
      Mapping summary:
OpenCV purecv
CV_LOG_ERROR(tag, ...) cv_log_error!(tag, ...)
CV_LOG_WARNING(tag, ...) cv_log_warning!(tag, ...)
CV_LOG_INFO(tag, ...) cv_log_info!(tag, ...)
CV_LOG_DEBUG(tag, ...) cv_log_debug!(tag, ...)
CV_LOG_VERBOSE(tag, v, ...) cv_log_verbose!(tag, v, ...)
CV_LOG_ONCE_* cv_log_once_*!
CV_LOG_IF_* cv_log_if_*!
setLogLevel / getLogLevel set_log_level / get_log_level
tag (NULL, "global") target: (&str, see tags)
CV_LOG_STRIP_LEVEL log cargo features max_level_* / release_max_level_*

Example:

use purecv::core::logging::{self, tags, LogLevel};
 
logging::set_log_level(LogLevel::Info);
purecv::cv_log_info!(tags::IMGPROC, "gaussian blur, ksize = {}", 5);
purecv::cv_log_if_warning!(tags::CORE, ksize % 2 == 0, "even kernel size: {}", ksize);

Describe alternatives you've considered

  • Use bare log::info!(target: ..., ...) everywhere. Idiomatic, but offers no ONCE/IF/VERBOSE variants and no source-level parity with the OpenCV macros, so ports stay manual.

  • Reimplement OpenCV's LogTagManager (in-library per-tag levels). Heavier and redundant: in the Rust ecosystem per-target filtering is the backend's job (RUST_LOG). Per-tag levels inside the library can be added later as an optional log::Log filter layer if a real need appears — out of scope here.

  • Adopt tracing instead of log. More powerful (spans), but a larger dependency and a departure from what the crate already uses. Not warranted for now.
    Additional context

  • A drop-in src/core/logging.rs already exists as a prototype; it compiles and passes unit + doc-tests against log 0.4 on edition 2021.

  • Open decision: core::utils already defines set_log_level / get_log_level taking log::LevelFilter. To avoid two diverging APIs, the LogLevel-based versions should be canonical in core::logging, with core::utils re-exporting them (pub use crate::core::logging::{get_log_level, set_log_level};). This changes the existing signature from log::LevelFilter to LogLevel — flagging it explicitly.

  • WASM (crates/wasm): the core stays backend-agnostic; the wasm entry point should initialize a console backend once (e.g. console_log / wasm-logger) so cv_log_* reach the browser console. Can be a follow-up.
    Tasks:

  • Add src/core/logging.rs (LogLevel, set/get_log_level, tags, macros)

  • Register pub mod logging; in src/core.rs

  • Re-export set_log_level / get_log_level from core::utils (or keep both — decide)

  • Optionally surface LogLevel / tags in the crate prelude

  • Unit tests for level round-trip and macro expansion (in module tests)

  • Document CV_LOG_STRIP_LEVELlog release_max_level_* in the README

  • Follow-up: console logger init in crates/wasm
    Verification per CONTRIBUTING.md: cargo test, cargo fmt -- --check, cargo clippy -- -D warnings. PR against dev.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions