• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*---------------------------------------------------------------------------*
2  *  plog.c  *
3  *                                                                           *
4  *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5  *                                                                           *
6  *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7  *  you may not use this file except in compliance with the License.         *
8  *                                                                           *
9  *  You may obtain a copy of the License at                                  *
10  *      http://www.apache.org/licenses/LICENSE-2.0                           *
11  *                                                                           *
12  *  Unless required by applicable law or agreed to in writing, software      *
13  *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15  *  See the License for the specific language governing permissions and      *
16  *  limitations under the License.                                           *
17  *                                                                           *
18  *---------------------------------------------------------------------------*/
19 
20 
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include "PFileSystem.h"
24 #include "ptypes.h"
25 #include "plog.h"
26 #include "pmemory.h"
27 #include "pstdio.h"
28 #include "ptimestamp.h"
29 #include "passert.h"
30 #ifdef USE_STACKTRACE
31 #include "PStackTrace.h"
32 #endif
33 
34 #ifdef USE_THREAD
35 #include "ptrd.h"
36 #include "pmutex.h"
37 #endif
38 
39 
40 #if defined (ANDROID)
41 #if defined (HAVE_ANDROID_OS)
42 #define LOG_TAG "Srec"
43 #include <utils/Log.h>
44 #endif
45 #endif
46 
47 #include "phashtable.h"
48 
49 #define MTAG __FILE__
50 
51 #define FILTER_MSG_1		"ESR_BUFFER_OVERFLOW"
52 #define FILTER_MSG_1_SIZE	( sizeof ( FILTER_MSG_1 ) - 1 )
53 
54 #define FILTER_MSG_2		"ESR_NO_MATCH_ERROR"
55 #define FILTER_MSG_2_SIZE	( sizeof ( FILTER_MSG_2 ) - 1 )
56 
57 static unsigned int GlogLevel = 0;
58 static PLogger *Glogger = NULL;
59 static LOG_OUTPUT_FORMAT GlogFormat = LOG_OUTPUT_FORMAT_MODULE_NAME |
60                                       LOG_OUTPUT_FORMAT_DATE_TIME;
61 /**
62  * Used to detect endless recursion where the PLog module calls itself.
63  */
64 static ESR_BOOL locked = ESR_FALSE;
65 #ifdef USE_THREAD
66 
67 static PtrdMutex* Gmutex = NULL;
68 #endif
69 
70 typedef struct FileLogger_t
71 {
72   PLogger base;
73   PFile* fp;
74 }
75 FileLogger;
76 
77 /**
78  * Prints and formats a message to the log.
79  *
80  * @param self the PLogger.
81  *
82  * @param format the format string specifying the next arguments (a la
83  * printf).
84  *
85  * @param args variable argument list.
86  *
87  * @return The number of bytes written to the PLogger or -1 if an error
88  * occurs.
89  */
FileLoggerPrintf(PLogger * self,const LCHAR * format,...)90 static ESR_ReturnCode FileLoggerPrintf(PLogger *self, const LCHAR *format, ...)
91 {
92   FileLogger *p = STATIC_CAST(self, FileLogger, base);
93   ESR_ReturnCode rc;
94   va_list args;
95 
96   va_start(args, format);
97   rc = pvfprintf(p->fp, format, args);
98   va_end(args);
99   return rc;
100 }
101 
FileLoggerFlush(PLogger * self)102 static ESR_ReturnCode FileLoggerFlush(PLogger *self)
103 {
104   FileLogger *p = STATIC_CAST(self, FileLogger, base);
105   return pfflush(p->fp) == 0 ? ESR_SUCCESS : ESR_FATAL_ERROR;
106 }
107 
108 
109 /**
110  * Destroys the logger.  This function is responsible to deallocate any
111  * resources used by the logger.  In particular, if buffering is internally
112  * used, it needs to flush the buffer.
113  */
FileLoggerDestroy(PLogger * self)114 static void FileLoggerDestroy(PLogger *self)
115 {
116   FileLogger *p = STATIC_CAST(self, FileLogger, base);
117   pfflush(p->fp);
118 
119   if (p->fp != PSTDERR && p->fp != PSTDOUT)
120     pfclose(p->fp);
121   FREE(p);
122 }
123 
createPFileLogger(PFile * fp,PLogger ** logger)124 static ESR_ReturnCode createPFileLogger(PFile* fp, PLogger** logger)
125 {
126   FileLogger* fileLogger;
127 
128   if (fp == NULL)
129     return ESR_INVALID_ARGUMENT;
130   fileLogger = NEW(FileLogger, MTAG);
131   if (fileLogger == NULL)
132     return ESR_OUT_OF_MEMORY;
133 
134   fileLogger->base.printf = FileLoggerPrintf;
135   fileLogger->base.flush = FileLoggerFlush;
136   fileLogger->base.destroy = FileLoggerDestroy;
137   fileLogger->fp = fp;
138 
139   *logger = &fileLogger->base;
140   return ESR_SUCCESS;
141 }
142 
143 /**
144  * Initializes the LOG library.  This function must be called before any
145  * logging can take place.
146  *
147  * @param logger The logger to be used to output the messages.  If NULL, then
148  * logging goes to PSTDERR.  @param logLevel The level of logging requested.
149  *
150  * @return ESR_SUCCESS if success, anything else if an error occurs.
151  *
152  */
PLogInit(PLogger * logger,unsigned int logLevel)153 ESR_ReturnCode PLogInit(PLogger *logger, unsigned int logLevel)
154 {
155   ESR_ReturnCode rc = ESR_SUCCESS;
156 
157   if (Glogger != NULL)
158     return ESR_INVALID_STATE;
159 
160   GlogLevel = logLevel;
161 
162 #ifdef USE_THREAD
163   if ((rc = PtrdMutexCreate(&Gmutex)) != ESR_SUCCESS)
164     return rc;
165 #endif
166 
167   if (logger != NULL)
168     Glogger = logger;
169   else
170   {
171     rc = createPFileLogger(PSTDERR, &Glogger);
172     if (rc != ESR_SUCCESS)
173       goto CLEANUP;
174   }
175 
176   return rc;
177 CLEANUP:
178 #ifdef USE_THREAD
179   if (Gmutex != NULL)
180   {
181     PtrdMutexDestroy(Gmutex);
182     Gmutex = NULL;
183   }
184 #endif
185   return rc;
186 }
187 
PLogIsInitialized(ESR_BOOL * isInit)188 ESR_ReturnCode PLogIsInitialized(ESR_BOOL* isInit)
189 {
190   if (isInit == NULL)
191     return ESR_INVALID_STATE;
192   *isInit = Glogger != NULL;
193   return ESR_SUCCESS;
194 }
195 
PLogIsLocked(ESR_BOOL * isLocked)196 ESR_ReturnCode PLogIsLocked(ESR_BOOL* isLocked)
197 {
198   if (isLocked == NULL)
199     return ESR_INVALID_STATE;
200   *isLocked = locked;
201   return ESR_SUCCESS;
202 }
203 
204 /**
205  * Shutdowns the LOG library.  Once this function is called, no logging activity can be performed.
206  * Also, the logger that was given to pLogInit is destroyed.
207  *
208  * @return ESR_SUCCESS if success, anything else if an error occurs.
209  *
210  */
PLogShutdown()211 ESR_ReturnCode PLogShutdown()
212 {
213   ESR_ReturnCode rc = ESR_SUCCESS;
214 
215   if (Glogger == NULL)
216     return ESR_INVALID_STATE;
217 
218 #ifdef USE_THREAD
219   if ((rc = PtrdMutexDestroy(Gmutex)) != ESR_SUCCESS)
220     return rc;
221   Gmutex = NULL;
222 #endif
223 
224   if (Glogger->flush != NULL)
225     Glogger->flush(Glogger);
226   Glogger->destroy(Glogger);
227   Glogger = NULL;
228   return rc;
229 }
230 
PLogGetLevel(unsigned int * logLevel)231 ESR_ReturnCode PLogGetLevel(unsigned int *logLevel)
232 {
233   if (Glogger == NULL)
234     return ESR_INVALID_STATE;
235   if (logLevel == NULL)
236     return ESR_INVALID_ARGUMENT;
237 
238   *logLevel = GlogLevel;
239   return ESR_SUCCESS;
240 }
241 
PLogSetLevel(unsigned int logLevel)242 ESR_ReturnCode PLogSetLevel(unsigned int logLevel)
243 {
244   if (Glogger == NULL)
245     return ESR_INVALID_STATE;
246 
247   GlogLevel = logLevel;
248   return ESR_SUCCESS;
249 }
250 
251 #define TIME_BUF_SIZE 24
252 #define TIME_FORMAT L("%Y/%m/%d %H:%M:%S")
253 #define PLOG_PANIC(x, rc) \
254   do \
255   { \
256     { \
257       pfprintf(PSTDERR, L("[%s:%d] %s failed with %s\n"), __FILE__, __LINE__, x, ESR_rc2str(rc)); \
258       pfflush(PSTDERR); \
259     } \
260   } while (0)
261 
logIt(const LCHAR * format,va_list args,ESR_BOOL showStackTrace)262 static ESR_ReturnCode logIt(const LCHAR *format, va_list args, ESR_BOOL showStackTrace)
263 {
264   ESR_ReturnCode rc = ESR_SUCCESS;
265   ESR_ReturnCode flushRC = ESR_SUCCESS;
266 #ifdef USE_STACKTRACE
267 #define BUFFER_SIZE P_MAX_STACKTRACE + 2000
268 #else
269 #define BUFFER_SIZE 2000
270 #endif
271   LCHAR buffer[BUFFER_SIZE] = L("");
272 
273   // TODO: Remove once logging subsystem supports "warn" level
274   if (strstr(format, "ESR_BUFFER_OVERFLOW")==format)
275     return ESR_SUCCESS;
276 
277 #ifdef USE_STACKTRACE
278   if (Glogger == NULL)
279   {
280     /*
281      * There are three possible scenerios for why logging would occur although the PLog module
282      * is uninitialized:
283      *
284      * 1) The code fails before PLog is initialized (perhaps in other portable components)
285      * 2) The user forgets to initialize the PLog module
286      * 3) The code fails after PLog is uninitialized (on shutdown)
287      *
288      * We do our best by logging any errors but this might result in the memory leak of
289      * the PStackTrace module in case 3.
290      */
291     rc = PStackTraceCreate();
292     if (rc != ESR_SUCCESS)
293     {
294       PLOG_PANIC(L("PStackTraceCreate"), rc);
295       goto CLEANUP;
296     }
297   }
298   else
299   {
300 #ifdef USE_THREAD
301     rc = PtrdMutexLock(Gmutex);
302     if (rc != ESR_SUCCESS)
303       return rc;
304 #endif
305   }
306   if (locked)
307     return ESR_INVALID_STATE;
308   locked = ESR_TRUE;
309 
310   if (GlogFormat & LOG_OUTPUT_FORMAT_DATE_TIME)
311   {
312     PTimeStamp now;
313     struct tm* loctime;
314     LCHAR timeStr[TIME_BUF_SIZE];
315     size_t timeStrSize;
316 
317     PTimeStampSet(&now);
318     loctime = localtime(&now.secs);
319     timeStrSize = LSTRFTIME(timeStr, TIME_BUF_SIZE, TIME_FORMAT, loctime);
320     passert(timeStrSize == (TIME_BUF_SIZE - 5));
321     psprintf(timeStr + (TIME_BUF_SIZE - 5), ".%03hu", now.msecs);
322 
323     psprintf(buffer + LSTRLEN(buffer), L("%s|"), timeStr);
324     passert(LSTRLEN(buffer) < BUFFER_SIZE);
325   }
326 
327   if (GlogFormat & LOG_OUTPUT_FORMAT_THREAD_ID)
328   {
329     rc = psprintf(buffer + LSTRLEN(buffer), L("trd=%u|"), PtrdGetCurrentThreadId());
330     passert(LSTRLEN(buffer) < BUFFER_SIZE);
331   }
332 
333   if (GlogFormat & LOG_OUTPUT_FORMAT_MODULE_NAME && showStackTrace)
334   {
335     size_t len = P_MAX_STACKTRACE;
336     LCHAR text[P_MAX_STACKTRACE];
337     LCHAR* index;
338     size_t i;
339 
340     rc = PStackTraceGetValue((LCHAR*) & text, &len);
341     if (rc == ESR_SUCCESS)
342     {
343       for (i = 0; i < 2; ++i)
344       {
345         rc = PStackTracePopLevel((LCHAR*) & text);
346         if (rc != ESR_SUCCESS)
347         {
348           PLOG_PANIC(L("PStackTracePopLevel"), rc);
349           goto CLEANUP;
350         }
351       }
352       index = text;
353       while (index)
354       {
355         index = LSTRSTR(index, L(" at\n"));
356         if (index != NULL)
357         {
358           *(index + 1) = L('<');
359           *(index + 2) = L('-');
360           *(index + 3) = L(' ');
361         }
362       }
363     }
364     else if (rc == ESR_NOT_SUPPORTED)
365       LSTRCPY(text, L(""));
366     else if (rc != ESR_SUCCESS)
367     {
368       PLOG_PANIC(L("PStackTraceGetValue"), rc);
369       goto CLEANUP;
370     }
371     rc = psprintf(buffer + LSTRLEN(buffer), L("Module=%s|"), text);
372     passert(LSTRLEN(buffer) < BUFFER_SIZE);
373   }
374 
375   pvsprintf(buffer + LSTRLEN(buffer), format, args);
376 #else
377   pvsprintf(buffer + LSTRLEN(buffer), format, args);
378 #endif
379   passert(LSTRLEN(buffer) < BUFFER_SIZE);
380 
381   psprintf(buffer + LSTRLEN(buffer), L("\n"));
382   passert(LSTRLEN(buffer) < BUFFER_SIZE);
383 
384   if (Glogger != NULL)
385   {
386     rc = Glogger->printf(Glogger, L("%s"), buffer);
387     if (rc != ESR_SUCCESS)
388       goto CLEANUP;
389     flushRC = Glogger->flush(Glogger);
390   }
391   else
392   {
393     /* We need to log but the logging module is disabled or is locked so we output to stderr instead */
394     {
395       pfprintf(PSTDERR, L("%s"), buffer);
396       pfflush(PSTDERR);
397     }
398   }
399   locked = ESR_FALSE;
400 #ifdef USE_THREAD
401   PtrdMutexUnlock(Gmutex);
402 #endif
403   return flushRC;
404 CLEANUP:
405   if (Glogger != NULL && Glogger->flush != NULL)
406     flushRC = Glogger->flush(Glogger);
407   locked = ESR_FALSE;
408 #ifdef USE_THREAD
409   PtrdMutexUnlock(Gmutex);
410 #endif
411   return rc != ESR_SUCCESS ? rc : flushRC;
412 }
413 
414 /**
415  * Conditionally PLogs a message.  The message is logged only if module is enabled.
416  *
417  * @param msg The message format specification (ala printf).
418  * @return ESR_SUCCESS if success, anything else if an error occurs.
419  */
PLogMessage(const char * msg,...)420 ESR_ReturnCode PLogMessage(const char* msg, ...)
421 {
422   va_list args;
423   ESR_ReturnCode rc;
424 #if USE_STACKTRACE
425   size_t depth;
426 #endif
427 
428 #if defined (ANDROID)
429 #if defined (HAVE_ANDROID_OS)
430   return ( ESR_SUCCESS );/* Get rid of this for phone device */
431 #endif
432 #endif
433 
434   if (Glogger == NULL)
435     return ESR_INVALID_STATE;
436 #ifdef USE_STACKTRACE
437   return ESR_SUCCESS;
438   rc = PStackTraceGetDepth(&depth);
439 
440   if (rc == ESR_NOT_SUPPORTED)
441   {
442     /* Debugging symbols are missing */
443     return ESR_SUCCESS;
444   }
445   else if (rc != ESR_SUCCESS)
446   {
447     pfprintf(PSTDERR, L("PStackTraceGetDepth"), ESR_rc2str(rc));
448     goto CLEANUP;
449   }
450 
451   /* Remove PLogMessage() from depth */
452   --depth;
453   if (GlogLevel < depth)
454     return ESR_SUCCESS;
455 #endif
456 
457   va_start(args, msg);
458   rc = logIt(msg, args, ESR_FALSE);
459   va_end(args);
460   return rc;
461 #if USE_STACKTRACE
462 CLEANUP:
463   return rc;
464 #endif
465 }
466 
467 
468 /**
469  * Unconditionally logs an error message.
470  *
471  * @param msg The message format specification (ala printf).
472  * @return ESR_SUCCESS if success, anything else if an error occurs.
473  */
PLogError(const char * msg,...)474 ESR_ReturnCode PLogError(const char* msg, ...)
475 {
476   va_list args;
477   ESR_ReturnCode rc;
478 #if defined (ANDROID)
479 #if defined (HAVE_ANDROID_OS)
480   char log_text [2048];
481 #endif
482 #endif
483 
484   va_start(args, msg);
485 #if defined (ANDROID)
486 #if defined (HAVE_ANDROID_OS)
487   pvsprintf ( log_text, msg, args);
488 /* We need to disable some error messages because they are frequently not
489  * errors but due to sloppy use of some functions. This prevents us from
490  * getting flooded with bad error messages. SteveR
491  */
492   if ( ( strncmp ( log_text, FILTER_MSG_1, FILTER_MSG_1_SIZE ) != 0 ) &&
493     ( strncmp ( log_text, FILTER_MSG_2, FILTER_MSG_2_SIZE ) != 0 ) )
494   {
495     LOGE ("%s", log_text );
496   }
497   rc = 0;
498 #else
499   rc = logIt(msg, args, ESR_TRUE);
500 #endif
501 #else
502   rc = logIt(msg, args, ESR_TRUE);
503 #endif
504   va_end(args);
505 
506   return rc;
507 }
508 
509 
510 
PLogCreateFileLogger(PFile * file,PLogger ** logger)511 ESR_ReturnCode PLogCreateFileLogger(PFile* file, PLogger **logger)
512 {
513   if (logger == NULL || file == NULL)
514     return ESR_INVALID_ARGUMENT;
515 
516   return createPFileLogger(file, logger);
517 }
518 
519 /**
520  * Creates a logger that logs to a circular file.
521  *
522  * @param filename The name of the file to be created.
523  * @param maxsize The maximum number of bytes that the file may have.
524  * @param logger logger handle receiving the created logger.
525  */
PLogCreateCircularFileLogger(const LCHAR * filename,unsigned int maxsize,PLogger ** logger)526 ESR_ReturnCode PLogCreateCircularFileLogger(const LCHAR *filename,
527     unsigned int maxsize,
528     PLogger **logger)
529 {
530   return ESR_NOT_SUPPORTED;
531 }
532 
533 
PLogSetFormat(LOG_OUTPUT_FORMAT format)534 ESR_ReturnCode PLogSetFormat(LOG_OUTPUT_FORMAT format)
535 {
536   GlogFormat = format;
537   return ESR_SUCCESS;
538 }
539