// ----------------------------------------------------------------------------- /// @file Logger.c /// @brief Description // ----------------------------------------------------------------------------- // Micro-Key bv // Industrieweg 28, 9804 TG Noordhorn // Postbus 92, 9800 AB Zuidhorn // The Netherlands // Tel: +31 594 503020 // Fax: +31 594 505825 // Email: support@microkey.nl // Web: www.microkey.nl // ----------------------------------------------------------------------------- /// $Revision$ /// $Author$ /// $Date$ // (c) 2017 Micro-Key bv // ----------------------------------------------------------------------------- /// @file main.c /// @ingroup {group_name} // ----------------------------------------------------------------------------- // Include files // ----------------------------------------------------------------------------- #include #include "FreeRTOS.h" #include "Logger.h" #include "semphr.h" #include "queue.h" #include "task.h" #include "misc.h" #include #include #include #include #include "uart.h" #include "stm32f10x_rtc.h" // ----------------------------------------------------------------------------- // Constant and macro definitions // ----------------------------------------------------------------------------- #define LOGQUEUE_SIZE (20) // Makefile compile options: // ENABLE_SERIAL_LOGGING: Use the serial port for logging. #if defined(ENABLE_SERIAL_LOGGING) #define ENABLE_LOGGING #else #undef ENABLE_LOGGING #endif // ----------------------------------------------------------------------------- // Type definitions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // File-scope variables // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Function declarations // ----------------------------------------------------------------------------- #if defined(ENABLE_LOGGING) static void composeLogQueueItem(struct LogQueueItem* logQueueItem, const char* fileName, const char* functionName, int lineNumber, LogType logType, const char* context); #endif #if defined(ENABLE_LOGGING) static void loggerTask(void* parameters); #endif // ----------------------------------------------------------------------------- // Function definitions // ----------------------------------------------------------------------------- ErrorStatus Logger_construct(struct Logger* self, struct IODevice* const device, int taskPriority, uint16_t stackSize) { ErrorStatus returnValue = SUCCESS; #if defined(ENABLE_LOGGING) if (!self->initialized) { self->loggingDevice = device; self->TaskPriority = taskPriority; self->stackSize = stackSize; if(returnValue == SUCCESS) { self->logQueue = xQueueCreate(LOGQUEUE_SIZE, sizeof(struct LogQueueItem)); if(self->logQueue == 0) { returnValue = ERROR; } } if(returnValue == SUCCESS) { if(xTaskCreate(loggerTask, (const char*)"loggerTask", self->stackSize, self, self->TaskPriority, &self->taskHandle) != pdPASS) { returnValue = ERROR; } } if(returnValue == SUCCESS) { LOGGER_INFO(self, "Logger started"); self->initialized = true; } } #endif return returnValue; } void Logger_destruct(struct Logger* self) { self->initialized = false; } void Logger_log(struct Logger* self, const char* fileName, const char* functionName, int lineNumber, LogType logType, const char* format, ...) { #if defined(ENABLE_LOGGING) static int nrLostMessages = 0; static bool overflowRecovery = false; int nrOfMessages; struct LogQueueItem logQueueItem; va_list ap; if (self->initialized) { nrOfMessages = uxQueueMessagesWaiting(&self->logQueue); if((nrOfMessages == LOGQUEUE_SIZE - 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(self->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(self->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(self->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_LOGGING) static void 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; } // All logtypes except LOGTYPE_PRINT need filename, functionname and linenumber. strncpy(&(logQueueItem->fileName[0]), &fileName[fileNameIndex], fileNameSize); strncpy(&(logQueueItem->functionName[0]), functionName, functionNameSize); 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 #if defined(ENABLE_LOGGING) static void loggerTask(void* parameters) { struct Logger* self = (struct Logger*)parameters; while(self->initialized) { struct LogQueueItem logQueueItem; xQueueReceive(self->logQueue, &logQueueItem, portMAX_DELAY); if(logQueueItem.logType == LOGTYPE_PRINT) { // Raw print #if defined(ENABLE_SERIAL_LOGGING) IODevice_write(self->loggingDevice, 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 = (unsigned int)RTC_GetCounter(); #if defined(ENABLE_SERIAL_LOGGING) // Formatted print snprintf(str, sizeof(str) / sizeof(str[0]), "%s[%s] %09d %s, %s, %d: %s%s\n", 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) IODevice_write(self->loggingDevice, str, strlen(str)); #endif } } vTaskDelete(self->taskHandle); } #endif