diff options
Diffstat (limited to 'source/logging')
| -rw-r--r-- | source/logging/ErrorReporter.c | 286 | ||||
| -rw-r--r-- | source/logging/ErrorReporter.h | 111 | ||||
| -rw-r--r-- | source/logging/EventLogger.c | 398 | ||||
| -rw-r--r-- | source/logging/EventLogger.h | 219 | ||||
| -rw-r--r-- | source/logging/LogPrinter.c | 144 | ||||
| -rw-r--r-- | source/logging/LogPrinter.h | 133 |
6 files changed, 1291 insertions, 0 deletions
diff --git a/source/logging/ErrorReporter.c b/source/logging/ErrorReporter.c new file mode 100644 index 0000000..29f6ac7 --- /dev/null +++ b/source/logging/ErrorReporter.c @@ -0,0 +1,286 @@ +// +// ErrorReport.c - MrsWatson +// Created by Nik Reiman on 9/22/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "app/BuildInfo.h" +#include "base/File.h" +#include "base/PlatformInfo.h" +#include "logging/ErrorReporter.h" +#include "logging/EventLogger.h" + +#if WINDOWS +#include <Shlobj.h> +#endif + +static const char *kErrorReportInfoText = "MrsWatson is now running \ +in error report mode, which will generate a report on your desktop with \ +any input/output sources and error logs. This also enables some extra \ +arguments and will disable console logging.\n"; +static const char *kErrorReportCopyPluginsPromptText = "Would you like to copy the \ +plugin to the report? All data sent to the official support address is kept \ +strictly confidential. If the plugin in question has copy protection (or is \ +cracked), or depends on external resources, this probably won't work. But if \ +the plugin can be copied, it greatly helps in fixing bugs.\n\ +Copy the plugin? (y/n) "; + + +ErrorReporter newErrorReporter(void) +{ + ErrorReporter errorReporter = (ErrorReporter)malloc(sizeof(ErrorReporterMembers)); + + errorReporter->started = false; + errorReporter->completed = false; + errorReporter->reportName = newCharString(); + + errorReporter->desktopPath = newCharString(); + errorReporter->reportDirPath = newCharString(); + + return errorReporter; +} + +void errorReporterInitialize(ErrorReporter self) +{ + CharString infoText = newCharStringWithCString(kErrorReportInfoText); + CharString wrappedInfoText; + time_t now; + size_t length; + size_t i; + + printf("=== Starting error report ===\n"); + wrappedInfoText = charStringWrap(infoText, 0); + // The second newline here is intentional + printf("%s\n", wrappedInfoText->data); + + time(&now); + self->started = true; + + snprintf(self->reportName->data, self->reportName->capacity, + "MrsWatson Report %s", ctime(&now)); + // Trim the final newline character from this string if it exists + length = strlen(self->reportName->data); + + if (self->reportName->data[length - 1] == '\n') { + self->reportName->data[length - 1] = '\0'; + length--; + } + + for (i = 0; i < length; i++) { + if (!(charStringIsLetter(self->reportName, i) || + charStringIsNumber(self->reportName, i))) { + self->reportName->data[i] = '-'; + } + } + +#if UNIX + snprintf(self->desktopPath->data, self->desktopPath->capacity, + "%s/Desktop", getenv("HOME")); +#elif WINDOWS + SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, self->desktopPath->data); +#endif + + // Try to place the report on the user's desktop. However, if we cannot find + // the desktop (which may occur with localized Linux installations, for instance), + // then just dump it in the current directory instead. + File desktopPath = newFileWithPath(self->desktopPath); + File reportPath; + + if (!fileExists(desktopPath)) { + logWarn("Could not find desktop location, placing error report in current directory instead"); + CharString currentDirString = fileGetCurrentDirectory(); + File currentDir = newFileWithPath(currentDirString); + reportPath = newFileWithParent(currentDir, self->reportName); + freeFile(currentDir); + freeCharString(currentDirString); + } else { + reportPath = newFileWithParent(desktopPath, self->reportName); + freeFile(desktopPath); + } + + if (fileExists(reportPath)) { + logCritical("The path '%s' already contains a previous error report. Please remove the report data and try again."); + } else { + fileCreate(reportPath, kFileTypeDirectory); + } + + // Now we should have a real error report path + charStringCopy(self->reportDirPath, reportPath->absolutePath); + + freeFile(reportPath); + freeCharString(wrappedInfoText); + freeCharString(infoText); +} + +void errorReporterCreateLauncher(ErrorReporter self, int argc, char *argv[]) +{ + CharString outScriptName = newCharString(); + FILE *scriptFilePointer; + int i; + + charStringCopyCString(outScriptName, "run.sh"); + errorReporterRemapPath(self, outScriptName); + scriptFilePointer = fopen(outScriptName->data, "w"); + fprintf(scriptFilePointer, "#!/bin/sh\n"); + fprintf(scriptFilePointer, "mrswatson"); + + for (i = 1; i < argc; i++) { + // Don't run with the error report option again + if (strcmp(argv[i], "--error-report")) { + fprintf(scriptFilePointer, " %s", argv[i]); + } + } + + fprintf(scriptFilePointer, "\n"); + fclose(scriptFilePointer); +} + +void errorReporterRemapPath(ErrorReporter self, CharString path) +{ + File pathAsFile = newFileWithPath(path); + CharString basename = fileGetBasename(pathAsFile); + File parent = newFileWithPath(self->reportDirPath); + File remappedPath = newFileWithParent(parent, basename); + + charStringCopy(path, remappedPath->absolutePath); + + freeCharString(basename); + freeFile(parent); + freeFile(pathAsFile); + freeFile(remappedPath); +} + +boolByte errorReportCopyFileToReport(ErrorReporter self, CharString path) +{ + boolByte success; + + // Copy the destination path so that the original is not modified + CharString destination = newCharString(); + charStringCopy(destination, path); + errorReporterRemapPath(self, destination); + + File reportDirPath = newFileWithPath(self->reportDirPath); + File distinationPath = newFileWithPath(path); + File result = fileCopyTo(distinationPath, reportDirPath); + success = fileExists(result); + + freeCharString(destination); + freeFile(reportDirPath); + freeFile(distinationPath); + freeFile(result); + return success; +} + +// This could live in File, however this is currently the only place it is being used +// and also it's a rather cheap hack, so I would prefer to keep it as a static function +// here until another use-case presents itself. If that should happen, then we should +// refactor this code properly and move it to File. +static boolByte _copyDirectoryToErrorReportDir(ErrorReporter self, CharString path) +{ + boolByte success; + +#if UNIX + int result; + CharString copyCommand = newCharString(); + // TODO: This is the lazy way of doing this... + snprintf(copyCommand->data, copyCommand->capacity, "/bin/cp -r \"%s\" \"%s\"", + path->data, self->reportDirPath->data); + result = system(copyCommand->data); + success = (boolByte)(WEXITSTATUS(result) == 0); + + if (!success) { + logError("Could not copy '%s' to '%s'\n", path->data, self->reportDirPath->data); + } + +#else + logUnsupportedFeature("Copy directory recursively"); + success = false; +#endif + + return success; +} + +boolByte errorReporterShouldCopyPlugins(void) +{ + CharString promptText = newCharStringWithCString(kErrorReportCopyPluginsPromptText); + CharString wrappedPromptText; + char response; + + wrappedPromptText = charStringWrap(promptText, 0); + printf("%s", wrappedPromptText->data); + freeCharString(wrappedPromptText); + freeCharString(promptText); + + response = (char)getchar(); + return (boolByte)(response == 'y' || response == 'Y'); +} + +boolByte errorReporterCopyPlugins(ErrorReporter self, PluginChain pluginChain) +{ + CharString pluginAbsolutePath = NULL; + Plugin currentPlugin = NULL; + boolByte failed = false; + unsigned int i; + PlatformInfo platform = newPlatformInfo(); + + for (i = 0; i < pluginChain->numPlugins; i++) { + currentPlugin = pluginChain->plugins[i]; + pluginAbsolutePath = currentPlugin->pluginAbsolutePath; + + if (charStringIsEmpty(pluginAbsolutePath)) { + logInfo("Plugin '%s' does not have an absolute path and could not be copied", currentPlugin->pluginName->data); + } else if (platform->type == PLATFORM_MACOSX) { + failed |= !_copyDirectoryToErrorReportDir(self, pluginAbsolutePath); + } else { + failed |= !errorReportCopyFileToReport(self, pluginAbsolutePath); + } + } + + freePlatformInfo(platform); + return (boolByte)!failed; +} + +void errorReporterClose(ErrorReporter self) +{ + // Always do this, just in case + flushErrorLog(); + + printf("\n=== Error report complete ===\n"); + printf("Created error report at %s\n", self->reportDirPath->data); + printf("Please compress and email the report to: %s\n", SUPPORT_EMAIL); + printf("Thanks!\n"); +} + +void freeErrorReporter(ErrorReporter errorReporter) +{ + freeCharString(errorReporter->reportName); + freeCharString(errorReporter->reportDirPath); + freeCharString(errorReporter->desktopPath); + free(errorReporter); +} diff --git a/source/logging/ErrorReporter.h b/source/logging/ErrorReporter.h new file mode 100644 index 0000000..74b4012 --- /dev/null +++ b/source/logging/ErrorReporter.h @@ -0,0 +1,111 @@ +// +// ErrorReporter.h - MrsWatson +// Created by Nik Reiman on 9/22/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_ErrorReporter_h +#define MrsWatson_ErrorReporter_h + +#include "base/CharString.h" +#include "plugin/PluginChain.h" + +typedef struct { + CharString reportName; + CharString reportDirPath; + CharString desktopPath; + boolByte started; + boolByte completed; +} ErrorReporterMembers; +typedef ErrorReporterMembers *ErrorReporter; + +/** + * Create a new ErrorReporter + * @return ErrorReporter object + */ +ErrorReporter newErrorReporter(void); + +/** + * Create the necessary resources used by the ErrorReporter. This is not done + * during object construction because large files can be copied and interactive + * input requested from the user. + * @param self + */ +void errorReporterInitialize(ErrorReporter self); + +/** + * Create a shell script that can be used to launch MrsWatson with the same + * arguments which caused the error to happen + * @param self + * @param argc Number of arguments, as taken from main() + * @param argv Argument array, as taken from main() + */ +void errorReporterCreateLauncher(ErrorReporter self, int argc, char *argv[]); + +/** + * Remap a resource to point to the ErrorReporter's directory. This ensures all + * resources are contained within the same folder, and can be easily compressed + * for sending after the program finishes executing. + * @param self + * @param path Input path to be remapped + */ +void errorReporterRemapPath(ErrorReporter self, CharString path); + +/** + * Copy a file to the ErrorReporter's directory + * @param self + * @param path File to copy + * @return True if the file successfully copied + */ +boolByte errorReportCopyFileToReport(ErrorReporter self, CharString path); + +/** + * Ask the user if plugins should be copied to the report directory in addition + * to other files. Because there can be copy protection issues, not to mention + * file sizes, it is better to ask the user before copying the plugins. + * @return True if the user wishes to copy all plugins + */ +boolByte errorReporterShouldCopyPlugins(void); + +/** + * Copy all plug-ins to the ErrorReporter directory + * @param self + * @param pluginChain Initialized plugin chain + * @return True if all plugins were copied + */ +boolByte errorReporterCopyPlugins(ErrorReporter self, PluginChain pluginChain); + +/** + * Close any resources associated with the ErrorReporter + * @param self + */ +void errorReporterClose(ErrorReporter self); + +/** + * Free all memory associated with an ErrorReporter instance + * @param self + */ +void freeErrorReporter(ErrorReporter self); + +#endif diff --git a/source/logging/EventLogger.c b/source/logging/EventLogger.c new file mode 100644 index 0000000..dc9b6a9 --- /dev/null +++ b/source/logging/EventLogger.c @@ -0,0 +1,398 @@ +// +// EventLogger.c - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "app/BuildInfo.h" +#include "audio/AudioSettings.h" +#include "logging/EventLogger.h" +#include "logging/LogPrinter.h" +#include "time/AudioClock.h" + +#include "MrsWatson.h" + +#if WINDOWS +#include <Windows.h> +#include <io.h> +#elif UNIX +#include <unistd.h> +#endif + +EventLogger eventLoggerInstance = NULL; + +void initEventLogger(void) +{ +#if WINDOWS + ULONGLONG currentTime; +#else + struct timeval currentTime; +#endif + + eventLoggerInstance = (EventLogger)malloc(sizeof(EventLoggerMembers)); + eventLoggerInstance->logLevel = LOG_INFO; + eventLoggerInstance->logFile = NULL; + eventLoggerInstance->useColor = false; + eventLoggerInstance->zebraStripeSize = (unsigned long)DEFAULT_SAMPLE_RATE; + eventLoggerInstance->systemErrorMessage = NULL; + +#if WINDOWS + currentTime = GetTickCount(); + eventLoggerInstance->startTimeInSec = (unsigned long)(currentTime / 1000); + eventLoggerInstance->startTimeInMs = (unsigned long)currentTime; +#else + gettimeofday(¤tTime, NULL); + eventLoggerInstance->startTimeInSec = currentTime.tv_sec; + eventLoggerInstance->startTimeInMs = currentTime.tv_usec / 1000; +#endif + + if (isatty(1)) { + eventLoggerInstance->useColor = true; + } +} + +static EventLogger _getEventLoggerInstance(void) +{ + return eventLoggerInstance; +} + +char *stringForLastError(int errorNumber) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + + if (eventLogger->systemErrorMessage == NULL) { + eventLogger->systemErrorMessage = newCharString(); + } + +#if UNIX + return strerror(errorNumber); +#elif WINDOWS + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, errorNumber, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + eventLogger->systemErrorMessage->data, eventLogger->systemErrorMessage->capacity - 1, NULL); + return eventLogger->systemErrorMessage->data; +#endif + +} + +boolByte isLogLevelAtLeast(LogLevel logLevel) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + return (boolByte)(logLevel >= eventLogger->logLevel); +} + +void setLogLevel(LogLevel logLevel) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + eventLogger->logLevel = logLevel; +} + +void setLogLevelFromString(const CharString logLevelString) +{ + if (charStringIsEqualToCString(logLevelString, "debug", true)) { + setLogLevel(LOG_DEBUG); + } else if (charStringIsEqualToCString(logLevelString, "info", true)) { + setLogLevel(LOG_INFO); + } else if (charStringIsEqualToCString(logLevelString, "warn", true)) { + setLogLevel(LOG_WARN); + } else if (charStringIsEqualToCString(logLevelString, "error", true)) { + setLogLevel(LOG_ERROR); + } else { + logCritical("Invalid log level '%s', see '--help full' for valid arguments", logLevelString->data); + } +} + +void setLogFile(const CharString logFileName) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + eventLogger->logFile = fopen(logFileName->data, "a"); + + if (eventLogger->logFile == NULL) { + logCritical("Could not open file '%s' for logging", logFileName->data); + } else { + eventLogger->useColor = false; + } +} + +void setLoggingColorEnabled(boolByte useColor) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + eventLogger->useColor = useColor; +} + +void setLoggingColorEnabledWithString(const CharString colorSchemeName) +{ + if (charStringIsEmpty(colorSchemeName)) { + setLoggingColorEnabled(false); + } else if (charStringIsEqualToCString(colorSchemeName, "none", false)) { + setLoggingColorEnabled(false); + } else if (charStringIsEqualToCString(colorSchemeName, "auto", false)) { + setLoggingColorEnabled((boolByte)isatty(1)); + } else if (charStringIsEqualToCString(colorSchemeName, "force", false)) { + setLoggingColorEnabled(true); + } else { + // Use critical log level to avoid colors + logCritical("Unknown color scheme '%s'", colorSchemeName->data); + } +} + +void setLoggingZebraSize(const unsigned long zebraStripeSize) +{ + EventLogger eventLogger = _getEventLoggerInstance(); + eventLogger->zebraStripeSize = zebraStripeSize; +} + +static char _logLevelStatusChar(const LogLevel logLevel) +{ + switch (logLevel) { + case LOG_DEBUG: + return 'D'; + + case LOG_INFO: + return '-'; + + case LOG_WARN: + return 'W'; + + case LOG_ERROR: + return 'E'; + + default: + return '!'; + } +} + +static LogColor _logLevelStatusColor(const LogLevel logLevel) +{ + switch (logLevel) { + case LOG_DEBUG: + return COLOR_FG_DKGRAY; + + case LOG_INFO: + return COLOR_RESET; + + case LOG_WARN: + return COLOR_FG_YELLOW; + + case LOG_ERROR: + return COLOR_FG_RED; + + default: + return COLOR_RESET; + } +} + +static LogColor _logTimeColor(void) +{ + return COLOR_FG_CYAN; +} + +static LogColor _logTimeZebraStripeColor(const long elapsedTime, const unsigned long zebraSizeInMs) +{ + boolByte zebraState = (boolByte)((elapsedTime / zebraSizeInMs) % 2); + return zebraState ? COLOR_FG_OLIVE : COLOR_FG_GREEN; +} + +static void _printMessage(const LogLevel logLevel, const long elapsedTimeInMs, const long numFramesProcessed, const char *message, const EventLogger eventLogger) +{ + char *logString = (char *)malloc(sizeof(char) * kCharStringLengthLong); + + if (eventLogger->useColor) { + snprintf(logString, kCharStringLengthLong, "%c ", _logLevelStatusChar(logLevel)); + printToLog(_logLevelStatusColor(logLevel), eventLogger->logFile, logString); + snprintf(logString, kCharStringLengthLong, "%08ld ", numFramesProcessed); + printToLog(_logTimeZebraStripeColor(numFramesProcessed, eventLogger->zebraStripeSize), + eventLogger->logFile, logString); + snprintf(logString, kCharStringLengthLong, "%06ld ", elapsedTimeInMs); + printToLog(_logTimeColor(), eventLogger->logFile, logString); + printToLog(_logLevelStatusColor(logLevel), eventLogger->logFile, message); + } else { + snprintf(logString, kCharStringLengthLong, "%c %08ld %06ld %s", _logLevelStatusChar(logLevel), numFramesProcessed, elapsedTimeInMs, message); + printToLog(COLOR_NONE, eventLogger->logFile, logString); + } + + flushLog(eventLogger->logFile); + free(logString); +} + +static void _logMessage(const LogLevel logLevel, const char *message, va_list arguments) +{ + long elapsedTimeInMs; + EventLogger eventLogger = _getEventLoggerInstance(); +#if WINDOWS + ULONGLONG currentTime; +#else + struct timeval currentTime; +#endif + + if (eventLogger != NULL && logLevel >= eventLogger->logLevel) { + CharString formattedMessage = newCharStringWithCapacity(kCharStringLengthDefault * 2); + vsnprintf(formattedMessage->data, formattedMessage->capacity, message, arguments); +#if WINDOWS + currentTime = GetTickCount(); + elapsedTimeInMs = (unsigned long)(currentTime - eventLogger->startTimeInMs); +#else + gettimeofday(¤tTime, NULL); + elapsedTimeInMs = ((currentTime.tv_sec - (eventLogger->startTimeInSec + 1)) * 1000) + + (currentTime.tv_usec / 1000) + (1000 - eventLogger->startTimeInMs); +#endif + _printMessage(logLevel, elapsedTimeInMs, getAudioClock()->currentFrame, formattedMessage->data, eventLogger); + freeCharString(formattedMessage); + } +} + +void logDebug(const char *message, ...) +{ + va_list arguments; + va_start(arguments, message); + _logMessage(LOG_DEBUG, message, arguments); + va_end(arguments); +} + +void logInfo(const char *message, ...) +{ + va_list arguments; + va_start(arguments, message); + _logMessage(LOG_INFO, message, arguments); + va_end(arguments); +} + +void logWarn(const char *message, ...) +{ + va_list arguments; + va_start(arguments, message); + _logMessage(LOG_WARN, message, arguments); + va_end(arguments); +} + +void logError(const char *message, ...) +{ + va_list arguments; + va_start(arguments, message); + _logMessage(LOG_ERROR, message, arguments); + va_end(arguments); +} + +void logCritical(const char *message, ...) +{ + va_list arguments; + CharString formattedMessage = newCharString(); + CharString wrappedMessage; + + va_start(arguments, message); + // Instead of going through the common logging method, we always dump critical + // messages to stderr + vsnprintf(formattedMessage->data, formattedMessage->capacity, message, arguments); + wrappedMessage = charStringWrap(formattedMessage, 0); + fprintf(stderr, "ERROR: %s\n", wrappedMessage->data); + + if (eventLoggerInstance != NULL && eventLoggerInstance->logFile != NULL) { + fprintf(eventLoggerInstance->logFile, "ERROR: %s\n", wrappedMessage->data); + } + + freeCharString(formattedMessage); + freeCharString(wrappedMessage); + va_end(arguments); +} + +void logInternalError(const char *message, ...) +{ + va_list arguments; + CharString formattedMessage = newCharString(); + + va_start(arguments, message); + // Instead of going through the common logging method, we always dump critical messages to stderr + vsnprintf(formattedMessage->data, formattedMessage->capacity, message, arguments); + fprintf(stderr, "INTERNAL ERROR: %s\n", formattedMessage->data); + + if (eventLoggerInstance != NULL && eventLoggerInstance->logFile != NULL) { + fprintf(eventLoggerInstance->logFile, "INTERNAL ERROR: %s\n", formattedMessage->data); + } + + freeCharString(formattedMessage); + va_end(arguments); + + fprintf(stderr, " This should not have happened. Please take a minute to report a bug.\n"); + fprintf(stderr, " Support website: %s\n", SUPPORT_WEBSITE); + fprintf(stderr, " Support email: %s\n", SUPPORT_EMAIL); +} + +void logUnsupportedFeature(const char *featureName) +{ + fprintf(stderr, "UNSUPPORTED FEATURE: %s\n", featureName); + fprintf(stderr, " This feature is not yet supported. Please help us out and submit a patch! :)\n"); + fprintf(stderr, " Project website: %s\n", PROJECT_WEBSITE); + fprintf(stderr, " Support email: %s\n", SUPPORT_EMAIL); +} + +void logDeprecated(const char *functionName, const char *plugin) +{ + logWarn("Call to deprecated function '%s' made by plugin '%s'", functionName, plugin); +} + +void logFileError(const char *filename, const char *message) +{ + logCritical("Could not parse file '%s'", filename); + logCritical("Got error message message: %s", message); + logPossibleBug("This file is either corrupt or was parsed incorrectly"); +} + +void logPossibleBug(const char *cause) +{ + CharString extraText = newCharStringWithCString("If you believe this to be a \ +bug in MrsWatson, please re-run the program with the --error-report option to \ +generate a diagnostic report to send to support."); + CharString wrappedCause; + CharString wrappedExtraText; + + wrappedCause = charStringWrap(newCharStringWithCString(cause), 0); + wrappedExtraText = charStringWrap(extraText, 0); + fprintf(stderr, "%s\n", wrappedCause->data); + fprintf(stderr, "%s\n", wrappedExtraText->data); + + freeCharString(wrappedCause); + freeCharString(wrappedExtraText); +} + +void flushErrorLog(void) +{ + if (eventLoggerInstance != NULL && eventLoggerInstance->logFile != NULL) { + fflush(eventLoggerInstance->logFile); + } +} + +void freeEventLogger(void) +{ + if (eventLoggerInstance->logFile != NULL) { + fclose(eventLoggerInstance->logFile); + } + + freeCharString(eventLoggerInstance->systemErrorMessage); + free(eventLoggerInstance); + eventLoggerInstance = NULL; +} diff --git a/source/logging/EventLogger.h b/source/logging/EventLogger.h new file mode 100644 index 0000000..ebc294a --- /dev/null +++ b/source/logging/EventLogger.h @@ -0,0 +1,219 @@ +// +// EventLogger.h - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_EventLogger_h +#define MrsWatson_EventLogger_h + +#include <sys/types.h> +#include <stdio.h> + +#include "base/CharString.h" +#include "base/Types.h" + +typedef enum { + LOG_DEBUG, + LOG_INFO, + LOG_WARN, + LOG_ERROR, + NUM_LOG_LEVELS +} LogLevel; + +typedef struct { + LogLevel logLevel; + long startTimeInSec; + long startTimeInMs; + boolByte useColor; + unsigned long zebraStripeSize; + FILE *logFile; + CharString systemErrorMessage; +} EventLoggerMembers; +typedef EventLoggerMembers *EventLogger; +extern EventLogger eventLoggerInstance; + +/** + * Initialize the global event logger instance. Unlike other classes, the event + * logger exists as a global singleton, since it is called from numerous places + * throughout the code base. + */ +void initEventLogger(void); + +/** + * Get a descriptive error message from the operating system. + * @param errorNumber Error code from the operating system + * @return String description of the error code + */ +char *stringForLastError(int errorNumber); + +/** + * Check if the current log level is at a given level or higher. This is useful + * to avoid doing work to generate logging messages which would not actually + * be seen by the user. + * @param logLevel Target log level + * @return True if the logging level is at this level or higher + */ +boolByte isLogLevelAtLeast(LogLevel logLevel); + +/** + * Setup the logging level to be used + * @param logLevel Log level + */ +void setLogLevel(LogLevel logLevel); + +/** + * Set the logging level from a human readable string + * @param logLevelString Log level as a string. If they given string does not + * match any log level, no change is made. + */ +void setLogLevelFromString(const CharString logLevelString); + +/** + * Send all logging outputs tree files instead of standard error + * @param logFileName File name to log to. The file will be opened for appending + * text, it will not be overwritten. + */ +void setLogFile(const CharString logFileName); + +/** + * Enable or disable the use of color for console logging. By default, the + * EventLogger will not use colors if outputting to a non-interactive terminal. + * @param useColor True if color should be used + */ +void setLoggingColorEnabled(boolByte useColor); + +/** + * Set the color scheme to be used with a string name. If no valid name is + * passed to this method, no action will be taken. + * @param colorSchemeName Color scheme string + */ +void setLoggingColorEnabledWithString(const CharString colorSchemeName); + +/** + * When colored logging is enabled, MrsWatson uses different color shades for + * the current sample number in the log output. By default, the colors alternate + * every one second of processed audio. This makes it much easier to follow the + * logging output, and to find a given event in a very large stream of text. + * This method allows one to set how often the colors should alternate, instead + * of the default rate of one second. + * @param zebraStripeSize How long each color should be displayed, in sample + * frames + */ +void setLoggingZebraSize(const unsigned long zebraStripeSize); + +/** + * Log a debug message. + * @param message Format string, like printf + * @param ... Arguments + */ +void logDebug(const char *message, ...); + +/** + * Log a message with regular priority + * @param message Format string, like printf + * @param ... Arguments + */ +void logInfo(const char *message, ...); + +/** + * Log a warning message + * @param message Format string, like printf + * @param ... Arguments + */ +void logWarn(const char *message, ...); + +/** + * Log an error message + * @param message Format string, like printf + * @param ... Arguments + */ +void logError(const char *message, ...); + +/** + * Log a severe error message. Unlike logError, this method does not use colors + * or check the log level. It is reserved for messages which must be shown to + * the user at all costs. + * @param message Format string, like printf + * @param ... Arguments + */ +void logCritical(const char *message, ...); + +/** + * Display a message to the user indicating that something terribly wrong has + * occurred within the program. This call should be reserved for errors which + * are not expected under normal circumstances. + * @param message Format string, like printf + * @param ... Arguments + */ +void logInternalError(const char *message, ...); + +/** + * Display a message to the user indicating that they have tried to use part of + * the program which is incomplete. For example, plugin to host callbacks which + * are known to exist but not yet not fully supported. In these cases, we should + * let the user know that they are not encountering a bug, and also encourage + * contribution to the project by indicating that this is a planned future. + * @param featureName Missing feature name + */ +void logUnsupportedFeature(const char *featureName); + +/** + * Used when a plug-in has attempted to utilize a deprecated host feature. In + * such cases, undefined behavior in the plugin may result, so it is useful for + * the user to know about this, either to tell the plugin's developer or simply + * to know that an incompatibility may exist. + * @param functionName Deprecated feature name + * @param plugin + */ +void logDeprecated(const char *functionName, const char *plugin); + +/** + * Used to indicate that a file may be corrupt or incorrectly parsed. This is + * similar to a critical log message, except that extra information about the + * file and situation is shown. + * @param filename File name + * @param message Description of what went wrong + */ +void logFileError(const char *filename, const char *message); + +/** + * Used when a serious error has occurred, and its source is not entirely known. + * This occurs usually with segmentation faults or other errors which result in + * the program crashing. + * @param cause Description of error cause + */ +void logPossibleBug(const char *cause); + +/** + * Flush the contents of the ErrorLogger + */ +void flushErrorLog(void); + +/** + * Free all memory and associated resources from the global EventLogger instance + */ +void freeEventLogger(void); + +#endif diff --git a/source/logging/LogPrinter.c b/source/logging/LogPrinter.c new file mode 100644 index 0000000..87d4bff --- /dev/null +++ b/source/logging/LogPrinter.c @@ -0,0 +1,144 @@ +// +// LogPrinter.c - MrsWatson +// Created by Nik Reiman on 10/23/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdlib.h> + +#include "logging/LogPrinter.h" + +#if UNIX +void printToLog(const char *color, FILE *logFile, const char *message) +{ + if (logFile == NULL) { + if (color == NULL) { + fprintf(stderr, "%s", message); + } else { + fprintf(stderr, "%s%s%s", color, message, COLOR_RESET); + } + } else { + fprintf(logFile, "%s", message); + } +} +#elif WINDOWS +void printToLog(const LogColor color, FILE *logFile, const char *message) +{ + static HANDLE consoleHandle = NULL; + CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; + + if (logFile == NULL) { + if (color != 0) { + if (consoleHandle == NULL) { + consoleHandle = GetStdHandle(STD_ERROR_HANDLE); + } + + GetConsoleScreenBufferInfo(consoleHandle, &screenBufferInfo); + SetConsoleTextAttribute(consoleHandle, color); + fprintf(stderr, "%s", message); + SetConsoleTextAttribute(consoleHandle, screenBufferInfo.wAttributes); + } else { + fprintf(stderr, "%s", message); + } + } else { + fprintf(logFile, "%s", message); + } +} +#endif + +void flushLog(FILE *logFile) +{ + if (logFile == NULL) { + fprintf(stderr, "\n"); + fflush(stderr); + } else { + fprintf(logFile, "\n"); + fflush(logFile); + } +} + +void printTestPattern(void) +{ + printToLog(COLOR_FG_BLACK, NULL, "COLOR_FG_BLACK"); + flushLog(NULL); + printToLog(COLOR_FG_MAROON, NULL, "COLOR_FG_MAROON"); + flushLog(NULL); + printToLog(COLOR_FG_GREEN, NULL, "COLOR_FG_GREEN"); + flushLog(NULL); + printToLog(COLOR_FG_OLIVE, NULL, "COLOR_FG_OLIVE"); + flushLog(NULL); + printToLog(COLOR_FG_NAVY, NULL, "COLOR_FG_NAVY"); + flushLog(NULL); + printToLog(COLOR_FG_PURPLE, NULL, "COLOR_FG_PURPLE"); + flushLog(NULL); + printToLog(COLOR_FG_TEAL, NULL, "COLOR_FG_TEAL"); + flushLog(NULL); + printToLog(COLOR_FG_GRAY, NULL, "COLOR_FG_GRAY"); + flushLog(NULL); + printToLog(COLOR_FG_DKGRAY, NULL, "COLOR_FG_DKGRAY"); + flushLog(NULL); + printToLog(COLOR_FG_RED, NULL, "COLOR_FG_RED"); + flushLog(NULL); + printToLog(COLOR_FG_YELLOW, NULL, "COLOR_FG_YELLOW"); + flushLog(NULL); + printToLog(COLOR_FG_BLUE, NULL, "COLOR_FG_BLUE"); + flushLog(NULL); + printToLog(COLOR_FG_FUCHSIA, NULL, "COLOR_FG_FUCHSIA"); + flushLog(NULL); + printToLog(COLOR_FG_CYAN, NULL, "COLOR_FG_CYAN"); + flushLog(NULL); + + printToLog(COLOR_FG_WHITE, NULL, "COLOR_FG_WHITE"); + flushLog(NULL); + printToLog(COLOR_BG_BLACK, NULL, "COLOR_BG_BLACK"); + flushLog(NULL); + printToLog(COLOR_BG_MAROON, NULL, "COLOR_BG_MAROON"); + flushLog(NULL); + printToLog(COLOR_BG_GREEN, NULL, "COLOR_BG_GREEN"); + flushLog(NULL); + printToLog(COLOR_BG_OLIVE, NULL, "COLOR_BG_OLIVE"); + flushLog(NULL); + printToLog(COLOR_BG_NAVY, NULL, "COLOR_BG_NAVY"); + flushLog(NULL); + printToLog(COLOR_BG_PURPLE, NULL, "COLOR_BG_PURPLE"); + flushLog(NULL); + printToLog(COLOR_BG_TEAL, NULL, "COLOR_BG_TEAL"); + flushLog(NULL); + printToLog(COLOR_BG_GRAY, NULL, "COLOR_BG_GRAY"); + flushLog(NULL); + printToLog(COLOR_BG_DKGRAY, NULL, "COLOR_BG_DKGRAY"); + flushLog(NULL); + printToLog(COLOR_BG_RED, NULL, "COLOR_BG_RED"); + flushLog(NULL); + printToLog(COLOR_BG_YELLOW, NULL, "COLOR_BG_YELLOW"); + flushLog(NULL); + printToLog(COLOR_BG_BLUE, NULL, "COLOR_BG_BLUE"); + flushLog(NULL); + printToLog(COLOR_BG_FUCHSIA, NULL, "COLOR_BG_FUCHSIA"); + flushLog(NULL); + printToLog(COLOR_BG_CYAN, NULL, "COLOR_BG_CYAN"); + flushLog(NULL); + printToLog(COLOR_BG_WHITE, NULL, "COLOR_BG_WHITE"); + flushLog(NULL); +} diff --git a/source/logging/LogPrinter.h b/source/logging/LogPrinter.h new file mode 100644 index 0000000..bed457c --- /dev/null +++ b/source/logging/LogPrinter.h @@ -0,0 +1,133 @@ +// +// LogPrinter.h - MrsWatson +// Created by Nik Reiman on 10/23/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_LogPrinter_h +#define MrsWatson_LogPrinter_h + +#include <stdio.h> + +#if WINDOWS +#include <Windows.h> +#endif + +#if UNIX +typedef const char *LogColor; + +#define COLOR_RESET "\x1b[0m" +#define COLOR_NONE NULL + +#define COLOR_FG_BLACK "\x1b[30m" +#define COLOR_FG_MAROON "\x1b[31m" +#define COLOR_FG_GREEN "\x1b[92m" +#define COLOR_FG_OLIVE "\x1b[32m" +#define COLOR_FG_NAVY "\x1b[34m" +#define COLOR_FG_PURPLE "\x1b[35m" +#define COLOR_FG_TEAL "\x1b[36m" +#define COLOR_FG_GRAY "\x1b[37m" +#define COLOR_FG_DKGRAY "\x1b[90m" +#define COLOR_FG_RED "\x1b[31m" +#define COLOR_FG_YELLOW "\x1b[33m" +#define COLOR_FG_BLUE "\x1b[94m" +#define COLOR_FG_FUCHSIA "\x1b[95m" +#define COLOR_FG_CYAN "\x1b[96m" +#define COLOR_FG_WHITE "\x1b[37m" + +#define COLOR_BG_BLACK "\x1b[40m\x1b[37m" +#define COLOR_BG_MAROON "\x1b[41m\x1b[37m" +#define COLOR_BG_GREEN "\x1b[102m\x1b[30m" +#define COLOR_BG_OLIVE "\x1b[42m\x1b[30m" +#define COLOR_BG_NAVY "\x1b[44m\x1b[37m" +#define COLOR_BG_PURPLE "\x1b[45m\x1b[37m" +#define COLOR_BG_TEAL "\x1b[46m\x1b[30m" +#define COLOR_BG_GRAY "\x1b[47m\x1b[30m" +#define COLOR_BG_DKGRAY "\x1b[100m\x1b[37m" +#define COLOR_BG_RED "\x1b[101m\x1b[37m" +#define COLOR_BG_YELLOW "\x1b[43m\x1b[30m" +#define COLOR_BG_BLUE "\x1b[104m\x1b[37m" +#define COLOR_BG_FUCHSIA "\x1b[105m\x1b[30m" +#define COLOR_BG_CYAN "\x1b[46m\x1b[30m" +#define COLOR_BG_WHITE "\x1b[47m\x1b[30m" + +#elif WINDOWS +typedef WORD LogColor; + +#define COLOR_RESET 0 +#define COLOR_NONE 0 + +#define COLOR_FG_BLACK 0 +#define COLOR_FG_MAROON 0x05 +#define COLOR_FG_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY +#define COLOR_FG_OLIVE FOREGROUND_GREEN +#define COLOR_FG_NAVY FOREGROUND_BLUE +#define COLOR_FG_PURPLE 0x0c +#define COLOR_FG_TEAL 0x03 +#define COLOR_FG_GRAY 0x07 +#define COLOR_FG_DKGRAY 0x08 +#define COLOR_FG_RED FOREGROUND_RED | FOREGROUND_INTENSITY +#define COLOR_FG_YELLOW 0x0e +#define COLOR_FG_BLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define COLOR_FG_FUCHSIA 0x0b +#define COLOR_FG_CYAN 0x03 | FOREGROUND_INTENSITY +#define COLOR_FG_WHITE 0x0f + +#define COLOR_BG_BLACK 0 +#define COLOR_BG_MAROON BACKGROUND_RED +#define COLOR_BG_GREEN BACKGROUND_GREEN | BACKGROUND_INTENSITY +#define COLOR_BG_OLIVE BACKGROUND_GREEN +#define COLOR_BG_NAVY BACKGROUND_BLUE +#define COLOR_BG_PURPLE 0xc0 +#define COLOR_BG_TEAL 0x30 +#define COLOR_BG_GRAY 0x70 +#define COLOR_BG_DKGRAY 0x80 +#define COLOR_BG_RED BACKGROUND_RED | BACKGROUND_INTENSITY +#define COLOR_BG_YELLOW 0xe0 +#define COLOR_BG_BLUE BACKGROUND_BLUE | BACKGROUND_INTENSITY +#define COLOR_BG_FUCHSIA 0xb0 +#define COLOR_BG_CYAN 0x30 +#define COLOR_BG_WHITE 0xf0 +#endif + +/** + * Print a message with colors to the log + * @param color Color to use + * @param logFile Log file to write to + * @param message Message contents + */ +void printToLog(const LogColor color, FILE *logFile, const char *message); + +/** + * Flush the log file to disk + * @param logFile Log file + */ +void flushLog(FILE *logFile); + +/** + * Print a test pattern of all known color combinations. + */ +void printTestPattern(void); + +#endif |
