// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #include #include #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/consolelogger.h" #if (defined(_MSC_VER)) #include "windows.h" /*returns a string as if printed by vprintf*/ static char* vprintf_alloc(const char* format, va_list va) { char* result; int neededSize = vsnprintf(NULL, 0, format, va); if (neededSize < 0 || (neededSize + 1) < 0) { result = NULL; } else { result = (char*)malloc((size_t)neededSize + 1); if (result == NULL) { /*return as is*/ } else { if (vsnprintf(result, (size_t)neededSize + 1, format, va) != neededSize) { free(result); result = NULL; } } } return result; } /*returns a string as if printed by printf*/ static char* printf_alloc(const char* format, ...) { char* result; va_list va; va_start(va, format); result = vprintf_alloc(format, va); va_end(va); return result; } /*returns NULL if it fails*/ static char* lastErrorToString(DWORD lastError) { char* result; if (lastError == 0) { result = printf_alloc(""); /*no error should appear*/ if (result == NULL) { (void)printf("failure in printf_alloc"); } else { /*return as is*/ } } else { char temp[MESSAGE_BUFFER_SIZE]; if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), temp, MESSAGE_BUFFER_SIZE, NULL) == 0) { result = printf_alloc("GetLastError()=0X%x", lastError); if (result == NULL) { (void)printf("failure in printf_alloc\n"); /*return as is*/ } else { /*return as is*/ } } else { /*eliminate the \r or \n from the string*/ /*one replace of each is enough*/ char* whereAreThey; if ((whereAreThey = strchr(temp, '\r')) != NULL) { *whereAreThey = '\0'; } if ((whereAreThey = strchr(temp, '\n')) != NULL) { *whereAreThey = '\0'; } result = printf_alloc("GetLastError()==0X%x (%s)", lastError, temp); if (result == NULL) { (void)printf("failure in printf_alloc\n"); /*return as is*/ } else { /*return as is*/ } } } return result; } /*this function will use 1x printf (in the happy case) .*/ /*more than 1x printf / function call can mean intermingled LogErrors in a multithreaded env*/ /*the function will also attempt to produce some human readable strings for GetLastError*/ void consolelogger_log_with_GetLastError(const char* file, const char* func, int line, const char* format, ...) { DWORD lastError; char* lastErrorAsString; int lastErrorAsString_should_be_freed; time_t t; int systemMessage_should_be_freed; char* systemMessage; int userMessage_should_be_freed; char* userMessage; char* timeString; va_list args; va_start(args, format); /*this is what this case will do: 1. snip the last error 2. create a string with what that last error means 3. printf the system message (__FILE__, __LINE__ etc) + the last error + whatever the user wanted */ /*1. snip the last error*/ lastError = GetLastError(); /*2. create a string with what that last error means*/ lastErrorAsString = lastErrorToString(lastError); if (lastErrorAsString == NULL) { (void)printf("failure in lastErrorToString"); lastErrorAsString = ""; lastErrorAsString_should_be_freed = 0; } else { lastErrorAsString_should_be_freed = 1; } #if LOGGER_DISABLE_PAL t = time(NULL); timeString = ctime(&t); #else // LOGGER_DISABLE_PAL t = get_time(NULL); timeString = get_ctime(&t); #endif // LOGGER_DISABLE_PAL // In case time is not implemented timeString = timeString == NULL ? "" : timeString; systemMessage = printf_alloc("Error: Time:%.24s File:%s Func:%s Line:%d %s", timeString, file, func, line, lastErrorAsString); if (systemMessage == NULL) { systemMessage = ""; (void)printf("Error: [FAILED] Time:%.24s File : %s Func : %s Line : %d %s", timeString, file, func, line, lastErrorAsString); systemMessage_should_be_freed = 0; } else { systemMessage_should_be_freed = 1; } userMessage = vprintf_alloc(format, args); if (userMessage == NULL) { (void)printf("[FAILED] "); (void)vprintf(format, args); (void)printf("\n"); userMessage_should_be_freed = 0; } else { /*3. printf the system message(__FILE__, __LINE__ etc) + the last error + whatever the user wanted*/ (void)printf("%s %s\n", systemMessage, userMessage); userMessage_should_be_freed = 1; } if (userMessage_should_be_freed == 1) { free(userMessage); } if (systemMessage_should_be_freed == 1) { free(systemMessage); } if (lastErrorAsString_should_be_freed == 1) { free(lastErrorAsString); } va_end(args); } #endif #if defined(__GNUC__) __attribute__ ((format (printf, 6, 7))) #endif void consolelogger_log(LOG_CATEGORY log_category, const char* file, const char* func, int line, unsigned int options, const char* format, ...) { time_t t; char* timeString; va_list args; va_start(args, format); #if LOGGER_DISABLE_PAL t = time(NULL); timeString = ctime(&t); #else // LOGGER_DISABLE_PAL t = get_time(NULL); timeString = get_ctime(&t); #endif // LOGGER_DISABLE_PAL // In case time is not implemented timeString = timeString == NULL ? "" : timeString; switch (log_category) { case AZ_LOG_INFO: (void)printf("Info: "); break; case AZ_LOG_ERROR: (void)printf("Error: Time:%.24s File:%s Func:%s Line:%d ", timeString, file, func, line); break; default: break; } (void)vprintf(format, args); va_end(args); (void)log_category; if (options & LOG_LINE) { (void)printf("\r\n"); } }