Coverage Report

Created: 2023-06-07 06:03

/src/resiprocate/rutil/Log.hxx
Line
Count
Source (jump to first uncovered line)
1
#ifndef RESIP_Log_hxx
2
#define RESIP_Log_hxx
3
4
#include "rutil/Data.hxx"
5
6
#ifndef WIN32
7
#include <syslog.h>
8
#include <unistd.h>
9
#endif
10
11
#include <set>
12
13
#include "rutil/ConfigParse.hxx"
14
#include "rutil/Mutex.hxx"
15
#include "rutil/Lock.hxx"
16
#include "rutil/HashMap.hxx"
17
#include "rutil/ThreadIf.hxx"
18
#include <iostream>
19
20
// !ipse! I think that this remark is no more valid with recent changes,
21
// but I don't have MacOs X to test with. Someone should take this duty.
22
//
23
// NOTE: disabling thread setting code for native mac os applications.
24
// since some logging takes place during static initialization we can't
25
// be sure all the pthread stuff is ready to go. this eventually causes
26
// crashes in the Mac OS native API.
27
#if !defined(TARGET_OS_MAC)
28
#define LOG_ENABLE_THREAD_SETTING
29
#endif
30
31
extern "C"
32
{
33
   // Forward declaration to make it friend of Log class.
34
   void freeLocalLogger(void* pThreadData);
35
};
36
37
38
namespace resip
39
{
40
41
class ExternalLogger;
42
class Subsystem;
43
44
/**
45
   @brief Singleton that handles logging calls.
46
47
   @see Logger for usage details
48
*/
49
class Log
50
{
51
   public:
52
      enum Type
53
      {
54
         Cout = 0,
55
         Syslog, 
56
         File, 
57
         Cerr,
58
         VSDebugWindow,        ///< Use only for Visual Studio Debug Window logging - WIN32 must be defined
59
         OnlyExternal,         ///< log messages are only written to external logger
60
         OnlyExternalNoHeaders ///< same as OnlyExternal, only the messageWithHeaders param of the ExternalLogger
61
                               ///< will be empty.  This parameter usually contains a pre-formatted log entry.
62
      };
63
      
64
      enum Level
65
      {
66
         None = -1,
67
#ifdef WIN32
68
         Crit = 2,
69
         Err = 3,
70
         Warning = 4,
71
         Info = 6,
72
         Debug = 7,
73
#else
74
         Crit = LOG_CRIT,
75
// #ifdef ERR // ncurses defines a macro called ERR 
76
//          SIP2_ERR = LOG_ERR,
77
// #else
78
//          ERR = LOG_ERR,
79
// #endif
80
         Err,
81
         Warning = LOG_WARNING,
82
         Info = LOG_INFO,
83
         Debug = LOG_DEBUG,
84
#endif
85
         Stack = 8,
86
         StdErr = 9,
87
         Bogus = 666
88
      };
89
90
      enum MessageStructure
91
      {
92
         Unstructured = 0,
93
         JSON_CEE
94
      };
95
96
      /// Thread Local logger ID type.
97
      typedef int LocalLoggerId;
98
99
      /**
100
         @brief Implementation for logging macros.
101
102
         Log::Guard(Log::Info, Subsystem::TEST, __FILE__, __LINE__, __func__) << ... ;
103
      */
104
      class Guard
105
      {
106
         public:
107
            /** Remember the logging values and be a a stream to receive
108
                the log contents. */
109
            Guard(Level level,
110
                  const Subsystem& system,
111
                  const char* file,
112
                  int line,
113
                  const char* methodName);
114
115
            /** Commit logging */
116
            ~Guard();
117
118
0
            EncodeStream& asStream() {return mStream;}
119
0
            operator EncodeStream&() {return mStream;}
120
121
         private:
122
            resip::Log::Level mLevel;
123
            const resip::Subsystem& mSubsystem;
124
            resip::Data::size_type mHeaderLength;
125
            const char* mFile;
126
            int mLine;
127
            const char* mMethodName;
128
            char mBuffer[128];
129
            Data mData;
130
            oDataStream mStream;
131
            Guard& operator=(const Guard&);
132
      };
133
134
      class ThreadSetting
135
      {
136
         public:
137
            ThreadSetting()
138
               : mService(-1),
139
                 mLevel(Err)
140
0
            {}
141
142
            ThreadSetting(int serv, Level level)
143
               : mService(serv),
144
                 mLevel(level)
145
0
            {
146
0
            }
147
            
148
            int mService;
149
            Level mLevel;
150
      };
151
152
      /// output the loglevel, hostname, appname, pid, tid, subsystem
153
      static EncodeStream& tags(Log::Level level,
154
                                const Subsystem& subsystem, 
155
                                const char* file,
156
                                int line,
157
                                const char* methodName,
158
                                EncodeStream& strm,
159
                                MessageStructure messageStructure);
160
161
      static Data& timestamp(Data& result);
162
      static Data timestamp();
163
      static ExternalLogger* getExternal()
164
0
      {
165
0
         return getLoggerData().mExternalLogger;
166
0
      }
167
      static Data getAppName()
168
0
      {
169
0
         return mAppName;
170
0
      }
171
172
      static int parseSyslogFacilityName(const Data& facilityName);
173
174
      static void initialize(Type type,
175
                             Level level,
176
                             const Data& appName,
177
                             const char * logFileName = 0,
178
                             ExternalLogger* externalLogger = 0,
179
                             const Data& syslogFacility = "LOG_DAEMON",
180
                             MessageStructure messageStructure = Unstructured,
181
                             const Data& instanceName = "");
182
      static void initialize(const Data& type,
183
                             const Data& level,
184
                             const Data& appName,
185
                             const char * logFileName = 0,
186
                             ExternalLogger* externalLogger = 0,
187
                             const Data& syslogFacility = "LOG_DAEMON",
188
                             const Data& messageStructure = "Unstructured",
189
                             const Data& instanceName = "");
190
      static void initialize(const char* type,
191
                             const char* level,
192
                             const char* appName,
193
                             const char * logFileName = 0,
194
                             ExternalLogger* externalLogger = 0,
195
                             const char* syslogFacility = "LOG_DAEMON",
196
                             const char* messageStructure = "Unstructured",
197
                             const char* instanceName = "");
198
      static void initialize(Type type,
199
                             Level level,
200
                             const Data& appName,
201
                             ExternalLogger& logger,
202
                             const Data& syslogFacility = "LOG_DAEMON",
203
                             MessageStructure messageStructure = Unstructured,
204
                             const Data& instanceName = "");
205
      static void initialize(const ConfigParse& configParse,
206
                             const Data& appName,
207
                             ExternalLogger* externalLogger = 0);
208
209
      /** @brief Set logging level for current thread.
210
      * If thread has no local logger attached, then set global logging level.
211
      */
212
      static void setLevel(Level level);
213
      /** @brief Set logging level for given subsystem. */
214
      static void setLevel(Level level, Subsystem& s);
215
      /** Set logging level for given local logger. Use 0 to set global logging level. */
216
      static void setLevel(Level level, LocalLoggerId loggerId);
217
      /** @brief Return logging level for current thread.
218
      * If thread has no local logger attached, then return global logging level.
219
      */
220
0
      static Level level() { Lock lock(_mutex); return getLoggerData().mLevel; }
221
      /** Return logging level for given local logger. Use 0 to set global logging level. */
222
      static Level level(LocalLoggerId loggerId);
223
0
      static LocalLoggerId id() { Lock lock(_mutex); return getLoggerData().id(); }
224
      static void setMaxLineCount(unsigned int maxLineCount);
225
      static void setMaxLineCount(unsigned int maxLineCount, LocalLoggerId loggerId);
226
      static void setMaxByteCount(unsigned int maxByteCount);
227
      static void setMaxByteCount(unsigned int maxByteCount, LocalLoggerId loggerId);
228
      static void setKeepAllLogFiles(bool keepAllLogFiles);
229
      static void setKeepAllLogFiles(bool keepAllLogFiles, LocalLoggerId loggerId);
230
      static Level toLevel(const Data& l);
231
      static Type toType(const Data& t);
232
      static Data toString(Level l);
233
234
      /// DEPRECATED! Left for backward compatibility - use localLoggers instead
235
      static void setServiceLevel(int service, Level l);
236
      static Level getServiceLevel(int service);
237
238
      /// DEPRECATED! Left for backward compatibility - use localLoggers instead
239
      static const ThreadSetting* getThreadSetting();
240
      static void setThreadSetting(ThreadSetting info);
241
      static void setThreadSetting(int serv, Level l);
242
      static void setThreadSetting(int serv);
243
244
      /// Create new logger instance and return its ID (zero on error)
245
      static LocalLoggerId localLoggerCreate(Type type,
246
                                             Level level,
247
                                             const char * logFileName = NULL,
248
                                             ExternalLogger* externalLogger = NULL,
249
                                             MessageStructure messageStructure = Unstructured);
250
251
      /** Reinitialize all new setting for a local logger instance
252
      * @retval 0 on success
253
      * @retval 1 if logger does not exist
254
      */
255
      static int localLoggerReinitialize(LocalLoggerId loggerId,
256
                                         Type type,
257
                                         Level level,
258
                                         const char * logFileName = NULL,
259
                                         ExternalLogger* externalLogger = NULL,
260
                                         MessageStructure messageStructure = Unstructured);
261
262
      /** Destroy existing logger instance.
263
      * @retval 0 on success
264
      * @retval 1 if logger does not exist
265
      * @retval 2 if logger is still in use
266
      * @retval >2 on other failures
267
      */
268
      static int localLoggerRemove(LocalLoggerId loggerId);
269
270
      /** Set logger instance with given ID as a thread local logger.
271
      * Pass zero \p loggerId to remove thread local logger.
272
      * @retval 0 on success
273
      * @retval 1 if logger does not exist
274
      * @retval >1 on other failures
275
      */
276
      static int setThreadLocalLogger(LocalLoggerId loggerId);
277
278
279
      static std::ostream& Instance(unsigned int bytesToWrite);
280
      static bool isLogging(Log::Level level, const Subsystem&);
281
      static void OutputToWin32DebugWindow(const Data& result);      
282
      static void reset(); ///< Frees logger stream
283
#ifndef WIN32
284
      static void droppingPrivileges(uid_t uid, pid_t pid);
285
#endif
286
287
   protected:
288
      static Mutex _mutex;
289
      static volatile short touchCount;
290
      static const Data delim;
291
292
      static unsigned int MaxLineCount;
293
      static unsigned int MaxByteCount;
294
      static bool KeepAllLogFiles;
295
296
      class ThreadData
297
      {
298
         public:
299
            ThreadData(LocalLoggerId id, Type type=Cout, Level level=Info,
300
                       const char *logFileName=NULL,
301
                       ExternalLogger *pExternalLogger=NULL,
302
                       MessageStructure messageStructure = Unstructured,
303
                       const Data& instanceName = "")
304
               : mLevel(level),
305
                 mMaxLineCount(0),
306
                 mMaxByteCount(0),
307
                 mExternalLogger(pExternalLogger),
308
                 mMessageStructure(messageStructure),
309
                 mInstanceName(instanceName),
310
                 mKeepAllLogFiles(false),
311
                 mKeepAllLogFilesSet(false),
312
                 mId(id),
313
                 mType(type),
314
                 mLogger(NULL),
315
                 mLineCount(0)
316
2
            {
317
2
               if (logFileName)
318
0
               {
319
0
                  mLogFileName = logFileName;
320
0
               }
321
2
            }
322
0
            ~ThreadData() { reset(); }
323
324
            void set(Type type = Cout, Level level = Info,
325
               const char* logFileName = NULL,
326
               ExternalLogger* pExternalLogger = NULL,
327
               MessageStructure messageStructure = Unstructured,
328
               const Data& instanceName = "");
329
330
0
            LocalLoggerId id() const {return mId;}
331
0
            unsigned int maxLineCount() { return mMaxLineCount ? mMaxLineCount : MaxLineCount; }  // return local max, if not set use global max
332
0
            unsigned int maxByteCount() { return mMaxByteCount ? mMaxByteCount : MaxByteCount; }  // return local max, if not set use global max
333
0
            bool keepAllLogFiles() { return mKeepAllLogFilesSet ? mKeepAllLogFiles : KeepAllLogFiles; } // return local if set, if not use global setting
334
0
            Type type() const {return mType;}
335
336
0
            void setKeepAllLogFiles(bool keepAllLogFiles) { mKeepAllLogFiles = keepAllLogFiles; mKeepAllLogFilesSet = true; }
337
338
            std::ostream& Instance(unsigned int bytesToWrite); ///< Return logger stream instance, creating it if needed.
339
            void reset(); ///< Frees logger stream
340
#ifndef WIN32
341
            void droppingPrivileges(uid_t uid, pid_t pid);
342
#endif
343
            volatile Level mLevel;
344
            volatile unsigned int mMaxLineCount;
345
            volatile unsigned int mMaxByteCount;
346
            ExternalLogger* mExternalLogger;
347
348
         protected:
349
            volatile MessageStructure mMessageStructure;
350
            Data mInstanceName;
351
352
            volatile bool mKeepAllLogFiles;
353
            volatile bool mKeepAllLogFilesSet;
354
355
            friend class Guard;
356
            const LocalLoggerId mId;
357
            Type mType;
358
            Data mLogFileName;
359
            std::ostream* mLogger;
360
            unsigned int mLineCount;
361
      };
362
363
      static ThreadData mDefaultLoggerData; ///< Default logger settings.
364
      static Data mAppName;
365
      static Data mHostname;
366
      static Data mFqdn;
367
      static int mSyslogFacility;
368
      static const char mDescriptions[][32];
369
      static const char mCEEPri[][32];
370
      static const int mSyslogPriority[];
371
      static Data mInstanceName;
372
373
      static ThreadData &getLoggerData()
374
2
      {
375
2
         ThreadData* pData = static_cast<ThreadData*>(ThreadIf::tlsGetValue(*Log::mLocalLoggerKey));
376
2
         return pData?*pData:mDefaultLoggerData;
377
2
      }
378
379
      /// Thread Local logger settings storage
380
      class LocalLoggerMap
381
      {
382
      public:
383
         LocalLoggerMap()
384
2
            : mLastLocalLoggerId(0) {};
385
386
         /// Create new logger instance and return its ID (zero on error)
387
         LocalLoggerId create(Type type,
388
                              Level level,
389
                              const char * logFileName = NULL,
390
                              ExternalLogger* externalLogger = NULL,
391
                              MessageStructure messageStructure = Unstructured);
392
393
         /** Reinitialize all new setting for a local logger instance
394
          * @retval 0 on success
395
          * @retval 1 if logger does not exist
396
          */
397
         int reinitialize(LocalLoggerId loggerId,
398
                          Type type,
399
                          Level level,
400
                          const char * logFileName = NULL,
401
                          ExternalLogger* externalLogger = NULL,
402
                          MessageStructure messageStructure = Unstructured);
403
404
         /** Remove existing logger instance from map and destroy.
405
         * @retval 0 on success
406
         * @retval 1 if logger does not exist
407
         * @retval 2 if logger is still in use
408
         * @retval >2 on other failures
409
         */
410
         int remove(LocalLoggerId loggerId);
411
412
         /** Get pointer to ThreadData for given ID and increase use counter.
413
         * @returns NULL if ID does not exist. */
414
         ThreadData *getData(LocalLoggerId loggerId);
415
416
         /// Decrease use counter for given loggerId.
417
         void decreaseUseCount(LocalLoggerId loggerId);
418
419
      protected:
420
         /// Storage for Thread Local loggers and their use-counts.
421
         typedef HashMap<LocalLoggerId, std::pair<ThreadData*, int> > LoggerInstanceMap;
422
         LoggerInstanceMap mLoggerInstancesMap;
423
         /// Last used LocalLoggerId
424
         LocalLoggerId mLastLocalLoggerId;
425
         /// Mutex to synchronize access to Thread Local logger settings storage
426
         Mutex mLoggerInstancesMapMutex;
427
      };
428
429
      friend void ::freeLocalLogger(void* pThreadData);
430
      friend class LogStaticInitializer;
431
      static LocalLoggerMap mLocalLoggerMap;
432
      static ThreadIf::TlsKey* mLocalLoggerKey;
433
434
435
      /// DEPRECATED! Left for backward compatibility - use localLoggers instead
436
#ifdef LOG_ENABLE_THREAD_SETTING
437
      static HashMap<ThreadIf::Id, std::pair<ThreadSetting, bool> > mThreadToLevel;
438
      static HashMap<int, std::set<ThreadIf::Id> > mServiceToThreads;
439
      static ThreadIf::TlsKey* mLevelKey;
440
#endif
441
      static HashMap<int, Level> mServiceToLevel;
442
};
443
444
/** @brief Interface functor for external logging.
445
*/
446
class ExternalLogger
447
{
448
public:
449
0
   virtual ~ExternalLogger() {};
450
   /** return true to also do default logging, false to suppress default logging. */
451
   virtual bool operator()(Log::Level level,
452
      const Subsystem& subsystem, 
453
      const Data& appName,
454
      const char* file,
455
      int line,
456
      const Data& message,
457
      const Data& messageWithHeaders,
458
      const Data& instanceName) = 0;
459
};
460
461
/// Class to initialize Log class static variables.
462
class LogStaticInitializer {
463
public:
464
   LogStaticInitializer();
465
   ~LogStaticInitializer();
466
protected:
467
   static unsigned int mInstanceCounter;
468
    
469
#ifdef WIN32
470
   // LogStaticInitializer calls ThreadIf::tlsKeyCreate which 
471
   // relies on the static TlsDestructorInitializer having set
472
   // up mTlsDestructorsMutex which is used to Lock TLS access
473
   // Since the order of static initialization is not reliable,
474
   // we must make sure that TlsDestructorInitializer is initialized
475
   // before LogStaticInitializer is inizialized:
476
   TlsDestructorInitializer tlsDestructorInitializer;
477
#endif
478
};
479
static LogStaticInitializer _staticLogInit;
480
481
}
482
483
#endif
484
485
/* ====================================================================
486
 * The Vovida Software License, Version 1.0 
487
 * 
488
 * Copyright (c) 2000-2005.
489
 * 
490
 * Redistribution and use in source and binary forms, with or without
491
 * modification, are permitted provided that the following conditions
492
 * are met:
493
 * 
494
 * 1. Redistributions of source code must retain the above copyright
495
 *    notice, this list of conditions and the following disclaimer.
496
 * 
497
 * 2. Redistributions in binary form must reproduce the above copyright
498
 *    notice, this list of conditions and the following disclaimer in
499
 *    the documentation and/or other materials provided with the
500
 *    distribution.
501
 * 
502
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
503
 *    and "Vovida Open Communication Application Library (VOCAL)" must
504
 *    not be used to endorse or promote products derived from this
505
 *    software without prior written permission. For written
506
 *    permission, please contact vocal@vovida.org.
507
 *
508
 * 4. Products derived from this software may not be called "VOCAL", nor
509
 *    may "VOCAL" appear in their name, without prior written
510
 *    permission of Vovida Networks, Inc.
511
 * 
512
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
513
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
514
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
515
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
516
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
517
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
518
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
519
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
520
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
521
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
522
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
523
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
524
 * DAMAGE.
525
 * 
526
 * ====================================================================
527
 * 
528
 * This software consists of voluntary contributions made by Vovida
529
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
530
 * Inc.  For more information on Vovida Networks, Inc., please see
531
 * <http://www.vovida.org/>.
532
 *
533
 */