From ec7e812b80140512552b973009b1f5061f7a7ab2 Mon Sep 17 00:00:00 2001 From: Ion Reguera Date: Wed, 29 Jul 2026 19:43:23 +0200 Subject: [PATCH 1/2] fix(log): arm CuemsLogger on the gradient_motion library; route bypasses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NOT MERGED, NOT DEPLOYED — authored on a branch for review at your convenience (active work is ongoing in this repo). gradient-motiond's daemon-side logging already works on the fleet (syslog, real priorities); this fixes the LIBRARY half. Root cause: HAVE_CUEMS_LOGGER was defined on mtcreceiver and the gradient-motiond executable but NOT on the gradient_motion static library, so every GME_LOG_* compiled into the library fell back to logging.h's std::cerr emitter — stamped PRIORITY=6 under systemd, invisible to `cuems-logs -e` — and GradientEngine.cpp, listed in BOTH targets, compiled twice with two different logging backends. That unavailable macro layer is also why src/motion/* hand-rolled fprintf(stderr, "WARNING ...") lines. * src/CMakeLists.txt: define + link PUBLIC on gradient_motion, guarded by ENABLE_CUEMS_LOGGER AND TARGET cuemslogger (covers BUILD_DAEMON=OFF). PUBLIC kills the dual-backend hazard: daemon and the test binaries now share one backend. Side effect to be aware of: ctest output goes to syslog when cuemslogger is enabled. * Root CMakeLists: drop the now-redundant PRIVATE arming on the daemon (it inherits via the library's usage requirements). * Hand-rolled fprintf/cerr sites -> GME_LOG_*: MotionFactory (unknown curve, lo_address_new failure -> WARNING; START_CROSSFADE stub -> INFO), MotionRegistry (crossfade drop -> INFO, cancelMotion miss -> WARNING), CurveFactory (unknown curve -> WARNING). * OscServer: liblo errorHandler + callback exceptions -> GME_LOG_ERROR; the two startup-aborting failures (UDP bind, lo_server_thread_start) -> GME_LOG_CRITICAL. These bypassed the GME_LOG_* layer the same file uses everywhere else. * GradientEngine::onTick (MIDI thread, 100 Hz) deliberately untouched — already level-gated and queue-pop-only. Verified: full build (daemon + tests), ctest 9/9 passed, libgradient_motion.a now resolves CuemsLogger symbols. --- CMakeLists.txt | 7 +++---- daemon/comms/OscServer.cpp | 17 ++++++++--------- src/CMakeLists.txt | 14 ++++++++++++++ src/gradient/CurveFactory.cpp | 5 +++-- src/motion/MotionFactory.cpp | 15 ++++++++------- src/motion/MotionRegistry.cpp | 10 +++++----- 6 files changed, 41 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dc76f9..2a07810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,10 +176,9 @@ if(BUILD_DAEMON) target_include_directories(gradient-motiond PRIVATE ${LIBLO_INCLUDE_DIRS}) endif() - if(ENABLE_CUEMS_LOGGER) - target_compile_definitions(gradient-motiond PRIVATE HAVE_CUEMS_LOGGER) - target_link_libraries(gradient-motiond PRIVATE cuemslogger) - endif() + # HAVE_CUEMS_LOGGER + cuemslogger now arrive via gradient_motion's + # PUBLIC usage requirements (src/CMakeLists.txt) — armed on the library + # so all its objects and every consumer share one logging backend. # Install rules — places the daemon at /bin/gradient-motiond. # debian/rules sets CMAKE_INSTALL_PREFIX=/usr so the .deb ships diff --git a/daemon/comms/OscServer.cpp b/daemon/comms/OscServer.cpp index 8687ce2..8522107 100644 --- a/daemon/comms/OscServer.cpp +++ b/daemon/comms/OscServer.cpp @@ -40,8 +40,8 @@ struct OscServer::Impl { bool started = false; static void errorHandler(int num, const char* msg, const char* where) { - std::fprintf(stderr, "ERROR OscServer: liblo error %d — %s (path: %s)\n", - num, msg ? msg : "", where ? where : ""); + GME_LOG_ERROR("OscServer: liblo error " + std::to_string(num) + " — " + + (msg ? msg : "") + " (path: " + (where ? where : "") + ")"); } // Per-address callback entry point (called on the liblo network thread). @@ -84,11 +84,11 @@ struct OscServer::Impl { break; } } catch (const std::exception& e) { - std::fprintf(stderr, "ERROR OscServer: exception in callback for %s: %s\n", - path, e.what()); + GME_LOG_ERROR("OscServer: exception in callback for " + std::string(path) + + ": " + e.what()); } catch (...) { - std::fprintf(stderr, "ERROR OscServer: unknown exception in callback for %s\n", - path); + GME_LOG_ERROR("OscServer: unknown exception in callback for " + + std::string(path)); } return 0; // 0 = handled; do not try further methods } @@ -125,8 +125,7 @@ bool OscServer::start() { impl_->server_thread = lo_server_thread_new(port_str.c_str(), Impl::errorHandler); if (!impl_->server_thread) { - std::fprintf(stderr, "FATAL OscServer: failed to bind UDP port %s\n", - port_str.c_str()); + GME_LOG_CRITICAL("OscServer: failed to bind UDP port " + port_str); return false; } @@ -143,7 +142,7 @@ bool OscServer::start() { Impl::onMessage, impl_.get()); if (lo_server_thread_start(impl_->server_thread) != 0) { - std::fprintf(stderr, "FATAL OscServer: lo_server_thread_start failed\n"); + GME_LOG_CRITICAL("OscServer: lo_server_thread_start failed"); lo_server_thread_free(impl_->server_thread); impl_->server_thread = nullptr; return false; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f434e3..d43a66c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,20 @@ target_include_directories(gradient_motion PUBLIC ${CMAKE_SOURCE_DIR} ) +# Arm the CuemsLogger backend on the LIBRARY, not just the daemon executable. +# Previously only mtcreceiver and gradient-motiond carried HAVE_CUEMS_LOGGER, +# so every GME_LOG_* compiled into this library fell back to the std::cerr +# emitter — under systemd those lines are stamped PRIORITY=6 and invisible to +# `cuems-logs -e` — and GradientEngine.cpp, listed in BOTH targets, was +# compiled twice with two different logging backends. PUBLIC on purpose: the +# daemon and the test binaries then share one backend (side effect: ctest +# output for the 5 test binaries goes to syslog when cuemslogger is enabled). +# The TARGET guard covers BUILD_DAEMON=OFF, where cuemslogger is never added. +if(ENABLE_CUEMS_LOGGER AND TARGET cuemslogger) + target_compile_definitions(gradient_motion PUBLIC HAVE_CUEMS_LOGGER) + target_link_libraries(gradient_motion PUBLIC cuemslogger) +endif() + target_link_libraries(gradient_motion PUBLIC nlohmann_json::nlohmann_json ) diff --git a/src/gradient/CurveFactory.cpp b/src/gradient/CurveFactory.cpp index a75513e..d781d0e 100644 --- a/src/gradient/CurveFactory.cpp +++ b/src/gradient/CurveFactory.cpp @@ -17,6 +17,7 @@ #include "EaseOutCurve.h" #include "SCurve.h" #include "ResampledCurve.h" +#include "daemon/logging.h" namespace gme { namespace gradient { @@ -54,8 +55,8 @@ CurveFactory::createCurve(const std::string& type, inner = std::make_unique(); } else { - std::cerr << "[CurveFactory] Unknown curve type: '" - << type << "' — returning nullopt\n"; + GME_LOG_WARNING("CurveFactory: unknown curve type '" + std::string(type) + + "' — returning nullopt"); return std::nullopt; } diff --git a/src/motion/MotionFactory.cpp b/src/motion/MotionFactory.cpp index 7e94e64..eab6d90 100644 --- a/src/motion/MotionFactory.cpp +++ b/src/motion/MotionFactory.cpp @@ -17,6 +17,7 @@ #include #include +#include "daemon/logging.h" namespace gme { namespace motion { @@ -29,8 +30,8 @@ static std::unique_ptr makeFadeMotion(const gme::signal::FadeCommand& c : cmd.curve_params; auto curveOpt = gme::gradient::CurveFactory::createCurve(cmd.curve_type, params); if (!curveOpt) { - std::fprintf(stderr, "WARNING MotionFactory: unknown curve type '%s' " - "(motion_id=%s)\n", cmd.curve_type.c_str(), cmd.motion_id.c_str()); + GME_LOG_WARNING("MotionFactory: unknown curve type '" + cmd.curve_type + + "' (motion_id=" + cmd.motion_id + ")"); ctx.emitStatus(gme::signal::StatusKind::MotionError, cmd.motion_id, "unknown_curve_type"); return nullptr; @@ -49,9 +50,9 @@ static std::unique_ptr makeFadeMotion(const gme::signal::FadeCommand& c // Build lo_address lo_address addr = gme::osc::makeAddress(cmd.osc_host, cmd.osc_port); if (!addr) { - std::fprintf(stderr, "WARNING MotionFactory: lo_address_new failed for " - "%s:%d (motion_id=%s)\n", - cmd.osc_host.c_str(), cmd.osc_port, cmd.motion_id.c_str()); + GME_LOG_WARNING("MotionFactory: lo_address_new failed for " + cmd.osc_host + + ":" + std::to_string(cmd.osc_port) + + " (motion_id=" + cmd.motion_id + ")"); ctx.emitStatus(gme::signal::StatusKind::MotionError, cmd.motion_id, "osc_address_failed"); return nullptr; @@ -82,8 +83,8 @@ std::unique_ptr MotionFactory::fromCommand(const gme::signal::FadeComma case Type::START_CROSSFADE: // TODO Phase 7: return makeCrossfadePair(cmd, ctx); - std::fprintf(stderr, "INFO MotionFactory: START_CROSSFADE not yet " - "implemented (motion_id=%s)\n", cmd.motion_id.c_str()); + GME_LOG_INFO("MotionFactory: START_CROSSFADE not yet implemented (motion_id=" + + cmd.motion_id + ")"); return nullptr; default: diff --git a/src/motion/MotionRegistry.cpp b/src/motion/MotionRegistry.cpp index 88dd8fb..2a83431 100644 --- a/src/motion/MotionRegistry.cpp +++ b/src/motion/MotionRegistry.cpp @@ -17,6 +17,7 @@ #include #include #include +#include "daemon/logging.h" namespace gme { namespace motion { @@ -89,9 +90,8 @@ void MotionRegistry::apply(gme::signal::FadeCommand& cmd) { cancelAll(); break; case Type::START_CROSSFADE: - std::fprintf(stderr, "INFO MotionRegistry: START_CROSSFADE dropped " - "(deferred to future feature, motion_id=%s)\n", - cmd.motion_id.c_str()); + GME_LOG_INFO("MotionRegistry: START_CROSSFADE dropped (deferred to " + "future feature, motion_id=" + cmd.motion_id + ")"); break; } } @@ -137,8 +137,8 @@ void MotionRegistry::addMotion(std::unique_ptr m) { void MotionRegistry::cancelMotion(const std::string& motion_id, bool snap_to_end) { auto it = motions_.find(motion_id); if (it == motions_.end()) { - std::fprintf(stderr, "WARNING MotionRegistry: cancelMotion: motion_id '%s' " - "not found\n", motion_id.c_str()); + GME_LOG_WARNING("MotionRegistry: cancelMotion: motion_id '" + motion_id + + "' not found"); return; } From adb7ad09d578e521b7329267b23dabceb7c13332 Mon Sep 17 00:00:00 2001 From: Ion Reguera Date: Wed, 29 Jul 2026 19:43:50 +0200 Subject: [PATCH 2/2] chore(submodules): converge cuemslogger + mtcreceiver on reconciled heads cuemslogger c39e2d8 -> 6463cd9: master now carries the anton closelog()-in-setNewSlug memory fix and the audioplayer header fix (ancestry-gated before any consumer bumped: git merge-base --is-ancestor 30c9cb4 6463cd9). mtcreceiver 8a30d05 -> 20ab95b: same 24h-continuity line this branch's base already tracks, plus the journald-aware fallback stub (with logDebug and a shared multi-header guard) and the MTC-RX FullFrame diagnostic at debug priority. This repo builds mtcreceiver with HAVE_CUEMS_LOGGER, so it uses the real logger path; run ctest after merging as the base branch intended. --- cuemslogger | 2 +- mtcreceiver | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cuemslogger b/cuemslogger index c39e2d8..6463cd9 160000 --- a/cuemslogger +++ b/cuemslogger @@ -1 +1 @@ -Subproject commit c39e2d82fedf82ce997e490735a85863e096ae21 +Subproject commit 6463cd90a682b2b66fd5a4d11f94987e9e43303e diff --git a/mtcreceiver b/mtcreceiver index 8a30d05..20ab95b 160000 --- a/mtcreceiver +++ b/mtcreceiver @@ -1 +1 @@ -Subproject commit 8a30d056e5fbe27ef19f0485c5ef142b7f7b4ef0 +Subproject commit 20ab95b96baa9693a6c59e786a1bbd7b27beaae0