src/SDL_log.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:33:28 -0800
changeset 10737 3406a0f8b041
parent 10686 964e600644f4
child 11284 3db78361e751
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "./SDL_internal.h"
    22 
    23 #if defined(__WIN32__) || defined(__WINRT__)
    24 #include "core/windows/SDL_windows.h"
    25 #endif
    26 
    27 /* Simple log messages in SDL */
    28 
    29 #include "SDL_error.h"
    30 #include "SDL_log.h"
    31 
    32 #if HAVE_STDIO_H
    33 #include <stdio.h>
    34 #endif
    35 
    36 #if defined(__ANDROID__)
    37 #include <android/log.h>
    38 #endif
    39 
    40 #define DEFAULT_PRIORITY                SDL_LOG_PRIORITY_CRITICAL
    41 #define DEFAULT_ASSERT_PRIORITY         SDL_LOG_PRIORITY_WARN
    42 #define DEFAULT_APPLICATION_PRIORITY    SDL_LOG_PRIORITY_INFO
    43 #define DEFAULT_TEST_PRIORITY           SDL_LOG_PRIORITY_VERBOSE
    44 
    45 typedef struct SDL_LogLevel
    46 {
    47     int category;
    48     SDL_LogPriority priority;
    49     struct SDL_LogLevel *next;
    50 } SDL_LogLevel;
    51 
    52 /* The default log output function */
    53 static void SDL_LogOutput(void *userdata,
    54                           int category, SDL_LogPriority priority,
    55                           const char *message);
    56 
    57 static SDL_LogLevel *SDL_loglevels;
    58 static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
    59 static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
    60 static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
    61 static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
    62 static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
    63 static void *SDL_log_userdata = NULL;
    64 
    65 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
    66     NULL,
    67     "VERBOSE",
    68     "DEBUG",
    69     "INFO",
    70     "WARN",
    71     "ERROR",
    72     "CRITICAL"
    73 };
    74 
    75 #ifdef __ANDROID__
    76 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
    77     "APP",
    78     "ERROR",
    79     "SYSTEM",
    80     "AUDIO",
    81     "VIDEO",
    82     "RENDER",
    83     "INPUT"
    84 };
    85 
    86 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
    87     ANDROID_LOG_UNKNOWN,
    88     ANDROID_LOG_VERBOSE,
    89     ANDROID_LOG_DEBUG,
    90     ANDROID_LOG_INFO,
    91     ANDROID_LOG_WARN,
    92     ANDROID_LOG_ERROR,
    93     ANDROID_LOG_FATAL
    94 };
    95 #endif /* __ANDROID__ */
    96 
    97 
    98 void
    99 SDL_LogSetAllPriority(SDL_LogPriority priority)
   100 {
   101     SDL_LogLevel *entry;
   102 
   103     for (entry = SDL_loglevels; entry; entry = entry->next) {
   104         entry->priority = priority;
   105     }
   106     SDL_default_priority = priority;
   107     SDL_assert_priority = priority;
   108     SDL_application_priority = priority;
   109 }
   110 
   111 void
   112 SDL_LogSetPriority(int category, SDL_LogPriority priority)
   113 {
   114     SDL_LogLevel *entry;
   115 
   116     for (entry = SDL_loglevels; entry; entry = entry->next) {
   117         if (entry->category == category) {
   118             entry->priority = priority;
   119             return;
   120         }
   121     }
   122 
   123     /* Create a new entry */
   124     entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
   125     if (entry) {
   126         entry->category = category;
   127         entry->priority = priority;
   128         entry->next = SDL_loglevels;
   129         SDL_loglevels = entry;
   130     }
   131 }
   132 
   133 SDL_LogPriority
   134 SDL_LogGetPriority(int category)
   135 {
   136     SDL_LogLevel *entry;
   137 
   138     for (entry = SDL_loglevels; entry; entry = entry->next) {
   139         if (entry->category == category) {
   140             return entry->priority;
   141         }
   142     }
   143 
   144     if (category == SDL_LOG_CATEGORY_TEST) {
   145         return SDL_test_priority;
   146     } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
   147         return SDL_application_priority;
   148     } else if (category == SDL_LOG_CATEGORY_ASSERT) {
   149         return SDL_assert_priority;
   150     } else {
   151         return SDL_default_priority;
   152     }
   153 }
   154 
   155 void
   156 SDL_LogResetPriorities(void)
   157 {
   158     SDL_LogLevel *entry;
   159 
   160     while (SDL_loglevels) {
   161         entry = SDL_loglevels;
   162         SDL_loglevels = entry->next;
   163         SDL_free(entry);
   164     }
   165 
   166     SDL_default_priority = DEFAULT_PRIORITY;
   167     SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
   168     SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
   169     SDL_test_priority = DEFAULT_TEST_PRIORITY;
   170 }
   171 
   172 void
   173 SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   174 {
   175     va_list ap;
   176 
   177     va_start(ap, fmt);
   178     SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
   179     va_end(ap);
   180 }
   181 
   182 void
   183 SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   184 {
   185     va_list ap;
   186 
   187     va_start(ap, fmt);
   188     SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
   189     va_end(ap);
   190 }
   191 
   192 void
   193 SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   194 {
   195     va_list ap;
   196 
   197     va_start(ap, fmt);
   198     SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
   199     va_end(ap);
   200 }
   201 
   202 void
   203 SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   204 {
   205     va_list ap;
   206 
   207     va_start(ap, fmt);
   208     SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
   209     va_end(ap);
   210 }
   211 
   212 void
   213 SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   214 {
   215     va_list ap;
   216 
   217     va_start(ap, fmt);
   218     SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
   219     va_end(ap);
   220 }
   221 
   222 void
   223 SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   224 {
   225     va_list ap;
   226 
   227     va_start(ap, fmt);
   228     SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
   229     va_end(ap);
   230 }
   231 
   232 void
   233 SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   234 {
   235     va_list ap;
   236 
   237     va_start(ap, fmt);
   238     SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
   239     va_end(ap);
   240 }
   241 
   242 void
   243 SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
   244 {
   245     va_list ap;
   246 
   247     va_start(ap, fmt);
   248     SDL_LogMessageV(category, priority, fmt, ap);
   249     va_end(ap);
   250 }
   251 
   252 #ifdef __ANDROID__
   253 static const char *
   254 GetCategoryPrefix(int category)
   255 {
   256     if (category < SDL_LOG_CATEGORY_RESERVED1) {
   257         return SDL_category_prefixes[category];
   258     }
   259     if (category < SDL_LOG_CATEGORY_CUSTOM) {
   260         return "RESERVED";
   261     }
   262     return "CUSTOM";
   263 }
   264 #endif /* __ANDROID__ */
   265 
   266 void
   267 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
   268 {
   269     char *message;
   270     size_t len;
   271 
   272     /* Nothing to do if we don't have an output function */
   273     if (!SDL_log_function) {
   274         return;
   275     }
   276 
   277     /* Make sure we don't exceed array bounds */
   278     if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
   279         return;
   280     }
   281 
   282     /* See if we want to do anything with this message */
   283     if (priority < SDL_LogGetPriority(category)) {
   284         return;
   285     }
   286 
   287     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   288     if (!message) {
   289         return;
   290     }
   291 
   292     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
   293 
   294     /* Chop off final endline. */
   295     len = SDL_strlen(message);
   296     if ((len > 0) && (message[len-1] == '\n')) {
   297         message[--len] = '\0';
   298         if ((len > 0) && (message[len-1] == '\r')) {  /* catch "\r\n", too. */
   299             message[--len] = '\0';
   300         }
   301     }
   302 
   303     SDL_log_function(SDL_log_userdata, category, priority, message);
   304     SDL_stack_free(message);
   305 }
   306 
   307 #if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   308 /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
   309 static int consoleAttached = 0;
   310 
   311 /* Handle to stderr output of console. */
   312 static HANDLE stderrHandle = NULL;
   313 #endif
   314 
   315 static void
   316 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
   317               const char *message)
   318 {
   319 #if defined(__WIN32__) || defined(__WINRT__)
   320     /* Way too many allocations here, urgh */
   321     /* Note: One can't call SDL_SetError here, since that function itself logs. */
   322     {
   323         char *output;
   324         size_t length;
   325         LPTSTR tstr;
   326 
   327 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   328         BOOL attachResult;
   329         DWORD attachError;
   330         unsigned long charsWritten; 
   331 
   332         /* Maybe attach console and get stderr handle */
   333         if (consoleAttached == 0) {
   334             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   335             if (!attachResult) {
   336                     attachError = GetLastError();
   337                     if (attachError == ERROR_INVALID_HANDLE) {
   338                         /* This is expected when running from Visual Studio */
   339                         /*OutputDebugString(TEXT("Parent process has no console\r\n"));*/
   340                         consoleAttached = -1;
   341                     } else if (attachError == ERROR_GEN_FAILURE) {
   342                          OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
   343                          consoleAttached = -1;
   344                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   345                          /* Already attached */
   346                         consoleAttached = 1;
   347                     } else {
   348                         OutputDebugString(TEXT("Error attaching console\r\n"));
   349                         consoleAttached = -1;
   350                     }
   351                 } else {
   352                     /* Newly attached */
   353                     consoleAttached = 1;
   354                 }
   355 			
   356                 if (consoleAttached == 1) {
   357                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   358                 }
   359         }
   360 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   361 
   362         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
   363         output = SDL_stack_alloc(char, length);
   364         SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
   365         tstr = WIN_UTF8ToString(output);
   366         
   367         /* Output to debugger */
   368         OutputDebugString(tstr);
   369        
   370 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
   371         /* Screen output to stderr, if console was attached. */
   372         if (consoleAttached == 1) {
   373                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   374                     OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
   375                     if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
   376                         OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
   377                     }
   378                 }
   379         }
   380 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
   381 
   382         SDL_free(tstr);
   383         SDL_stack_free(output);
   384     }
   385 #elif defined(__ANDROID__)
   386     {
   387         char tag[32];
   388 
   389         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   390         __android_log_write(SDL_android_priority[priority], tag, message);
   391     }
   392 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
   393     /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
   394     */
   395     extern void SDL_NSLog(const char *text);
   396     {
   397         char *text;
   398 
   399         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   400         if (text) {
   401             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   402             SDL_NSLog(text);
   403             SDL_stack_free(text);
   404             return;
   405         }
   406     }
   407 #elif defined(__PSP__)
   408     {
   409         FILE*        pFile;
   410         pFile = fopen ("SDL_Log.txt", "a");
   411         fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
   412         fclose (pFile);
   413     }
   414 #endif
   415 #if HAVE_STDIO_H
   416     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   417 #if __NACL__
   418     fflush(stderr);
   419 #endif
   420 #endif
   421 }
   422 
   423 void
   424 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   425 {
   426     if (callback) {
   427         *callback = SDL_log_function;
   428     }
   429     if (userdata) {
   430         *userdata = SDL_log_userdata;
   431     }
   432 }
   433 
   434 void
   435 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   436 {
   437     SDL_log_function = callback;
   438     SDL_log_userdata = userdata;
   439 }
   440 
   441 /* vi: set ts=4 sw=4 expandtab: */