20 #include <winpr/config.h>
27 #include <winpr/crt.h>
28 #include <winpr/assert.h>
29 #include <winpr/print.h>
30 #include <winpr/debug.h>
31 #include <winpr/environment.h>
32 #include <winpr/wlog.h>
35 #include <android/log.h>
48 #define WLOG_FILTER_NOT_FILTERED (-1)
49 #define WLOG_FILTER_NOT_INITIALIZED (-2)
60 LPCSTR WLOG_LEVELS[7] = {
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"OFF" };
62 static INIT_ONCE g_WLogInitialized = INIT_ONCE_STATIC_INIT;
63 static DWORD g_FilterCount = 0;
64 static wLogFilter* g_Filters = NULL;
65 static wLog* g_RootLog = NULL;
67 static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
68 static void WLog_Free(wLog* log);
69 static LONG WLog_GetFilterLogLevel(wLog* log);
70 static int WLog_ParseLogLevel(LPCSTR level);
71 static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
72 static BOOL WLog_ParseFilters(wLog* root);
73 static wLog* WLog_Get_int(wLog* root, LPCSTR name);
76 static void WLog_Uninit_(
void) __attribute__((destructor));
79 static void WLog_Uninit_(
void)
82 wLog* root = g_RootLog;
87 for (DWORD index = 0; index < root->ChildrenCount; index++)
89 child = root->Children[index];
97 static void WLog_Lock(wLog* log)
100 EnterCriticalSection(&log->lock);
103 static void WLog_Unlock(wLog* log)
106 LeaveCriticalSection(&log->lock);
109 static BOOL CALLBACK WLog_InitializeRoot(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
113 DWORD logAppenderType = 0;
114 LPCSTR appender =
"WLOG_APPENDER";
116 WINPR_UNUSED(InitOnce);
117 WINPR_UNUSED(Parameter);
118 WINPR_UNUSED(Context);
120 if (!(g_RootLog = WLog_New(
"", NULL)))
123 g_RootLog->IsRoot = TRUE;
124 logAppenderType = WLOG_APPENDER_CONSOLE;
125 nSize = GetEnvironmentVariableA(appender, NULL, 0);
129 env = (LPSTR)malloc(nSize);
134 if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
136 (void)fprintf(stderr,
"%s environment variable modified in my back", appender);
141 if (_stricmp(env,
"CONSOLE") == 0)
142 logAppenderType = WLOG_APPENDER_CONSOLE;
143 else if (_stricmp(env,
"FILE") == 0)
144 logAppenderType = WLOG_APPENDER_FILE;
145 else if (_stricmp(env,
"BINARY") == 0)
146 logAppenderType = WLOG_APPENDER_BINARY;
148 #ifdef WINPR_HAVE_SYSLOG_H
149 else if (_stricmp(env,
"SYSLOG") == 0)
150 logAppenderType = WLOG_APPENDER_SYSLOG;
153 #ifdef WINPR_HAVE_JOURNALD_H
154 else if (_stricmp(env,
"JOURNALD") == 0)
155 logAppenderType = WLOG_APPENDER_JOURNALD;
158 else if (_stricmp(env,
"UDP") == 0)
159 logAppenderType = WLOG_APPENDER_UDP;
164 if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
167 if (!WLog_ParseFilters(g_RootLog))
170 (void)atexit(WLog_Uninit_);
178 static BOOL log_recursion(LPCSTR file, LPCSTR fkt,
size_t line)
183 void* bt = winpr_backtrace(20);
185 LPCSTR tag = WINPR_TAG(
"utils.wlog");
191 msg = winpr_backtrace_symbols(bt, &used);
198 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Recursion detected!!!") < 0)
201 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Check %s [%s:%zu]", fkt, file, line) < 0)
204 for (
size_t i = 0; i < used; i++)
205 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"%zu: %s", i, msg[i]) < 0)
210 if (fprintf(stderr,
"[%s]: Recursion detected!\n", fkt) < 0)
213 if (fprintf(stderr,
"[%s]: Check %s:%" PRIuz
"\n", fkt, file, line) < 0)
216 for (
size_t i = 0; i < used; i++)
217 if (fprintf(stderr,
"%s: %" PRIuz
": %s\n", fkt, i, msg[i]) < 0)
224 winpr_backtrace_free(bt);
228 static BOOL WLog_Write(wLog* log,
wLogMessage* message)
231 wLogAppender* appender = NULL;
232 appender = WLog_GetLogAppender(log);
237 if (!appender->active)
238 if (!WLog_OpenAppender(log))
241 EnterCriticalSection(&appender->lock);
243 if (appender->WriteMessage)
245 if (appender->recursive)
246 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
249 appender->recursive = TRUE;
250 status = appender->WriteMessage(log, appender, message);
251 appender->recursive = FALSE;
255 LeaveCriticalSection(&appender->lock);
259 static BOOL WLog_WriteData(wLog* log,
wLogMessage* message)
262 wLogAppender* appender = NULL;
263 appender = WLog_GetLogAppender(log);
268 if (!appender->active)
269 if (!WLog_OpenAppender(log))
272 if (!appender->WriteDataMessage)
275 EnterCriticalSection(&appender->lock);
277 if (appender->recursive)
278 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
281 appender->recursive = TRUE;
282 status = appender->WriteDataMessage(log, appender, message);
283 appender->recursive = FALSE;
286 LeaveCriticalSection(&appender->lock);
290 static BOOL WLog_WriteImage(wLog* log,
wLogMessage* message)
293 wLogAppender* appender = NULL;
294 appender = WLog_GetLogAppender(log);
299 if (!appender->active)
300 if (!WLog_OpenAppender(log))
303 if (!appender->WriteImageMessage)
306 EnterCriticalSection(&appender->lock);
308 if (appender->recursive)
309 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
312 appender->recursive = TRUE;
313 status = appender->WriteImageMessage(log, appender, message);
314 appender->recursive = FALSE;
317 LeaveCriticalSection(&appender->lock);
321 static BOOL WLog_WritePacket(wLog* log,
wLogMessage* message)
324 wLogAppender* appender = NULL;
325 appender = WLog_GetLogAppender(log);
330 if (!appender->active)
331 if (!WLog_OpenAppender(log))
334 if (!appender->WritePacketMessage)
337 EnterCriticalSection(&appender->lock);
339 if (appender->recursive)
340 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
343 appender->recursive = TRUE;
344 status = appender->WritePacketMessage(log, appender, message);
345 appender->recursive = FALSE;
348 LeaveCriticalSection(&appender->lock);
352 BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
353 const char*
function, va_list args)
358 message.Level = level;
359 message.LineNumber = line;
360 message.FileName = file;
361 message.FunctionName =
function;
365 case WLOG_MESSAGE_TEXT:
366 message.FormatString = va_arg(args,
const char*);
368 if (!strchr(message.FormatString,
'%'))
370 message.TextString = message.FormatString;
371 status = WLog_Write(log, &message);
375 char formattedLogMessage[WLOG_MAX_STRING_SIZE] = { 0 };
377 WINPR_PRAGMA_DIAG_PUSH
378 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
379 if (vsnprintf(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message.FormatString,
382 WINPR_PRAGMA_DIAG_POP
384 message.TextString = formattedLogMessage;
385 status = WLog_Write(log, &message);
390 case WLOG_MESSAGE_DATA:
391 message.Data = va_arg(args,
void*);
392 message.Length = va_arg(args,
size_t);
393 status = WLog_WriteData(log, &message);
396 case WLOG_MESSAGE_IMAGE:
397 message.ImageData = va_arg(args,
void*);
398 message.ImageWidth = va_arg(args,
size_t);
399 message.ImageHeight = va_arg(args,
size_t);
400 message.ImageBpp = va_arg(args,
size_t);
401 status = WLog_WriteImage(log, &message);
404 case WLOG_MESSAGE_PACKET:
405 message.PacketData = va_arg(args,
void*);
406 message.PacketLength = va_arg(args,
size_t);
407 message.PacketFlags = va_arg(args,
unsigned);
408 status = WLog_WritePacket(log, &message);
418 BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
419 const char*
function, ...)
423 va_start(args,
function);
424 status = WLog_PrintMessageVA(log, type, level, line, file,
function, args);
429 DWORD WLog_GetLogLevel(wLog* log)
434 if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
435 log->FilterLevel = WLog_GetFilterLogLevel(log);
437 if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
438 return (DWORD)log->FilterLevel;
439 else if (log->Level == WLOG_LEVEL_INHERIT)
440 log->Level = WLog_GetLogLevel(log->Parent);
445 BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
452 level = WLog_GetLogLevel(_log);
454 if (level == WLOG_OFF)
457 return _log_level >= level;
460 BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
467 lvl = WLog_ParseLogLevel(level);
472 return WLog_SetLogLevel(log, (DWORD)lvl);
475 static BOOL WLog_reset_log_filters(wLog* log)
480 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
482 for (DWORD x = 0; x < log->ChildrenCount; x++)
484 wLog* child = log->Children[x];
486 if (!WLog_reset_log_filters(child))
493 static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
496 LPCSTR filterStr = NULL;
504 while ((cpp = strchr(cpp,
',')) != NULL)
510 DWORD pos = g_FilterCount;
511 DWORD size = g_FilterCount + count;
512 wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size *
sizeof(wLogFilter));
518 LPSTR cp = (LPSTR)_strdup(filter);
535 if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
551 g_FilterCount = size;
553 return WLog_reset_log_filters(root);
556 BOOL WLog_AddStringLogFilters(LPCSTR filter)
559 wLog* root = WLog_GetRoot();
560 return WLog_AddStringLogFilters_int(root, filter);
563 static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
570 log->Level = logLevel;
572 for (DWORD x = 0; x < log->ChildrenCount; x++)
574 wLog* child = log->Children[x];
576 if (!WLog_UpdateInheritLevel(child, logLevel))
584 BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
589 if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
592 log->Level = logLevel;
593 log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE;
595 for (DWORD x = 0; x < log->ChildrenCount; x++)
597 wLog* child = log->Children[x];
599 if (!WLog_UpdateInheritLevel(child, logLevel))
603 return WLog_reset_log_filters(log);
606 int WLog_ParseLogLevel(LPCSTR level)
613 if (_stricmp(level,
"TRACE") == 0)
615 else if (_stricmp(level,
"DEBUG") == 0)
617 else if (_stricmp(level,
"INFO") == 0)
619 else if (_stricmp(level,
"WARN") == 0)
621 else if (_stricmp(level,
"ERROR") == 0)
623 else if (_stricmp(level,
"FATAL") == 0)
625 else if (_stricmp(level,
"OFF") == 0)
631 BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
633 const char* pc = NULL;
650 while ((pc = strchr(pc,
'.')) != NULL)
657 names = _strdup(name);
662 filter->NameCount = count;
663 filter->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
668 filter->NameCount = 0;
672 filter->Names[count] = NULL;
675 filter->Names[count++] = p;
682 filter->Names = NULL;
683 filter->NameCount = 0;
689 iLevel = WLog_ParseLogLevel(q);
695 filter->Names = NULL;
696 filter->NameCount = 0;
700 filter->Level = (DWORD)iLevel;
702 while ((p = strchr(p,
'.')) != NULL)
704 if (count < filter->NameCount)
705 filter->Names[count++] = p + 1;
714 BOOL WLog_ParseFilters(wLog* root)
716 LPCSTR filter =
"WLOG_FILTER";
723 nSize = GetEnvironmentVariableA(filter, NULL, 0);
728 env = (LPSTR)malloc(nSize);
733 if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
734 res = WLog_AddStringLogFilters_int(root, env);
740 LONG WLog_GetFilterLogLevel(wLog* log)
744 if (log->FilterLevel >= 0)
745 return log->FilterLevel;
747 log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
748 for (DWORD i = 0; i < g_FilterCount; i++)
750 const wLogFilter* filter = &g_Filters[i];
751 for (DWORD j = 0; j < filter->NameCount; j++)
753 if (j >= log->NameCount)
756 if (_stricmp(filter->Names[j],
"*") == 0)
759 assert(filter->Level <= INT32_MAX);
760 log->FilterLevel = (LONG)filter->Level;
764 if (_stricmp(filter->Names[j], log->Names[j]) != 0)
767 if (j == (log->NameCount - 1))
769 match = log->NameCount == filter->NameCount;
772 assert(filter->Level <= INT32_MAX);
773 log->FilterLevel = (LONG)filter->Level;
783 return log->FilterLevel;
786 static BOOL WLog_ParseName(wLog* log, LPCSTR name)
788 const char* cp = name;
793 while ((cp = strchr(cp,
'.')) != NULL)
799 names = _strdup(name);
804 log->NameCount = count;
805 log->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
813 log->Names[count] = NULL;
816 log->Names[count++] = p;
818 while ((p = strchr(p,
'.')) != NULL)
820 if (count < log->NameCount)
821 log->Names[count++] = p + 1;
830 wLog* WLog_New(LPCSTR name, wLog* rootLogger)
836 log = (wLog*)calloc(1,
sizeof(wLog));
841 log->Name = _strdup(name);
846 if (!WLog_ParseName(log, name))
849 log->Parent = rootLogger;
850 log->ChildrenCount = 0;
851 log->ChildrenSize = 16;
852 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
854 if (!(log->Children = (wLog**)calloc(log->ChildrenSize,
sizeof(wLog*))))
857 log->Appender = NULL;
861 log->Level = WLOG_LEVEL_INHERIT;
866 LPCSTR level =
"WLOG_LEVEL";
867 log->Level = WLOG_INFO;
868 nSize = GetEnvironmentVariableA(level, NULL, 0);
872 env = (LPSTR)malloc(nSize);
877 if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
879 (void)fprintf(stderr,
"%s environment variable changed in my back !\n", level);
884 iLevel = WLog_ParseLogLevel(env);
889 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
895 iLevel = WLog_GetFilterLogLevel(log);
899 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
903 InitializeCriticalSectionAndSpinCount(&log->lock, 4000);
913 void WLog_Free(wLog* log)
919 WLog_Appender_Free(log, log->Appender);
920 log->Appender = NULL;
927 DeleteCriticalSection(&log->lock);
932 wLog* WLog_GetRoot(
void)
934 if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
940 static BOOL WLog_AddChild(wLog* parent, wLog* child)
946 if (parent->ChildrenCount >= parent->ChildrenSize)
949 parent->ChildrenSize *= 2;
951 if (!parent->ChildrenSize)
953 if (parent->Children)
954 free(parent->Children);
956 parent->Children = NULL;
960 tmp = (wLog**)realloc(parent->Children,
sizeof(wLog*) * parent->ChildrenSize);
964 if (parent->Children)
965 free(parent->Children);
967 parent->Children = NULL;
971 parent->Children = tmp;
975 if (!parent->Children)
978 parent->Children[parent->ChildrenCount++] = child;
979 child->Parent = parent;
988 static wLog* WLog_FindChild(wLog* root, LPCSTR name)
998 for (DWORD index = 0; index < root->ChildrenCount; index++)
1000 child = root->Children[index];
1002 if (strcmp(child->Name, name) == 0)
1011 return (found) ? child : NULL;
1014 static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1018 if (!(log = WLog_FindChild(root, name)))
1023 if (!(log = WLog_New(name, root)))
1026 if (!WLog_AddChild(root, log))
1036 wLog* WLog_Get(LPCSTR name)
1038 wLog* root = WLog_GetRoot();
1039 return WLog_Get_int(root, name);
1042 #if defined(WITH_WINPR_DEPRECATED)
1043 BOOL WLog_Init(
void)
1045 return WLog_GetRoot() != NULL;
1048 BOOL WLog_Uninit(
void)
1050 wLog* root = g_RootLog;
1057 for (DWORD index = 0; index < root->ChildrenCount; index++)
1059 wLog* child = root->Children[index];
1072 BOOL WLog_SetContext(wLog* log,
const char* (*fkt)(
void*),
void* context)
1077 log->context = context;