Merge branch 'awakecoding' of https://github.com/vworkspace/FreeRDP
[platform/upstream/freerdp.git] / winpr / libwinpr / utils / wlog / wlog.c
1 /**
2  * WinPR: Windows Portable Runtime
3  * WinPR Logger
4  *
5  * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <winpr/crt.h>
28 #include <winpr/print.h>
29 #include <winpr/debug.h>
30 #include <winpr/environment.h>
31
32 #if defined(ANDROID)
33 #include <android/log.h>
34 #endif
35
36 #include <winpr/wlog.h>
37
38 #include "wlog/wlog.h"
39
40 #include "../../log.h"
41
42 /**
43  * References for general logging concepts:
44  *
45  * Short introduction to log4j:
46  * http://logging.apache.org/log4j/1.2/manual.html
47  *
48  * logging - Logging facility for Python:
49  * http://docs.python.org/2/library/logging.html
50  */
51
52 const char* WLOG_LEVELS[7] =
53 {
54         "TRACE",
55         "DEBUG",
56         "INFO",
57         "WARN",
58         "ERROR",
59         "FATAL",
60         "OFF"
61 };
62
63 static DWORD g_FilterCount = 0;
64 static wLogFilter* g_Filters = NULL;
65
66 static void log_recursion(const char* file, const char* fkt, int line)
67 {
68         size_t used, i;
69         void* bt = winpr_backtrace(20);
70         char** msg = winpr_backtrace_symbols(bt, &used);
71 #if defined(ANDROID)
72         const char* tag = WINPR_TAG("utils.wlog");
73         __android_log_print(ANDROID_LOG_FATAL, tag, "Recursion detected!!!");
74         __android_log_print(ANDROID_LOG_FATAL, tag, "Check %s [%s:%d]", fkt, file, line);
75
76         for (i=0; i<used; i++)
77                 __android_log_print(ANDROID_LOG_FATAL, tag, "%d: %s", i, msg[i]);
78
79 #else
80         fprintf(stderr, "[%s]: Recursion detected!\n", fkt);
81         fprintf(stderr, "[%s]: Check %s:%d\n", fkt, file, line);
82
83         for (i=0; i<used; i++)
84                 fprintf(stderr, "%s: %zd: %s\n", fkt, i, msg[i]);
85
86 #endif
87
88         free(msg);
89
90         winpr_backtrace_free(bt);
91 }
92
93 int WLog_Write(wLog* log, wLogMessage* message)
94 {
95         int status = -1;
96         wLogAppender* appender;
97         appender = WLog_GetLogAppender(log);
98
99         if (!appender)
100                 return -1;
101
102         if (!appender->State)
103                 WLog_OpenAppender(log);
104
105         if (!appender->WriteMessage)
106                 return -1;
107
108         EnterCriticalSection(&appender->lock);
109
110         if (appender->recursive)
111                 log_recursion(message->FileName, message->FunctionName, message->LineNumber);
112         else
113         {
114                 appender->recursive = TRUE;
115                 status = appender->WriteMessage(log, appender, message);
116                 appender->recursive = FALSE;
117         }
118
119         LeaveCriticalSection(&appender->lock);
120         return status;
121 }
122
123 int WLog_WriteData(wLog* log, wLogMessage* message)
124 {
125         int status = -1;
126         wLogAppender* appender;
127         appender = WLog_GetLogAppender(log);
128
129         if (!appender)
130                 return -1;
131
132         if (!appender->State)
133                 WLog_OpenAppender(log);
134
135         if (!appender->WriteDataMessage)
136                 return -1;
137
138         EnterCriticalSection(&appender->lock);
139
140         if (appender->recursive)
141                 log_recursion(message->FileName, message->FunctionName, message->LineNumber);
142         else
143         {
144                 appender->recursive = TRUE;
145                 status = appender->WriteDataMessage(log, appender, message);
146                 appender->recursive = FALSE;
147         }
148
149         LeaveCriticalSection(&appender->lock);
150         return status;
151 }
152
153 int WLog_WriteImage(wLog* log, wLogMessage* message)
154 {
155         int status = -1;
156         wLogAppender* appender;
157         appender = WLog_GetLogAppender(log);
158
159         if (!appender)
160                 return -1;
161
162         if (!appender->State)
163                 WLog_OpenAppender(log);
164
165         if (!appender->WriteImageMessage)
166                 return -1;
167
168         EnterCriticalSection(&appender->lock);
169
170         if (appender->recursive)
171                 log_recursion(message->FileName, message->FunctionName, message->LineNumber);
172         else
173         {
174                 appender->recursive = TRUE;
175                 status = appender->WriteImageMessage(log, appender, message);
176                 appender->recursive = FALSE;
177         }
178
179         LeaveCriticalSection(&appender->lock);
180         return status;
181 }
182
183 int WLog_WritePacket(wLog* log, wLogMessage* message)
184 {
185         int status = -1;
186         wLogAppender* appender;
187         appender = WLog_GetLogAppender(log);
188
189         if (!appender)
190                 return -1;
191
192         if (!appender->State)
193                 WLog_OpenAppender(log);
194
195         if (!appender->WritePacketMessage)
196                 return -1;
197
198         EnterCriticalSection(&appender->lock);
199
200         if (appender->recursive)
201                 log_recursion(message->FileName, message->FunctionName, message->LineNumber);
202         else
203         {
204                 appender->recursive = TRUE;
205                 status = appender->WritePacketMessage(log, appender, message);
206                 appender->recursive = FALSE;
207         }
208
209         LeaveCriticalSection(&appender->lock);
210         return status;
211 }
212
213 int WLog_PrintMessageVA(wLog* log, wLogMessage* message, va_list args)
214 {
215         int status = -1;
216
217         if (message->Type == WLOG_MESSAGE_TEXT)
218         {
219                 if (!strchr(message->FormatString, '%'))
220                 {
221                         message->TextString = (LPSTR) message->FormatString;
222                         status = WLog_Write(log, message);
223                 }
224                 else
225                 {
226                         char formattedLogMessage[WLOG_MAX_STRING_SIZE];
227                         wvsnprintfx(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message->FormatString, args);
228                         message->TextString = formattedLogMessage;
229                         status = WLog_Write(log, message);
230                 }
231         }
232         else if (message->Type == WLOG_MESSAGE_DATA)
233         {
234                 message->Data = va_arg(args, void*);
235                 message->Length = va_arg(args, int);
236                 status = WLog_WriteData(log, message);
237         }
238         else if (message->Type == WLOG_MESSAGE_IMAGE)
239         {
240                 message->ImageData = va_arg(args, void*);
241                 message->ImageWidth = va_arg(args, int);
242                 message->ImageHeight = va_arg(args, int);
243                 message->ImageBpp = va_arg(args, int);
244                 status = WLog_WriteImage(log, message);
245         }
246         else if (message->Type == WLOG_MESSAGE_PACKET)
247         {
248                 message->PacketData = va_arg(args, void*);
249                 message->PacketLength = va_arg(args, int);
250                 message->PacketFlags = va_arg(args, int);
251                 status = WLog_WritePacket(log, message);
252         }
253
254         return status;
255 }
256
257 void WLog_PrintMessage(wLog* log, wLogMessage* message, ...)
258 {
259         int status;
260         va_list args;
261         va_start(args, message);
262         status = WLog_PrintMessageVA(log, message, args);
263         va_end(args);
264 }
265
266 DWORD WLog_GetLogLevel(wLog* log)
267 {
268         if (log->Level == WLOG_LEVEL_INHERIT)
269         {
270                 return WLog_GetLogLevel(log->Parent);
271         }
272         else
273         {
274                 return log->Level;
275         }
276 }
277
278 void WLog_SetLogLevel(wLog* log, DWORD logLevel)
279 {
280         if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
281         {
282                 logLevel = WLOG_OFF;
283         }
284
285         log->Level = logLevel;
286 }
287
288 int WLog_ParseLogLevel(const char* level)
289 {
290         int iLevel = -1;
291
292         if (!level)
293                 return -1;
294
295         if (_stricmp(level, "TRACE") == 0)
296                 iLevel = WLOG_TRACE;
297         else if (_stricmp(level, "DEBUG") == 0)
298                 iLevel = WLOG_DEBUG;
299         else if (_stricmp(level, "INFO") == 0)
300                 iLevel = WLOG_INFO;
301         else if (_stricmp(level, "WARN") == 0)
302                 iLevel = WLOG_WARN;
303         else if (_stricmp(level, "ERROR") == 0)
304                 iLevel = WLOG_ERROR;
305         else if (_stricmp(level, "FATAL") == 0)
306                 iLevel = WLOG_FATAL;
307         else if (_stricmp(level, "OFF") == 0)
308                 iLevel = WLOG_OFF;
309
310         return iLevel;
311 }
312
313 int WLog_ParseFilter(wLogFilter* filter, LPCSTR name)
314 {
315         char* p;
316         char* q;
317         int count;
318         LPSTR names;
319         int iLevel;
320         count = 1;
321
322         if(!name)
323                 return -1;
324
325         p = (char*) name;
326
327         if (p)
328         {
329                 while ((p = strchr(p, '.')) != NULL)
330                 {
331                         count++;
332                         p++;
333                 }
334         }
335
336         names = _strdup(name);
337         if (!names)
338                 return -1;
339         filter->NameCount = count;
340         filter->Names = (LPSTR*) calloc((count + 1UL), sizeof(LPSTR));
341         if(!filter->Names)
342         {
343                 free(names);
344                 filter->NameCount = 0;
345                 return -1;
346         }
347         filter->Names[count] = NULL;
348         count = 0;
349         p = (char*) names;
350         filter->Names[count++] = p;
351         q = strrchr(p, ':');
352
353         if (!q)
354         {
355                 free(names);
356                 free(filter->Names);
357                 filter->Names = NULL;
358                 filter->NameCount = 0;
359                 return -1;
360         }
361
362         *q = '\0';
363         q++;
364         iLevel = WLog_ParseLogLevel(q);
365
366         if (iLevel < 0)
367         {
368                 free(names);
369                 free(filter->Names);
370                 filter->Names = NULL;
371                 filter->NameCount = 0;
372                 return -1;
373         }
374
375         filter->Level = (DWORD) iLevel;
376
377         while ((p = strchr(p, '.')) != NULL)
378         {
379                 if (count < filter->NameCount)
380                         filter->Names[count++] = p + 1;
381                 *p = '\0';
382                 p++;
383         }
384
385         return 0;
386 }
387
388 int WLog_ParseFilters()
389 {
390         char* p;
391         char* env;
392         DWORD count;
393         DWORD nSize;
394         int status;
395         LPCSTR* strs;
396
397         nSize = GetEnvironmentVariableA("WLOG_FILTER", NULL, 0);
398
399         if (nSize < 1)
400                 return 0;
401
402         env = (LPSTR) malloc(nSize);
403
404         if (!env)
405                 return -1;
406
407         if (!GetEnvironmentVariableA("WLOG_FILTER", env, nSize))
408                 return -1;
409
410         count = 1;
411         p = env;
412
413         while ((p = strchr(p, ',')) != NULL)
414         {
415                 count++;
416                 p++;
417         }
418
419         g_FilterCount = count;
420         p = env;
421
422         count = 0;
423         strs = (LPCSTR*) calloc(g_FilterCount, sizeof(LPCSTR));
424
425         if (!strs)
426         {
427                 free(env);
428                 return -1;
429         }
430
431         strs[count++] = p;
432
433         while ((p = strchr(p, ',')) != NULL)
434         {
435                 if (count < g_FilterCount)
436                         strs[count++] = p + 1;
437                 *p = '\0';
438                 p++;
439         }
440
441         g_Filters = calloc(g_FilterCount, sizeof(wLogFilter));
442
443         if (!g_Filters)
444         {
445                 free(strs);
446                 free(env);
447                 return -1;
448         }
449
450         for (count = 0; count < g_FilterCount; count++)
451         {
452                 status = WLog_ParseFilter(&g_Filters[count], strs[count]);
453
454                 if (status < 0)
455                 {
456                         free(strs);
457                         free(env);
458                         return -1;
459                 }
460         }
461
462         free(strs);
463         free(env);
464
465         return 0;
466 }
467
468 int WLog_GetFilterLogLevel(wLog* log)
469 {
470         DWORD i, j;
471         int iLevel = -1;
472         BOOL match = FALSE;
473
474         for (i = 0; i < g_FilterCount; i++)
475         {
476                 for (j = 0; j < g_Filters[i].NameCount; j++)
477                 {
478                         if (j >= log->NameCount)
479                                 break;
480
481                         if (_stricmp(g_Filters[i].Names[j], "*") == 0)
482                         {
483                                 match = TRUE;
484                                 break;
485                         }
486
487                         if (_stricmp(g_Filters[i].Names[j], log->Names[j]) != 0)
488                                 break;
489
490                         if (j == (log->NameCount - 1))
491                         {
492                                 match = TRUE;
493                                 break;
494                         }
495                 }
496
497                 if (match)
498                         break;
499         }
500
501         if (match)
502         {
503                 iLevel = (int) g_Filters[i].Level;
504         }
505
506         return iLevel;
507 }
508
509 int WLog_ParseName(wLog* log, LPCSTR name)
510 {
511         char* p;
512         int count;
513         LPSTR names;
514         count = 1;
515         p = (char*) name;
516
517         while ((p = strchr(p, '.')) != NULL)
518         {
519                 count++;
520                 p++;
521         }
522
523         names = _strdup(name);
524         if (!names)
525                 return -1;
526         log->NameCount = count;
527         log->Names = (LPSTR*) calloc((count + 1UL), sizeof(LPSTR));
528         if(!log->Names)
529         {
530                 free(names);
531                 return -1;
532         }
533         log->Names[count] = NULL;
534         count = 0;
535         p = (char*) names;
536         log->Names[count++] = p;
537
538         while ((p = strchr(p, '.')) != NULL)
539         {
540                 if (count < log->NameCount)
541                         log->Names[count++] = p + 1;
542                 *p = '\0';
543                 p++;
544         }
545
546         return 0;
547 }
548
549 wLog* WLog_New(LPCSTR name, wLog* rootLogger)
550 {
551         wLog* log = NULL;
552         char* env = NULL;
553         DWORD nSize;
554         int iLevel;
555         log = (wLog*) calloc(1, sizeof(wLog));
556
557         if (!log)
558                 return NULL;
559
560     log->Name = _strdup(name);
561
562     if (!log->Name)
563                 goto out_fail;
564
565     if (WLog_ParseName(log, name) != 0)
566                 goto out_fail;
567
568     log->Parent = rootLogger;
569     log->ChildrenCount = 0;
570     log->ChildrenSize = 16;
571
572     if (!(log->Children = (wLog**) calloc(log->ChildrenSize, sizeof(wLog*))))
573                 goto out_fail;
574
575     log->Appender = NULL;
576
577     if (rootLogger)
578     {
579         log->Level = WLOG_LEVEL_INHERIT;
580     }
581     else
582     {
583         log->Level = WLOG_INFO;
584         nSize = GetEnvironmentVariableA("WLOG_LEVEL", NULL, 0);
585
586         if (nSize)
587         {
588             env = (LPSTR) malloc(nSize);
589                         if (env)
590                         {
591                                 if (GetEnvironmentVariableA("WLOG_LEVEL", env, nSize))
592                                 {
593                                         iLevel = WLog_ParseLogLevel(env);
594
595                                         if (iLevel >= 0)
596                                                 log->Level = (DWORD) iLevel;
597
598                                 }
599                                 free(env);
600                         }
601         }
602     }
603
604     iLevel = WLog_GetFilterLogLevel(log);
605
606     if (iLevel >= 0)
607         log->Level = (DWORD) iLevel;
608
609         return log;
610
611 out_fail:
612         free (log->Children);
613         free (log->Name);
614         free (log);
615         return NULL;
616 }
617
618 void WLog_Free(wLog* log)
619 {
620         if (log)
621         {
622                 if (log->Appender)
623                 {
624                         WLog_Appender_Free(log, log->Appender);
625                         log->Appender = NULL;
626                 }
627
628                 free(log->Name);
629                 free(log->Names[0]);
630                 free(log->Names);
631                 free(log->Children);
632                 free(log);
633         }
634 }
635
636 static wLog* g_RootLog = NULL;
637
638 wLog* WLog_GetRoot()
639 {
640         char* env;
641         DWORD nSize;
642         DWORD logAppenderType;
643
644         if (!g_RootLog)
645         {
646                 if (!(g_RootLog = WLog_New("", NULL)))
647                         return NULL;
648
649                 g_RootLog->IsRoot = TRUE;
650                 WLog_ParseFilters();
651                 logAppenderType = WLOG_APPENDER_CONSOLE;
652                 nSize = GetEnvironmentVariableA("WLOG_APPENDER", NULL, 0);
653
654                 if (nSize)
655                 {
656                         env = (LPSTR) malloc(nSize);
657                         if (env)
658                         {
659                                 if (GetEnvironmentVariableA("WLOG_APPENDER", env, nSize))
660                                 {
661                                         if (_stricmp(env, "CONSOLE") == 0)
662                                                 logAppenderType = WLOG_APPENDER_CONSOLE;
663                                         else if (_stricmp(env, "FILE") == 0)
664                                                 logAppenderType = WLOG_APPENDER_FILE;
665                                         else if (_stricmp(env, "BINARY") == 0)
666                                                 logAppenderType = WLOG_APPENDER_BINARY;
667                                 }
668                                 free(env);
669                         }
670                 }
671
672                 WLog_SetLogAppenderType(g_RootLog, logAppenderType);
673         }
674
675         return g_RootLog;
676 }
677
678 int WLog_AddChild(wLog* parent, wLog* child)
679 {
680         if (parent->ChildrenCount >= parent->ChildrenSize)
681         {
682                 wLog **tmp;
683                 parent->ChildrenSize *= 2;
684                 if (!parent->ChildrenSize)
685                 {
686                         if (parent->Children)
687                                 free (parent->Children);
688                         parent->Children = NULL;
689                         
690                 }
691                 else
692                 {
693                         tmp = (wLog**) realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize);
694                         if (!tmp)
695                         {
696                                 if (parent->Children)
697                                         free (parent->Children);
698                                 parent->Children = NULL;
699                                 return -1;
700                         }
701                         parent->Children = tmp;
702                 }
703         }
704
705         if (!parent->Children)
706                 return -1;
707
708         parent->Children[parent->ChildrenCount++] = child;
709         child->Parent = parent;
710         return 0;
711 }
712
713 wLog* WLog_FindChild(LPCSTR name)
714 {
715         DWORD index;
716         wLog* root;
717         wLog* child = NULL;
718         BOOL found = FALSE;
719         root = WLog_GetRoot();
720
721         for (index = 0; index < root->ChildrenCount; index++)
722         {
723                 child = root->Children[index];
724
725                 if (strcmp(child->Name, name) == 0)
726                 {
727                         found = TRUE;
728                         break;
729                 }
730         }
731
732         return (found) ? child : NULL;
733 }
734
735 wLog* WLog_Get(LPCSTR name)
736 {
737         wLog* log;
738         if (!(log = WLog_FindChild(name)))
739         {
740                 wLog* root = WLog_GetRoot();
741                 if (!root)
742                         return NULL;
743                 if (!(log = WLog_New(name, root)))
744                         return NULL;
745                 WLog_AddChild(root, log);
746         }
747         return log;
748 }
749
750 void WLog_Init()
751 {
752         WLog_GetRoot();
753 }
754
755 void WLog_Uninit()
756 {
757         DWORD index;
758         wLog* child = NULL;
759         wLog* root = g_RootLog;
760
761         if (!root)
762                 return;
763
764         for (index = 0; index < root->ChildrenCount; index++)
765         {
766                 child = root->Children[index];
767                 WLog_Free(child);
768         }
769
770         WLog_Free(root);
771         g_RootLog = NULL;
772 }