Skip to content

Latest commit

 

History

History
593 lines (438 loc) · 16.5 KB

README.md

File metadata and controls

593 lines (438 loc) · 16.5 KB

DebugLog

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

Feature

  • Output logs to Serial (or any other Stream) and File 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

Basic Usage

Logging API Comparison

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

Simple Log Example

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

Log Level control

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

Log Destination Control

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);

Log Preamble Control

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>

Simple LOG_PREAMBLE:

#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

Default LOG_PREAMBLE:

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

Assertion

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 PRINTLN (always output to Serial)

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

Logging to File

Enable File Logger

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>

Attach to File System

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);

Log Leval Control for File Output

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);

Notes for Auto Logging to File

  • 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() or LOG_FILE_CLOSE() manually

Flush File 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 PRINTLN_FILE (always output to File)

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");

Disable Logging Macro (Release Mode)

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>

Practical Example

// 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

Output Log to both Serial and File

// 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

Control Log Level Scope

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)

Logging APIs for both Serial and File

Both Serial and File

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)

Log only to Serial

PRINT and PRINTLN are available in both release and debug mode.

#define PRINT(...)
#define PRINTLN(...)

Log only to File

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()

Log Options

Log Option APIs

#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)

Log Level

enum class DebugLogLevel {
    NONE  = 0,
    ERROR = 1,
    WARN  = 2,
    INFO  = 3,
    DEBUG = 4,
    TRACE = 5
};

Log Base

enum class DebugLogBase {
    DEC = 10,
    HEX = 16,
    OCT = 8,
    BIN = 2,  // only for Arduino
};

Log Precision

enum class LogPrecision {
    ZERO,
    ONE,
    TWO,
    THREE,
    FOUR,
    FIVE,
    SIX,
    SEVEN,
    EIGHT,
};

Dependent Libraries

Used Inside of

License

MIT