From 39c42bdd1334734a8fd71f13b824189704c2f714 Mon Sep 17 00:00:00 2001 From: Abasz <32517724+Abasz@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:17:25 +0000 Subject: [PATCH 1/5] Fix platformio-basic example Fix the `printTimestamp` and `printCarret` call signature to match `printfunction` typedef. --- examples/platformio-basic/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/platformio-basic/src/main.cpp b/examples/platformio-basic/src/main.cpp index 0a87923..b4e6338 100644 --- a/examples/platformio-basic/src/main.cpp +++ b/examples/platformio-basic/src/main.cpp @@ -23,13 +23,13 @@ String stringValue1 = "this is a string"; float floatValue; double doubleValue; -void printTimestamp(Print *_logOutput) { +void printTimestamp(Print *_logOutput, int) { char c[12]; int m = sprintf(c, "%10lu ", millis()); _logOutput->print(c); } -void printCarret(Print *_logOutput) { _logOutput->print('>'); } +void printCarret(Print *_logOutput, int) { _logOutput->print('>'); } void setup() { // Set up serial port and wait until connected From 9e2e4375538a6c5accab65c180c72012dc804302 Mon Sep 17 00:00:00 2001 From: Abasz <32517724+Abasz@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:28:36 +0000 Subject: [PATCH 2/5] Fix linker error when compiling with optimization Move writeLog definition into the header file. Fixes #9. (https://github.com/JSC-TechMinds/Arduino-Log/issues/9) --- src/ArduinoLog.cpp | 10 ---------- src/ArduinoLog.h | 10 +++++++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ArduinoLog.cpp b/src/ArduinoLog.cpp index d2f27a0..b9e3a82 100644 --- a/src/ArduinoLog.cpp +++ b/src/ArduinoLog.cpp @@ -279,16 +279,6 @@ void Logging::printFormat(const char format, va_list *args) { #endif } -#ifndef DISABLE_LOGGING -template void Logging::writeLog(Args... args) { - for (int i = 0; i < _handlerCount; i++) { - if (_logOutputs[i]) { - _logOutputs[i]->print(args...); - } - } -} -#endif - #ifndef __DO_NOT_INSTANTIATE__ Logging Log = Logging(); #endif diff --git a/src/ArduinoLog.h b/src/ArduinoLog.h index 71b82e1..2d1e949 100644 --- a/src/ArduinoLog.h +++ b/src/ArduinoLog.h @@ -450,7 +450,15 @@ class Logging { Print* _logOutputs[LOG_MAX_HANDLERS]; int _handlerCount; - template void writeLog(Args... args); +#ifndef DISABLE_LOGGING + template void writeLog(Args... args) { + for (int i = 0; i < _handlerCount; i++) { + if (_logOutputs[i]) { + _logOutputs[i]->print(args...); + } + } + } +#endif #ifdef ESP32 SemaphoreHandle_t _semaphore; #endif From bbb5c5a37876d6174bdf4f2c8a66de4d4452f778 Mon Sep 17 00:00:00 2001 From: Abasz <32517724+Abasz@users.noreply.github.com> Date: Mon, 7 Jul 2025 09:15:55 +0000 Subject: [PATCH 3/5] Introduce class enums to replace int log levels Add `ArduinoLogLevel` enum to replace int log levels. Update code to use the new enum type for log levels as well as add backward compatibility for existing int-based log level usage. This change improves type safety and readability of the code and most importantly fixes naming collision with nimble log levels. --- src/ArduinoLog.cpp | 37 ++++++++++++++-- src/ArduinoLog.h | 108 +++++++++++++++++++++++++++++++-------------- 2 files changed, 108 insertions(+), 37 deletions(-) diff --git a/src/ArduinoLog.cpp b/src/ArduinoLog.cpp index b9e3a82..fccf09a 100644 --- a/src/ArduinoLog.cpp +++ b/src/ArduinoLog.cpp @@ -13,7 +13,7 @@ Logging::Logging() #ifndef DISABLE_LOGGING - : _level(LOG_LEVEL_SILENT), + : _level(ArduinoLogLevel::LogLevelSilent), _showLevel(true), _showColors(false), _handlerCount(0) @@ -26,7 +26,17 @@ Logging::Logging() {} #endif -void Logging::begin(int level, Print* logOutput, bool showLevel, bool showColors) { +// Static helper function to constrain int values to valid enum range +ArduinoLogLevel Logging::constrainLevel(int level) { + if (level < static_cast(ArduinoLogLevel::LogLevelSilent)) { + return ArduinoLogLevel::LogLevelSilent; + } else if (level > static_cast(ArduinoLogLevel::LogLevelVerbose)) { + return ArduinoLogLevel::LogLevelVerbose; + } + return static_cast(level); +} + +void Logging::begin(ArduinoLogLevel level, Print* logOutput, bool showLevel, bool showColors) { #ifndef DISABLE_LOGGING setLevel(level); setShowLevel(showLevel); @@ -45,15 +55,34 @@ void Logging::begin(int level, Print* logOutput, bool showLevel, bool showColors #endif } +// Backward compatibility: int overload +void Logging::begin(int level, Print *logOutput, bool showLevel, bool showColors) { + begin(constrainLevel(level), logOutput, showLevel, showColors); +} + +void Logging::setLevel(ArduinoLogLevel level) { +#ifndef DISABLE_LOGGING + _level = level; +#endif +} + +// Backward compatibility: int overload void Logging::setLevel(int level) { + setLevel(constrainLevel(level)); +} + +ArduinoLogLevel Logging::getLogLevel() const { #ifndef DISABLE_LOGGING - _level = constrain(level, LOG_LEVEL_SILENT, LOG_LEVEL_VERBOSE); + return _level; +#else + return ArduinoLogLevel::LogLevelSilent; #endif } +// Backward compatibility: return level as int int Logging::getLevel() const { #ifndef DISABLE_LOGGING - return _level; + return static_cast(_level); #else return 0; #endif diff --git a/src/ArduinoLog.h b/src/ArduinoLog.h index 2d1e949..5451e23 100644 --- a/src/ArduinoLog.h +++ b/src/ArduinoLog.h @@ -46,14 +46,26 @@ typedef void (*printfunction)(Print*, int); // ************************************************************************ //#define DISABLE_LOGGING -#define LOG_LEVEL_SILENT 0 -#define LOG_LEVEL_FATAL 1 -#define LOG_LEVEL_ERROR 2 -#define LOG_LEVEL_WARNING 3 -#define LOG_LEVEL_INFO 4 -#define LOG_LEVEL_NOTICE 4 -#define LOG_LEVEL_TRACE 5 -#define LOG_LEVEL_VERBOSE 6 +enum class ArduinoLogLevel : unsigned char { + LogLevelSilent = 0, + LogLevelFatal = 1, + LogLevelError = 2, + LogLevelWarning = 3, + LogLevelInfo = 4, + LogLevelNotice = 4, // Same as INFO, kept for backward compatibility + LogLevelTrace = 5, + LogLevelVerbose = 6 +}; + +// Backward compatibility: Legacy constants for existing code +constexpr ArduinoLogLevel LOG_LEVEL_SILENT = ArduinoLogLevel::LogLevelSilent; +constexpr ArduinoLogLevel LOG_LEVEL_FATAL = ArduinoLogLevel::LogLevelFatal; +constexpr ArduinoLogLevel LOG_LEVEL_ERROR = ArduinoLogLevel::LogLevelError; +constexpr ArduinoLogLevel LOG_LEVEL_WARNING = ArduinoLogLevel::LogLevelWarning; +constexpr ArduinoLogLevel LOG_LEVEL_INFO = ArduinoLogLevel::LogLevelInfo; +constexpr ArduinoLogLevel LOG_LEVEL_NOTICE = ArduinoLogLevel::LogLevelNotice; +constexpr ArduinoLogLevel LOG_LEVEL_TRACE = ArduinoLogLevel::LogLevelTrace; +constexpr ArduinoLogLevel LOG_LEVEL_VERBOSE = ArduinoLogLevel::LogLevelVerbose; #define LOG_COLOR_BOLD_RED F("\033[1;31m") #define LOG_COLOR_BOLD_WHITE F("\033[1;37m") @@ -120,6 +132,18 @@ class Logging { * \return void * */ + void begin(ArduinoLogLevel level, Print *output, bool showLevel = true, + bool showColors = false); + + /** + * Backward compatibility: Initializing with int level + * + * \param level - logging levels <= this will be logged (as integer). + * \param output - place that logging output will be sent to. + * \param showLevel - if true, the level will be shown in the output. + * \param showColors - if true, the color will be shown in the output. + * \return void + */ void begin(int level, Print *output, bool showLevel = true, bool showColors = false); /** @@ -128,6 +152,14 @@ class Logging { * \param level - The new log level. * \return void */ + void setLevel(ArduinoLogLevel level); + + /** + * Backward compatibility: Set the log level with int + * + * \param level - The new log level (as integer). + * \return void + */ void setLevel(int level); /** @@ -135,6 +167,13 @@ class Logging { * * \return The current log level. */ + ArduinoLogLevel getLogLevel() const; + + /** + * Backward compatibility: Get the log level as int + * + * \return The current log level as integer. + */ int getLevel() const; /** @@ -224,13 +263,13 @@ class Logging { */ template void fatal(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_FATAL, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelFatal, false, msg, args...); #endif } template void fatalln(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_FATAL, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelFatal, true, msg, args...); #endif } @@ -246,13 +285,13 @@ class Logging { */ template void error(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_ERROR, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelError, false, msg, args...); #endif } template void errorln(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_ERROR, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelError, true, msg, args...); #endif } @@ -268,13 +307,13 @@ class Logging { */ template void warning(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_WARNING, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelWarning, false, msg, args...); #endif } template void warningln(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_WARNING, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelWarning, true, msg, args...); #endif } @@ -290,25 +329,25 @@ class Logging { */ template void notice(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_NOTICE, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelNotice, false, msg, args...); #endif } template void noticeln(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_NOTICE, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelNotice, true, msg, args...); #endif } template void info(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_INFO, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelInfo, false, msg, args...); #endif } template void infoln(T msg, Args...args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_INFO, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelInfo, true, msg, args...); #endif } @@ -324,13 +363,13 @@ class Logging { */ template void trace(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_TRACE, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelTrace, false, msg, args...); #endif } template void traceln(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_TRACE, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelTrace, true, msg, args...); #endif } @@ -346,17 +385,20 @@ class Logging { */ template void verbose(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_VERBOSE, false, msg, args...); + printLevel(ArduinoLogLevel::LogLevelVerbose, false, msg, args...); #endif } template void verboseln(T msg, Args... args) { #ifndef DISABLE_LOGGING - printLevel(LOG_LEVEL_VERBOSE, true, msg, args...); + printLevel(ArduinoLogLevel::LogLevelVerbose, true, msg, args...); #endif } -private: + private: + // Static helper function to constrain int values to valid enum range + static ArduinoLogLevel constrainLevel(int level); + void print(const char *format, va_list args); void print(const __FlashStringHelper *format, va_list args); @@ -373,13 +415,13 @@ class Logging { void printFormat(const char format, va_list *args); - template void printLevel(int level, bool cr, T msg, ...) { + template void printLevel(ArduinoLogLevel level, bool cr, T msg, ...) { #ifndef DISABLE_LOGGING if (level > _level) { return; } - if (level < LOG_LEVEL_SILENT) { - level = LOG_LEVEL_SILENT; + if (level < ArduinoLogLevel::LogLevelSilent) { + level = ArduinoLogLevel::LogLevelSilent; } #ifdef ESP32 @@ -387,13 +429,13 @@ class Logging { #endif if (_showColors) { switch (level) { - case LOG_LEVEL_FATAL: + case ArduinoLogLevel::LogLevelFatal: writeLog(LOG_COLOR_BOLD_RED); break; - case LOG_LEVEL_ERROR: + case ArduinoLogLevel::LogLevelError: writeLog(LOG_COLOR_RED); break; - case LOG_LEVEL_WARNING: + case ArduinoLogLevel::LogLevelWarning: writeLog(LOG_COLOR_YELLOW); break; default: @@ -404,14 +446,14 @@ class Logging { if (_prefix != NULL && _showLevel) { for (int i = 0; i < _handlerCount; i++) { if (_logOutputs[i]) { - _prefix(_logOutputs[i], level); + _prefix(_logOutputs[i], static_cast(level)); } } } if (_showLevel) { static const char levels[] = "FEWITV"; - writeLog(levels[level - 1]); + writeLog(levels[static_cast(level) - 1]); writeLog(": "); } @@ -422,7 +464,7 @@ class Logging { if (_suffix != NULL && _showLevel) { for (int i = 0; i < _handlerCount; i++) { if (_logOutputs[i]) { - _suffix(_logOutputs[i], level); + _suffix(_logOutputs[i], static_cast(level)); } } } @@ -444,7 +486,7 @@ class Logging { } #ifndef DISABLE_LOGGING - int _level; + ArduinoLogLevel _level; bool _showLevel; bool _showColors; Print* _logOutputs[LOG_MAX_HANDLERS]; From b3544f44fef9d1f44026f7aca256159667744f1d Mon Sep 17 00:00:00 2001 From: Abasz <32517724+Abasz@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:10:00 +0000 Subject: [PATCH 4/5] Add undefine for old log level macros Add undefine for old log level macros to avoid conflicts with other libraries (e.g. NimBLE) while keeping backward compatibility This ensures that the ArduinoLog library can be used without issues related to naming collisions. However, this may not 100% safe as include order will matter and that potentially can break other code that uses these macros and included after ArduinoLog.h. --- src/ArduinoLog.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/ArduinoLog.h b/src/ArduinoLog.h index 5451e23..3e6f412 100644 --- a/src/ArduinoLog.h +++ b/src/ArduinoLog.h @@ -57,8 +57,41 @@ enum class ArduinoLogLevel : unsigned char { LogLevelVerbose = 6 }; -// Backward compatibility: Legacy constants for existing code -constexpr ArduinoLogLevel LOG_LEVEL_SILENT = ArduinoLogLevel::LogLevelSilent; +// Undefine macros if conflicting (e.g. from NimBLE) to avoid compilation errors but these cause +// issues if these names are used in other libraries as #defines (e.g. NimBLE). This is not 100% +// safe as include order will matter: After including ArduinoLog.h these macros will not be +// available unless specifically defined. This should mean that if for instance NimBLE is included +// before ArduinoLog.h, anything that comes after ArduinoLog.h will not be able to use these macros, +// but if ArduinoLog.h is included first, then the macros will be available. +#ifdef LOG_LEVEL_SILENT +#undef LOG_LEVEL_SILENT +#endif +#ifdef LOG_LEVEL_SILENT +#undef LOG_LEVEL_SILENT +#endif +#ifdef LOG_LEVEL_FATAL +#undef LOG_LEVEL_FATAL +#endif +#ifdef LOG_LEVEL_ERROR +#undef LOG_LEVEL_ERROR +#endif +#ifdef LOG_LEVEL_WARNING +#undef LOG_LEVEL_WARNING +#endif +#ifdef LOG_LEVEL_INFO +#undef LOG_LEVEL_INFO +#endif +#ifdef LOG_LEVEL_NOTICE +#undef LOG_LEVEL_NOTICE +#endif +#ifdef LOG_LEVEL_TRACE +#undef LOG_LEVEL_TRACE +#endif +#ifdef LOG_LEVEL_VERBOSE +#undef LOG_LEVEL_VERBOSE +#endif + +// ArduinoLog maintains these names for backward compatibility constexpr ArduinoLogLevel LOG_LEVEL_FATAL = ArduinoLogLevel::LogLevelFatal; constexpr ArduinoLogLevel LOG_LEVEL_ERROR = ArduinoLogLevel::LogLevelError; constexpr ArduinoLogLevel LOG_LEVEL_WARNING = ArduinoLogLevel::LogLevelWarning; From e3a34c413eb70e19bf884b9888a3019eea4ddf69 Mon Sep 17 00:00:00 2001 From: Abasz <32517724+Abasz@users.noreply.github.com> Date: Mon, 7 Jul 2025 08:27:34 +0000 Subject: [PATCH 5/5] Save and restore potentially conflicting defines When including ArduinoLog save any existing definitions of LOG_LEVEL_* macros (e.g. from NimBLE) so that they can be restored after ArduinoLog is included to avoid compilation errors. This allows this library to coexist with others while keeping backward compatibility. --- src/ArduinoLog.h | 55 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/ArduinoLog.h b/src/ArduinoLog.h index 3e6f412..3af509b 100644 --- a/src/ArduinoLog.h +++ b/src/ArduinoLog.h @@ -57,37 +57,39 @@ enum class ArduinoLogLevel : unsigned char { LogLevelVerbose = 6 }; -// Undefine macros if conflicting (e.g. from NimBLE) to avoid compilation errors but these cause -// issues if these names are used in other libraries as #defines (e.g. NimBLE). This is not 100% -// safe as include order will matter: After including ArduinoLog.h these macros will not be -// available unless specifically defined. This should mean that if for instance NimBLE is included -// before ArduinoLog.h, anything that comes after ArduinoLog.h will not be able to use these macros, -// but if ArduinoLog.h is included first, then the macros will be available. -#ifdef LOG_LEVEL_SILENT -#undef LOG_LEVEL_SILENT -#endif +// Save existing macro definitions if they exist, then undefine them temporarily +// This allows us to use our own LOG_LEVEL_* constants while preserving any +// previously defined macros from other libraries (e.g. NimBLE) #ifdef LOG_LEVEL_SILENT +#define ARDUINOLOG_SAVED_LOG_LEVEL_SILENT LOG_LEVEL_SILENT #undef LOG_LEVEL_SILENT #endif #ifdef LOG_LEVEL_FATAL +#define ARDUINOLOG_SAVED_LOG_LEVEL_FATAL LOG_LEVEL_FATAL #undef LOG_LEVEL_FATAL #endif #ifdef LOG_LEVEL_ERROR +#define ARDUINOLOG_SAVED_LOG_LEVEL_ERROR LOG_LEVEL_ERROR #undef LOG_LEVEL_ERROR #endif #ifdef LOG_LEVEL_WARNING +#define ARDUINOLOG_SAVED_LOG_LEVEL_WARNING LOG_LEVEL_WARNING #undef LOG_LEVEL_WARNING #endif #ifdef LOG_LEVEL_INFO +#define ARDUINOLOG_SAVED_LOG_LEVEL_INFO LOG_LEVEL_INFO #undef LOG_LEVEL_INFO #endif #ifdef LOG_LEVEL_NOTICE +#define ARDUINOLOG_SAVED_LOG_LEVEL_NOTICE LOG_LEVEL_NOTICE #undef LOG_LEVEL_NOTICE #endif #ifdef LOG_LEVEL_TRACE +#define ARDUINOLOG_SAVED_LOG_LEVEL_TRACE LOG_LEVEL_TRACE #undef LOG_LEVEL_TRACE #endif #ifdef LOG_LEVEL_VERBOSE +#define ARDUINOLOG_SAVED_LOG_LEVEL_VERBOSE LOG_LEVEL_VERBOSE #undef LOG_LEVEL_VERBOSE #endif @@ -546,4 +548,39 @@ class Logging { #ifndef __DO_NOT_INSTANTIATE__ extern Logging Log; #endif + +// Restore previously defined macros if they existed +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_SILENT +#define LOG_LEVEL_SILENT ARDUINOLOG_SAVED_LOG_LEVEL_SILENT +#undef ARDUINOLOG_SAVED_LOG_LEVEL_SILENT +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_FATAL +#define LOG_LEVEL_FATAL ARDUINOLOG_SAVED_LOG_LEVEL_FATAL +#undef ARDUINOLOG_SAVED_LOG_LEVEL_FATAL +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_ERROR +#define LOG_LEVEL_ERROR ARDUINOLOG_SAVED_LOG_LEVEL_ERROR +#undef ARDUINOLOG_SAVED_LOG_LEVEL_ERROR +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_WARNING +#define LOG_LEVEL_WARNING ARDUINOLOG_SAVED_LOG_LEVEL_WARNING +#undef ARDUINOLOG_SAVED_LOG_LEVEL_WARNING +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_INFO +#define LOG_LEVEL_INFO ARDUINOLOG_SAVED_LOG_LEVEL_INFO +#undef ARDUINOLOG_SAVED_LOG_LEVEL_INFO +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_NOTICE +#define LOG_LEVEL_NOTICE ARDUINOLOG_SAVED_LOG_LEVEL_NOTICE +#undef ARDUINOLOG_SAVED_LOG_LEVEL_NOTICE +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_TRACE +#define LOG_LEVEL_TRACE ARDUINOLOG_SAVED_LOG_LEVEL_TRACE +#undef ARDUINOLOG_SAVED_LOG_LEVEL_TRACE +#endif +#ifdef ARDUINOLOG_SAVED_LOG_LEVEL_VERBOSE +#define LOG_LEVEL_VERBOSE ARDUINOLOG_SAVED_LOG_LEVEL_VERBOSE +#undef ARDUINOLOG_SAVED_LOG_LEVEL_VERBOSE +#endif + #endif \ No newline at end of file