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
) andFile
with 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_LOG
can 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_PREAMBLE
exposes 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_INFO
LOG_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 printed
LOG_TRACE("this is trace: log level", 5); // won't be printed
Serial
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)
#define DEBUGLOG_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 macro
LOG_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);
// or
LOG_ATTACH_STREAM(your_tcp_client);
// or
LOG_ATTACH_STREAM(any_other_stream);
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>
#define LOG_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.
#define LOG_PREAMBLE LOG_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 here
ASSERTM
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
#define DEBUGLOG_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)
#define DEBUGLOG_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 macro
LOG_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 logging
LOG_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.
#define DEBUGLOG_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>
void setup() {
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: TRACE
PRINTLN("current log level is", (int)LOG_GET_LEVEL());
// The default log_leval is DebugLogLevel::LVL_INFO
LOG_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 printed
LOG_TRACE("this is trace log"); // won't be printed
// Log array
float 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);
#endif
delay(1000);
// You can also use assert
// If assertion failed, Serial endlessly prints message
int x = 1;
// ASSERT(x != 1);
// You can also use assert with messages by ASSERTM macro
ASSERTM(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>
#define fs SD
// 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>
void setup() {
if (fs.begin()) {
String filename = "log.txt";
// Set file system to save every log automatically
LOG_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 SD
LOG_ERROR("error!");
// PRINT_FILE and PRINTLN_FILE is not affected by file_level (always visible)
// PRINT_FILE and PRINTLN_FILE is not displayed to Serial
PRINT_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: TRACE
PRINTLN_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_ERROR
LOG_ERROR("this is error log"); // printed to both Serial and File
LOG_WARN("this is warn log"); // won't be saved but printed
LOG_INFO("this is info log"); // won't be saved but printed
LOG_DEBUG("this is debug log"); // won't be printed
LOG_TRACE("this is trace log"); // won't be printed
// Log array
float 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);
#endif
delay(1000);
// You can also use assert
// If assertion failed, suspend program after prints message and close files
int x = 1;
// ASSERT(x != 1);
// You can also use assert with messages by ASSERTM macro
ASSERTM(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 logging
LOG_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.h
DebugLogDisable.h
DebugLogRestoreState.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.
#define DEBUGLOG_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.
#define LOG_ERROR(...)
#define LOG_WARN(...)
#define LOG_INFO(...)
#define LOG_DEBUG(...)
#define LOG_TRACE(...)
Assertion suspends program if the condition is true
.
#define ASSERT(b)
#define ASSERTM(b, msg)
PRINT
and PRINTLN
are available in both release and debug mode.
#define PRINT(...)
#define PRINTLN(...)
PRINT_FILE
and PRINTLN_FILE
are available in both release and debug mode.
#define PRINT_FILE(...)
#define PRINTLN_FILE(...)
If you use LOG_ATTACH_FS_MANUAL
, these macros are used to flush files manually.
// Arduino Only (Manual operation)
#define LOG_FILE_FLUSH()
#define LOG_FILE_CLOSE()
#define LOG_AS_ARR(arr, size)
#define LOG_GET_LEVEL()
#define LOG_SET_LEVEL(level)
#define LOG_SET_OPTION(file, line, func)
#define LOG_SET_DELIMITER(delim)
#define LOG_SET_BASE_RESET(b)
// Arduino Only
#define LOG_ATTACH_SERIAL(serial)
#define LOG_ATTACH_STREAM(stream)
#define LOG_FILE_IS_OPEN()
#define LOG_FILE_GET_LEVEL()
#define LOG_FILE_SET_LEVEL(lvl)
#define LOG_ATTACH_FS_AUTO(fs, path, mode)
#define LOG_ATTACH_FS_MANUAL(fs, path, mode)
enum class DebugLogLevel {
NONE = 0,
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 4,
TRACE = 5
};
enum class DebugLogBase {
DEC = 10,
HEX = 16,
OCT = 8,
BIN = 2, // only for Arduino
};
enum class LogPrecision {
ZERO,
ONE,
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
};
MIT