Browse Source

Log framework: allow user-specified log file & multiple output callbacks

LouYihua 7 years ago
parent
commit
ae0757d1c0
11 changed files with 154 additions and 66 deletions
  1. 2 2
      android/app/src/main/cpp/android_jni.cpp
  2. 2 0
      common.h
  3. 2 10
      global.c
  4. 7 0
      main.c
  5. 28 2
      palcfg.c
  6. 9 2
      palcfg.h
  7. 2 2
      unix/unix.cpp
  8. 66 30
      util.c
  9. 32 14
      util.h
  10. 2 2
      win32/win32.cpp
  11. 2 2
      winrt/SDLPal.Common/WinRTUtil.cpp

+ 2 - 2
android/app/src/main/cpp/android_jni.cpp

@@ -185,7 +185,7 @@ UTIL_Platform_Init(
    char* argv[]
 )
 {
-	UTIL_LogSetOutput([](LOGLEVEL level, const char*, const char* str)->void {
+	UTIL_LogAddOutputCallback([](LOGLEVEL level, const char*, const char* str)->void {
 		const static int level_mapping[] = {
 			ANDROID_LOG_VERBOSE,
 			ANDROID_LOG_DEBUG,
@@ -194,7 +194,7 @@ UTIL_Platform_Init(
 			ANDROID_LOG_ERROR
 		};
 		__android_log_print(level_mapping[level], TAG, "%s", str);
-	}, 1024, TRUE);
+	});
 
    gConfig.fLaunchSetting = FALSE;
    return 0;

+ 2 - 0
common.h

@@ -226,4 +226,6 @@ typedef enum tagLOGLEVEL
 	LOGLEVEL_MAX = LOGLEVEL_FATAL,
 } LOGLEVEL;
 
+#define PAL_LOG_MAX_OUTPUTS   4
+
 #endif

+ 2 - 10
global.c

@@ -205,20 +205,12 @@ PAL_FreeGlobals(
    if (!gConfig.fIsWIN95)
       PAL_FreeObjectDesc(gpGlobals->lpObjectDesc);
 
-#if USE_RIX_EXTRA_INIT
-   free(gConfig.pExtraFMRegs);
-   free(gConfig.pExtraFMVals);
-   free(gConfig.dwExtraLength);
-#endif
-   free(gConfig.pszMsgFile);
-   free(gConfig.pszBdfFile);
-   free(gConfig.pszGamePath);
-
    //
    // Clear the instance
    //
    memset(gpGlobals, 0, sizeof(GLOBALVARS));
-   memset(&gConfig, 0, sizeof(CONFIGURATION));
+
+   PAL_FreeConfig();
 }
 
 

+ 7 - 0
main.c

@@ -493,6 +493,13 @@ main(
    //
    if (gConfig.fLaunchSetting)
 	   return 0;
+
+   //
+   // If user requests a file-based log, then add it after the system-specific one.
+   //
+   if (gConfig.pszLogFile)
+	   UTIL_LogAddOutputCallback(UTIL_LogToFile);
+
    //
    // Initialize everything
    //

+ 28 - 2
palcfg.c

@@ -64,6 +64,7 @@ static const ConfigItem gConfigItems[PALCFG_ALL_MAX] = {
 	{ PALCFG_BDFFILE,           PALCFG_STRING,   "BDFFileName",       11, MAKE_VALUE(NULL,     NULL, NULL) },
 	{ PALCFG_MUSIC,             PALCFG_STRING,   "Music",              5, MAKE_VALUE("RIX",    NULL, NULL) },
 	{ PALCFG_OPL,               PALCFG_STRING,   "OPL",                3, MAKE_VALUE("DOSBOX", NULL, NULL) },
+	{ PALCFG_LOGFILE,           PALCFG_STRING,   "LogFile",            7, MAKE_VALUE(NULL,     NULL, NULL) },
 	{ PALCFG_RIXEXTRAINIT,      PALCFG_STRING,   "RIXExtraInit",      12, MAKE_VALUE(NULL,     NULL, NULL) },
 	{ PALCFG_CLIMIDIPLAYER,     PALCFG_STRING,   "CLIMIDIPlayer",     13, MAKE_VALUE(NULL,     NULL, NULL) },
 };
@@ -222,7 +223,28 @@ PAL_LimitConfig(
 	}
 }
 
-VOID
+
+void
+PAL_FreeConfig(
+	void
+)
+{
+
+#if USE_RIX_EXTRA_INIT
+	free(gConfig.pExtraFMRegs);
+	free(gConfig.pExtraFMVals);
+	free(gConfig.dwExtraLength);
+#endif
+	free(gConfig.pszMsgFile);
+	free(gConfig.pszBdfFile);
+	free(gConfig.pszGamePath);
+	free(gConfig.pszSavePath);
+	free(gConfig.pszLogFile);
+
+	memset(&gConfig, 0, sizeof(CONFIGURATION));
+}
+
+void
 PAL_LoadConfig(
 	BOOL fFromFile
 )
@@ -293,6 +315,9 @@ PAL_LoadConfig(
 				case PALCFG_SAVEPATH:
 					gConfig.pszSavePath = ParseStringValue(value.sValue, gConfig.pszSavePath);
 					break;
+				case PALCFG_LOGFILE:
+					gConfig.pszLogFile = ParseStringValue(value.sValue, gConfig.pszLogFile);
+					break;
 				case PALCFG_CD:
 				{
 					if (PAL_HAS_MP3 && SDL_strncasecmp(value.sValue, "MP3", 3) == 0)
@@ -427,7 +452,7 @@ PAL_LoadConfig(
 
 BOOL
 PAL_SaveConfig(
-	VOID
+	void
 )
 {
 	static const char *music_types[] = { "MIDI", "RIX", "MP3", "OGG", "RAW" };
@@ -466,6 +491,7 @@ PAL_SaveConfig(
 		if (gConfig.pszSavePath && *gConfig.pszSavePath && strncmp(gConfig.pszSavePath, PAL_SAVE_PREFIX, strnlen(gConfig.pszSavePath,PATH_MAX)) != 0) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_SAVEPATH), gConfig.pszSavePath); fputs(buf, fp); }
 		if (gConfig.pszMsgFile && *gConfig.pszMsgFile) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_MESSAGEFILE), gConfig.pszMsgFile); fputs(buf, fp); }
 		if (gConfig.pszBdfFile && *gConfig.pszBdfFile) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_BDFFILE), gConfig.pszBdfFile); fputs(buf, fp); }
+		if (gConfig.pszLogFile) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_LOGFILE), gConfig.pszLogFile); fputs(buf, fp); }
 		if (gConfig.pszCLIMIDIPlayerPath && *gConfig.pszCLIMIDIPlayerPath) { sprintf(buf, "%s=%s\n", PAL_ConfigName(PALCFG_CLIMIDIPLAYER), gConfig.pszCLIMIDIPlayerPath); fputs(buf, fp); }
 
 		fclose(fp);

+ 9 - 2
palcfg.h

@@ -78,6 +78,7 @@ typedef enum tagPALCFG_ITEM
 	PALCFG_BDFFILE,
 	PALCFG_MUSIC,
 	PALCFG_OPL,
+	PALCFG_LOGFILE,
 	PALCFG_RIXEXTRAINIT,
 	PALCFG_CLIMIDIPLAYER,
 	/* Strings */
@@ -167,6 +168,7 @@ typedef struct tagCONFIGURATION
 	char            *pszMsgFile;
 	char            *pszBdfFile;
 	char            *pszCLIMIDIPlayerPath;
+	char            *pszLogFile;
 	CODEPAGE         uCodePage;
 	DWORD            dwWordLength;
 	DWORD            dwScreenWidth;
@@ -203,14 +205,19 @@ PAL_C_LINKAGE_BEGIN
 
 extern CONFIGURATION gConfig;
 
-VOID
+void
 PAL_LoadConfig(
 	BOOL fFromFile
 );
 
 BOOL
 PAL_SaveConfig(
-	VOID
+	void
+);
+
+void
+PAL_FreeConfig(
+	void
 );
 
 BOOL

+ 2 - 2
unix/unix.cpp

@@ -253,7 +253,7 @@ UTIL_Platform_Init(
 )
 {
 	openlog("sdlpal", LOG_PERROR | LOG_PID, LOG_USER);
-	UTIL_LogSetOutput([](LOGLEVEL level, const char* str, const char*)->void {
+	UTIL_LogAddOutputCallback([](LOGLEVEL level, const char* str, const char*)->void {
 		const static int priorities[] = {
 			LOG_DEBUG,
 			LOG_DEBUG,
@@ -263,7 +263,7 @@ UTIL_Platform_Init(
 			LOG_EMERG
 		};
 		syslog(priorities[level], "%s", str);
-	}, 1024, TRUE);
+	});
 
 #if !defined(UNIT_TEST) && !defined(PAL_NO_LAUNCH_UI)
    if (gConfig.fLaunchSetting)

+ 66 - 30
util.c

@@ -580,10 +580,14 @@ UTIL_Platform_Quit(
 * Logging utilities
 */
 
-static LOGCALLBACK _log_callback = NULL;
-static char *_global_log_buffer = NULL;
-static int _max_log_length = 0;
-static const int _log_extra_length = 32;
+#ifndef PAL_LOG_BUFFER_SIZE
+# define PAL_LOG_BUFFER_SIZE      4096
+#endif
+
+#define PAL_LOG_BUFFER_EXTRA_SIZE 32
+
+static LOGCALLBACK _log_callbacks[PAL_LOG_MAX_OUTPUTS];
+static char _log_buffer[PAL_LOG_BUFFER_SIZE + PAL_LOG_BUFFER_EXTRA_SIZE];
 
 static const char * const _loglevel_str[] = {
 	"[VERBOSE]",
@@ -594,24 +598,42 @@ static const char * const _loglevel_str[] = {
 	"  [FATAL]",
 };
 
-void
-UTIL_LogSetOutput(
-	LOGCALLBACK    callback,
-	int            maxloglen,
-	BOOL           staticbuffer
+int
+UTIL_LogAddOutputCallback(
+	LOGCALLBACK    callback
 )
 {
-	_log_callback = callback;
-	_max_log_length = maxloglen;
-	if (staticbuffer)
+	if (!callback) return -1;
+
+	// De-duplication
+	for (int i = 0; i < PAL_LOG_MAX_OUTPUTS; i++)
 	{
-		_global_log_buffer = (char *)realloc(_global_log_buffer, maxloglen + _log_extra_length);
+		if (!_log_callbacks[i])
+		{
+			_log_callbacks[i] = callback;
+		}
+		if (_log_callbacks[i] == callback)
+		{
+			return i;
+		}
 	}
-	else
+
+	return -1;
+}
+
+void
+UTIL_LogRemoveOutputCallback(
+	int            id
+)
+{
+	if (id < 0 || id >= PAL_LOG_MAX_OUTPUTS) return;
+
+	while (id < PAL_LOG_MAX_OUTPUTS - 1)
 	{
-		free(_global_log_buffer);
-		_global_log_buffer = NULL;
+		_log_callbacks[id] = _log_callbacks[id + 1];
+		id++;
 	}
+	_log_callbacks[id] = NULL;
 }
 
 void
@@ -621,29 +643,28 @@ UTIL_LogOutput(
 	...
 )
 {
-	va_list      va;
-	time_t       tv = time(NULL);
-	struct tm   *tmval = localtime(&tv);
-	char        *buf = _global_log_buffer;
-	LOGCALLBACK  callback = _log_callback;
-	int          maxloglen = _max_log_length;
-	int          local_alloc = (buf == NULL);
-
-	if (level < gConfig.iLogLevel || !callback || maxloglen <= 0) return;
-	if (local_alloc && NULL == (buf = (char *)malloc(maxloglen + _log_extra_length))) return;
+	va_list    va;
+	time_t     tv = time(NULL);
+	struct tm *tmval = localtime(&tv);
+	int        id;
+
+	if (level < gConfig.iLogLevel || !_log_callbacks[0]) return;
 	if (level > LOGLEVEL_MAX) level = LOGLEVEL_MAX;
 
-	snprintf(buf, _log_extra_length, "%04d-%02d-%02d %02d:%02d:%02d %s: ",
+	snprintf(_log_buffer, PAL_LOG_BUFFER_EXTRA_SIZE,
+		"%04d-%02d-%02d %02d:%02d:%02d %s: ",
 		tmval->tm_year + 1900, tmval->tm_mon, tmval->tm_mday,
 		tmval->tm_hour, tmval->tm_min, tmval->tm_sec,
 		_loglevel_str[level]);
 
 	va_start(va, fmt);
-	vsnprintf(buf + _log_extra_length - 1, maxloglen + 1, fmt, va);
+	vsnprintf(_log_buffer + PAL_LOG_BUFFER_EXTRA_SIZE - 1, PAL_LOG_BUFFER_SIZE + 1, fmt, va);
 	va_end(va);
 
-	callback(level, buf, buf + 31);
-	if (local_alloc) free(buf);
+	for(id = 0; id < PAL_LOG_MAX_OUTPUTS && _log_callbacks[id]; id++)
+	{
+		_log_callbacks[id](level, _log_buffer, _log_buffer + PAL_LOG_BUFFER_EXTRA_SIZE - 1);
+	}
 }
 
 void
@@ -658,3 +679,18 @@ UTIL_LogSetLevel(
 	else
 		gConfig.iLogLevel = minlevel;
 }
+
+void
+UTIL_LogToFile(
+	LOGLEVEL       _,
+	const char    *string,
+	const char    *__
+)
+{
+	FILE *fp = fopen(gConfig.pszLogFile, "a");
+	if (fp)
+	{
+		fputs(string, fp);
+		fclose(fp);
+	}
+}

+ 32 - 14
util.h

@@ -161,29 +161,40 @@ typedef void(*LOGCALLBACK)(LOGLEVEL level, const char *full_log, const char *use
 /*++
   Purpose:
 
-    Initialize the internal log system.
+    Adds a log output callback.
 
   Parameters:
 
-    [IN]  callback     - The callback function to be called at each
-	                     call of UTIL_LogOutput.
-    [IN]  maxloglen    - The max buffer size that holds the output
-	                     correspoind to user-provided format string,
-					     not including the terminal null-character.
-    [IN]  staticbuffer - Whether UTIL_LogOutput should generate the
-	                     output string into a one-time allocated global
-						 buffer, or to a per-call allocated locall buffer.
+    [IN]  callback     - The callback function to be added. Once added,
+	                     it will be called by UTIL_LogOutput.
 
   Return value:
 
-    None.
+    The id of this callback, -1 if all slots are used.
+
+--*/
+int
+UTIL_LogAddOutputCallback(
+	LOGCALLBACK    callback
+);
+
+/*++
+  Purpose:
+
+    Removes a log output callback.
+
+  Parameters:
+
+    [IN]  id           - The id of callback function to be removed.
+
+  Return value:
+
+    None
 
 --*/
 void
-UTIL_LogSetOutput(
-	LOGCALLBACK    callback,
-	int            maxloglen,
-	BOOL           staticbuffer
+UTIL_LogRemoveOutputCallback(
+	int            id
 );
 
 /*++
@@ -230,6 +241,13 @@ UTIL_LogSetLevel(
 	LOGLEVEL       minlevel
 );
 
+void
+UTIL_LogToFile(
+	LOGLEVEL       _,
+	const char    *string,
+	const char    *__
+);
+
 PAL_C_LINKAGE_END
 
 #endif

+ 2 - 2
win32/win32.cpp

@@ -286,9 +286,9 @@ INT_PTR CALLBACK LauncherDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPAR
 extern "C" int UTIL_Platform_Init(int argc, char* argv[])
 {
 	// Defaults log to debug output
-	UTIL_LogSetOutput([](LOGLEVEL, const char* str, const char*)->void {
+	UTIL_LogAddOutputCallback([](LOGLEVEL, const char* str, const char*)->void {
 		OutputDebugStringA(str);
-	}, 1024, TRUE);
+	});
 
 	g_hInstance = GetModuleHandle(nullptr);
 #if !defined(__MINGW32__) || _WIN32_WINNT > _WIN32_WINNT_WS03 // compile time switch; use `make CCFLAGS=-D_WIN32_WINNT=_WIN32_WINNT_VISTA` for vista+ only automatic language detection

+ 2 - 2
winrt/SDLPal.Common/WinRTUtil.cpp

@@ -163,9 +163,9 @@ extern "C"
 INT UTIL_Platform_Init(int argc, char* argv[])
 {
 	// Defaults log to debug output
-	UTIL_LogSetOutput([](LOGLEVEL, const char* str, const char*)->void {
+	UTIL_LogAddOutputCallback([](LOGLEVEL, const char* str, const char*)->void {
 		OutputDebugStringA(str);
-	}, 1024, TRUE);
+	});
 
 	CreateRunningFile();