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