// -------------------------------------------------------------------------------------------------------------------- /// \file logger.cpp /// \brief Description // -------------------------------------------------------------------------------------------------------------------- // // vbchaos software design // // -------------------------------------------------------------------------------------------------------------------- /// $Revision: $ /// $Author: $ /// $Date: $ // (c) 2023 vbchaos // -------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------- // Include files // -------------------------------------------------------------------------------------------------------------------- #include #include #include "logger.h" // -------------------------------------------------------------------------------------------------------------------- // Constant and macro definitions // -------------------------------------------------------------------------------------------------------------------- #define ENABLE_SERIAL_LOGGING //#undef ENABLE_SERIAL_LOGGING // -------------------------------------------------------------------------------------------------------------------- // Type definitions // -------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------- // File-scope variables // -------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------- // Function declarations // -------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------- // Function definitions // -------------------------------------------------------------------------------------------------------------------- TaskHandle_t Logger::logTaskHandle = NULL; uart_port_t Logger::uartPort = 0; QueueHandle_t Logger::logQueue = NULL; int Logger::queuesize = 0; Logger::Logger(int queuesize, uart_port_t uartPort) { Logger::uartPort = uartPort; Logger::queuesize = queuesize; logQueue = xQueueCreate(queuesize, sizeof(struct LogQueueItem)); xTaskCreate(loggerTask, (const char*)"loggerTask", 3072, NULL, 3, &logTaskHandle); } void Logger::Logger_log(const char* fileName, const char* functionName, int lineNumber, LogType logType, const char* format, ...) { #if defined(ENABLE_SERIAL_LOGGING) static int nrLostMessages = 0; static bool overflowRecovery = false; int nrOfMessages; struct LogQueueItem logQueueItem; va_list ap; nrOfMessages = uxQueueMessagesWaiting(Logger::logQueue); if((nrOfMessages == queuesize - 1) && !overflowRecovery) { // Queue almost full, only one entry left. Log a warning instead composeLogQueueItem(&logQueueItem, __FILE__, __func__, __LINE__, LOGTYPE_WARNING, "Log queue overflow"); (void)xQueueSend(logQueue, &logQueueItem, 0); overflowRecovery = true; nrLostMessages = 1; } else if((nrOfMessages == 0) && overflowRecovery) { // Queue empty again after an overflow char str[128]; snprintf(str, sizeof(str) / sizeof(str[0]), "%d messages lost", nrLostMessages); composeLogQueueItem(&logQueueItem, __FILE__, __func__, __LINE__, LOGTYPE_WARNING, str); (void)xQueueSend(logQueue, &logQueueItem, 0); overflowRecovery = false; } else if(!overflowRecovery) { // Normal behaviour, queue not full char str[128]; va_start(ap, format); vsnprintf(str, sizeof(str) / sizeof(str[0]), format, ap); va_end(ap); composeLogQueueItem(&logQueueItem, fileName, functionName, lineNumber, logType, str); (void)xQueueSend(logQueue, &logQueueItem, 0); } else { // Count number of lost messages ++nrLostMessages; } #endif } void Logger_logISR(struct Logger* self, const char* fileName, const char* functionName, int lineNumber, LogType logType, const char* context) { #if defined(ENABLE_LOGGING) if (self->initialized) { struct LogQueueItem logQueueItem; portBASE_TYPE higherPriorityTaskWoken = pdFALSE; composeLogQueueItem(&logQueueItem, fileName, functionName, lineNumber, logType, context); if(xQueueSendFromISR(self->logQueue, &logQueueItem, &higherPriorityTaskWoken) != pdTRUE) { // Queue failed } portEND_SWITCHING_ISR(higherPriorityTaskWoken); } #endif } #if defined(ENABLE_SERIAL_LOGGING) void Logger::composeLogQueueItem(struct LogQueueItem* logQueueItem, const char* fileName, const char* functionName, int lineNumber, LogType logType, const char* context) { const size_t fileNameSize = sizeof(logQueueItem->fileName) / sizeof(logQueueItem->fileName[0]); const size_t functionNameSize = sizeof(logQueueItem->functionName) / sizeof(logQueueItem->functionName[0]); const size_t contextSize = sizeof(logQueueItem->context) / sizeof(logQueueItem->context[0]); logQueueItem->logType = logType; strncpy(&(logQueueItem->context[0]), context, contextSize); logQueueItem->context[contextSize - 1] = '\0'; if(logType != LOGTYPE_PRINT) { int fileNameIndex = 0; // If filename starts with "src/", strip this part if((fileName[0] == 's') && (fileName[1] == 'r') && (fileName[2] == 'c') && (fileName[3] == '/')) { fileNameIndex = 4; } // It is known that the strncpy use can potentially truncate the source string, meaning // that the target string size is smaller then the original string // This is not a problem in this particular case, so the compiler warning is disabled // for this situation #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-truncation" // All logtypes except LOGTYPE_PRINT need filename, functionname and linenumber. strncpy(&(logQueueItem->fileName[0]), &fileName[fileNameIndex], fileNameSize); strncpy(&(logQueueItem->functionName[0]), functionName, functionNameSize); #pragma GCC diagnostic pop logQueueItem->lineNumber = lineNumber; // Fix terminating null byte in strncpy in case string to be copied is too long logQueueItem->fileName[fileNameSize - 1] = '\0'; logQueueItem->functionName[functionNameSize - 1] = '\0'; } } #endif void Logger::loggerTask(void* parameters) { while(true) { struct LogQueueItem logQueueItem; xQueueReceive(logQueue, &logQueueItem, portMAX_DELAY); if(logQueueItem.logType == LOGTYPE_PRINT) { // Raw print #if defined(ENABLE_SERIAL_LOGGING) uart_write_bytes(uartPort, logQueueItem.context, strlen(logQueueItem.context)); #endif } else { #if defined(ENABLE_SERIAL_LOGGING) char str[256]; char* vt100Prefix = ""; const char* vt100Postfix = "\033[0m"; #endif #if defined(ENABLE_SERIAL_LOGGING) if(logQueueItem.logType == LOGTYPE_INFO) { vt100Prefix = "\033[33m"; } else if(logQueueItem.logType == LOGTYPE_WARNING) { vt100Prefix = "\033[35m"; } else if(logQueueItem.logType == LOGTYPE_ERROR) { vt100Prefix = "\033[31m"; } #endif unsigned int seconds = 0; #if defined(ENABLE_SERIAL_LOGGING) // Formatted print snprintf(str, sizeof(str) / sizeof(str[0]), "%s[%s] %09d %s, %s, %d: %s%s\n\r", vt100Prefix, (logQueueItem.logType == LOGTYPE_DEBUG) ? "DBG" : (logQueueItem.logType == LOGTYPE_INFO) ? "INF" : (logQueueItem.logType == LOGTYPE_WARNING) ? "WRN" : "ERR", seconds, logQueueItem.fileName, logQueueItem.functionName, logQueueItem.lineNumber, logQueueItem.context, vt100Postfix); #endif #if defined(ENABLE_SERIAL_LOGGING) uart_write_bytes(uartPort, str, strlen(str)); #endif } } vTaskDelete(NULL); }