diff --git a/src/BridgeLogger.cpp b/src/BridgeLogger.cpp new file mode 100644 index 000000000..f2078560d --- /dev/null +++ b/src/BridgeLogger.cpp @@ -0,0 +1,13 @@ +#include "BridgeLogger.h" + +#ifndef CUSTOM_BRIDGE_LOG +#include "DebugLogger.h" + +namespace mesh { + +// The default logger for bridge logging is just the debug logger. +Logger& bridgeLog = debugLog; + +} // namespace mesh + +#endif diff --git a/src/BridgeLogger.h b/src/BridgeLogger.h new file mode 100644 index 000000000..ba9ec6628 --- /dev/null +++ b/src/BridgeLogger.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Logger.h" + +namespace mesh { + +/// @brief The global logging instance meant for logs from bridge implementations +/// +/// By default this is set to debugLog, but can be overriden by defining +/// CUSTOM_BRIDGE_LOG in a build configuration and then providing an +/// alternative global definition (eg. `Logger& bridgeLog = myCustomLogger;`) +extern Logger& bridgeLog; + +} // namespace mesh diff --git a/src/DebugLogger.cpp b/src/DebugLogger.cpp new file mode 100644 index 000000000..0603a6d59 --- /dev/null +++ b/src/DebugLogger.cpp @@ -0,0 +1,81 @@ +#include "DebugLogger.h" + +#ifndef CUSTOM_DEBUG_LOG +#include +#include + +namespace mesh { + +class DefaultDebugLogger : public Logger { +public: + DefaultDebugLogger() {} + + size_t write(uint8_t b) override { return Serial.write(b); } + + size_t write(const uint8_t *buffer, size_t size) override { return Serial.write(buffer, size); } + + size_t printf(const char *format, ...) override __attribute__((format(printf, 2, 3))) { + va_list args; + va_start(args, format); + size_t written = vprintf(format, args); + va_end(args); + return written; + } + + size_t print(char c) override { return Serial.print(c); } + + size_t print(const char str[]) override { return Serial.print(str); } + + size_t printlnf(const char *format, ...) override __attribute__((format(printf, 2, 3))) { + va_list args; + va_start(args, format); + size_t written = vprintf(format, args); + va_end(args); + written += Serial.println(); + return written; + } + + size_t println(void) override { return Serial.println(); } + + size_t println(char c) override { return Serial.println(c); } + + size_t println(const char str[]) override { return Serial.println(str); } + + void printHex(const uint8_t* src, size_t len) override { + mesh::Utils::printHex(Serial, src, len); + } + + void flush() override { Serial.flush(); } + +private: + size_t vprintf(const char* format, va_list arg) { + char loc_buf[64]; + char *temp = loc_buf; + va_list copy; + va_copy(copy, arg); + int len = vsnprintf(temp, sizeof(loc_buf), format, copy); + va_end(copy); + if (len < 0) { + return 0; + } + if (len >= (int)sizeof(loc_buf)) { // comparation of same sign type for the compiler + temp = (char *)malloc(len + 1); + if (temp == NULL) { + return 0; + } + len = vsnprintf(temp, len + 1, format, arg); + } + len = write((uint8_t *)temp, len); + if (temp != loc_buf) { + free(temp); + } + return len; + } +}; + +DefaultDebugLogger defaultDebugLogger; +Logger& debugLog = defaultDebugLogger; + +} // namespace mesh + +#endif diff --git a/src/DebugLogger.h b/src/DebugLogger.h new file mode 100644 index 000000000..bb873f2f4 --- /dev/null +++ b/src/DebugLogger.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Logger.h" + +namespace mesh { + +/// @brief The global logging instance meant for debug logs +/// +/// By default this is set to an implementation which forwards to the +/// equivalent methods in the Arduino core `Serial` instance. This can be +/// overridden by defining CUSTOM_DEBUG_LOG in a build configuration and then +/// providing an alternative global definition. For example: +/// `Logger& debugLog = myCustomLogger;`. +extern Logger& debugLog; + +} // namespace mesh diff --git a/src/Logger.h b/src/Logger.h new file mode 100644 index 000000000..fe80ca3bb --- /dev/null +++ b/src/Logger.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include + +namespace mesh { + +/// @brief An abstraction for text based logging in MeshCore +class Logger { +public: + virtual ~Logger() = default; + + /// @brief Writes a single byte to the log + /// + /// @param b An unsigned byte to be written to the log. + /// @return The number of bytes written to the log (always one). + virtual size_t write(uint8_t b) = 0; + + /// @brief Writes the specified number of bytes to the log + /// + /// @param buffer A pointer to an array of bytes to be written to the log. + /// @param size The number of bytes in the array to be written to the log. + /// @return The number of bytes written to the log. + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + + /// @brief Formats a string and writes it to the log + /// + /// @param format A format string used to generate what will be written to + /// the log. + /// @param args Any values required by the format string. + /// @return The number of bytes written to the log. + virtual size_t printf(const char * format, ...) + __attribute__ ((format (printf, 2, 3))) = 0; + + /// @brief Writes a single character to the log + /// + /// @param c A character to be written to the log. + /// @return The number of bytes written to the log (always one). + virtual size_t print(char c) = 0; + + /// @brief Writes a null-terminated string to the log + /// + /// NOTE: The null terminator is not written to the log. + /// + /// @param str A pointer to a null-terminated string to be written to the + /// log. + /// @return The number of bytes written to the log. + virtual size_t print(const char str[]) = 0; + + /// @brief Formats a string and writes it to the log followed by a newline + /// + /// NOTE: The type of newline (eg. LF vs. CRLF) is implementation dependent. + /// + /// @param format A format string used to generate what will be written to + /// the log. + /// @param args Any values required by the format string. + /// @return The number of bytes written to the log. + virtual size_t printlnf(const char * format, ...) + __attribute__ ((format (printf, 2, 3))) = 0; + + /// @brief Writes a newline to the log. + /// + /// NOTE: The type of newline (eg. LF vs. CRLF) is implementation dependent. + /// + /// @return The number of bytes written to the log. + virtual size_t println(void) = 0; + + /// @brief Writes a single character to the log follwed by a newline + /// + /// NOTE: The type of newline (eg. LF vs. CRLF) is implementation dependent. + /// + /// @param c A character to be written to the log. + /// @return The number of bytes written to the log. + virtual size_t println(char c) = 0; + + /// @brief Writes a null-terminated string to the log followed by a newline + /// + /// NOTE: The null terminator is not written to the log. + /// NOTE: The type of newline (eg. LF vs. CRLF) is implementation dependent. + /// + /// @param str A pointer to a null-terminated string to be written to the + /// log. + /// @return The number of bytes written to the log. + virtual size_t println(const char str[]) = 0; + + /// @brief Write a byte array to the log in ASCII hex format. + /// + /// @param src An array of bytes to convert to ASCII hex format and then + /// write to the log. + /// @param len The number of bytes in the array to convert and write. + virtual void printHex(const uint8_t* src, size_t len) = 0; + + /// @brief Flush any data buffered by the Logger implementation + virtual void flush() = 0; +}; + +} // namespace mesh diff --git a/src/PacketLogger.cpp b/src/PacketLogger.cpp new file mode 100644 index 000000000..12b6d4258 --- /dev/null +++ b/src/PacketLogger.cpp @@ -0,0 +1,13 @@ +#include "PacketLogger.h" + +#ifndef CUSTOM_PACKET_LOG +#include + +namespace mesh { + +// The default logger for packet logging is just the debug logger. +Logger& packetLog = debugLog; + +} // namespace mesh + +#endif diff --git a/src/PacketLogger.h b/src/PacketLogger.h new file mode 100644 index 000000000..14153f46d --- /dev/null +++ b/src/PacketLogger.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Logger.h" + +namespace mesh { + +/// @brief The global logging instance meant for packet logging +/// +/// By default this is set to debugLog, but can be overriden by defining +/// CUSTOM_PACKET_LOG in a build configuration and then providing an +/// alternative global definition (eg. `Logger& packetLog = myCustomLogger;`) +extern Logger& packetLog; + +} // namespace mesh