Logging library for Arduino that can output to both Serial and File with one line
Warning
Dependent libraries removed (>= v0.8.0). If you have already installed this library, please follow:
- Cloned from GitHub (manually): Please install dependent libraries manually
- Installed from library manager: re-install this library from library manager
- Dependent libraries will be installed automatically
- Output logs to
Serial(or any otherStream) andFilewith one line at the same time - Output logs with variadic arguments
- Assertion support (suspend program with messages if assertion fails)
- Release Mode
#define DEBUGLOG_DISABLE_LOGcan easily disable logging (LOG_XXXX,ASSERT) - Log level control (
NONE,ERROR,WARN,INFO,DEBUG,TRACE) - Automatically or manually output log to file
- Multiple file system support (
SD,SdFat,SPIFFS, etc.) - Support array and container (
std::vector,std::deque,std::map) output - APIs can also be used in standard C++ apps
- Log preamble control
#define LOG_PREAMBLEexposes customization of string that comes before each log message
| APIs | Serial | File | Log Level | Release Mode |
|---|---|---|---|---|
LOG_XXXXX | YES | YES * | CONTROLLED | DISABLED |
ASSERT, ASSERTM | YES | YES * | IGNORED | DISABLED |
PRINT, PRINTLN | YES | NO | IGNORED | ENABLED |
PRINT_FILE, PRINTLN_FILE | NO | YES * | IGNORED | ENABLED * |
* : Only after LOG_FS_ATTACH_AUTO or LOG_FS_ATTACH_MANUAL is called
LOG_XXXX output is controlled by log level. Default log level is DebugLogLevel::LVL_INFO
#include<DebugLog.h>// The default log_leval is DebugLogLevel::LVL_INFOLOG_ERROR("this is error: log level", 1);
LOG_WARN("this is warn: log level", 2);
LOG_INFO("this is info: log level", 3);
LOG_DEBUG("this is debug: log level", 4); // won't be printedLOG_TRACE("this is trace: log level", 5); // won't be printedSerial output example
[ERROR] basic.ino L.26 setup : this is error: log level 1
[WARN] basic.ino L.27 setup : this is warn: log level 2
[INFO] basic.ino L.28 setup : this is info: log level 3
By defining DEBUGLOG_DEFAULT_LOG_LEVEL_XXXX, you can change default log level
// You can also set default log level by defining macro (default: INFO)
#defineDEBUGLOG_DEFAULT_LOG_LEVEL_TRACE// Include DebugLog after that
#include<DebugLog.h>Or you can change log level dynamically by
// You can change log_leval by following macroLOG_SET_LEVEL(DebugLogLevel::LVL_TRACE);After setting log level to DebugLogLevel::LVL_TRACE
LOG_ERROR("this is error log");
LOG_WARN("this is warn log");
LOG_INFO("this is info log");
LOG_DEBUG("this is debug log");
LOG_TRACE("this is trace log");will output
[ERROR] basic.ino L.26 setup : this is error: log level 1
[WARN] basic.ino L.27 setup : this is warn: log level 2
[INFO] basic.ino L.28 setup : this is info: log level 3
[DEBUG] basic.ino L.29 setup : this is debug: log level 4
[TRACE] basic.ino L.30 setup : this is trace: log level 5
You can output the log to another Serial easily:
LOG_ATTACH_SERIAL(Serial2);Also this library supports the log output to any Stream based instances. For example, you can change the log output destination to Ethernet/WiFiClient, Ethernet/WiFiUDP instead of default Serial as follows:
LOG_ATTACH_STREAM(your_udp_client);
// orLOG_ATTACH_STREAM(your_tcp_client);
// orLOG_ATTACH_STREAM(any_other_stream);The default can be changed by defining DEBUGLOG_DEFAULT_ARDUINO_STREAM before including DebugLog.h:
#defineDEBUGLOG_DEFAULT_ARDUINO_STREAM Serial2
#include<DebugLog.h>The LOG_PREAMBLE macro is called every LOG_XXXX. It defines a string that will be printed between the [LEVEL] and your custom message. To override the default definition, you must define the macro before you #include <DebugLog.h>
#defineLOG_PREAMBLE"||We the People||"
#include<DebugLog.h>LOG_ERROR("Message 1");
LOG_WARN("Message 2");
LOG_INFO("Message 3");Serial output
[ERROR] ||We the People|| Message 1
[WARN] ||We the People|| Message 2
[INFO] ||We the People|| Message 3
The Default LOG_PREAMBLE calls other macros and functions. Note the comma separated fields will be space delimeted as they are inputs to the LOG_XXXX(...) macro.
#defineLOG_PREAMBLELOG_SHORT_FILENAME, LOG_MACRO_APPEND_STR(L.__LINE__), __func__, ":"The
LOG_MACRO_APPEND_STR()macro will append a string to the result of a second macro
ASSERT suspends program if the provided condition is false
int x = 1;
ASSERT(x != 1); // suspends program hereASSERTM can also output message in addition to ASSERT
int x = 1;
ASSERTM(x != 1, "It's good to write the reason of fatal error");PRINT and PRINTLN is not affected by log level (always visible) and log format
PRINT("DebugLog", "can print variable args: ");
PRINTLN(1, 2.2, "three", "=> like this");Serial output example
DebugLog can print variable args: 1 2.20 three => like this
This preparation is required to use the file logging
// define DEBUGLOG_ENABLE_FILE_LOGGER to enable file logger
#defineDEBUGLOG_ENABLE_FILE_LOGGER
#include<DebugLog.h>You can log to File automatically by calling this macro. LOG_XXXX and ASSERT are automatically wrote to file depending on the log level
LOG_ATTACH_FS_AUTO(SD, "/log.txt", FILE_WRITE);By defining DEBUGLOG_DEFAULT_FILE_LEVEL_XXXX, you can change default log level. Default level is DebugLogLevel::LVL_ERROR.
// You can also set default file level by defining macro (default: ERROR)
#defineDEBUGLOG_DEFAULT_FILE_LEVEL_INFO// Include DebugLog after that
#include<DebugLog.h>Or you can change log level dynamically by
// You can change log_leval by following macroLOG_FILE_SET_LEVEL(DebugLogLevel::LVL_INFO);- One log function call can takes 3-20 ms if you log to file (depending on environment)
- There is option to flush to file manually to avoid flushing every log
- If you've disabled auto saving, you should call
LOG_FILE_FLUSH()orLOG_FILE_CLOSE()manually
By calling LOG_ATTACH_FS_MANUAL, you can control flush timing manually
LOG_ATTACH_FS_MANUAL(fs, filename, FILE_WRITE);You should call LOG_FILE_FLUSH() or LOG_FILE_CLOSE() manually to flush to the file
// If LOG_ATTACH_FS_MANUAL is used, you should manually save logs// however this is much faster than auto save (saving takes few milliseconds)LOG_FILE_FLUSH(); // manually save to SD card and continue loggingLOG_FILE_CLOSE(); // flush() and finish logging (ASSERT won't be saved to SD)PRINT_FILE and PRINTLN_FILE is not affected by log level (always visible) and log format
PRINT_FILE("DebugLog", "can print variable args: ");
PRINTLN_FILE(1, 2.2, "three", "=> like this");You can disable LOG_XXXX and ASSERT macro completely by defining following macro. By disabling log macros, you can save memory usage and cpu overhead (macro receives/calls nothing). Note that PRINT macros are not disabled even in the release mode.
#defineDEBUGLOG_DISABLE_LOG
#include<DebugLog.h>// Uncommenting DEBUGLOG_DISABLE_LOG disables ASSERT and all log (Release Mode)// PRINT and PRINTLN are always valid even in Release Mode// #define DEBUGLOG_DISABLE_LOG// You can also set default log level by defining macro (default: INFO)// #define DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
#include<DebugLog.h>voidsetup() {
Serial.begin(115200);
delay(2000);
// PRINT and PRINTLN is not affected by log_level (always visible)PRINT("DebugLog", "can print variable args: ");
PRINTLN(1, 2.2, "three", "=> like this");
// You can change log_leval by following macro// LOG_SET_LEVEL(DebugLogLevel::LVL_TRACE);// The default log_leval is DebugLogLevel::LVL_INFO// 0: NONE, 1: ERROR, 2: WARN, 3: INFO, 4: DEBUG, 5: TRACEPRINTLN("current log level is", (int)LOG_GET_LEVEL());
// The default log_leval is DebugLogLevel::LVL_INFOLOG_ERROR("this is error log");
LOG_WARN("this is warn log");
LOG_INFO("this is info log");
LOG_DEBUG("this is debug log"); // won't be printedLOG_TRACE("this is trace log"); // won't be printed// Log arrayfloat arr[3] {1.1, 2.2, 3.3};
PRINTLN("Array can be also printed like this", LOG_AS_ARR(arr, 3));
#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11// Log containers
std::vector<int> vs {1, 2, 3};
std::deque<float> ds {1.1, 2.2, 3.3};
std::map<String, int> ms {{"one", 1}, {"two", 2}, {"three", 3}};
PRINTLN("Containers can also be printed like", vs, ds, ms);
#endifdelay(1000);
// You can also use assert// If assertion failed, Serial endlessly prints messageint x = 1;
// ASSERT(x != 1);// You can also use assert with messages by ASSERTM macroASSERTM(x != 1, "This always fails");
}Serial Output
DebugLog can print variable args: 1 2.20 three => like this
current log level is 3
[ERROR] basic.ino L.26 setup : this is error log
[WARN] basic.ino L.27 setup : this is warn log
[INFO] basic.ino L.28 setup : this is info log
Array can be also printed like this [1.10, 2.20, 3.30]
Containers can also be printed like [1, 2, 3] [1.10, 2.20, 3.30] {one:1, three:3, two:2}
[ASSERT] basic.ino 51 setup : x != 1 => This always fails
// You can also set default file level by defining macro (default: ERROR)// #define DEBUGLOG_DEFAULT_FILE_LEVEL_WARN// if you want to use standard SD library
#include<SD.h>
#definefsSD// If you want to use SdFat// #include <SdFat.h>// SdFat fs;// SdFatSdio fs;// If you want use SPIFFS (ESP32) or other FileSystems// #include <SPIFFS.h>// #define fs SPIFFS// after that, include DebugLog.h
#include<DebugLog.h>voidsetup() {
if (fs.begin()) {
String filename = "log.txt";
// Set file system to save every log automaticallyLOG_ATTACH_FS_AUTO(fs, filename, FILE_WRITE);
// Set file system to save log manually// LOG_ATTACH_FS_MANUAL(fs, filename, FILE_WRITE);
}
// Apart from the log level to be displayed,// you can set the log level to be saved to a file (Default is DebugLogLevel::LVL_ERROR)// LOG_FILE_SET_LEVEL(DebugLogLevel::LVL_INFO);// If LOG_ATTACH_FS_AUTO is used, logs will be automatically saved to SDLOG_ERROR("error!");
// PRINT_FILE and PRINTLN_FILE is not affected by file_level (always visible)// PRINT_FILE and PRINTLN_FILE is not displayed to SerialPRINT_FILE("DebugLog", "can print variable args: ");
PRINTLN_FILE(1, 2.2, "three", "=> like this");
// Apart from the log level to be displayed,// you can set the log level to be saved to a file (Default is DebugLogLevel::LVL_ERROR)// LOG_FILE_SET_LEVEL(DebugLogLevel::LVL_INFO);// The default log_leval is DebugLogLevel::LVL_INFO// 0: NONE, 1: ERROR, 2: WARN, 3: INFO, 4: DEBUG, 5: TRACEPRINTLN_FILE("current log level is", (int)LOG_FILE_GET_LEVEL());
// LOG_XXXX outpus both Serial and File based on log_level and file_level// The default log_leval is DebugLogLevel::LVL_INFO// The default file_leval is DebugLogLevel::LVL_ERRORLOG_ERROR("this is error log"); // printed to both Serial and FileLOG_WARN("this is warn log"); // won't be saved but printedLOG_INFO("this is info log"); // won't be saved but printedLOG_DEBUG("this is debug log"); // won't be printedLOG_TRACE("this is trace log"); // won't be printed// Log arrayfloat arr[3] {1.1, 2.2, 3.3};
PRINTLN_FILE("Array can be also printed like this", LOG_AS_ARR(arr, 3));
#if ARX_HAVE_LIBSTDCPLUSPLUS >= 201103L // Have libstdc++11// Log containers
std::vector<int> vs {1, 2, 3};
std::deque<float> ds {1.1, 2.2, 3.3};
std::map<String, int> ms {{"one", 1}, {"two", 2}, {"three", 3}};
PRINTLN_FILE("Containers can also be printed like", vs, ds, ms);
#endifdelay(1000);
// You can also use assert// If assertion failed, suspend program after prints message and close filesint x = 1;
// ASSERT(x != 1);// You can also use assert with messages by ASSERTM macroASSERTM(x != 1, "This always fails");
// If LOG_ATTACH_FS_MANUAL is used, you should manually save logs// however this is much faster than auto save (saving takes few milliseconds)// LOG_FILE_FLUSH(); // manually save to SD card and continue loggingLOG_FILE_CLOSE(); // flush() and finish logging (ASSERT won't be saved to SD)
}Serial Output
[ERROR] log_to_file.ino L.97 setup : this is error log
[WARN] log_to_file.ino L.98 setup : this is warn log
[INFO] log_to_file.ino L.99 setup : this is info log
[ASSERT] log_to_file.ino 122 setup : x != 1 => This always fails
File Output
DebugLog can print variable args: 1 2.20 three => like this
current log level is 1
[ERROR] log_to_file.ino L.97 setup : this is error log
Array can be also printed like this [1.10, 2.20, 3.30]
Containers can also be printed like [1, 2, 3] [1.10, 2.20, 3.30] {one:1, three:3, two:2}
[ASSERT] log_to_file.ino 122 setup : x != 1 => This always fails
You can control the scope of DebugLog by including following header files.
DebugLogEnable.hDebugLogDisable.hDebugLogRestoreState.h
After including DebugLogEnable.h or DebugLogDisable.h, macros are enabled/disabled.
Finally you should include DebugLogRestoreState.h to restore the previous state.
Please see practical example examples/control_scope for details.
#defineDEBUGLOG_DISABLE_LOG
#include<DebugLog.h>// here is release mode (disable DebugLog)
#include<DebugLogEnable.h>// here is debug mode (enable DebugLog)
#include<DebugLogRestoreState.h>// here is release mode (restored)These logging APIs are enabled only if log level is control the visibility.
#defineLOG_ERROR(...)
#defineLOG_WARN(...)
#defineLOG_INFO(...)
#defineLOG_DEBUG(...)
#defineLOG_TRACE(...)Assertion suspends program if the condition is true.
#defineASSERT(b)
#defineASSERTM(b, msg)PRINT and PRINTLN are available in both release and debug mode.
#definePRINT(...)
#definePRINTLN(...)PRINT_FILE and PRINTLN_FILE are available in both release and debug mode.
#definePRINT_FILE(...)
#definePRINTLN_FILE(...)If you use LOG_ATTACH_FS_MANUAL, these macros are used to flush files manually.
// Arduino Only (Manual operation)
#defineLOG_FILE_FLUSH()
#defineLOG_FILE_CLOSE()#defineLOG_AS_ARR(arr, size)
#defineLOG_GET_LEVEL()
#defineLOG_SET_LEVEL(level)
#defineLOG_SET_OPTION(file, line, func)
#defineLOG_SET_DELIMITER(delim)
#defineLOG_SET_BASE_RESET(b)
// Arduino Only
#defineLOG_ATTACH_SERIAL(serial)
#defineLOG_ATTACH_STREAM(stream)
#defineLOG_FILE_IS_OPEN()
#defineLOG_FILE_GET_LEVEL()
#defineLOG_FILE_SET_LEVEL(lvl)
#defineLOG_ATTACH_FS_AUTO(fs, path, mode)
#defineLOG_ATTACH_FS_MANUAL(fs, path, mode)enumclassDebugLogLevel {
NONE = 0,
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 4,
TRACE = 5
};enumclassDebugLogBase {
DEC = 10,
HEX = 16,
OCT = 8,
BIN = 2, // only for Arduino
};enumclassLogPrecision {
ZERO,
ONE,
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
};MIT