20#include <winpr/config.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>
41#define WLOG_MAX_STRING_SIZE 16384
50#define WLOG_FILTER_NOT_FILTERED (-1)
51#define WLOG_FILTER_NOT_INITIALIZED (-2)
62LPCSTR WLOG_LEVELS[7] = {
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"OFF" };
64static INIT_ONCE g_WLogInitialized = INIT_ONCE_STATIC_INIT;
65static DWORD g_FilterCount = 0;
66static wLogFilter* g_Filters = NULL;
67static wLog* g_RootLog = NULL;
69static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
70static void WLog_Free(wLog* log);
71static LONG WLog_GetFilterLogLevel(wLog* log);
72static int WLog_ParseLogLevel(LPCSTR level);
73static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
74static BOOL WLog_ParseFilters(wLog* root);
75static wLog* WLog_Get_int(wLog* root, LPCSTR name);
77static void WLog_Uninit_(
void)
80 wLog* root = g_RootLog;
85 for (DWORD index = 0; index < root->ChildrenCount; index++)
87 child = root->Children[index];
95static void WLog_Lock(wLog* log)
98 EnterCriticalSection(&log->lock);
101static void WLog_Unlock(wLog* log)
104 LeaveCriticalSection(&log->lock);
107static BOOL CALLBACK WLog_InitializeRoot(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
111 DWORD logAppenderType = 0;
112 LPCSTR appender =
"WLOG_APPENDER";
114 WINPR_UNUSED(InitOnce);
115 WINPR_UNUSED(Parameter);
116 WINPR_UNUSED(Context);
118 if (!(g_RootLog = WLog_New(
"", NULL)))
121 g_RootLog->IsRoot = TRUE;
122 logAppenderType = WLOG_APPENDER_CONSOLE;
123 nSize = GetEnvironmentVariableA(appender, NULL, 0);
127 env = (LPSTR)malloc(nSize);
132 if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
134 (void)fprintf(stderr,
"%s environment variable modified in my back", appender);
139 if (_stricmp(env,
"CONSOLE") == 0)
140 logAppenderType = WLOG_APPENDER_CONSOLE;
141 else if (_stricmp(env,
"FILE") == 0)
142 logAppenderType = WLOG_APPENDER_FILE;
143 else if (_stricmp(env,
"BINARY") == 0)
144 logAppenderType = WLOG_APPENDER_BINARY;
146#ifdef WINPR_HAVE_SYSLOG_H
147 else if (_stricmp(env,
"SYSLOG") == 0)
148 logAppenderType = WLOG_APPENDER_SYSLOG;
151#ifdef WINPR_HAVE_JOURNALD_H
152 else if (_stricmp(env,
"JOURNALD") == 0)
153 logAppenderType = WLOG_APPENDER_JOURNALD;
156 else if (_stricmp(env,
"UDP") == 0)
157 logAppenderType = WLOG_APPENDER_UDP;
162 if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
165 if (!WLog_ParseFilters(g_RootLog))
168 (void)atexit(WLog_Uninit_);
176static BOOL log_recursion(LPCSTR file, LPCSTR fkt,
size_t line)
181 void* bt = winpr_backtrace(20);
183 LPCSTR tag = WINPR_TAG(
"utils.wlog");
189 msg = winpr_backtrace_symbols(bt, &used);
196 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Recursion detected!!!") < 0)
199 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"Check %s [%s:%zu]", fkt, file, line) < 0)
202 for (
size_t i = 0; i < used; i++)
203 if (__android_log_print(ANDROID_LOG_FATAL, tag,
"%zu: %s", i, msg[i]) < 0)
208 if (fprintf(stderr,
"[%s]: Recursion detected!\n", fkt) < 0)
211 if (fprintf(stderr,
"[%s]: Check %s:%" PRIuz
"\n", fkt, file, line) < 0)
214 for (
size_t i = 0; i < used; i++)
215 if (fprintf(stderr,
"%s: %" PRIuz
": %s\n", fkt, i, msg[i]) < 0)
222 winpr_backtrace_free(bt);
226static BOOL WLog_Write(wLog* log,
const wLogMessage* message)
229 wLogAppender* appender = WLog_GetLogAppender(log);
234 if (!appender->active)
235 if (!WLog_OpenAppender(log))
238 EnterCriticalSection(&appender->lock);
240 if (appender->WriteMessage)
242 if (appender->recursive)
243 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
246 appender->recursive = TRUE;
247 status = appender->WriteMessage(log, appender, message);
248 appender->recursive = FALSE;
252 LeaveCriticalSection(&appender->lock);
256static BOOL WLog_WriteData(wLog* log,
const wLogMessage* message)
259 wLogAppender* appender = WLog_GetLogAppender(log);
264 if (!appender->active)
265 if (!WLog_OpenAppender(log))
268 if (!appender->WriteDataMessage)
271 EnterCriticalSection(&appender->lock);
273 if (appender->recursive)
274 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
277 appender->recursive = TRUE;
278 status = appender->WriteDataMessage(log, appender, message);
279 appender->recursive = FALSE;
282 LeaveCriticalSection(&appender->lock);
286static BOOL WLog_WriteImage(wLog* log,
wLogMessage* message)
289 wLogAppender* appender = NULL;
290 appender = WLog_GetLogAppender(log);
295 if (!appender->active)
296 if (!WLog_OpenAppender(log))
299 if (!appender->WriteImageMessage)
302 EnterCriticalSection(&appender->lock);
304 if (appender->recursive)
305 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
308 appender->recursive = TRUE;
309 status = appender->WriteImageMessage(log, appender, message);
310 appender->recursive = FALSE;
313 LeaveCriticalSection(&appender->lock);
317static BOOL WLog_WritePacket(wLog* log,
wLogMessage* message)
320 wLogAppender* appender = NULL;
321 appender = WLog_GetLogAppender(log);
326 if (!appender->active)
327 if (!WLog_OpenAppender(log))
330 if (!appender->WritePacketMessage)
333 EnterCriticalSection(&appender->lock);
335 if (appender->recursive)
336 status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
339 appender->recursive = TRUE;
340 status = appender->WritePacketMessage(log, appender, message);
341 appender->recursive = FALSE;
344 LeaveCriticalSection(&appender->lock);
348static BOOL WLog_PrintTextMessageInternal(wLog* log,
const wLogMessage* cmessage, va_list args)
352 char formattedLogMessage[WLOG_MAX_STRING_SIZE] = { 0 };
354 message.TextString = formattedLogMessage;
356 WINPR_PRAGMA_DIAG_PUSH
357 WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
358 if (vsnprintf(formattedLogMessage, ARRAYSIZE(formattedLogMessage) - 1, cmessage->FormatString,
361 WINPR_PRAGMA_DIAG_POP
363 return WLog_Write(log, &message);
366BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
367 const char* function, va_list args)
372 message.Level = level;
373 message.LineNumber = line;
374 message.FileName = file;
375 message.FunctionName = function;
379 case WLOG_MESSAGE_TEXT:
380 message.FormatString = va_arg(args,
const char*);
382 status = WLog_PrintTextMessageInternal(log, &message, args);
385 case WLOG_MESSAGE_DATA:
386 message.Data = va_arg(args,
void*);
387 message.Length = va_arg(args,
size_t);
388 status = WLog_WriteData(log, &message);
391 case WLOG_MESSAGE_IMAGE:
392 message.ImageData = va_arg(args,
void*);
393 message.ImageWidth = va_arg(args,
size_t);
394 message.ImageHeight = va_arg(args,
size_t);
395 message.ImageBpp = va_arg(args,
size_t);
396 status = WLog_WriteImage(log, &message);
399 case WLOG_MESSAGE_PACKET:
400 message.PacketData = va_arg(args,
void*);
401 message.PacketLength = va_arg(args,
size_t);
402 message.PacketFlags = va_arg(args,
unsigned);
403 status = WLog_WritePacket(log, &message);
413BOOL WLog_PrintTextMessageVA(wLog* log, DWORD level,
size_t line,
const char* file,
414 const char* function,
const char* fmt, va_list args)
417 message.Type = WLOG_MESSAGE_TEXT;
418 message.Level = level;
419 message.LineNumber = line;
420 message.FileName = file;
421 message.FunctionName = function;
423 message.FormatString = fmt;
425 return WLog_PrintTextMessageInternal(log, &message, args);
428BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level,
size_t line,
const char* file,
429 const char* function, ...)
433 va_start(args, function);
434 status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
439BOOL WLog_PrintTextMessage(wLog* log, DWORD level,
size_t line,
const char* file,
440 const char* function,
const char* fmt, ...)
445 status = WLog_PrintTextMessageVA(log, level, line, file, function, fmt, args);
450DWORD WLog_GetLogLevel(wLog* log)
455 if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
456 log->FilterLevel = WLog_GetFilterLogLevel(log);
458 if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
459 return (DWORD)log->FilterLevel;
460 else if (log->Level == WLOG_LEVEL_INHERIT)
461 log->Level = WLog_GetLogLevel(log->Parent);
466BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
473 level = WLog_GetLogLevel(_log);
475 if (level == WLOG_OFF)
478 return _log_level >= level;
481BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
488 lvl = WLog_ParseLogLevel(level);
493 return WLog_SetLogLevel(log, (DWORD)lvl);
496static BOOL WLog_reset_log_filters(wLog* log)
501 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
503 for (DWORD x = 0; x < log->ChildrenCount; x++)
505 wLog* child = log->Children[x];
507 if (!WLog_reset_log_filters(child))
514static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
517 LPCSTR filterStr = NULL;
525 while ((cpp = strchr(cpp,
',')) != NULL)
531 DWORD pos = g_FilterCount;
532 DWORD size = g_FilterCount + count;
533 wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size *
sizeof(wLogFilter));
539 LPSTR cp = (LPSTR)_strdup(filter);
556 if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
572 g_FilterCount = size;
574 return WLog_reset_log_filters(root);
577BOOL WLog_AddStringLogFilters(LPCSTR filter)
580 wLog* root = WLog_GetRoot();
581 return WLog_AddStringLogFilters_int(root, filter);
584static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
591 log->Level = logLevel;
593 for (DWORD x = 0; x < log->ChildrenCount; x++)
595 wLog* child = log->Children[x];
597 if (!WLog_UpdateInheritLevel(child, logLevel))
605BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
610 if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
613 log->Level = logLevel;
614 log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE;
616 for (DWORD x = 0; x < log->ChildrenCount; x++)
618 wLog* child = log->Children[x];
620 if (!WLog_UpdateInheritLevel(child, logLevel))
624 return WLog_reset_log_filters(log);
627int WLog_ParseLogLevel(LPCSTR level)
634 if (_stricmp(level,
"TRACE") == 0)
636 else if (_stricmp(level,
"DEBUG") == 0)
638 else if (_stricmp(level,
"INFO") == 0)
640 else if (_stricmp(level,
"WARN") == 0)
642 else if (_stricmp(level,
"ERROR") == 0)
644 else if (_stricmp(level,
"FATAL") == 0)
646 else if (_stricmp(level,
"OFF") == 0)
652BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
654 const char* pc = NULL;
671 while ((pc = strchr(pc,
'.')) != NULL)
678 names = _strdup(name);
683 filter->NameCount = count;
684 filter->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
689 filter->NameCount = 0;
693 filter->Names[count] = NULL;
696 filter->Names[count++] = p;
702 free((
void*)filter->Names);
703 filter->Names = NULL;
704 filter->NameCount = 0;
710 iLevel = WLog_ParseLogLevel(q);
715 free((
void*)filter->Names);
716 filter->Names = NULL;
717 filter->NameCount = 0;
721 filter->Level = (DWORD)iLevel;
723 while ((p = strchr(p,
'.')) != NULL)
725 if (count < filter->NameCount)
726 filter->Names[count++] = p + 1;
735BOOL WLog_ParseFilters(wLog* root)
737 LPCSTR filter =
"WLOG_FILTER";
744 nSize = GetEnvironmentVariableA(filter, NULL, 0);
749 env = (LPSTR)malloc(nSize);
754 if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
755 res = WLog_AddStringLogFilters_int(root, env);
761LONG WLog_GetFilterLogLevel(wLog* log)
765 if (log->FilterLevel >= 0)
766 return log->FilterLevel;
768 log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
769 for (DWORD i = 0; i < g_FilterCount; i++)
771 const wLogFilter* filter = &g_Filters[i];
772 for (DWORD j = 0; j < filter->NameCount; j++)
774 if (j >= log->NameCount)
777 if (_stricmp(filter->Names[j],
"*") == 0)
780 assert(filter->Level <= INT32_MAX);
781 log->FilterLevel = (LONG)filter->Level;
785 if (_stricmp(filter->Names[j], log->Names[j]) != 0)
788 if (j == (log->NameCount - 1))
790 match = log->NameCount == filter->NameCount;
793 assert(filter->Level <= INT32_MAX);
794 log->FilterLevel = (LONG)filter->Level;
804 return log->FilterLevel;
807static BOOL WLog_ParseName(wLog* log, LPCSTR name)
809 const char* cp = name;
814 while ((cp = strchr(cp,
'.')) != NULL)
820 names = _strdup(name);
825 log->NameCount = count;
826 log->Names = (LPSTR*)calloc((count + 1UL),
sizeof(LPSTR));
834 log->Names[count] = NULL;
837 log->Names[count++] = p;
839 while ((p = strchr(p,
'.')) != NULL)
841 if (count < log->NameCount)
842 log->Names[count++] = p + 1;
851wLog* WLog_New(LPCSTR name, wLog* rootLogger)
857 log = (wLog*)calloc(1,
sizeof(wLog));
862 log->Name = _strdup(name);
867 if (!WLog_ParseName(log, name))
870 log->Parent = rootLogger;
871 log->ChildrenCount = 0;
872 log->ChildrenSize = 16;
873 log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
875 if (!(log->Children = (wLog**)calloc(log->ChildrenSize,
sizeof(wLog*))))
878 log->Appender = NULL;
882 log->Level = WLOG_LEVEL_INHERIT;
887 LPCSTR level =
"WLOG_LEVEL";
888 log->Level = WLOG_INFO;
889 nSize = GetEnvironmentVariableA(level, NULL, 0);
893 env = (LPSTR)malloc(nSize);
898 if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
900 (void)fprintf(stderr,
"%s environment variable changed in my back !\n", level);
905 iLevel = WLog_ParseLogLevel(env);
910 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
916 iLevel = WLog_GetFilterLogLevel(log);
920 if (!WLog_SetLogLevel(log, (DWORD)iLevel))
924 InitializeCriticalSectionAndSpinCount(&log->lock, 4000);
932void WLog_Free(wLog* log)
938 WLog_Appender_Free(log, log->Appender);
939 log->Appender = NULL;
947 free((
void*)log->Names);
948 free((
void*)log->Children);
949 DeleteCriticalSection(&log->lock);
954wLog* WLog_GetRoot(
void)
956 if (!InitOnceExecuteOnce(&g_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
962static BOOL WLog_AddChild(wLog* parent, wLog* child)
968 if (parent->ChildrenCount >= parent->ChildrenSize)
971 parent->ChildrenSize *= 2;
973 if (!parent->ChildrenSize)
975 free((
void*)parent->Children);
976 parent->Children = NULL;
980 tmp = (wLog**)realloc((
void*)parent->Children,
sizeof(wLog*) * parent->ChildrenSize);
984 free((
void*)parent->Children);
985 parent->Children = NULL;
989 parent->Children = tmp;
993 if (!parent->Children)
996 parent->Children[parent->ChildrenCount++] = child;
997 child->Parent = parent;
1006static wLog* WLog_FindChild(wLog* root, LPCSTR name)
1016 for (DWORD index = 0; index < root->ChildrenCount; index++)
1018 child = root->Children[index];
1020 if (strcmp(child->Name, name) == 0)
1029 return (found) ? child : NULL;
1032static wLog* WLog_Get_int(wLog* root, LPCSTR name)
1036 if (!(log = WLog_FindChild(root, name)))
1041 if (!(log = WLog_New(name, root)))
1044 if (!WLog_AddChild(root, log))
1054wLog* WLog_Get(LPCSTR name)
1056 wLog* root = WLog_GetRoot();
1057 return WLog_Get_int(root, name);
1060#if defined(WITH_WINPR_DEPRECATED)
1063 return WLog_GetRoot() != NULL;
1066BOOL WLog_Uninit(
void)
1068 wLog* root = g_RootLog;
1075 for (DWORD index = 0; index < root->ChildrenCount; index++)
1077 wLog* child = root->Children[index];
1090BOOL WLog_SetContext(wLog* log,
const char* (*fkt)(
void*),
void* context)
1095 log->context = context;