20#include <winpr/config.h>
28#include <winpr/atexit.h>
29#include <winpr/assert.h>
30#include <winpr/print.h>
31#include <winpr/debug.h>
32#include <winpr/environment.h>
33#include <winpr/wlog.h>
36#include <android/log.h>
42#define WLOG_MAX_STRING_SIZE 16384
51#define WLOG_FILTER_NOT_FILTERED (-1)
52#define WLOG_FILTER_NOT_INITIALIZED (-2)
63LPCSTR WLOG_LEVELS[7] = {
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"OFF" };
65static INIT_ONCE g_WLogInitialized = INIT_ONCE_STATIC_INIT;
66static DWORD g_FilterCount = 0;
67static wLogFilter* g_Filters =
nullptr;
68static wLog* g_RootLog =
nullptr;
70static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
71static void WLog_Free(wLog* log);
72static LONG WLog_GetFilterLogLevel(wLog* log);
73static int WLog_ParseLogLevel(LPCSTR level);
74static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
75static BOOL WLog_ParseFilters(wLog* root);
76static wLog* WLog_Get_int(wLog* root, LPCSTR name);
78static void WLog_Uninit_(
void)
80 wLog* child =
nullptr;
81 wLog* root = g_RootLog;
86 for (DWORD index = 0; index < root->ChildrenCount; index++)
88 child = root->Children[index];
96static void WLog_Lock(wLog* log)
99 EnterCriticalSection(&log->lock);
102static void WLog_Unlock(wLog* log)
105 LeaveCriticalSection(&log->lock);
108static BOOL CALLBACK WLog_InitializeRoot(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
112 DWORD logAppenderType = 0;
113 LPCSTR appender =
"WLOG_APPENDER";
115 WINPR_UNUSED(InitOnce);
116 WINPR_UNUSED(Parameter);
117 WINPR_UNUSED(Context);
119 if (!(g_RootLog = WLog_New(
"",
nullptr)))
122 g_RootLog->IsRoot = TRUE;
123 logAppenderType = WLOG_APPENDER_CONSOLE;
124 nSize = GetEnvironmentVariableA(appender,
nullptr, 0);
128 env = (LPSTR)malloc(nSize);
133 if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
135 (void)fprintf(stderr,
"%s environment variable modified in my back", appender);
140 if (_stricmp(env,
"CONSOLE") == 0)
141 logAppenderType = WLOG_APPENDER_CONSOLE;
142 else if (_stricmp(env,
"FILE") == 0)
143 logAppenderType = WLOG_APPENDER_FILE;
144 else if (_stricmp(env,
"BINARY") == 0)
145 logAppenderType = WLOG_APPENDER_BINARY;
147#ifdef WINPR_HAVE_SYSLOG_H
148 else if (_stricmp(env,
"SYSLOG") == 0)
149 logAppenderType = WLOG_APPENDER_SYSLOG;
152#ifdef WINPR_HAVE_JOURNALD_H
153 else if (_stricmp(env,
"JOURNALD") == 0)
154 logAppenderType = WLOG_APPENDER_JOURNALD;
157 else if (_stricmp(env,
"UDP") == 0)
158 logAppenderType = WLOG_APPENDER_UDP;
163 if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
166 if (!WLog_ParseFilters(g_RootLog))
169 (void)winpr_atexit(WLog_Uninit_);
177static BOOL log_recursion(LPCSTR file, LPCSTR fkt,
size_t line)
180 char** msg =
nullptr;
182 void* bt = winpr_backtrace(20);
184 LPCSTR tag = WINPR_TAG(
"utils.wlog");
190 msg = winpr_backtrace_symbols(bt, &used);
197 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Recursion detected!!!") < 0)
200 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Check %s [%s:%zu]", fkt, file, line) < 0)
203 for (
size_t i = 0; i < used; i++)
204 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"%zu: %s", i, msg[i]) < 0)
209 if (fprintf(stderr,
"[%s]: Recursion detected!\n", fkt) < 0)
212 if (fprintf(stderr,
"[%s]: Check %s:%" PRIuz
"\n", fkt, file, line) < 0)
215 for (
size_t i = 0; i < used; i++)
216 if (fprintf(stderr,
"%s: %" PRIuz
": %s\n", fkt, i, msg[i]) < 0)
223 winpr_backtrace_free(bt);
227static BOOL WLog_Write(wLog* log,
const wLogMessage* message)
230 wLogAppender* appender = WLog_GetLogAppender(log);
235 if (!appender->active)
236 if (!WLog_OpenAppender(log))
239 EnterCriticalSection(&appender->lock);
241 if (appender->WriteMessage)
243 if (appender->recursive)
244 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
247 appender->recursive = TRUE;
248 status = appender->WriteMessage(log, appender, message);
249 appender->recursive = FALSE;
253 LeaveCriticalSection(&appender->lock);
257static BOOL WLog_WriteData(wLog* log,
const wLogMessage* message)
260 wLogAppender* appender = WLog_GetLogAppender(log);
265 if (!appender->active)
266 if (!WLog_OpenAppender(log))
269 if (!appender->WriteDataMessage)
272 EnterCriticalSection(&appender->lock);
274 if (appender->recursive)
275 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
278 appender->recursive = TRUE;
279 status = appender->WriteDataMessage(log, appender, message);
280 appender->recursive = FALSE;
283 LeaveCriticalSection(&appender->lock);
287static BOOL WLog_WriteImage(wLog* log,
wLogMessage* message)
290 wLogAppender* appender =
nullptr;
291 appender = WLog_GetLogAppender(log);
296 if (!appender->active)
297 if (!WLog_OpenAppender(log))
300 if (!appender->WriteImageMessage)
303 EnterCriticalSection(&appender->lock);
305 if (appender->recursive)
306 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
309 appender->recursive = TRUE;
310 status = appender->WriteImageMessage(log, appender, message);
311 appender->recursive = FALSE;
314 LeaveCriticalSection(&appender->lock);
318static BOOL WLog_WritePacket(wLog* log,
wLogMessage* message)
321 wLogAppender* appender =
nullptr;
322 appender = WLog_GetLogAppender(log);
327 if (!appender->active)
328 if (!WLog_OpenAppender(log))
331 if (!appender->WritePacketMessage)
334 EnterCriticalSection(&appender->lock);
336 if (appender->recursive)
337 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
340 appender->recursive = TRUE;
341 status = appender->WritePacketMessage(log, appender, message);
342 appender->recursive = FALSE;
345 LeaveCriticalSection(&appender->lock);
349static BOOL WLog_PrintTextMessageInternal(wLog* log,
const wLogMessage* cmessage, va_list args)
353 char formattedLogMessage[WLOG_MAX_STRING_SIZE] = WINPR_C_ARRAY_INIT;
355 message.TextString = formattedLogMessage;
357 WINPR_PRAGMA_DIAG_PUSH
358 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
359 if (vsnprintf(formattedLogMessage, ARRAYSIZE(formattedLogMessage) - 1, cmessage->FormatString,
362 WINPR_PRAGMA_DIAG_POP
364 return WLog_Write(log, &message);
367BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
368 const char* function, va_list args)
373 message.Level = level;
374 message.LineNumber = line;
375 message.FileName = file;
376 message.FunctionName = function;
380 case WLOG_MESSAGE_TEXT:
381 message.FormatString = va_arg(args,
const char*);
383 status = WLog_PrintTextMessageInternal(log, &message, args);
386 case WLOG_MESSAGE_DATA:
387 message.Data = va_arg(args,
void*);
388 message.Length = va_arg(args,
size_t);
389 status = WLog_WriteData(log, &message);
392 case WLOG_MESSAGE_IMAGE:
393 message.ImageData = va_arg(args,
void*);
394 message.ImageWidth = va_arg(args,
size_t);
395 message.ImageHeight = va_arg(args,
size_t);
396 message.ImageBpp = va_arg(args,
size_t);
397 status = WLog_WriteImage(log, &message);
400 case WLOG_MESSAGE_PACKET:
401 message.PacketData = va_arg(args,
void*);
402 message.PacketLength = va_arg(args,
size_t);
403 message.PacketFlags = va_arg(args,
unsigned);
404 status = WLog_WritePacket(log, &message);
414BOOL WLog_PrintTextMessageVA(wLog* log, DWORD level,
size_t line,
const char* file,
415 const char* function,
const char* fmt, va_list args)
418 message.Type = WLOG_MESSAGE_TEXT;
419 message.Level = level;
420 message.LineNumber = line;
421 message.FileName = file;
422 message.FunctionName = function;
424 message.FormatString = fmt;
426 return WLog_PrintTextMessageInternal(log, &message, args);
429BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
430 const char* function, ...)
433 va_list args = WINPR_C_ARRAY_INIT;
434 va_start(args, function);
435 status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
440BOOL WLog_PrintTextMessage(wLog* log, DWORD level,
size_t line,
const char* file,
441 const char* function,
const char* fmt, ...)
444 va_list args = WINPR_C_ARRAY_INIT;
446 status = WLog_PrintTextMessageVA(log, level, line, file, function, fmt, args);
451DWORD WLog_GetLogLevel(wLog* log)
456 if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
457 log->FilterLevel = WLog_GetFilterLogLevel(log);
459 if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
460 return (DWORD)log->FilterLevel;
461 else if (log->Level == WLOG_LEVEL_INHERIT)
462 log->Level = WLog_GetLogLevel(log->Parent);
467BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
474 level = WLog_GetLogLevel(_log);
476 if (level == WLOG_OFF)
479 return _log_level >= level;
482BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
489 lvl = WLog_ParseLogLevel(level);
494 return WLog_SetLogLevel(log, (DWORD)lvl);
497static BOOL WLog_reset_log_filters(wLog* log)
502 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
504 for (DWORD x = 0; x < log->ChildrenCount; x++)
506 wLog* child = log->Children[x];
508 if (!WLog_reset_log_filters(child))
515static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
518 LPCSTR filterStr =
nullptr;
526 while ((cpp = strchr(cpp,
',')) !=
nullptr)
532 DWORD pos = g_FilterCount;
533 DWORD size = g_FilterCount + count;
534 wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size *
sizeof(wLogFilter));
540 LPSTR cp = (LPSTR)_strdup(filter);
557 if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
571 }
while (p !=
nullptr);
573 g_FilterCount = size;
575 return WLog_reset_log_filters(root);
578BOOL WLog_AddStringLogFilters(LPCSTR filter)
581 wLog* root = WLog_GetRoot();
582 return WLog_AddStringLogFilters_int(root, filter);
585static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
592 log->Level = logLevel;
594 for (DWORD x = 0; x < log->ChildrenCount; x++)
596 wLog* child = log->Children[x];
598 if (!WLog_UpdateInheritLevel(child, logLevel))
606BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
611 if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
614 log->Level = logLevel;
615 log->inherit = (logLevel == WLOG_LEVEL_INHERIT);
617 for (DWORD x = 0; x < log->ChildrenCount; x++)
619 wLog* child = log->Children[x];
621 if (!WLog_UpdateInheritLevel(child, logLevel))
625 return WLog_reset_log_filters(log);
628int WLog_ParseLogLevel(LPCSTR level)
635 if (_stricmp(level,
"TRACE") == 0)
637 else if (_stricmp(level,
"DEBUG") == 0)
639 else if (_stricmp(level,
"INFO") == 0)
641 else if (_stricmp(level,
"WARN") == 0)
643 else if (_stricmp(level,
"ERROR") == 0)
645 else if (_stricmp(level,
"FATAL") == 0)
647 else if (_stricmp(level,
"OFF") == 0)
653BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
655 const char* pc =
nullptr;
659 LPSTR names =
nullptr;
672 while ((pc = strchr(pc,
'.')) !=
nullptr)
679 names = _strdup(name);
684 filter->NameCount = count;
685 filter->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
690 filter->NameCount = 0;
694 filter->Names[count] =
nullptr;
697 filter->Names[count++] = p;
703 free((
void*)filter->Names);
704 filter->Names =
nullptr;
705 filter->NameCount = 0;
711 iLevel = WLog_ParseLogLevel(q);
716 free((
void*)filter->Names);
717 filter->Names =
nullptr;
718 filter->NameCount = 0;
722 filter->Level = (DWORD)iLevel;
724 while ((p = strchr(p,
'.')) !=
nullptr)
726 if (count < filter->NameCount)
727 filter->Names[count++] = p + 1;
736BOOL WLog_ParseFilters(wLog* root)
738 LPCSTR filter =
"WLOG_FILTER";
745 nSize = GetEnvironmentVariableA(filter,
nullptr, 0);
750 env = (LPSTR)malloc(nSize);
755 if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
756 res = WLog_AddStringLogFilters_int(root, env);
762LONG WLog_GetFilterLogLevel(wLog* log)
766 if (log->FilterLevel >= 0)
767 return log->FilterLevel;
769 log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
770 for (DWORD i = 0; i < g_FilterCount; i++)
772 const wLogFilter* filter = &g_Filters[i];
773 for (DWORD j = 0; j < filter->NameCount; j++)
775 if (j >= log->NameCount)
778 if (_stricmp(filter->Names[j],
"*") == 0)
781 assert(filter->Level <= INT32_MAX);
782 log->FilterLevel = (LONG)filter->Level;
786 if (_stricmp(filter->Names[j], log->Names[j]) != 0)
789 if (j == (log->NameCount - 1))
791 match = log->NameCount == filter->NameCount;
794 assert(filter->Level <= INT32_MAX);
795 log->FilterLevel = (LONG)filter->Level;
805 return log->FilterLevel;
808static BOOL WLog_ParseName(wLog* log, LPCSTR name)
810 const char* cp = name;
813 LPSTR names =
nullptr;
815 while ((cp = strchr(cp,
'.')) !=
nullptr)
821 names = _strdup(name);
826 log->NameCount = count;
827 log->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
835 log->Names[count] =
nullptr;
838 log->Names[count++] = p;
840 while ((p = strchr(p,
'.')) !=
nullptr)
842 if (count < log->NameCount)
843 log->Names[count++] = p + 1;
852wLog* WLog_New(LPCSTR name, wLog* rootLogger)
858 log = (wLog*)calloc(1,
sizeof(wLog));
863 log->Name = _strdup(name);
868 if (!WLog_ParseName(log, name))
871 log->Parent = rootLogger;
872 log->ChildrenCount = 0;
873 log->ChildrenSize = 16;
874 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
876 if (!(log->Children = (wLog**)calloc(log->ChildrenSize,
sizeof(wLog*))))
879 log->Appender =
nullptr;
883 log->Level = WLOG_LEVEL_INHERIT;
888 LPCSTR level =
"WLOG_LEVEL";
889 log->Level = WLOG_INFO;
890 nSize = GetEnvironmentVariableA(level,
nullptr, 0);
894 env = (LPSTR)malloc(nSize);
899 if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
901 (void)fprintf(stderr,
"%s environment variable changed in my back !\n", level);
906 iLevel = WLog_ParseLogLevel(env);
911 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
917 iLevel = WLog_GetFilterLogLevel(log);
921 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
925 if (!InitializeCriticalSectionAndSpinCount(&log->lock, 4000))
934void WLog_Free(wLog* log)
940 WLog_Appender_Free(log, log->Appender);
941 log->Appender =
nullptr;
949 free((
void*)log->Names);
950 free((
void*)log->Children);
951 DeleteCriticalSection(&log->lock);
956wLog* WLog_GetRoot(
void)
958 if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot,
nullptr,
nullptr))
964static BOOL WLog_AddChild(wLog* parent, wLog* child)
970 if (parent->ChildrenCount >= parent->ChildrenSize)
972 wLog** tmp =
nullptr;
973 parent->ChildrenSize *= 2;
975 if (!parent->ChildrenSize)
977 free((
void*)parent->Children);
978 parent->Children =
nullptr;
982 tmp = (wLog**)realloc((
void*)parent->Children,
sizeof(wLog*) * parent->ChildrenSize);
986 free((
void*)parent->Children);
987 parent->Children =
nullptr;
991 parent->Children = tmp;
995 if (!parent->Children)
998 parent->Children[parent->ChildrenCount++] = child;
999 child->Parent = parent;
1001 WLog_Unlock(parent);
1008static wLog* WLog_FindChild(wLog* root, LPCSTR name)
1010 wLog* child =
nullptr;
1018 for (DWORD index = 0; index < root->ChildrenCount; index++)
1020 child = root->Children[index];
1022 if (strcmp(child->Name, name) == 0)
1031 return (found) ? child :
nullptr;
1034static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1036 wLog* log =
nullptr;
1038 if (!(log = WLog_FindChild(root, name)))
1043 if (!(log = WLog_New(name, root)))
1046 if (!WLog_AddChild(root, log))
1056wLog* WLog_Get(LPCSTR name)
1058 wLog* root = WLog_GetRoot();
1059 return WLog_Get_int(root, name);
1062#if defined(WITH_WINPR_DEPRECATED)
1065 return WLog_GetRoot() !=
nullptr;
1068BOOL WLog_Uninit(
void)
1070 wLog* root = g_RootLog;
1077 for (DWORD index = 0; index < root->ChildrenCount; index++)
1079 wLog* child = root->Children[index];
1086 g_RootLog =
nullptr;
1092BOOL WLog_SetContext(wLog* log,
const char* (*fkt)(
void*),
void* context)
1097 log->context = context;