diff --git a/libraries/logging/README.md b/libraries/logging/README.md new file mode 100644 index 00000000000..53100e02061 --- /dev/null +++ b/libraries/logging/README.md @@ -0,0 +1,90 @@ +## Sample Implementation of Logging Interface + +This sample provides reference implementations of the [logging interface](#using-your-custom-implementation-of-logging-interface) that is used by FreeRTOS libraries and demos to generate log messages. This sample provides +implementations of the logging interface for both the ISO C90 and ISO C99 (with GNU extension) standards. + +The sample implementations add metadata information of **task name**, **FreeRTOS timer tick count** and **incrementing counter value** as prefixes to each log message. Additionally, the ISO C99 implementation adds metadata information of the **library name** and **source file location** to each log message. + +**Note**: Users can provide their own implementations of the logging interface to enable logging, if they choose to not use this implementation. Please refer [here](#using-your-custom-implementation-of-logging-interface) for more information. + +### Logging Task +The sample implementation serializes logging messages from different tasks using a FreeRTOS queue that is serviced by a daemon logging task. The daemon task reads log message strings from the queue and forwards them to the vendor-specific function through the `configPRINT` macro. + +Each of the implementations (ISO C90 and ISO C99 with GNU extension) route the logging interface macros to a logging function (defined in [`iot_logging_task.h`](./include/iot_logging_task.h)) that pushes the message to the FreeRTOS queue, thereby serializing messages logged through the logging interfaces. + +### Using the Sample Implementation + +To enable logging for a FreeRTOS library and/or demo using the sample implementation, +* Configure logging level by defining the `LIBRARY_LOG_LEVEL` macro to one of `LOG_NONE` (this disables logging), `LOG_ERROR`, `LOG_WARN`, `LOG_INFO` or `LOG_DEBUG` constants. +* By default, the ISO C90 implementation is enabled. If your compiler supports ISO C99 (with GNU extension), you can enable the ISO C99 implementation by setting the `LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION` macro to `1`. When using the ISO C99 implementation, define the `LIBRARY_LOG_NAME` macro to configure the library name which is prefixed in +the log messages. + +The above macro definitions should occur in the FreeRTOS library-specific config file (like `core_mqtt_config.h` file for the coreMQTT library) for which logging is being configured. + +The logging file include and macro definitions must follow the following order in the config file: +* Include [`logging_levels.h`](./include/logging_levels.h). +* Define the `LIBRARY_LOG_LEVEL`, `LIBRARY_LOG_NAME`, and `LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION` macros as mentioned above. +* Include [`logging_stack.h`](./include/logging_stack.h). + +Here is a sample logging configuration for the coreMQTT library which is defined in +`core_mqtt_config.h` file: + +``` +#include "logging_levels.h" + +#ifndef LIBRARY_LOG_NAME + #define LIBRARY_LOG_NAME "MQTT" +#endif + +#ifndef LIBRARY_LOG_LEVEL + #define LIBRARY_LOG_LEVEL LOG_INFO +#endif + +#include "logging_stack.h" +``` + +When using the ISO C90 implementation, the above configuration generates the following log messages for the coreMQTT library. + +``` +78 1950 [iot_thread] [INFO] Packet received. ReceivedBytes=2. +79 1950 [iot_thread] [INFO] Ack packet deserialized with result: MQTTSuccess. +80 1951 [iot_thread] [INFO] State record updated. New state=MQTTPublishDone. +``` + +If using the ISO C99 implementation (i.e. when `LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION` macro is set to `1`), the log messages from coreMQTT library look like the following. +Note that the library name (`[MQTT]`) and source file location (`[core_mqtt.c:]`) +are present in each log message. + +``` +78 1950 [iot_thread] [INFO] [MQTT] [core_mqtt.c:886] Packet received. ReceivedBytes=2. +79 1950 [iot_thread] [INFO] [MQTT] [core_mqtt.c:1162] Ack packet deserialized with result: MQTTSuccess. +80 1951 [iot_thread] [INFO] [MQTT] [core_mqtt.c:1175] State record updated. New state=MQTTPublishDone. +``` + +### Using your custom implementation of Logging Interface +The logging interface comprises of the following 4 logging macros, listed in increasing order of verbosity: + +1. LogError +2. LogWarn +3. LogInfo +4. LogDebug + +`LogError` is only called when there is an error, so is the least verbose, whereas `LogDebug` is called more frequently to provide debug level information and is therefore, the most verbose. + +An example call of the logging macros (in FreeRTOS libraries) is the following: + +``` +LogInfo( ( “This prints an integer %d”, 100 ) ); +``` + +Your custom implementation of the logging interface needs to define these macros to +map them to your logging implementation, depending on the logging levels you want to enable. +Logging macros use format specifier and variable number of arguments, just like standard function, `printf`, but they use double parenthesis to support ISO C90 standard which should be taken care while defining them. + +If you have a thread safe printf function, LogInfo should be defined like +the following. Notice how one set of parentheses are removed because X +already contains a set of parentheses. + +``` +#define LogInfo( X ) printf X +``` diff --git a/libraries/logging/include/iot_logging_task.h b/libraries/logging/include/iot_logging_task.h index f680a9632c8..0244c0e983a 100644 --- a/libraries/logging/include/iot_logging_task.h +++ b/libraries/logging/include/iot_logging_task.h @@ -57,4 +57,40 @@ BaseType_t xLoggingTaskInitialize( uint16_t usStackSize, void vLoggingPrintf( const char * pcFormat, ... ); +/** + * @brief Interface for logging message at Error level. + * + * This function adds a "[ERROR]" prefix to the + * log message to label it as an error. + */ +void vLoggingPrintfError( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Warn level. + * + * This function adds a "[WARN]" prefix to the + * log message to label it as a warning. + */ +void vLoggingPrintfWarn( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Info level. + * + * This function adds a "[INFO]" prefix to the + * log message to label it as an informational message. + */ +void vLoggingPrintfInfo( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Debug level. + * + * This function adds a "[DEBUG]" prefix to the + * log message to label it as a debug level message. + */ +void vLoggingPrintfDebug( const char * pcFormat, + ... ); + #endif /* AWS_LOGGING_TASK_H */ diff --git a/libraries/logging/include/logging_stack.h b/libraries/logging/include/logging_stack.h index 926e151b2b8..2b2f188a1bd 100644 --- a/libraries/logging/include/logging_stack.h +++ b/libraries/logging/include/logging_stack.h @@ -33,30 +33,50 @@ /* FreeRTOS Include. */ #include "FreeRTOS.h" +#include "iot_logging_task.h" /* Standard Include. */ #include #include -/* Metadata information to prepend to every log message. */ - -/* Macro to extract only the file name from file path to use for metadata in - * log messages. */ -#ifdef _MSC_VER - #define FILENAME ( strrchr( __FILE__, '\\' ) ? strrchr( __FILE__, '\\' ) + 1 : __FILE__ ) -#else - #define FILENAME ( strrchr( __FILE__, '/' ) ? strrchr( __FILE__, '/' ) + 1 : __FILE__ ) +/** + * @brief This config enables extra verbosity in log messages with metadata information + * about the source library and location of the log message. + * This requires that the toolchain support C99 (for variadic macros) and the GNU + * extension for comma elision in variadic macros (with ##__VA_ARGS__). + * If this flag is enabled, you can change the metadata information from the logging_stack.h + * file. + * + * @note By default, this configuration is disabled by + */ +#ifndef LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION + #define LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION 0 #endif -#define LOG_METADATA_FORMAT "[%s:%d] " -#define LOG_METADATA_ARGS FILENAME, __LINE__ +#if LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION == 1 -/* Common macro for all logging interface macros. */ -#if !defined( DISABLE_LOGGING ) - #define SdkLog( string ) vLoggingPrintf string -#else - #define SdkLog( string ) -#endif +/* Macro to extract only the file name from file path to use for metadata in + * log messages. */ + #if defined( _MSC_VER_ ) || defined( __ARMCC_VERSION ) + #define FILENAME ( strrchr( __FILE__, '\\' ) ? strrchr( __FILE__, '\\' ) + 1 : __FILE__ ) + #else + #define FILENAME ( strrchr( __FILE__, '/' ) ? strrchr( __FILE__, '/' ) + 1 : __FILE__ ) + #endif + + #define SdkLogError( message ) SdkLogErrorC99 message + #define SdkLogErrorC99( format, ... ) vLoggingPrintf( "[ERROR] [%s] [%s:%d] " format "\r\n", LIBRARY_LOG_NAME, FILENAME, __LINE__, ## __VA_ARGS__ ) + #define SdkLogWarn( message ) SdkLogWarnC99 message + #define SdkLogWarnC99( format, ... ) vLoggingPrintf( "[WARN] [%s] [%s:%d] " format "\r\n", LIBRARY_LOG_NAME, FILENAME, __LINE__, ## __VA_ARGS__ ) + #define SdkLogInfo( message ) SdkLogInfoC99 message + #define SdkLogInfoC99( format, ... ) vLoggingPrintf( "[INFO] [%s] [%s:%d] " format "\r\n", LIBRARY_LOG_NAME, FILENAME, __LINE__, ## __VA_ARGS__ ) + #define SdkLogDebug( message ) SdkLogDebugC99 message + #define SdkLogDebugC99( format, ... ) vLoggingPrintf( "[DEBUG] [%s] [%s:%d] " format "\r\n", LIBRARY_LOG_NAME, FILENAME, __LINE__, ## __VA_ARGS__ ) +#else /* if LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION == 1 */ + #define SdkLogError( message ) vLoggingPrintfError message + #define SdkLogWarn( message ) vLoggingPrintfWarn message + #define SdkLogInfo( message ) vLoggingPrintfInfo message + #define SdkLogDebug( message ) vLoggingPrintfDebug message +#endif /* if defined( LOGGING_METADATA_WITH_C99_SUPPORT ) && ( LOGGING_METADATA_WITH_C99_SUPPORT == 1 ) */ /* Check that LIBRARY_LOG_LEVEL is defined and has a valid value. */ #if !defined( LIBRARY_LOG_LEVEL ) || \ @@ -71,28 +91,28 @@ #else #if LIBRARY_LOG_LEVEL == LOG_DEBUG /* All log level messages will logged. */ - #define LogError( message ) SdkLog( ( "[ERROR] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogWarn( message ) SdkLog( ( "[WARN] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogInfo( message ) SdkLog( ( "[INFO] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogDebug( message ) SdkLog( ( "[DEBUG] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) + #define LogError( message ) SdkLogError( message ) + #define LogWarn( message ) SdkLogWarn( message ) + #define LogInfo( message ) SdkLogInfo( message ) + #define LogDebug( message ) SdkLogDebug( message ) #elif LIBRARY_LOG_LEVEL == LOG_INFO /* Only INFO, WARNING and ERROR messages will be logged. */ - #define LogError( message ) SdkLog( ( "[ERROR] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogWarn( message ) SdkLog( ( "[WARN] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogInfo( message ) SdkLog( ( "[INFO] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) + #define LogError( message ) SdkLogError( message ) + #define LogWarn( message ) SdkLogWarn( message ) + #define LogInfo( message ) SdkLogInfo( message ) #define LogDebug( message ) #elif LIBRARY_LOG_LEVEL == LOG_WARN /* Only WARNING and ERROR messages will be logged.*/ - #define LogError( message ) SdkLog( ( "[ERROR] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) - #define LogWarn( message ) SdkLog( ( "[WARN] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) + #define LogError( message ) SdkLogError( message ) + #define LogWarn( message ) SdkLogWarn( message ) #define LogInfo( message ) #define LogDebug( message ) #elif LIBRARY_LOG_LEVEL == LOG_ERROR /* Only ERROR messages will be logged. */ - #define LogError( message ) SdkLog( ( "[ERROR] [%s] "LOG_METADATA_FORMAT, LIBRARY_LOG_NAME, LOG_METADATA_ARGS ) ); SdkLog( message ); SdkLog( ( "\r\n" ) ) + #define LogError( message ) SdkLogError( message ) #define LogWarn( message ) #define LogInfo( message ) #define LogDebug( message ) diff --git a/libraries/logging/iot_logging_task_dynamic_buffers.c b/libraries/logging/iot_logging_task_dynamic_buffers.c index 83fe8fca7c9..2396450db96 100644 --- a/libraries/logging/iot_logging_task_dynamic_buffers.c +++ b/libraries/logging/iot_logging_task_dynamic_buffers.c @@ -27,9 +27,11 @@ #include "FreeRTOS.h" #include "task.h" #include "queue.h" +#include "semphr.h" /* Logging includes. */ #include "iot_logging_task.h" +#include "logging_levels.h" /* Standard includes. */ #include @@ -121,29 +123,26 @@ static void prvLoggingTask( void * pvParameters ) if( xQueueReceive( xQueue, &pcReceivedString, portMAX_DELAY ) == pdPASS ) { configPRINT_STRING( pcReceivedString ); + vPortFree( ( void * ) pcReceivedString ); } } } + /*-----------------------------------------------------------*/ -/*! - * \brief Formats a string to be printed and sends it - * to the print queue. - * - * Appends the message number, time (in ticks), and task - * that called vLoggingPrintf to the beginning of each - * print statement. - * - */ -void vLoggingPrintf( const char * pcFormat, - ... ) +static void prvLoggingPrintfCommon( uint8_t usLoggingLevel, + const char * pcFormat, + va_list args ) { size_t xLength = 0; int32_t xLength2 = 0; - va_list args; char * pcPrintString = NULL; + configASSERT( usLoggingLevel <= LOG_DEBUG ); + configASSERT( pcFormat != NULL ); + configASSERT( configLOGGING_MAX_MESSAGE_LENGTH > 0 ); + /* The queue is created by xLoggingTaskInitialize(). Check * xLoggingTaskInitialize() has been called. */ configASSERT( xQueue ); @@ -153,11 +152,13 @@ void vLoggingPrintf( const char * pcFormat, if( pcPrintString != NULL ) { - /* There are a variable number of parameters. */ - va_start( args, pcFormat ); + const char * pcLevelString = NULL; + size_t ulFormatLen = 0UL; + /* Add metadata of task name and tick time for a new log message. */ if( strcmp( pcFormat, "\n" ) != 0 ) { + /* Add metadata of task name and tick count if config is enabled. */ #if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 ) { const char * pcTaskName; @@ -175,18 +176,40 @@ void vLoggingPrintf( const char * pcFormat, pcTaskName = pcNoTask; } - xLength = snprintf( pcPrintString, configLOGGING_MAX_MESSAGE_LENGTH, "%lu %lu [%s] ", - ( unsigned long ) xMessageNumber++, - ( unsigned long ) xTaskGetTickCount(), - pcTaskName ); - } - #else /* if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 ) */ - { - xLength = 0; + xLength += snprintf( pcPrintString, configLOGGING_MAX_MESSAGE_LENGTH, "%lu %lu [%s] ", + ( unsigned long ) xMessageNumber++, + ( unsigned long ) xTaskGetTickCount(), + pcTaskName ); } #endif /* if ( configLOGGING_INCLUDE_TIME_AND_TASK_NAME == 1 ) */ } + /* Choose the string for the log level metadata for the log message. */ + switch( usLoggingLevel ) + { + case LOG_ERROR: + pcLevelString = "ERROR"; + break; + + case LOG_WARN: + pcLevelString = "WARN"; + break; + + case LOG_INFO: + pcLevelString = "INFO"; + break; + + case LOG_DEBUG: + pcLevelString = "DEBUG"; + } + + /* Add the chosen log level information as prefix for the message. */ + if( pcLevelString != NULL ) + { + xLength += snprintf( pcPrintString + xLength, configLOGGING_MAX_MESSAGE_LENGTH - xLength, "[%s] ", pcLevelString ); + configASSERT( xLength > 0 ); + } + xLength2 = vsnprintf( pcPrintString + xLength, configLOGGING_MAX_MESSAGE_LENGTH - xLength, pcFormat, args ); if( xLength2 < 0 ) @@ -204,7 +227,15 @@ void vLoggingPrintf( const char * pcFormat, } xLength += ( size_t ) xLength2; - va_end( args ); + + /* Add newline characters if the message does not end with them.*/ + ulFormatLen = strlen( pcFormat ); + + if( ( ulFormatLen >= 2 ) && ( strncmp( pcFormat + ulFormatLen, "\r\n", 2 ) != 0 ) ) + { + xLength += snprintf( pcPrintString + xLength, configLOGGING_MAX_MESSAGE_LENGTH - xLength, "%s", "\r\n" ); + configASSERT( xLength > 0 ); + } /* Only send the buffer to the logging task if it is * not empty. */ @@ -225,6 +256,81 @@ void vLoggingPrintf( const char * pcFormat, } } } + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfError( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintfCommon( LOG_ERROR, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfWarn( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintfCommon( LOG_WARN, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfInfo( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintfCommon( LOG_INFO, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfDebug( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintfCommon( LOG_DEBUG, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +/*! + * \brief Formats a string to be printed and sends it + * to the print queue. + * + * Appends the message number, time (in ticks), and task + * that called vLoggingPrintf to the beginning of each + * print statement. + * + */ +void vLoggingPrintf( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintfCommon( LOG_NONE, pcFormat, args ); + + va_end( args ); +} + /*-----------------------------------------------------------*/ void vLoggingPrint( const char * pcMessage ) diff --git a/vendors/espressif/boards/esp32/aws_demos/config_files/FreeRTOSConfig.h b/vendors/espressif/boards/esp32/aws_demos/config_files/FreeRTOSConfig.h index 567e9a6f771..9f9a87d4a4f 100644 --- a/vendors/espressif/boards/esp32/aws_demos/config_files/FreeRTOSConfig.h +++ b/vendors/espressif/boards/esp32/aws_demos/config_files/FreeRTOSConfig.h @@ -422,4 +422,12 @@ #define UNTESTED_FUNCTION() #endif +/* This config enables extra verbosity in log messages with metadata information + * about the source library and location of the log message. + * This requires that the toolchain support C99 (for variadic macros) and the GNU + * extension for comma elision in variadic macros (with ##__VA_ARGS__). + * If this flag is enabled, you can change the metadata information from the logging_stack.h + * file.*/ +#define LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION ( 1 ) + #endif /* #define FREERTOS_CONFIG_H */ diff --git a/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.c b/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.c index 5d299f2b64f..27a97263ed1 100644 --- a/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.c +++ b/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.c @@ -52,6 +52,7 @@ /* Demo includes. */ #include "aws_demo_logging.h" +#include "logging_levels.h" /*-----------------------------------------------------------*/ @@ -117,7 +118,8 @@ static void prvCreatePrintSocket( void * pvParameter1, * Write a messages to stdout, either with or without a time-stamp. * A Windows thread will finally call printf() and fflush(). */ -static void prvLoggingPrintf( BaseType_t xFormatted, +static void prvLoggingPrintf( uint8_t usLoggingLevel, + BaseType_t xFormatted, const char * pcFormat, va_list xArgs ); @@ -284,18 +286,70 @@ void vLoggingPrintf( const char * pcFormat, va_list xArgs; va_start( xArgs, pcFormat ); - prvLoggingPrintf( pdTRUE, pcFormat, xArgs ); + prvLoggingPrintf( LOG_NONE, pdTRUE, pcFormat, xArgs ); va_end( xArgs ); } + /*-----------------------------------------------------------*/ void vLoggingPrint( const char * pcFormat ) { - prvLoggingPrintf( pdFALSE, pcFormat, NULL ); + prvLoggingPrintf( LOG_NONE, pdFALSE, pcFormat, NULL ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfError( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_ERROR, pdTRUE, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfWarn( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_WARN, pdTRUE, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfInfo( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_INFO, pdTRUE, pcFormat, args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfDebug( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_DEBUG, pdTRUE, pcFormat, args ); + + va_end( args ); } /*-----------------------------------------------------------*/ -static void prvLoggingPrintf( BaseType_t xFormatted, +static void prvLoggingPrintf( uint8_t usLoggingLevel, + BaseType_t xFormatted, const char * pcFormat, va_list xArgs ) { @@ -304,12 +358,13 @@ static void prvLoggingPrintf( BaseType_t xFormatted, char * pcSource, * pcTarget, * pcBegin; size_t xLength, xLength2, rc; static BaseType_t xMessageNumber = 0; - static BaseType_t xAfterLineBreak = pdTRUE; uint32_t ulIPAddress; const char * pcTaskName; const char * pcNoTask = "None"; int iOriginalPriority; HANDLE xCurrentTask; + const char * pcLevelString = NULL; + size_t ulFormatLen = 0UL; if( ( xStdoutLoggingUsed != pdFALSE ) || ( xDiskFileLoggingUsed != pdFALSE ) || @@ -325,8 +380,7 @@ static void prvLoggingPrintf( BaseType_t xFormatted, pcTaskName = pcNoTask; } - if( ( xAfterLineBreak == pdTRUE ) && - ( strcmp( pcFormat, "\r\n" ) != 0 ) && + if( ( strcmp( pcFormat, "\r\n" ) != 0 ) && ( xFormatted != pdFALSE ) ) { xLength = snprintf( cPrintString, @@ -335,13 +389,36 @@ static void prvLoggingPrintf( BaseType_t xFormatted, xMessageNumber++, ( unsigned long ) xTaskGetTickCount(), pcTaskName ); - xAfterLineBreak = pdFALSE; } else { xLength = 0; memset( cPrintString, 0x00, dlMAX_PRINT_STRING_LENGTH ); - xAfterLineBreak = pdTRUE; + } + + /* Choose the string for the log level metadata for the log message. */ + switch( usLoggingLevel ) + { + case LOG_ERROR: + pcLevelString = "ERROR"; + break; + + case LOG_WARN: + pcLevelString = "WARN"; + break; + + case LOG_INFO: + pcLevelString = "INFO"; + break; + + case LOG_DEBUG: + pcLevelString = "DEBUG"; + } + + /* Add the chosen log level information as prefix for the message. */ + if( pcLevelString != NULL ) + { + xLength += snprintf( cPrintString + xLength, dlMAX_PRINT_STRING_LENGTH - xLength, "[%s] ", pcLevelString ); } if( xArgs != NULL ) @@ -351,23 +428,24 @@ static void prvLoggingPrintf( BaseType_t xFormatted, pcFormat, xArgs ); } - else - { - xLength2 = snprintf( cPrintString + xLength, - dlMAX_PRINT_STRING_LENGTH - xLength, - "%s", - pcFormat ); - } if( xLength2 < 0 ) { /* Clean up. */ - xLength2 = sizeof( cPrintString ) - 1 - xLength; - cPrintString[ sizeof( cPrintString ) - 1 ] = '\0'; + xLength2 = 0; + cPrintString[ xLength ] = '\0'; } xLength += xLength2; + /* Add newline characters if the message does not end with them.*/ + ulFormatLen = strlen( pcFormat ); + + if( ( ulFormatLen >= 2 ) && ( strncmp( pcFormat + ulFormatLen, "\r\n", 2 ) != 0 ) ) + { + xLength += snprintf( cPrintString + xLength, dlMAX_PRINT_STRING_LENGTH - xLength, "%s", "\r\n" ); + } + /* For ease of viewing, copy the string into another buffer, converting * IP addresses to dot notation on the way. */ pcSource = cPrintString; diff --git a/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.h b/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.h index 4df933128c9..62c4ca9c113 100644 --- a/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.h +++ b/vendors/microchip/boards/ecc608a_plus_winsim/aws_demos/application_code/aws_demo_logging.h @@ -30,7 +30,7 @@ * threads. Do not call printf() directly while the scheduler is running. * * Set xLogToStdout, xLogToFile and xLogToUDP to either pdTRUE or pdFALSE to - * lot to stdout, a disk file and a UDP port respectively. + * log to stdout, a disk file and a UDP port respectively. * * If xLogToUDP is pdTRUE then ulRemoteIPAddress and usRemotePort must be set * to the IP address and port number to which UDP log messages will be sent. @@ -41,4 +41,66 @@ void vLoggingInit( BaseType_t xLogToStdout, uint32_t ulRemoteIPAddress, uint16_t usRemotePort ); +/** + * @brief Printf like logging interface for logging in + * from FreeRTOS tasks in the Windows platform. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrint( const char * pcFormat ); + +/** + * @brief Interface for logging message at Error level. + * + * This function adds a "[ERROR]" prefix to the + * log message to label it as an error. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfError( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Warn level. + * + * This function adds a "[WARN]" prefix to the + * log message to label it as a warning. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfWarn( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Info level. + * + * This function adds a "[INFO]" prefix to the + * log message to label it as an informational message. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfInfo( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Debug level. + * + * This function adds a "[DEBUG]" prefix to the + * log message to label it as a debug level message. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfDebug( const char * pcFormat, + ... ); + #endif /* AWS_DEMO_LOGGING_H */ diff --git a/vendors/nxp/boards/lpc54018iotmodule/aws_demos/config_files/FreeRTOSConfig.h b/vendors/nxp/boards/lpc54018iotmodule/aws_demos/config_files/FreeRTOSConfig.h index 3e90d2c77dc..7f38ff556b1 100644 --- a/vendors/nxp/boards/lpc54018iotmodule/aws_demos/config_files/FreeRTOSConfig.h +++ b/vendors/nxp/boards/lpc54018iotmodule/aws_demos/config_files/FreeRTOSConfig.h @@ -213,4 +213,12 @@ * take up unnecessary RAM. */ #define configCOMMAND_INT_MAX_OUTPUT_SIZE 1 +/* This config enables extra verbosity in log messages with metadata information + * about the source library and location of the log message. + * This requires that the toolchain support C99 (for variadic macros) and the GNU + * extension for comma elision in variadic macros (with ##__VA_ARGS__). + * If this flag is enabled, you can change the metadata information from the logging_stack.h + * file.*/ +#define LOGGING_ENABLE_METADATA_WITH_C99_AND_GNU_EXTENSION ( 1 ) + #endif /* FREERTOS_CONFIG_H */ diff --git a/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.c b/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.c index 5d299f2b64f..84b2b635608 100644 --- a/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.c +++ b/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.c @@ -52,6 +52,7 @@ /* Demo includes. */ #include "aws_demo_logging.h" +#include "logging_levels.h" /*-----------------------------------------------------------*/ @@ -117,7 +118,8 @@ static void prvCreatePrintSocket( void * pvParameter1, * Write a messages to stdout, either with or without a time-stamp. * A Windows thread will finally call printf() and fflush(). */ -static void prvLoggingPrintf( BaseType_t xFormatted, +static void prvLoggingPrintf( uint8_t usLoggingLevel, + BaseType_t xFormatted, const char * pcFormat, va_list xArgs ); @@ -284,18 +286,72 @@ void vLoggingPrintf( const char * pcFormat, va_list xArgs; va_start( xArgs, pcFormat ); - prvLoggingPrintf( pdTRUE, pcFormat, xArgs ); + prvLoggingPrintf( LOG_NONE, pdTRUE, pcFormat, xArgs ); va_end( xArgs ); } /*-----------------------------------------------------------*/ void vLoggingPrint( const char * pcFormat ) { - prvLoggingPrintf( pdFALSE, pcFormat, NULL ); + prvLoggingPrintf( LOG_NONE, pdFALSE, pcFormat, NULL ); } + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfError( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_ERROR, pdTRUE, pcFormat, args ); + + va_end( args ); +} + /*-----------------------------------------------------------*/ -static void prvLoggingPrintf( BaseType_t xFormatted, +void vLoggingPrintfWarn( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_WARN, pdTRUE, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfInfo( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_INFO, pdTRUE, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +void vLoggingPrintfDebug( const char * pcFormat, + ... ) +{ + va_list args; + + va_start( args, pcFormat ); + prvLoggingPrintf( LOG_DEBUG, pdTRUE, pcFormat, args ); + + va_end( args ); +} + +/*-----------------------------------------------------------*/ + +static void prvLoggingPrintf( uint8_t usLoggingLevel, + BaseType_t xFormatted, const char * pcFormat, va_list xArgs ) { @@ -304,12 +360,13 @@ static void prvLoggingPrintf( BaseType_t xFormatted, char * pcSource, * pcTarget, * pcBegin; size_t xLength, xLength2, rc; static BaseType_t xMessageNumber = 0; - static BaseType_t xAfterLineBreak = pdTRUE; uint32_t ulIPAddress; const char * pcTaskName; const char * pcNoTask = "None"; int iOriginalPriority; HANDLE xCurrentTask; + const char * pcLevelString = NULL; + size_t ulFormatLen = 0UL; if( ( xStdoutLoggingUsed != pdFALSE ) || ( xDiskFileLoggingUsed != pdFALSE ) || @@ -325,8 +382,7 @@ static void prvLoggingPrintf( BaseType_t xFormatted, pcTaskName = pcNoTask; } - if( ( xAfterLineBreak == pdTRUE ) && - ( strcmp( pcFormat, "\r\n" ) != 0 ) && + if( ( strcmp( pcFormat, "\r\n" ) != 0 ) && ( xFormatted != pdFALSE ) ) { xLength = snprintf( cPrintString, @@ -335,13 +391,36 @@ static void prvLoggingPrintf( BaseType_t xFormatted, xMessageNumber++, ( unsigned long ) xTaskGetTickCount(), pcTaskName ); - xAfterLineBreak = pdFALSE; } else { xLength = 0; memset( cPrintString, 0x00, dlMAX_PRINT_STRING_LENGTH ); - xAfterLineBreak = pdTRUE; + } + + /* Choose the string for the log level metadata for the log message. */ + switch( usLoggingLevel ) + { + case LOG_ERROR: + pcLevelString = "ERROR"; + break; + + case LOG_WARN: + pcLevelString = "WARN"; + break; + + case LOG_INFO: + pcLevelString = "INFO"; + break; + + case LOG_DEBUG: + pcLevelString = "DEBUG"; + } + + /* Add the chosen log level information as prefix for the message. */ + if( pcLevelString != NULL ) + { + xLength += snprintf( cPrintString + xLength, dlMAX_PRINT_STRING_LENGTH - xLength, "[%s] ", pcLevelString ); } if( xArgs != NULL ) @@ -351,23 +430,24 @@ static void prvLoggingPrintf( BaseType_t xFormatted, pcFormat, xArgs ); } - else - { - xLength2 = snprintf( cPrintString + xLength, - dlMAX_PRINT_STRING_LENGTH - xLength, - "%s", - pcFormat ); - } if( xLength2 < 0 ) { /* Clean up. */ - xLength2 = sizeof( cPrintString ) - 1 - xLength; - cPrintString[ sizeof( cPrintString ) - 1 ] = '\0'; + xLength2 = 0; + cPrintString[ xLength ] = '\0'; } xLength += xLength2; + /* Add newline characters if the message does not end with them.*/ + ulFormatLen = strlen( pcFormat ); + + if( ( ulFormatLen >= 2 ) && ( strncmp( pcFormat + ulFormatLen, "\r\n", 2 ) != 0 ) ) + { + xLength += snprintf( cPrintString + xLength, dlMAX_PRINT_STRING_LENGTH - xLength, "%s", "\r\n" ); + } + /* For ease of viewing, copy the string into another buffer, converting * IP addresses to dot notation on the way. */ pcSource = cPrintString; diff --git a/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.h b/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.h index 4df933128c9..131427c6437 100644 --- a/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.h +++ b/vendors/pc/boards/windows/aws_demos/application_code/aws_demo_logging.h @@ -41,4 +41,66 @@ void vLoggingInit( BaseType_t xLogToStdout, uint32_t ulRemoteIPAddress, uint16_t usRemotePort ); +/** + * @brief Printf like logging interface for logging in + * from FreeRTOS tasks in the Windows platform. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrint( const char * pcFormat ); + +/** + * @brief Interface for logging message at Error level. + * + * This function adds a "[ERROR]" prefix to the + * log message to label it as an error. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfError( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Warn level. + * + * This function adds a "[WARN]" prefix to the + * log message to label it as a warning. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfWarn( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Info level. + * + * This function adds a "[INFO]" prefix to the + * log message to label it as an informational message. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfInfo( const char * pcFormat, + ... ); + +/** + * @brief Interface for logging message at Debug level. + * + * This function adds a "[DEBUG]" prefix to the + * log message to label it as a debug level message. + * + * Depending on the configuration made through vLoggingInit(), + * this function will print to stdout, log to disk file OR + * transmit over a UDP port. + */ +void vLoggingPrintfDebug( const char * pcFormat, + ... ); + #endif /* AWS_DEMO_LOGGING_H */