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>
43#define WLOG_MAX_STRING_SIZE 16384
52#define WLOG_FILTER_NOT_FILTERED (-1)
53#define WLOG_FILTER_NOT_INITIALIZED (-2)
64LPCSTR WLOG_LEVELS[7] = {
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"OFF" };
66static INIT_ONCE g_WLogInitialized = INIT_ONCE_STATIC_INIT;
67static DWORD g_FilterCount = 0;
68static wLogFilter* g_Filters =
nullptr;
69static wLog* g_RootLog =
nullptr;
70static char* g_GlobalPrefix =
nullptr;
72static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
73static void WLog_Free(wLog* log);
74static LONG WLog_GetFilterLogLevel(wLog* log);
75static int WLog_ParseLogLevel(LPCSTR level);
76static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
77static BOOL WLog_ParseFilters(wLog* root);
78static wLog* WLog_Get_int(wLog* root, LPCSTR name);
80static void WLog_Uninit_(
void)
82 wLog* child =
nullptr;
83 wLog* root = g_RootLog;
88 for (DWORD index = 0; index < root->ChildrenCount; index++)
90 child = root->Children[index];
97 g_GlobalPrefix =
nullptr;
100static void WLog_Lock(wLog* log)
103 EnterCriticalSection(&log->lock);
106static void WLog_Unlock(wLog* log)
109 LeaveCriticalSection(&log->lock);
112static BOOL CALLBACK WLog_InitializeRoot(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
116 DWORD logAppenderType = 0;
117 LPCSTR appender =
"WLOG_APPENDER";
119 WINPR_UNUSED(InitOnce);
120 WINPR_UNUSED(Parameter);
121 WINPR_UNUSED(Context);
123 if (!(g_RootLog = WLog_New(
"",
nullptr)))
126 g_RootLog->IsRoot = TRUE;
127 logAppenderType = WLOG_APPENDER_CONSOLE;
128 nSize = GetEnvironmentVariableA(appender,
nullptr, 0);
132 env = (LPSTR)malloc(nSize);
137 if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
139 (void)fprintf(stderr,
"%s environment variable modified in my back", appender);
144 if (_stricmp(env,
"CONSOLE") == 0)
145 logAppenderType = WLOG_APPENDER_CONSOLE;
146 else if (_stricmp(env,
"FILE") == 0)
147 logAppenderType = WLOG_APPENDER_FILE;
148 else if (_stricmp(env,
"BINARY") == 0)
149 logAppenderType = WLOG_APPENDER_BINARY;
151#ifdef WINPR_HAVE_SYSLOG_H
152 else if (_stricmp(env,
"SYSLOG") == 0)
153 logAppenderType = WLOG_APPENDER_SYSLOG;
156#ifdef WINPR_HAVE_JOURNALD_H
157 else if (_stricmp(env,
"JOURNALD") == 0)
158 logAppenderType = WLOG_APPENDER_JOURNALD;
161 else if (_stricmp(env,
"UDP") == 0)
162 logAppenderType = WLOG_APPENDER_UDP;
167 if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
170 if (!WLog_ParseFilters(g_RootLog))
173 (void)winpr_atexit(WLog_Uninit_);
181static BOOL log_recursion(LPCSTR file, LPCSTR fkt,
size_t line)
184 char** msg =
nullptr;
186 void* bt = winpr_backtrace(20);
188 LPCSTR tag = WINPR_TAG(
"utils.wlog");
194 msg = winpr_backtrace_symbols(bt, &used);
201 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Recursion detected!!!") < 0)
204 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Check %s [%s:%zu]", fkt, file, line) < 0)
207 for (
size_t i = 0; i < used; i++)
208 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"%zu: %s", i, msg[i]) < 0)
213 if (fprintf(stderr,
"[%s]: Recursion detected!\n", fkt) < 0)
216 if (fprintf(stderr,
"[%s]: Check %s:%" PRIuz
"\n", fkt, file, line) < 0)
219 for (
size_t i = 0; i < used; i++)
220 if (fprintf(stderr,
"%s: %" PRIuz
": %s\n", fkt, i, msg[i]) < 0)
227 winpr_backtrace_free(bt);
231static BOOL WLog_Write(wLog* log,
const wLogMessage* message)
234 wLogAppender* appender = WLog_GetLogAppender(log);
239 if (!appender->active)
240 if (!WLog_OpenAppender(log))
243 EnterCriticalSection(&appender->lock);
245 if (appender->WriteMessage)
247 if (appender->recursive)
248 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
251 appender->recursive = TRUE;
252 status = appender->WriteMessage(log, appender, message);
253 appender->recursive = FALSE;
257 LeaveCriticalSection(&appender->lock);
261static BOOL WLog_WriteData(wLog* log,
const wLogMessage* message)
264 wLogAppender* appender = WLog_GetLogAppender(log);
269 if (!appender->active)
270 if (!WLog_OpenAppender(log))
273 if (!appender->WriteDataMessage)
276 EnterCriticalSection(&appender->lock);
278 if (appender->recursive)
279 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
282 appender->recursive = TRUE;
283 status = appender->WriteDataMessage(log, appender, message);
284 appender->recursive = FALSE;
287 LeaveCriticalSection(&appender->lock);
291static BOOL WLog_WriteImage(wLog* log,
wLogMessage* message)
294 wLogAppender* appender =
nullptr;
295 appender = WLog_GetLogAppender(log);
300 if (!appender->active)
301 if (!WLog_OpenAppender(log))
304 if (!appender->WriteImageMessage)
307 EnterCriticalSection(&appender->lock);
309 if (appender->recursive)
310 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
313 appender->recursive = TRUE;
314 status = appender->WriteImageMessage(log, appender, message);
315 appender->recursive = FALSE;
318 LeaveCriticalSection(&appender->lock);
322static BOOL WLog_WritePacket(wLog* log,
wLogMessage* message)
325 wLogAppender* appender =
nullptr;
326 appender = WLog_GetLogAppender(log);
331 if (!appender->active)
332 if (!WLog_OpenAppender(log))
335 if (!appender->WritePacketMessage)
338 EnterCriticalSection(&appender->lock);
340 if (appender->recursive)
341 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
344 appender->recursive = TRUE;
345 status = appender->WritePacketMessage(log, appender, message);
346 appender->recursive = FALSE;
349 LeaveCriticalSection(&appender->lock);
353static BOOL WLog_PrintTextMessageInternal(wLog* log,
const wLogMessage* cmessage, va_list args)
357 char formattedLogMessage[WLOG_MAX_STRING_SIZE] = WINPR_C_ARRAY_INIT;
359 message.TextString = formattedLogMessage;
361 WINPR_PRAGMA_DIAG_PUSH
362 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
363 if (vsnprintf(formattedLogMessage, ARRAYSIZE(formattedLogMessage) - 1, cmessage->FormatString,
366 WINPR_PRAGMA_DIAG_POP
368 return WLog_Write(log, &message);
371BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
372 const char* function, va_list args)
377 message.Level = level;
378 message.LineNumber = line;
379 message.FileName = file;
380 message.FunctionName = function;
384 case WLOG_MESSAGE_TEXT:
385 message.FormatString = va_arg(args,
const char*);
387 status = WLog_PrintTextMessageInternal(log, &message, args);
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);
418BOOL WLog_PrintTextMessageVA(wLog* log, DWORD level,
size_t line,
const char* file,
419 const char* function,
const char* fmt, va_list args)
422 message.Type = WLOG_MESSAGE_TEXT;
423 message.Level = level;
424 message.LineNumber = line;
425 message.FileName = file;
426 message.FunctionName = function;
428 message.FormatString = fmt;
430 return WLog_PrintTextMessageInternal(log, &message, args);
433BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
434 const char* function, ...)
437 va_list args = WINPR_C_ARRAY_INIT;
438 va_start(args, function);
439 status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
444BOOL WLog_PrintTextMessage(wLog* log, DWORD level,
size_t line,
const char* file,
445 const char* function,
const char* fmt, ...)
448 va_list args = WINPR_C_ARRAY_INIT;
450 status = WLog_PrintTextMessageVA(log, level, line, file, function, fmt, args);
455DWORD WLog_GetLogLevel(wLog* log)
460 if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
461 log->FilterLevel = WLog_GetFilterLogLevel(log);
463 if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
464 return (DWORD)log->FilterLevel;
465 else if (log->Level == WLOG_LEVEL_INHERIT)
466 log->Level = WLog_GetLogLevel(log->Parent);
471BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
478 level = WLog_GetLogLevel(_log);
480 if (level == WLOG_OFF)
483 return _log_level >= level;
486BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
493 lvl = WLog_ParseLogLevel(level);
498 return WLog_SetLogLevel(log, (DWORD)lvl);
501static BOOL WLog_reset_log_filters(wLog* log)
506 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
508 for (DWORD x = 0; x < log->ChildrenCount; x++)
510 wLog* child = log->Children[x];
512 if (!WLog_reset_log_filters(child))
519static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
522 LPCSTR filterStr =
nullptr;
530 while ((cpp = strchr(cpp,
',')) !=
nullptr)
536 DWORD pos = g_FilterCount;
537 DWORD size = g_FilterCount + count;
538 wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size *
sizeof(wLogFilter));
544 LPSTR cp = (LPSTR)_strdup(filter);
561 if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
575 }
while (p !=
nullptr);
577 g_FilterCount = size;
579 return WLog_reset_log_filters(root);
582BOOL WLog_AddStringLogFilters(LPCSTR filter)
585 wLog* root = WLog_GetRoot();
586 return WLog_AddStringLogFilters_int(root, filter);
589static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
596 log->Level = logLevel;
598 for (DWORD x = 0; x < log->ChildrenCount; x++)
600 wLog* child = log->Children[x];
602 if (!WLog_UpdateInheritLevel(child, logLevel))
610BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
615 if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
618 log->Level = logLevel;
619 log->inherit = (logLevel == WLOG_LEVEL_INHERIT);
621 for (DWORD x = 0; x < log->ChildrenCount; x++)
623 wLog* child = log->Children[x];
625 if (!WLog_UpdateInheritLevel(child, logLevel))
629 return WLog_reset_log_filters(log);
632int WLog_ParseLogLevel(LPCSTR level)
639 if (_stricmp(level,
"TRACE") == 0)
641 else if (_stricmp(level,
"DEBUG") == 0)
643 else if (_stricmp(level,
"INFO") == 0)
645 else if (_stricmp(level,
"WARN") == 0)
647 else if (_stricmp(level,
"ERROR") == 0)
649 else if (_stricmp(level,
"FATAL") == 0)
651 else if (_stricmp(level,
"OFF") == 0)
657BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
659 const char* pc =
nullptr;
663 LPSTR names =
nullptr;
676 while ((pc = strchr(pc,
'.')) !=
nullptr)
683 names = _strdup(name);
688 filter->NameCount = count;
689 filter->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
694 filter->NameCount = 0;
698 filter->Names[count] =
nullptr;
701 filter->Names[count++] = p;
707 free((
void*)filter->Names);
708 filter->Names =
nullptr;
709 filter->NameCount = 0;
715 iLevel = WLog_ParseLogLevel(q);
720 free((
void*)filter->Names);
721 filter->Names =
nullptr;
722 filter->NameCount = 0;
726 filter->Level = (DWORD)iLevel;
728 while ((p = strchr(p,
'.')) !=
nullptr)
730 if (count < filter->NameCount)
731 filter->Names[count++] = p + 1;
740BOOL WLog_ParseFilters(wLog* root)
742 LPCSTR filter =
"WLOG_FILTER";
749 nSize = GetEnvironmentVariableA(filter,
nullptr, 0);
754 env = (LPSTR)malloc(nSize);
759 if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
760 res = WLog_AddStringLogFilters_int(root, env);
766LONG WLog_GetFilterLogLevel(wLog* log)
770 if (log->FilterLevel >= 0)
771 return log->FilterLevel;
773 log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
774 for (DWORD i = 0; i < g_FilterCount; i++)
776 const wLogFilter* filter = &g_Filters[i];
777 for (DWORD j = 0; j < filter->NameCount; j++)
779 if (j >= log->NameCount)
782 if (_stricmp(filter->Names[j],
"*") == 0)
785 assert(filter->Level <= INT32_MAX);
786 log->FilterLevel = (LONG)filter->Level;
790 if (_stricmp(filter->Names[j], log->Names[j]) != 0)
793 if (j == (log->NameCount - 1))
795 match = log->NameCount == filter->NameCount;
798 assert(filter->Level <= INT32_MAX);
799 log->FilterLevel = (LONG)filter->Level;
809 return log->FilterLevel;
812static BOOL WLog_ParseName(wLog* log, LPCSTR name)
814 const char* cp = name;
817 LPSTR names =
nullptr;
819 while ((cp = strchr(cp,
'.')) !=
nullptr)
825 names = _strdup(name);
830 log->NameCount = count;
831 log->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
839 log->Names[count] =
nullptr;
842 log->Names[count++] = p;
844 while ((p = strchr(p,
'.')) !=
nullptr)
846 if (count < log->NameCount)
847 log->Names[count++] = p + 1;
856wLog* WLog_New(LPCSTR name, wLog* rootLogger)
862 log = (wLog*)calloc(1,
sizeof(wLog));
867 log->Name = _strdup(name);
872 if (!WLog_ParseName(log, name))
875 log->Parent = rootLogger;
876 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
880 log->Level = WLOG_LEVEL_INHERIT;
885 LPCSTR level =
"WLOG_LEVEL";
886 log->Level = WLOG_INFO;
887 nSize = GetEnvironmentVariableA(level,
nullptr, 0);
891 env = (LPSTR)malloc(nSize);
896 if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
898 (void)fprintf(stderr,
"%s environment variable changed in my back !\n", level);
903 iLevel = WLog_ParseLogLevel(env);
908 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
914 iLevel = WLog_GetFilterLogLevel(log);
918 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
922 if (!InitializeCriticalSectionAndSpinCount(&log->lock, 4000))
931void WLog_Free(wLog* log)
937 WLog_Appender_Free(log, log->Appender);
938 log->Appender =
nullptr;
946 free((
void*)log->Names);
947 free((
void*)log->Children);
948 DeleteCriticalSection(&log->lock);
953wLog* WLog_GetRoot(
void)
955 if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot,
nullptr,
nullptr))
961static BOOL WLog_AddChild(wLog* parent, wLog* child)
967 if (parent->ChildrenCount >= parent->ChildrenSize)
969 parent->ChildrenSize = parent->ChildrenCount + 4;
971 if (parent->ChildrenSize == 0)
973 free((
void*)parent->Children);
974 parent->Children =
nullptr;
979 (wLog**)realloc((
void*)parent->Children,
sizeof(wLog*) * parent->ChildrenSize);
983 free((
void*)parent->Children);
984 parent->Children =
nullptr;
988 parent->Children = tmp;
992 if (!parent->Children)
995 parent->Children[parent->ChildrenCount++] = child;
996 child->Parent = parent;
1005static wLog* WLog_FindChild(wLog* root, LPCSTR name)
1007 wLog* child =
nullptr;
1014 for (DWORD index = 0; index < root->ChildrenCount; index++)
1016 wLog* cchild = root->Children[index];
1018 if (strcmp(cchild->Name, name) == 0)
1030static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1032 wLog* log =
nullptr;
1034 if (!(log = WLog_FindChild(root, name)))
1039 if (!(log = WLog_New(name, root)))
1042 if (!WLog_AddChild(root, log))
1052wLog* WLog_Get(LPCSTR name)
1054 wLog* root = WLog_GetRoot();
1055 return WLog_Get_int(root, name);
1058#if defined(WITH_WINPR_DEPRECATED)
1061 return WLog_GetRoot() !=
nullptr;
1064BOOL WLog_Uninit(
void)
1066 wLog* root = g_RootLog;
1073 for (DWORD index = 0; index < root->ChildrenCount; index++)
1075 wLog* child = root->Children[index];
1082 g_RootLog =
nullptr;
1088BOOL WLog_SetContext(wLog* log,
const char* (*fkt)(
void*),
void* context)
1093 log->context = context;
1097BOOL WLog_SetGlobalContext(
const char* globalprefix)
1099 free(g_GlobalPrefix);
1100 g_GlobalPrefix =
nullptr;
1104 g_GlobalPrefix = _strdup(globalprefix);
1105 return g_GlobalPrefix !=
nullptr;
1110const char* WLog_GetGlobalPrefix(
void)
1112 return g_GlobalPrefix;
1115wLog* WLog_Create(LPCSTR name, wLog* root)
1117 wLog* log = WLog_New(name, root);
1120 log->independent = TRUE;
1124void WLog_Discard(wLog* log)
1128 if (!log->independent)
1130 const char tag[] = WINPR_TAG(
"wlog");
1131 WLog_ERR(tag,
"Passed invalid wLog* instance");
1132 winpr_log_backtrace(tag, WLOG_ERROR, 20);