Skip to content

Add a pluggable diagnostic output channel#1223

Open
bnaras wants to merge 1 commit into
openfheorg:devfrom
bnaras:pr/pluggable-log-io
Open

Add a pluggable diagnostic output channel#1223
bnaras wants to merge 1 commit into
openfheorg:devfrom
bnaras:pr/pluggable-log-io

Conversation

@bnaras

@bnaras bnaras commented Jul 17, 2026

Copy link
Copy Markdown

Summary

OpenFHE writes diagnostics (warnings, timing dumps, debug notes) directly to std::cerr / std::cout in a handful of core, binfhe, and pke sources. This PR routes those writes through a thin indirection so an embedding application can redirect them, without changing behavior for anyone who doesn't opt in. Such redirection is necessary, for example, for the openfhe.R package to be hosted on CRAN.

It's a no-op by default. With the new OPENFHE_DEFAULT_LOG_SINK CMake option ON (the default), the diagnostic streams are std::cerr / std::cout and the output is byte-for-byte identical to before.

Motivation

The direct std::cerr / std::cout writes don't support two things:

  1. Redirecting diagnostics to a log file, a GUI, or another logging framework.
  2. Keeping std::cerr / std::cout out of the compiled archive. R packages on CRAN may not ship compiled code that writes to stdout/stderr (Writing R Extensions §1.1.3.1); output must go through REprintf / Rprintf. This isn't possible today without patching the library.

Design

  • src/core/include/utils/openfhe_log.h — declares lbcrypto::OpenFHEErrStream() / OpenFHEOutStream() (returning std::ostream&) plus SetOpenFHEErrStream() / SetOpenFHEOutStream() for runtime redirection. Two macros, OPENFHE_LOG_ERR / OPENFHE_LOG_OUT, expand to the accessors. Returning std::ostream& means every existing operator<< overload and manipulator (std::endl, std::hex, …) keeps working, so call sites change only the stream token.

  • src/core/lib/utils/openfhe_log_default.cpp — the default sink, and the only translation unit in the library that names std::cerr / std::cout. Compiled only when OPENFHE_DEFAULT_LOG_SINK is defined.

  • OPENFHE_DEFAULT_LOG_SINK CMake option, default ON. ON: the default sink is compiled, diagnostics go to std::cerr / std::cout, and Set* lets an embedder redirect at runtime. OFF: the default TU compiles to nothing and the embedder supplies its own definitions of the accessors — keeping std::cerr / std::cout out of the compiled archive.

  • The std::cerr / std::cout diagnostic sites in core, binfhe, and pke are converted to the macros.

Behavior / compatibility

  • Default build: no change. OPENFHE_DEFAULT_LOG_SINK=ON reproduces the current behavior exactly.
  • No API break. Only additions; the macros and accessors are new symbols.
  • Runtime redirect is available to any embedder via SetOpenFHEErrStream(std::ostream&) / SetOpenFHEOutStream(std::ostream&).
  • Build-time swap (-DOPENFHE_DEFAULT_LOG_SINK=OFF + embedder-provided accessors) is for embedders that must exclude std::cerr / std::cout symbols from the archive.

Testing

Standalone compile/link/symbol check of both configurations:

  • ON: OPENFHE_LOG_ERR << … << std::endl writes to stderr, OPENFHE_LOG_OUT to stdout — matching std::cerr / std::cout.
  • OFF: nm confirms the default TU is empty (no cerr / cout symbols) and converted call sites reference OpenFHEErrStream() rather than std::cerr; the target links when an embedder supplies the accessors.

Notes

  • No functional change under the default configuration; the seam is purely additive.
  • If a fuller logging framework (levels, structured logging) is preferred long-term, this seam is compatible with that direction — the accessors can be backed by a richer implementation later — but the intent here is the minimal, behavior-preserving indirection.

@pascoec
pascoec changed the base branch from main to dev July 18, 2026 05:50
@pascoec

pascoec commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

please rebase with the dev branch

Route the library's diagnostic writes through OPENFHE_LOG_ERR /
OPENFHE_LOG_OUT (each a std::ostream&) instead of std::cerr / std::cout
directly, so an embedding application can redirect OpenFHE's diagnostics
without editing call sites. Because the macros expand to std::ostream&,
every operator<< overload and manipulator keeps working unchanged.

The default sink (std::cerr / std::cout) lives in one translation unit,
openfhe_log_default.cpp, compiled only when the new OPENFHE_DEFAULT_LOG_SINK
CMake option is ON (the default). With the default sink ON the behavior is
byte-for-byte identical to before. Embedders may redirect at runtime via
SetOpenFHEErrStream / SetOpenFHEOutStream, or build with
-DOPENFHE_DEFAULT_LOG_SINK=OFF and supply their own accessor definitions to
keep std::cerr / std::cout out of the compiled archive entirely -- e.g. an R
package, where CRAN forbids compiled code that writes to stdout/stderr.

Converts the existing std::cerr / std::cout diagnostic sites in core,
binfhe, and pke to the new macros. No functional change under the default
configuration.
@bnaras
bnaras force-pushed the pr/pluggable-log-io branch from cbe5b3d to a498cd9 Compare July 19, 2026 14:41
@bnaras

bnaras commented Jul 19, 2026

Copy link
Copy Markdown
Author

Rebased onto dev — the PR is now a single commit against current dev.

Two notes on what changed in the process:

  • The rebased diff no longer touches src/core/lib/math/hal/bigintdyn/ubintdyn.cpp. That file had std::cout in PrintIntegerConstants() when this branch was cut from main; on dev it no longer does, so there is nothing left to convert there. The other eight call-site conversions are unchanged.
  • I expanded the comment above the OPENFHE_DEFAULT_LOG_SINK option to document the shared-build interaction: OFF is meant for BUILD_STATIC, where the accessors resolve when the embedding application links. OFF with a shared build leaves the accessors undefined in the library itself — fine on ELF platforms, which resolve them at load time, but rejected at link time on macOS and Windows. The default (ON) is unaffected on every platform and remains byte-for-byte identical to current behavior.

I verified all eight converted translation units compile and link in a single configuration, and checked the option differentially: with ON the built archive still references cerr/cout, with OFF it has zero such references.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants