Coverage Report

Created: 2025-11-16 06:32

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