/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 | | */ |