replace : iotivity -> iotivity-sec
[platform/upstream/iotivity.git] / resource / csdk / logger / src / logger.c
index 8e21e33..6758d09 100644 (file)
 #define _POSIX_C_SOURCE 200809L
 #endif
 
-// Platform check can be extended to check and/or define more, or could be
-// moved into a config.h
-#if !defined(__ARDUINO__) && !defined(ARDUINO)
-#define HAVE_UNISTD_H 1
-#endif
+#include "iotivity_config.h"
 
 // Pull in _POSIX_TIMERS feature test macro to check for
 // clock_gettime() support.
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 
-// if we have unistd.h, we're a Unix system
+#ifdef HAVE_ARDUINO_TIME_H
+#include <Time.h>
+#else
 #include <time.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
 
 #include "logger.h"
 #include "string.h"
-#include "oc_logger.h"
-#include "oc_console_logger.h"
+#include "logger_types.h"
+
+// log level
+LogLevel g_level = DEBUG;
+// privacy log
+bool g_hidePrivateLogEntries = false;
 
 #ifndef __TIZEN__
 static oc_log_ctx_t *logCtx = 0;
+#endif
 
-static oc_log_level LEVEL_XTABLE[] = {OC_LOG_DEBUG, OC_LOG_INFO, OC_LOG_WARNING, OC_LOG_ERROR, OC_LOG_FATAL};
+#if defined(_MSC_VER)
+#define LINE_BUFFER_SIZE (16 * 2) + 16 + 1  // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
+#define S_LINE_BUFFER_SIZE (50 * 2) + 50 + 1  // Show 50 bytes, 2 chars/byte, spaces between bytes, null termination
+#else
+static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 + 1;  // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
+static const uint16_t S_LINE_BUFFER_SIZE = (50 * 2) + 50 + 1;  // Show 50 bytes, 2 chars/byte, spaces between bytes, null termination
+#endif //defined(_MSC_VER)
 
+#ifdef __ANDROID__
+#elif defined __linux__ || defined __APPLE__ || defined _WIN32 || defined(__TIZENRT__)
+static oc_log_level LEVEL_XTABLE[] = {OC_LOG_DEBUG, OC_LOG_INFO,
+                                      OC_LOG_WARNING, OC_LOG_ERROR, OC_LOG_FATAL};
 #endif
-static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 + 1;  // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
 
 // Convert LogLevel to platform-specific severity level.  Store in PROGMEM on Arduino
 #ifdef __ANDROID__
-    static android_LogPriority LEVEL[] = {ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
-#elif defined(__linux__) || defined(__APPLE__)
+#ifdef ADB_SHELL
+    static const char *LEVEL[] =
+    {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
+
+#else
+    static android_LogPriority LEVEL[] =
+    {ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL};
+#endif
+#elif defined(__linux__) || defined(__APPLE__) || defined(__msys_nt__)|| defined(__TIZENRT__)
     static const char * LEVEL[] __attribute__ ((unused)) = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
+#elif defined(_MSC_VER)
+    static const char * LEVEL[] = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
 #elif defined ARDUINO
-    #include <stdarg.h>
+#include <stdarg.h>
+#include "Arduino.h"
+#include "oic_string.h"
 
     PROGMEM const char level0[] = "DEBUG";
     PROGMEM const char level1[] = "INFO";
@@ -78,28 +108,133 @@ static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 + 1;  // Show 16 bytes, 2
     static void OCLogString(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr);
 #ifdef ARDUINO_ARCH_AVR
     //Mega2560 and other 8-bit AVR microcontrollers
-    #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_word(addr));}
+    #define GET_PROGMEM_BUFFER(buffer, addr) { OICStrcpy(buffer, sizeof(buffer), (char*)pgm_read_word(addr));}
 #elif defined ARDUINO_ARCH_SAM
     //Arduino Due and other 32-bit ARM micro-controllers
-    #define GET_PROGMEM_BUFFER(buffer, addr) { strcpy_P(buffer, (char*)pgm_read_dword(addr));}
+    #define GET_PROGMEM_BUFFER(buffer, addr) { OICStrcpy(buffer, sizeof(buffer), (char*)pgm_read_dword(addr));}
 #else
     #define GET_PROGMEM_BUFFER(buffer, addr) { buffer[0] = '\0';}
 #endif
-#endif // __ANDROID__
-
+#else // !defined(__ANDROID__) && !defined(ARDUINO)
+    static const char *LEVEL[] __attribute__ ((unused)) =
+    {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
+#endif
 
 #ifndef ARDUINO
+
+/**
+ * Output the contents of the specified buffer (in hex) with the specified priority level.
+ *
+ * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
+ * @param tag        - Module name
+ * @param buffer     - pointer to buffer of bytes
+ * @param bufferSize - max number of byte in buffer
+ */
+void OCLogBuffer(LogLevel level, const char * tag, const uint8_t * buffer, uint16_t bufferSize)
+{
+    if (!buffer || !tag || (bufferSize == 0))
+    {
+        return;
+    }
+
+    // No idea why the static initialization won't work here, it seems the compiler is convinced
+    // that this is a variable-sized object.
+    char lineBuffer[LINE_BUFFER_SIZE];
+    memset(lineBuffer, 0, sizeof lineBuffer);
+    int lineIndex = 0;
+    int i;
+    for (i = 0; i < bufferSize; i++)
+    {
+        // Format the buffer data into a line
+        snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
+        lineIndex++;
+        // Output 16 values per line
+        if (((i+1)%16) == 0)
+        {
+            OCLogv(level, tag, "%s", lineBuffer);
+            memset(lineBuffer, 0, sizeof lineBuffer);
+            lineIndex = 0;
+        }
+    }
+    // Output last values in the line, if any
+    if (bufferSize % 16)
+    {
+        OCLogv(level, tag, "%s", lineBuffer);
+    }
+}
+
+void OCPrintCALogBuffer(LogLevel level, const char *tag, const uint8_t *buffer,
+                        uint16_t bufferSize, uint8_t isHeader)
+{
+    if (!buffer || !tag || (bufferSize == 0))
+    {
+        return;
+    }
+
+    // No idea why the static initialization won't work here, it seems the compiler is convinced
+    // that this is a variable-sized object.
+    char lineBuffer[S_LINE_BUFFER_SIZE];
+    int lineIndex = 0;
+    int i;
+    for (i = 0; i < bufferSize; i++)
+    {
+        // Format the buffer data into a line
+        snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
+        lineIndex++;
+        // Output 50 values per line
+        if (((i+1)%50) == 0)
+        {
+            if (1 == isHeader)
+            {
+                OCLogv(level, tag, "| Analyzer(Header) | %s", lineBuffer);
+            }
+            else
+            {
+                OCLogv(level, tag, "| Analyzer(Body) | %s", lineBuffer);
+            }
+            memset(lineBuffer, 0, sizeof lineBuffer);
+            lineIndex = 0;
+        }
+    }
+
+    if (bufferSize % 50)
+    {
+        if (1 == isHeader)
+        {
+            OCLogv(level, tag, "| Analyzer(Header) | %s", lineBuffer);
+        }
+        else
+        {
+            OCLogv(level, tag, "| Analyzer(Body) | %s", lineBuffer);
+        }
+    }
+}
+
+void OCSetLogLevel(LogLevel level, bool hidePrivateLogEntries)
+{
+    g_level = level;
+    g_hidePrivateLogEntries = hidePrivateLogEntries;
+}
+
+bool OCGetPrivateLogLevel()
+{
+    return g_hidePrivateLogEntries;
+}
+
 #ifndef __TIZEN__
-void OCLogConfig(oc_log_ctx_t *ctx) {
+void OCLogConfig(oc_log_ctx_t *ctx)
+{
     logCtx = ctx;
 }
 
-void OCLogInit() {
+void OCLogInit()
+{
 
 }
 
-void OCLogShutdown() {
-#if defined(__linux__) || defined(__APPLE__)
+void OCLogShutdown()
+{
+#if defined(__linux__) || defined(__APPLE__) || defined(_WIN32)|| defined(__TIZENRT__)
     if (logCtx && logCtx->destroy)
     {
         logCtx->destroy(logCtx);
@@ -115,11 +250,23 @@ void OCLogShutdown() {
  * @param tag    - Module name
  * @param format - variadic log string
  */
-void OCLogv(LogLevel level, const char * tag, const char * format, ...) {
+void OCLogv(LogLevel level, const char * tag, const char * format, ...)
+{
     if (!format || !tag) {
         return;
     }
-    char buffer[MAX_LOG_V_BUFFER_SIZE] = {};
+
+    if (g_level > level && ERROR != level && WARNING != level && FATAL != level)
+    {
+        return;
+    }
+
+    if (true == g_hidePrivateLogEntries && INFO_PRIVATE == level)
+    {
+        return;
+    }
+
+    char buffer[MAX_LOG_V_BUFFER_SIZE] = {0};
     va_list args;
     va_start(args, format);
     vsnprintf(buffer, sizeof buffer - 1, format, args);
@@ -127,247 +274,306 @@ void OCLogv(LogLevel level, const char * tag, const char * format, ...) {
     OCLog(level, tag, buffer);
 }
 
-static void osalGetTime(int *min,int *sec, int *ms)
+/**
+ * Output a log string with the specified priority level.
+ * Only defined for Linux and Android
+ *
+ * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
+ * @param tag    - Module name
+ * @param logStr - log string
+ */
+void OCLog(LogLevel level, const char * tag, const char * logStr)
 {
-    if (min && sec && ms)
+    if (!logStr || !tag)
     {
-#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
-        struct timespec when = {0};
-        clockid_t clk = CLOCK_REALTIME;
-#ifdef CLOCK_REALTIME_COARSE
-        clk = CLOCK_REALTIME_COARSE;
-#endif
-        if (!clock_gettime(clk, &when))
-        {
-            *min = (when.tv_sec / 60) % 60;
-            *sec = when.tv_sec % 60;
-            *ms = when.tv_nsec / 1000000;
-        }
-#else
-        struct timeval now;
-        if (!gettimeofday(&now, NULL))
-        {
-            *min = (now.tv_sec / 60) % 60;
-            *sec = now.tv_sec % 60;
-            *ms = now.tv_usec * 1000;
-        }
-#endif
+       return;
+    }
+
+    if (g_level > level && ERROR != level && WARNING != level && FATAL != level)
+    {
+        return;
     }
+
+    if (true == g_hidePrivateLogEntries && INFO_PRIVATE == level)
+    {
+        return;
+    }
+
+    switch(level)
+    {
+        case DEBUG_LITE:
+            level = DEBUG;
+            break;
+        case INFO_LITE:
+            level = INFO;
+            break;
+        case INFO_PRIVATE:
+            level = INFO;
+            break;
+        default:
+            break;
+    }
+
+   #ifdef __ANDROID__
+
+   #ifdef ADB_SHELL
+       printf("%s: %s: %s\n", LEVEL[level], tag, logStr);
+   #else
+       __android_log_write(LEVEL[level], tag, logStr);
+   #endif
+
+   #else
+       if (logCtx && logCtx->write_level)
+       {
+           logCtx->write_level(logCtx, LEVEL_XTABLE[level], logStr);
+
+       }
+       else
+       {
+           int min = 0;
+           int sec = 0;
+           int ms = 0;
+   #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
+           struct timespec when = { .tv_sec = 0, .tv_nsec = 0 };
+           clockid_t clk = CLOCK_REALTIME;
+   #ifdef CLOCK_REALTIME_COARSE
+           clk = CLOCK_REALTIME_COARSE;
+   #endif
+           if (!clock_gettime(clk, &when))
+           {
+               min = (when.tv_sec / 60) % 60;
+               sec = when.tv_sec % 60;
+               ms = when.tv_nsec / 1000000;
+           }
+   #elif defined(_WIN32)
+           SYSTEMTIME systemTime = {0};
+           GetLocalTime(&systemTime);
+           min = (int)systemTime.wMinute;
+           sec = (int)systemTime.wSecond;
+           ms  = (int)systemTime.wMilliseconds;
+   #else
+           struct timeval now;
+           if (!gettimeofday(&now, NULL))
+           {
+               min = (now.tv_sec / 60) % 60;
+               sec = now.tv_sec % 60;
+               ms = now.tv_usec * 1000;
+           }
+   #endif
+           printf("%02d:%02d.%03d %s: %s: %s\n", min, sec, ms, LEVEL[level], tag, logStr);
+       }
+   #endif
+   }
+#endif //__TIZEN__
+#endif //ARDUINO
+#ifdef ARDUINO
+/**
+ * Initialize the serial logger for Arduino
+ * Only defined for Arduino
+ */
+void OCLogInit()
+{
+    Serial.begin(115200);
 }
 
 /**
  * Output a log string with the specified priority level.
- * Only defined for Linux and Android
+ * Only defined for Arduino.  Only uses PROGMEM strings
+ * for the tag parameter
  *
  * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
  * @param tag    - Module name
  * @param logStr - log string
  */
-void OCLog(LogLevel level, const char * tag, const char * logStr) {
-    if (!logStr || !tag) {
-        return;
+void OCLogString(LogLevel level, PROGMEM const char * tag, const char * logStr)
+{
+    if (!logStr || !tag)
+    {
+      return;
     }
 
-#ifdef __ANDROID__
-    __android_log_write(LEVEL[level], tag, logStr);
-#elif defined(__linux__) || defined(__APPLE__)
-    if (logCtx && logCtx->write_level)
+    char buffer[LINE_BUFFER_SIZE];
+
+    GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
+    Serial.print(buffer);
+
+    char c;
+    Serial.print(F(": "));
+    while ((c = pgm_read_byte(tag)))
     {
-        logCtx->write_level(logCtx, LEVEL_XTABLE[level], logStr);
+      Serial.write(c);
+      tag++;
     }
-    else
-    {
-        int min = 0;
-        int sec = 0;
-        int ms = 0;
-        osalGetTime(&min,&sec,&ms);
+    Serial.print(F(": "));
 
-        printf("%02d:%02d.%03d %s: %s: %s\n", min, sec, ms, LEVEL[level], tag, logStr);
-    }
-#endif
+    Serial.println(logStr);
 }
 
 /**
- * Output the contents of the specified buffer (in hex) with the specified priority level.
+ * Output the contents of the specified buffer (in hex) with the specified
+ * priority level.
  *
  * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
  * @param tag        - Module name
  * @param buffer     - pointer to buffer of bytes
  * @param bufferSize - max number of byte in buffer
  */
-void OCLogBuffer(LogLevel level, const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
-    if (!buffer || !tag || (bufferSize == 0)) {
-        return;
-    }
-
-    // No idea why the static initialization won't work here, it seems the compiler is convinced
-    // that this is a variable-sized object.
-    char lineBuffer[LINE_BUFFER_SIZE];
-    memset(lineBuffer, 0, sizeof lineBuffer);
-    int lineIndex = 0;
-    int i;
-    for (i = 0; i < bufferSize; i++) {
+ void OCLogBuffer(LogLevel level, PROGMEM const char * tag,
+                  const uint8_t * buffer, size_t bufferSize)
+ {
+     if (!buffer || !tag || (bufferSize == 0))
+     {
+         return;
+     }
+
+     char lineBuffer[LINE_BUFFER_SIZE] = {0};
+     uint8_t lineIndex = 0;
+     for (uint8_t i = 0; i < bufferSize; i++)
+     {
         // Format the buffer data into a line
         snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
         lineIndex++;
-        // Output 16 values per line
-        if (((i+1)%16) == 0) {
-            OCLog(level, tag, lineBuffer);
-            memset(lineBuffer, 0, sizeof lineBuffer);
-            lineIndex = 0;
-        }
-    }
-    // Output last values in the line, if any
-    if (bufferSize % 16) {
-        OCLog(level, tag, lineBuffer);
-    }
-}
-#endif //__TIZEN__
-#else
-    /**
-     * Initialize the serial logger for Arduino
-     * Only defined for Arduino
-     */
-    void OCLogInit() {
-        Serial.begin(115200);
-    }
+         // Output 16 values per line
+         if (((i+1)%16) == 0)
+         {
+             OCLogString(level, tag, lineBuffer);
+             memset(lineBuffer, 0, sizeof lineBuffer);
+             lineIndex = 0;
+         }
+     }
+     // Output last values in the line, if any
+     if (bufferSize % 16)
+     {
+         OCLogString(level, tag, lineBuffer);
+     }
+ }
 
-    /**
-     * Output a log string with the specified priority level.
-     * Only defined for Arduino.  Only uses PROGMEM strings
-     * for the tag parameter
-     *
-     * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
-     * @param tag    - Module name
-     * @param logStr - log string
-     */
-    void OCLogString(LogLevel level, PROGMEM const char * tag, const char * logStr) {
-        if (!logStr || !tag) {
-          return;
-        }
-
-        char buffer[LINE_BUFFER_SIZE];
-
-        GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
-        Serial.print(buffer);
-
-        char c;
-        Serial.print(F(": "));
-        while ((c = pgm_read_byte(tag))) {
-          Serial.write(c);
-          tag++;
-        }
-        Serial.print(F(": "));
-
-        Serial.println(logStr);
+/**
+ * Output a log string with the specified priority level.
+ * Only defined for Arduino.  Uses PROGMEM strings
+ *
+ * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
+ * @param tag    - Module name
+ * @param logStr - log string
+ */
+void OCLog(LogLevel level, PROGMEM const char *tag, const int lineNum,
+           PROGMEM const char *logStr)
+{
+    if (!logStr || !tag)
+    {
+        return;
     }
-
-    /**
-     * Output the contents of the specified buffer (in hex) with the specified priority level.
-     *
-     * @param level      - DEBUG, INFO, WARNING, ERROR, FATAL
-     * @param tag        - Module name
-     * @param buffer     - pointer to buffer of bytes
-     * @param bufferSize - max number of byte in buffer
-     */
-    void OCLogBuffer(LogLevel level, PROGMEM const char * tag, const uint8_t * buffer, uint16_t bufferSize) {
-        if (!buffer || !tag || (bufferSize == 0)) {
-            return;
-        }
-
-        char lineBuffer[LINE_BUFFER_SIZE] = {0};
-        uint8_t lineIndex = 0;
-        for (uint8_t i = 0; i < bufferSize; i++) {
-            // Format the buffer data into a line
-            snprintf(&lineBuffer[lineIndex*3], sizeof(lineBuffer)-lineIndex*3, "%02X ", buffer[i]);
-            lineIndex++;
-            // Output 16 values per line
-            if (((i+1)%16) == 0) {
-                OCLogString(level, tag, lineBuffer);
-                memset(lineBuffer, 0, sizeof lineBuffer);
-                lineIndex = 0;
-            }
-        }
-        // Output last values in the line, if any
-        if (bufferSize % 16) {
-            OCLogString(level, tag, lineBuffer);
-        }
+    char buffer[LINE_BUFFER_SIZE] = {0};
+    GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
+    Serial.print(buffer);
+    char c;
+    Serial.print(F(": "));
+    while ((c = pgm_read_byte(tag)))
+    {
+        Serial.write(c);
+        tag++;
     }
-
-    /**
-     * Output a log string with the specified priority level.
-     * Only defined for Arduino.  Uses PROGMEM strings
-     *
-     * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
-     * @param tag    - Module name
-     * @param logStr - log string
-     */
-    void OCLog(LogLevel level, PROGMEM const char * tag, PROGMEM const char * logStr) {
-        if (!logStr || !tag) {
-          return;
-        }
-
-        char buffer[LINE_BUFFER_SIZE];
-
-        GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
-        Serial.print(buffer);
-
-        char c;
-        Serial.print(F(": "));
-        while ((c = pgm_read_byte(tag))) {
-          Serial.write(c);
-          tag++;
-        }
-        Serial.print(F(": "));
-
-        while ((c = pgm_read_byte(logStr))) {
-          Serial.write(c);
-          logStr++;
-        }
-        Serial.println();
+    Serial.print(F(": "));
+    Serial.print(lineNum);
+    Serial.print(F(": "));
+    while ((c = pgm_read_byte(logStr)))
+    {
+        Serial.write(c);
+        logStr++;
     }
+    Serial.println();
+}
 
-    /**
-     * Output a variable argument list log string with the specified priority level.
-     * Only defined for Arduino as depicted below.
-     *
-     * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
-     * @param tag    - Module name
-     * @param format - variadic log string
-     */
-    void OCLogv(LogLevel level, PROGMEM const char * tag, PROGMEM const char * format, ...)
+/**
+ * Output a variable argument list log string with the specified priority level.
+ * Only defined for Arduino as depicted below.
+ *
+ * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
+ * @param tag    - Module name
+ * @param format - variadic log string
+ */
+void OCLogv(LogLevel level, PROGMEM const char *tag, const int lineNum,
+                PROGMEM const char *format, ...)
+{
+    char buffer[LINE_BUFFER_SIZE];
+    va_list ap;
+    va_start(ap, format);
+    GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
+    Serial.print(buffer);
+
+    char c;
+    Serial.print(F(": "));
+    while ((c = pgm_read_byte(tag)))
     {
-        char buffer[LINE_BUFFER_SIZE];
-        va_list ap;
-        va_start(ap, format);
-
-        GET_PROGMEM_BUFFER(buffer, &(LEVEL[level]));
-        Serial.print(buffer);
+     Serial.write(c);
+     tag++;
+     }
+    Serial.print(F(": "));
+    Serial.print(lineNum);
+    Serial.print(F(": "));
 
-        char c;
-        Serial.print(F(": "));
-
-        while ((c = pgm_read_byte(tag))) {
-            Serial.write(c);
-            tag++;
+#ifdef __AVR__
+    vsnprintf_P(buffer, sizeof(buffer), format, ap);
+#else
+    vsnprintf(buffer, sizeof(buffer), format, ap);
+#endif
+    for (char *p = &buffer[0]; *p; p++)
+    {
+        // emulate cooked mode for newlines
+        if (*p == '\n')
+        {
+            Serial.write('\r');
         }
-        Serial.print(F(": "));
+        Serial.write(*p);
+    }
+    Serial.println();
+    va_end(ap);
+}
+/**
+ * Output a variable argument list log string with the specified priority level.
+ * Only defined for Arduino as depicted below.
+ *
+ * @param level  - DEBUG, INFO, WARNING, ERROR, FATAL
+ * @param tag    - Module name
+ * @param format - variadic log string
+ */
+void OCLogv(LogLevel level, const char *tag, const __FlashStringHelper *format, ...)
+{
+    char buffer[LINE_BUFFER_SIZE];
+    va_list ap;
+    va_start(ap, format);
+    // strcpy_P(buffer, (char*)pgm_read_word(&(LEVEL[level])));
+    // Serial.print(buffer);
+
+    Serial.print(LEVEL[level]);
+    // char c;
+    Serial.print(F(": "));
+
+    /*while ((c = pgm_read_byte(tag))) {
+     Serial.write(c);
+     tag++;
+     }*/
+    Serial.print(tag);
+    Serial.print(F(": "));
 
 #ifdef __AVR__
-        vsnprintf_P(buffer, sizeof(buffer), format, ap);
+    vsnprintf_P(buffer, sizeof(buffer), (const char *)format, ap); // progmem for AVR
 #else
-        vsnprintf(buffer, sizeof(buffer), format, ap);
+    vsnprintf(buffer, sizeof(buffer), (const char *)format, ap); // for the rest of the world
 #endif
-        for(char *p = &buffer[0]; *p; p++) // emulate cooked mode for newlines
+    for (char *p = &buffer[0]; *p; p++)
+    {
+        // emulate cooked mode for newlines
+        if (*p == '\n')
         {
-            if(*p == '\n')
-            {
-                Serial.write('\r');
-            }
-            Serial.write(*p);
+            // Serial.write('\r');
+            Serial.print('\r');
         }
-        Serial.println();
-        va_end(ap);
+        //Serial.write(*p);
+        Serial.print(p);
     }
+    Serial.println();
+    va_end(ap);
+}
 
-#endif
+#endif //ARDUINO