Ver código fonte

Added the environment variable SDL_LOGGING to control default log output

Sam Lantinga 1 ano atrás
pai
commit
12bdb2b4d0

+ 1 - 0
VisualC/tests/testautomation/testautomation.vcxproj

@@ -206,6 +206,7 @@
     <ClCompile Include="..\..\..\test\testautomation_intrinsics.c" />
     <ClCompile Include="..\..\..\test\testautomation_joystick.c" />
     <ClCompile Include="..\..\..\test\testautomation_keyboard.c" />
+    <ClCompile Include="..\..\..\test\testautomation_log.c" />
     <ClCompile Include="..\..\..\test\testautomation_main.c" />
     <ClCompile Include="..\..\..\test\testautomation_math.c" />
     <ClCompile Include="..\..\..\test\testautomation_mouse.c" />

+ 1 - 0
WhatsNew.txt

@@ -32,3 +32,4 @@ General:
 * Added SDL_PlayAudioDevice() to start audio playback
 * Added SDL_ConvertAudioSamples() to convert audio samples from one format to another
 * Added the hint SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY to control re-creation of Android SDL activity.
+* Added the environment variable SDL_LOGGING to control default log output

+ 16 - 0
include/SDL3/SDL_hints.h

@@ -1282,6 +1282,22 @@ extern "C" {
   */
 #define SDL_HINT_LINUX_JOYSTICK_DEADZONES "SDL_LINUX_JOYSTICK_DEADZONES"
 
+/**
+ *  A variable controlling the default SDL log levels.
+ *
+ *  This variable is a comma separated set of category=level tokens that define the default logging levels for SDL applications.
+ *
+ *  The category can be a numeric category, one of "app", "error", "assert", "system", "audio", "video", "render", "input", "test", or `*` for any unspecified category.
+ *
+ *  The level can be a numeric level, one of "verbose", "debug", "info", "warn", "error", "critical", or "quiet" to disable that category.
+ *
+ *  You can omit the category if you want to set the logging level for all categories.
+ *
+ *  If this hint isn't set, the default log levels are equivalent to:
+ *  "app=info,assert=warn,test=verbose,*=error"
+ */
+#define SDL_HINT_LOGGING   "SDL_LOGGING"
+
 /**
 *  When set don't force the SDL app to become a foreground process
 *

+ 125 - 14
src/SDL_log.c

@@ -41,6 +41,8 @@
 /* The size of the stack buffer to use for rendering log messages. */
 #define SDL_MAX_LOG_MESSAGE_STACK 256
 
+#define DEFAULT_CATEGORY -1
+
 typedef struct SDL_LogLevel
 {
     int category;
@@ -63,7 +65,8 @@ static SDL_Mutex *log_function_mutex = NULL;
 #pragma GCC diagnostic ignored "-Wunused-variable"
 #endif
 
-static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
+/* If this list changes, update the documentation for SDL_HINT_LOGGING */
+static const char *SDL_priority_prefixes[] = {
     NULL,
     "VERBOSE",
     "DEBUG",
@@ -72,12 +75,9 @@ static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
     "ERROR",
     "CRITICAL"
 };
+SDL_COMPILE_TIME_ASSERT(priority_prefixes, SDL_arraysize(SDL_priority_prefixes) == SDL_NUM_LOG_PRIORITIES);
 
-#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
-#pragma GCC diagnostic pop
-#endif
-
-#ifdef SDL_PLATFORM_ANDROID
+/* If this list changes, update the documentation for SDL_HINT_LOGGING */
 static const char *SDL_category_prefixes[] = {
     "APP",
     "ERROR",
@@ -89,9 +89,13 @@ static const char *SDL_category_prefixes[] = {
     "INPUT",
     "TEST"
 };
+SDL_COMPILE_TIME_ASSERT(category_prefixes, SDL_arraysize(SDL_category_prefixes) == SDL_LOG_CATEGORY_RESERVED1);
 
-SDL_COMPILE_TIME_ASSERT(category_prefixes_enum, SDL_TABLESIZE(SDL_category_prefixes) == SDL_LOG_CATEGORY_RESERVED1);
+#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
+#pragma GCC diagnostic pop
+#endif
 
+#ifdef SDL_PLATFORM_ANDROID
 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
     ANDROID_LOG_UNKNOWN,
     ANDROID_LOG_VERBOSE,
@@ -153,18 +157,108 @@ void SDL_LogSetPriority(int category, SDL_LogPriority priority)
     }
 }
 
-SDL_LogPriority SDL_LogGetPriority(int category)
+static SDL_bool SDL_ParseLogCategory(const char *string, size_t length, int *category)
 {
-    SDL_LogLevel *entry;
+    int i;
 
-    for (entry = SDL_loglevels; entry; entry = entry->next) {
-        if (entry->category == category) {
-            return entry->priority;
+    if (SDL_isdigit(*string)) {
+        *category = SDL_atoi(string);
+        return SDL_TRUE;
+    }
+
+    if (*string == '*') {
+        *category = DEFAULT_CATEGORY;
+        return SDL_TRUE;
+    }
+
+    for (i = 0; i < SDL_arraysize(SDL_category_prefixes); ++i) {
+        if (SDL_strncasecmp(string, SDL_category_prefixes[i], length) == 0) {
+            *category = i;
+            return SDL_TRUE;
         }
     }
+    return SDL_FALSE;
+}
 
-    if (SDL_forced_priority) {
-        return SDL_forced_priority_level;
+static SDL_bool SDL_ParseLogPriority(const char *string, size_t length, SDL_LogPriority *priority)
+{
+    int i;
+
+    if (SDL_isdigit(*string)) {
+        i = SDL_atoi(string);
+        if (i == 0) {
+            /* 0 has a special meaning of "disable this category" */
+            *priority = SDL_NUM_LOG_PRIORITIES;
+            return SDL_TRUE;
+        }
+        if (i >= SDL_LOG_PRIORITY_VERBOSE && i < SDL_NUM_LOG_PRIORITIES) {
+            *priority = (SDL_LogPriority)i;
+            return SDL_TRUE;
+        }
+        return SDL_FALSE;
+    }
+
+    if (SDL_strncasecmp(string, "quiet", length) == 0) {
+        *priority = SDL_NUM_LOG_PRIORITIES;
+        return SDL_TRUE;
+    }
+
+    for (i = SDL_LOG_PRIORITY_VERBOSE; i < SDL_NUM_LOG_PRIORITIES; ++i) {
+        if (SDL_strncasecmp(string, SDL_priority_prefixes[i], length) == 0) {
+            *priority = (SDL_LogPriority)i;
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static SDL_bool SDL_ParseLogCategoryPriority(const char *hint, int category, SDL_LogPriority *priority)
+{
+    const char *name, *next;
+    int current_category;
+
+    if (category == DEFAULT_CATEGORY && SDL_strchr(hint, '=') == NULL) {
+        return SDL_ParseLogPriority(hint, SDL_strlen(hint), priority);
+    }
+
+    for (name = hint; name; name = next) {
+        const char *sep = SDL_strchr(name, '=');
+        if (!sep) {
+            break;
+        }
+        next = SDL_strchr(sep, ',');
+        if (next) {
+            ++next;
+        }
+
+        if (SDL_ParseLogCategory(name, (sep - name), &current_category)) {
+            if (current_category == category) {
+                const char *value = sep + 1;
+                size_t len;
+                if (next) {
+                    len = (next - value - 1);
+                } else {
+                    len = SDL_strlen(value);
+                }
+                return SDL_ParseLogPriority(value, len, priority);
+            }
+        }
+    }
+    return SDL_FALSE;
+}
+
+static SDL_LogPriority SDL_GetDefaultLogPriority(int category)
+{
+    const char *hint = SDL_GetHint(SDL_HINT_LOGGING);
+    if (hint) {
+        SDL_LogPriority priority;
+
+        if (SDL_ParseLogCategoryPriority(hint, category, &priority)) {
+            return priority;
+        }
+        if (SDL_ParseLogCategoryPriority(hint, DEFAULT_CATEGORY, &priority)) {
+            return priority;
+        }
     }
 
     switch (category) {
@@ -179,6 +273,23 @@ SDL_LogPriority SDL_LogGetPriority(int category)
     }
 }
 
+SDL_LogPriority SDL_LogGetPriority(int category)
+{
+    SDL_LogLevel *entry;
+
+    for (entry = SDL_loglevels; entry; entry = entry->next) {
+        if (entry->category == category) {
+            return entry->priority;
+        }
+    }
+
+    if (SDL_forced_priority) {
+        return SDL_forced_priority_level;
+    }
+
+    return SDL_GetDefaultLogPriority(category);
+}
+
 void SDL_LogResetPriorities(void)
 {
     SDL_LogLevel *entry;

+ 1 - 0
test/testautomation.c

@@ -30,6 +30,7 @@ static SDLTest_TestSuiteReference *testSuites[] = {
     &intrinsicsTestSuite,
     &joystickTestSuite,
     &keyboardTestSuite,
+    &logTestSuite,
     &mainTestSuite,
     &mathTestSuite,
     &mouseTestSuite,

+ 209 - 0
test/testautomation_log.c

@@ -0,0 +1,209 @@
+/**
+ * Log test suite
+ */
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_test.h>
+#include "testautomation_suites.h"
+
+static SDL_LogOutputFunction original_function;
+static void *original_userdata;
+
+static void TestLogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message)
+{
+    int *message_count = (int *)userdata;
+    ++(*message_count);
+}
+
+static void EnableTestLog(int *message_count)
+{
+    *message_count = 0;
+    SDL_LogGetOutputFunction(&original_function, &original_userdata);
+    SDL_LogSetOutputFunction(TestLogOutput, message_count);
+}
+
+static void DisableTestLog()
+{
+    SDL_LogSetOutputFunction(original_function, original_userdata);
+}
+
+/* Fixture */
+
+/* Test case functions */
+
+/**
+ * Check SDL_HINT_LOGGING functionality
+ */
+static int log_testHint(void *arg)
+{
+    int count;
+
+    SDL_SetHint(SDL_HINT_LOGGING, NULL);
+    SDLTest_AssertPass("SDL_SetHint(SDL_HINT_LOGGING, NULL)");
+    {
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+    }
+
+    SDL_SetHint(SDL_HINT_LOGGING, "debug");
+    SDLTest_AssertPass("SDL_SetHint(SDL_HINT_LOGGING, \"debug\")");
+    {
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_VERBOSE, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+    }
+
+    SDL_SetHint(SDL_HINT_LOGGING, "system=debug");
+    SDLTest_AssertPass("SDL_SetHint(SDL_HINT_LOGGING, \"system=debug\")");
+    {
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+    }
+
+    SDL_SetHint(SDL_HINT_LOGGING, "app=warn,system=debug,assert=quiet,*=info");
+    SDLTest_AssertPass("SDL_SetHint(SDL_HINT_LOGGING, \"app=warn,system=debug,assert=quiet,*=info\")");
+    {
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_CRITICAL, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_CRITICAL, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_INFO, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_INFO, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+    }
+
+    SDL_SetHint(SDL_HINT_LOGGING, "0=4,3=2,2=0,*=3");
+    SDLTest_AssertPass("SDL_SetHint(SDL_HINT_LOGGING, \"0=4,3=2,2=0,*=3\")");
+    {
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_PRIORITY_VERBOSE, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_CRITICAL, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_CRITICAL, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_INFO, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_INFO, \"test\")");
+        SDLTest_AssertCheck(count == 1, "Check result value, expected: 1, got: %d", count);
+
+        EnableTestLog(&count);
+        SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_DEBUG, "test");
+        DisableTestLog();
+        SDLTest_AssertPass("SDL_LogMessage(SDL_LOG_CATEGORY_CUSTOM, SDL_LOG_PRIORITY_DEBUG, \"test\")");
+        SDLTest_AssertCheck(count == 0, "Check result value, expected: 0, got: %d", count);
+
+    }
+
+    return TEST_COMPLETED;
+}
+
+/* ================= Test References ================== */
+
+/* Log test cases */
+static const SDLTest_TestCaseReference logTestHint = {
+    (SDLTest_TestCaseFp)log_testHint, "log_testHint", "Check SDL_HINT_LOGGING functionality", TEST_ENABLED
+};
+
+/* Sequence of Log test cases */
+static const SDLTest_TestCaseReference *logTests[] = {
+    &logTestHint, NULL
+};
+
+/* Timer test suite (global) */
+SDLTest_TestSuiteReference logTestSuite = {
+    "Log",
+    NULL,
+    logTests,
+    NULL
+};

+ 1 - 0
test/testautomation_suites.h

@@ -17,6 +17,7 @@ extern SDLTest_TestSuiteReference hintsTestSuite;
 extern SDLTest_TestSuiteReference intrinsicsTestSuite;
 extern SDLTest_TestSuiteReference joystickTestSuite;
 extern SDLTest_TestSuiteReference keyboardTestSuite;
+extern SDLTest_TestSuiteReference logTestSuite;
 extern SDLTest_TestSuiteReference mainTestSuite;
 extern SDLTest_TestSuiteReference mathTestSuite;
 extern SDLTest_TestSuiteReference mouseTestSuite;