/src/resiprocate/rutil/Log.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | #include "rutil/Socket.hxx" |
2 | | |
3 | | #include "rutil/ResipAssert.h" |
4 | | #include <chrono> |
5 | | #include <iomanip> |
6 | | #include <iostream> |
7 | | #include <fstream> |
8 | | #include <stdio.h> |
9 | | #include "rutil/Data.hxx" |
10 | | |
11 | | #ifndef WIN32 |
12 | | #include <sys/time.h> |
13 | | #endif |
14 | | |
15 | | #include <sys/types.h> |
16 | | #include <time.h> |
17 | | |
18 | | #include "rutil/Log.hxx" |
19 | | #include "rutil/Logger.hxx" |
20 | | #include "rutil/ParseBuffer.hxx" |
21 | | #include "rutil/ThreadIf.hxx" |
22 | | #include "rutil/Subsystem.hxx" |
23 | | #include "rutil/SysLogStream.hxx" |
24 | | #include "rutil/WinLeakCheck.hxx" |
25 | | |
26 | | #ifdef USE_FMT |
27 | | #include <fmt/format.h> |
28 | | #endif |
29 | | |
30 | | using namespace resip; |
31 | | using namespace std; |
32 | | |
33 | | const Data Log::delim(" | "); |
34 | | Log::ThreadData Log::mDefaultLoggerData(0, Log::Cout, Log::Info, NULL, NULL); |
35 | | Data Log::mAppName; |
36 | | Data Log::mInstanceName; |
37 | | Data Log::mHostname; |
38 | | Data Log::mFqdn; |
39 | | #ifndef WIN32 |
40 | | int Log::mSyslogFacility = LOG_DAEMON; |
41 | | #else |
42 | | int Log::mSyslogFacility = -1; |
43 | | #endif |
44 | 0 | #define RESIP_LOG_MAX_LINE_COUNT_DEFAULT 0 |
45 | 0 | #define RESIP_LOG_MAX_BYTE_COUNT_DEFAULT 0 |
46 | | unsigned int Log::MaxLineCount = RESIP_LOG_MAX_LINE_COUNT_DEFAULT; // no limit by default |
47 | | unsigned int Log::MaxByteCount = RESIP_LOG_MAX_BYTE_COUNT_DEFAULT; // no limit by default |
48 | | bool Log::KeepAllLogFiles = false; // do not keep all log files by default |
49 | | |
50 | | volatile short Log::touchCount = 0; |
51 | | |
52 | | |
53 | | /// DEPRECATED! Left for backward compatibility - use localLoggers instead |
54 | | #ifdef LOG_ENABLE_THREAD_SETTING |
55 | | HashMap<ThreadIf::Id, std::pair<Log::ThreadSetting, bool> > Log::mThreadToLevel; |
56 | | HashMap<int, std::set<ThreadIf::Id> > Log::mServiceToThreads; |
57 | | ThreadIf::TlsKey* Log::mLevelKey; |
58 | | #endif |
59 | | HashMap<int, Log::Level> Log::mServiceToLevel; |
60 | | |
61 | | Log::LocalLoggerMap Log::mLocalLoggerMap; |
62 | | ThreadIf::TlsKey* Log::mLocalLoggerKey; |
63 | | |
64 | | const char |
65 | | Log::mDescriptions[][32] = {"NONE", "EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG", "STACK", "CERR", ""}; |
66 | | |
67 | | const char |
68 | | Log::mCEEPri[][32] = { "", "CRIT", "CRIT", "CRIT", "ERROR", "WARN", "DEBUG", "DEBUG", "DEBUG", "DEBUG", "ERROR", ""}; |
69 | | |
70 | | #ifdef WIN32 |
71 | | #define LOG_EMERG 0 /* system is unusable */ |
72 | | #define LOG_ALERT 1 /* action must be taken immediately */ |
73 | | #define LOG_CRIT 2 /* critical conditions */ |
74 | | #define LOG_ERR 3 /* error conditions */ |
75 | | #define LOG_WARNING 4 /* warning conditions */ |
76 | | #define LOG_NOTICE 5 /* normal but significant condition */ |
77 | | #define LOG_INFO 6 /* informational */ |
78 | | #define LOG_DEBUG 7 /* debug-level messages */ |
79 | | #endif |
80 | | const int |
81 | | Log::mSyslogPriority[] = { 0, LOG_CRIT, LOG_CRIT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_DEBUG, LOG_ERR, 0 }; |
82 | | |
83 | | Mutex Log::_mutex; |
84 | | |
85 | | extern "C" |
86 | | { |
87 | | void freeThreadSetting(void* setting) |
88 | 0 | { |
89 | 0 | delete static_cast<Log::ThreadSetting*>(setting); |
90 | 0 | } |
91 | | |
92 | | void freeLocalLogger(void* pThreadData) |
93 | 0 | { |
94 | 0 | if (pThreadData) |
95 | 0 | { |
96 | | // There was some local logger installed. Decrease its use count before we |
97 | | // continue. |
98 | 0 | Log::mLocalLoggerMap.decreaseUseCount((static_cast<Log::ThreadData*>(pThreadData))->id()); |
99 | 0 | } |
100 | 0 | } |
101 | | } |
102 | | |
103 | | unsigned int LogStaticInitializer::mInstanceCounter=0; |
104 | | LogStaticInitializer::LogStaticInitializer() |
105 | 136 | { |
106 | 136 | if (mInstanceCounter++ == 0) |
107 | 2 | { |
108 | 2 | #ifdef LOG_ENABLE_THREAD_SETTING |
109 | 2 | Log::mLevelKey = new ThreadIf::TlsKey; |
110 | 2 | ThreadIf::tlsKeyCreate(*Log::mLevelKey, freeThreadSetting); |
111 | 2 | #endif |
112 | | |
113 | 2 | Log::mLocalLoggerKey = new ThreadIf::TlsKey; |
114 | 2 | ThreadIf::tlsKeyCreate(*Log::mLocalLoggerKey, freeLocalLogger); |
115 | 2 | } |
116 | 136 | } |
117 | | LogStaticInitializer::~LogStaticInitializer() |
118 | 0 | { |
119 | 0 | if (--mInstanceCounter == 0) |
120 | 0 | { |
121 | 0 | #ifdef LOG_ENABLE_THREAD_SETTING |
122 | 0 | ThreadIf::tlsKeyDelete(*Log::mLevelKey); |
123 | 0 | delete Log::mLevelKey; |
124 | 0 | #endif |
125 | |
|
126 | 0 | ThreadIf::tlsKeyDelete(*Log::mLocalLoggerKey); |
127 | 0 | delete Log::mLocalLoggerKey; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | void |
132 | | Log::initialize(const char* typed, const char* leveld, const char* appName, const char *logFileName, ExternalLogger* externalLogger, const char* syslogFacilityName, const char* messageStructure, const char* instanceName) |
133 | 0 | { |
134 | 0 | Log::initialize(Data(typed), Data(leveld), Data(appName), logFileName, externalLogger, syslogFacilityName, Data(messageStructure), Data(instanceName)); |
135 | 0 | } |
136 | | |
137 | | void |
138 | | Log::initialize(const Data& typed, const Data& leveld, const Data& appName, |
139 | | const char *logFileName, ExternalLogger* externalLogger, |
140 | | const Data& syslogFacilityName, |
141 | | const Data& messageStructure, |
142 | | const Data& instanceName) |
143 | 0 | { |
144 | 0 | Type type = Log::Cout; |
145 | 0 | if (isEqualNoCase(typed, "cout")) type = Log::Cout; |
146 | 0 | else if (isEqualNoCase(typed, "cerr")) type = Log::Cerr; |
147 | 0 | else if (isEqualNoCase(typed, "file")) type = Log::File; |
148 | 0 | #ifndef WIN32 |
149 | 0 | else type = Log::Syslog; |
150 | 0 | #endif |
151 | | |
152 | 0 | Level level = Log::Info; |
153 | 0 | level = toLevel(leveld); |
154 | |
|
155 | 0 | MessageStructure _messageStructure = Unstructured; |
156 | 0 | if (isEqualNoCase(messageStructure, "JSON_CEE")) |
157 | 0 | { |
158 | 0 | _messageStructure = JSON_CEE; |
159 | 0 | } |
160 | |
|
161 | 0 | Log::initialize(type, level, appName, logFileName, externalLogger, syslogFacilityName, _messageStructure, instanceName); |
162 | 0 | } |
163 | | |
164 | | int |
165 | | Log::parseSyslogFacilityName(const Data& facilityName) |
166 | 2 | { |
167 | 2 | #ifndef WIN32 |
168 | | /* In theory, some platforms may not have all the log facilities |
169 | | defined in syslog.h. Only LOG_USER and LOG_LOCAL[0-7] are considered |
170 | | mandatory. |
171 | | If the compile fails with errors in this method, then the unsupported |
172 | | facility names could be wrapped in conditional logic. |
173 | | */ |
174 | 2 | if(facilityName == "LOG_AUTH") |
175 | 0 | { |
176 | 0 | return LOG_AUTH; |
177 | 0 | } |
178 | 2 | else if(facilityName == "LOG_AUTHPRIV") |
179 | 0 | { |
180 | 0 | return LOG_AUTHPRIV; |
181 | 0 | } |
182 | 2 | else if(facilityName == "LOG_CRON") |
183 | 0 | { |
184 | 0 | return LOG_CRON; |
185 | 0 | } |
186 | 2 | else if(facilityName == "LOG_DAEMON") |
187 | 2 | { |
188 | 2 | return LOG_DAEMON; |
189 | 2 | } |
190 | 0 | else if(facilityName == "LOG_FTP") |
191 | 0 | { |
192 | 0 | return LOG_FTP; |
193 | 0 | } |
194 | 0 | else if(facilityName == "LOG_KERN") |
195 | 0 | { |
196 | 0 | return LOG_KERN; |
197 | 0 | } |
198 | 0 | else if(facilityName == "LOG_LOCAL0") |
199 | 0 | { |
200 | 0 | return LOG_LOCAL0; |
201 | 0 | } |
202 | 0 | else if(facilityName == "LOG_LOCAL1") |
203 | 0 | { |
204 | 0 | return LOG_LOCAL1; |
205 | 0 | } |
206 | 0 | else if(facilityName == "LOG_LOCAL2") |
207 | 0 | { |
208 | 0 | return LOG_LOCAL2; |
209 | 0 | } |
210 | 0 | else if(facilityName == "LOG_LOCAL3") |
211 | 0 | { |
212 | 0 | return LOG_LOCAL3; |
213 | 0 | } |
214 | 0 | else if(facilityName == "LOG_LOCAL4") |
215 | 0 | { |
216 | 0 | return LOG_LOCAL4; |
217 | 0 | } |
218 | 0 | else if(facilityName == "LOG_LOCAL5") |
219 | 0 | { |
220 | 0 | return LOG_LOCAL5; |
221 | 0 | } |
222 | 0 | else if(facilityName == "LOG_LOCAL6") |
223 | 0 | { |
224 | 0 | return LOG_LOCAL6; |
225 | 0 | } |
226 | 0 | else if(facilityName == "LOG_LOCAL7") |
227 | 0 | { |
228 | 0 | return LOG_LOCAL7; |
229 | 0 | } |
230 | 0 | else if(facilityName == "LOG_LPR") |
231 | 0 | { |
232 | 0 | return LOG_LPR; |
233 | 0 | } |
234 | 0 | else if(facilityName == "LOG_MAIL") |
235 | 0 | { |
236 | 0 | return LOG_MAIL; |
237 | 0 | } |
238 | 0 | else if(facilityName == "LOG_NEWS") |
239 | 0 | { |
240 | 0 | return LOG_NEWS; |
241 | 0 | } |
242 | 0 | else if(facilityName == "LOG_SYSLOG") |
243 | 0 | { |
244 | 0 | return LOG_SYSLOG; |
245 | 0 | } |
246 | 0 | else if(facilityName == "LOG_USER") |
247 | 0 | { |
248 | 0 | return LOG_USER; |
249 | 0 | } |
250 | 0 | else if(facilityName == "LOG_UUCP") |
251 | 0 | { |
252 | 0 | return LOG_UUCP; |
253 | 0 | } |
254 | 0 | #endif |
255 | | // Nothing matched or syslog not supported on this platform |
256 | 0 | return -1; |
257 | 2 | } |
258 | | |
259 | | void |
260 | | Log::initialize(Type type, Level level, const Data& appName, |
261 | | const char * logFileName, |
262 | | ExternalLogger* externalLogger, |
263 | | const Data& syslogFacilityName, |
264 | | MessageStructure messageStructure, |
265 | | const Data& instanceName) |
266 | 2 | { |
267 | 2 | { |
268 | 2 | Lock lock(_mutex); |
269 | 2 | mDefaultLoggerData.reset(); |
270 | | |
271 | 2 | mDefaultLoggerData.set(type, level, logFileName, externalLogger, messageStructure, instanceName); |
272 | | |
273 | 2 | ParseBuffer pb(appName); |
274 | 2 | pb.skipToEnd(); |
275 | | #ifdef _WIN32 |
276 | | pb.skipBackToChar('\\'); |
277 | | #else |
278 | 2 | pb.skipBackToChar('/'); |
279 | 2 | #endif |
280 | 2 | mAppName = pb.position(); |
281 | | |
282 | 2 | mInstanceName = instanceName; |
283 | | |
284 | 2 | #ifndef WIN32 |
285 | 2 | if (!syslogFacilityName.empty()) |
286 | 2 | { |
287 | 2 | mSyslogFacility = parseSyslogFacilityName(syslogFacilityName); |
288 | 2 | if(mSyslogFacility == -1) |
289 | 0 | { |
290 | 0 | mSyslogFacility = LOG_DAEMON; |
291 | 0 | if(type == Log::Syslog) |
292 | 0 | { |
293 | 0 | syslog(LOG_DAEMON | LOG_ERR, "invalid syslog facility name specified (%s), falling back to LOG_DAEMON", syslogFacilityName.c_str()); |
294 | 0 | } |
295 | 0 | } |
296 | 2 | } |
297 | | #else |
298 | | if (type == Syslog) |
299 | | { |
300 | | std::cerr << "syslog not supported on windows, using cout!" << std::endl; |
301 | | type = Cout; |
302 | | } |
303 | | #endif |
304 | | |
305 | 2 | char buffer[1024]; |
306 | 2 | buffer[1023] = '\0'; |
307 | 2 | if(gethostname(buffer, sizeof(buffer)) == -1) |
308 | 0 | { |
309 | 0 | mHostname = "?"; |
310 | 0 | } |
311 | 2 | else |
312 | 2 | { |
313 | 2 | mHostname = buffer; |
314 | 2 | } |
315 | | |
316 | | // Note: for Windows users, you must call initNetwork to initialize WinSock before calling |
317 | | // Log::initialize in order for getaddrinfo to be successful |
318 | 2 | { |
319 | 2 | struct addrinfo hints; |
320 | 2 | struct addrinfo* info = nullptr; |
321 | 2 | int gai_result; |
322 | | |
323 | 2 | memset (&hints, 0, sizeof (hints)); |
324 | 2 | hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6 */ |
325 | 2 | hints.ai_socktype = SOCK_STREAM; |
326 | 2 | hints.ai_flags = AI_CANONNAME; |
327 | | |
328 | 2 | if ((gai_result = getaddrinfo (buffer, 0, &hints, &info)) != 0) { |
329 | 0 | mFqdn = mHostname; |
330 | 2 | } else if (info == NULL) { |
331 | 0 | mFqdn = mHostname; |
332 | 2 | } else { |
333 | 2 | mFqdn = info->ai_canonname; |
334 | 2 | } |
335 | | |
336 | 2 | freeaddrinfo (info); |
337 | 2 | } |
338 | | |
339 | 2 | } |
340 | 2 | #ifdef ENABLE_LOG_REPOSITORY_DETAILS |
341 | 2 | GenericLog(resip::Subsystem::NONE, resip::Log::Info, << "logger initialized app=" << appName << " version=" << VERSION << " git-commit=" << RESIPROCATE_GIT_ID << " git-branch=" << RESIPROCATE_BRANCH_NAME); |
342 | | #else |
343 | | #ifdef VERSION |
344 | | GenericLog(resip::Subsystem::NONE, resip::Log::Info, << "logger initialized app=" << appName << " version=" << VERSION << " git repository details unknown"); |
345 | | #else |
346 | | GenericLog(resip::Subsystem::NONE, resip::Log::Info, << "logger initialized app=" << appName << " version and git repository details unknown"); |
347 | | #endif |
348 | | #endif |
349 | 2 | } |
350 | | |
351 | | void |
352 | | Log::initialize(Type type, |
353 | | Level level, |
354 | | const Data& appName, |
355 | | ExternalLogger& logger, |
356 | | const Data& syslogFacilityName, |
357 | | MessageStructure messageStructure, |
358 | | const Data& instanceName) |
359 | 0 | { |
360 | 0 | initialize(type, level, appName, 0, &logger, syslogFacilityName, messageStructure, instanceName); |
361 | 0 | } |
362 | | |
363 | | void |
364 | | Log::initialize(const ConfigParse& configParse, const Data& appName, ExternalLogger* externalLogger) |
365 | 0 | { |
366 | 0 | Log::setMaxByteCount(configParse.getConfigUnsignedLong("LogFileMaxBytes", RESIP_LOG_MAX_BYTE_COUNT_DEFAULT)); |
367 | |
|
368 | 0 | Log::setKeepAllLogFiles(configParse.getConfigBool("KeepAllLogFiles", false)); |
369 | |
|
370 | 0 | Data loggingType = configParse.getConfigData("LoggingType", "cout", true); |
371 | 0 | Data syslogFacilityName = configParse.getConfigData("SyslogFacility", "LOG_DAEMON", true); |
372 | | // Most applications now use LogLevel |
373 | | // Some applications had been using LoggingLevel, that is not deprecated |
374 | 0 | Data loggingLevel = configParse.getConfigData("LogLevel", |
375 | 0 | configParse.getConfigData("LoggingLevel", "INFO", true), true); |
376 | 0 | Data loggingFilename = configParse.getConfigData("LogFilename", |
377 | 0 | configParse.getConfigData("LoggingFilename", appName + Data(".log")), true); |
378 | 0 | configParse.AddBasePathIfRequired(loggingFilename); |
379 | 0 | Data loggingMessageStructure = configParse.getConfigData("LogMessageStructure", "Unstructured", true); |
380 | 0 | Data loggingInstanceName = configParse.getConfigData("LoggingInstanceName", "", true); |
381 | |
|
382 | 0 | Log::initialize( |
383 | 0 | loggingType, |
384 | 0 | loggingLevel, |
385 | 0 | appName.c_str(), |
386 | 0 | loggingFilename.c_str(), |
387 | 0 | externalLogger, |
388 | 0 | syslogFacilityName, |
389 | 0 | loggingMessageStructure, |
390 | 0 | loggingInstanceName); |
391 | |
|
392 | 0 | unsigned int loggingFileMaxLineCount = configParse.getConfigUnsignedLong("LogFileMaxLines", RESIP_LOG_MAX_LINE_COUNT_DEFAULT); |
393 | 0 | Log::setMaxLineCount(loggingFileMaxLineCount); |
394 | 0 | } |
395 | | |
396 | | void |
397 | | Log::setLevel(Level level) |
398 | 0 | { |
399 | 0 | Lock lock(_mutex); |
400 | 0 | getLoggerData().mLevel = level; |
401 | 0 | } |
402 | | |
403 | | void |
404 | | Log::setLevel(Level level, Subsystem& s) |
405 | 0 | { |
406 | 0 | Lock lock(_mutex); |
407 | 0 | s.setLevel(level); |
408 | 0 | } |
409 | | |
410 | | void |
411 | | Log::setLevel(Level level, Log::LocalLoggerId loggerId) |
412 | 0 | { |
413 | 0 | if (loggerId) |
414 | 0 | { |
415 | 0 | ThreadData *pData = mLocalLoggerMap.getData(loggerId); |
416 | 0 | if (pData) |
417 | 0 | { |
418 | | // Local logger found. Set logging level. |
419 | 0 | pData->mLevel = level; |
420 | | |
421 | | // We don't need local logger instance anymore. |
422 | 0 | mLocalLoggerMap.decreaseUseCount(loggerId); |
423 | 0 | pData = NULL; |
424 | 0 | } |
425 | 0 | } |
426 | 0 | else |
427 | 0 | { |
428 | 0 | Lock lock(_mutex); |
429 | 0 | mDefaultLoggerData.mLevel = level; |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | | Log::Level |
434 | | Log::level(Log::LocalLoggerId loggerId) |
435 | 0 | { |
436 | 0 | Level level; |
437 | 0 | ThreadData *pData; |
438 | 0 | if (loggerId && (pData = mLocalLoggerMap.getData(loggerId))) |
439 | 0 | { |
440 | | // Local logger found. Set logging level. |
441 | 0 | level = pData->mLevel; |
442 | | |
443 | | // We don't need local logger instance anymore. |
444 | 0 | mLocalLoggerMap.decreaseUseCount(loggerId); |
445 | 0 | pData = NULL; |
446 | 0 | } |
447 | 0 | else |
448 | 0 | { |
449 | 0 | Lock lock(_mutex); |
450 | 0 | level = mDefaultLoggerData.mLevel; |
451 | 0 | } |
452 | 0 | return level; |
453 | 0 | } |
454 | | |
455 | | void |
456 | | Log::setMaxLineCount(unsigned int maxLineCount) |
457 | 0 | { |
458 | 0 | Lock lock(_mutex); |
459 | 0 | getLoggerData().mMaxLineCount = maxLineCount; |
460 | 0 | } |
461 | | |
462 | | void |
463 | | Log::setMaxLineCount(unsigned int maxLineCount, Log::LocalLoggerId loggerId) |
464 | 0 | { |
465 | 0 | if (loggerId) |
466 | 0 | { |
467 | 0 | ThreadData *pData = mLocalLoggerMap.getData(loggerId); |
468 | 0 | if (pData) |
469 | 0 | { |
470 | | // Local logger found. Set logging level. |
471 | 0 | pData->mMaxLineCount = maxLineCount; |
472 | | |
473 | | // We don't need local logger instance anymore. |
474 | 0 | mLocalLoggerMap.decreaseUseCount(loggerId); |
475 | 0 | pData = NULL; |
476 | 0 | } |
477 | 0 | } |
478 | 0 | else |
479 | 0 | { |
480 | 0 | Lock lock(_mutex); |
481 | 0 | mDefaultLoggerData.mMaxLineCount = maxLineCount; |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | void |
486 | | Log::setMaxByteCount(unsigned int maxByteCount) |
487 | 0 | { |
488 | 0 | Lock lock(_mutex); |
489 | 0 | getLoggerData().mMaxByteCount = maxByteCount; |
490 | 0 | } |
491 | | |
492 | | void |
493 | | Log::setMaxByteCount(unsigned int maxByteCount, Log::LocalLoggerId loggerId) |
494 | 0 | { |
495 | 0 | if (loggerId) |
496 | 0 | { |
497 | 0 | ThreadData *pData = mLocalLoggerMap.getData(loggerId); |
498 | 0 | if (pData) |
499 | 0 | { |
500 | | // Local logger found. Set logging level. |
501 | 0 | pData->mMaxByteCount = maxByteCount; |
502 | | |
503 | | // We don't need local logger instance anymore. |
504 | 0 | mLocalLoggerMap.decreaseUseCount(loggerId); |
505 | 0 | pData = NULL; |
506 | 0 | } |
507 | 0 | } |
508 | 0 | else |
509 | 0 | { |
510 | 0 | Lock lock(_mutex); |
511 | 0 | mDefaultLoggerData.mMaxByteCount = maxByteCount; |
512 | 0 | } |
513 | 0 | } |
514 | | |
515 | | void |
516 | | Log::setKeepAllLogFiles(bool keepAllLogFiles) |
517 | 0 | { |
518 | 0 | Lock lock(_mutex); |
519 | 0 | getLoggerData().setKeepAllLogFiles(keepAllLogFiles); |
520 | 0 | } |
521 | | |
522 | | void |
523 | | Log::setKeepAllLogFiles(bool keepAllLogFiles, Log::LocalLoggerId loggerId) |
524 | 0 | { |
525 | 0 | if (loggerId) |
526 | 0 | { |
527 | 0 | ThreadData *pData = mLocalLoggerMap.getData(loggerId); |
528 | 0 | if (pData) |
529 | 0 | { |
530 | | // Local logger found. Set logging level. |
531 | 0 | pData->setKeepAllLogFiles(keepAllLogFiles); |
532 | | |
533 | | // We don't need local logger instance anymore. |
534 | 0 | mLocalLoggerMap.decreaseUseCount(loggerId); |
535 | 0 | pData = NULL; |
536 | 0 | } |
537 | 0 | } |
538 | 0 | else |
539 | 0 | { |
540 | 0 | Lock lock(_mutex); |
541 | 0 | mDefaultLoggerData.setKeepAllLogFiles(keepAllLogFiles); |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | const static Data log_("LOG_"); |
546 | | |
547 | | Data |
548 | | Log::toString(Level l) |
549 | 0 | { |
550 | 0 | return log_ + mDescriptions[l+1]; |
551 | 0 | } |
552 | | |
553 | | Log::Level |
554 | | Log::toLevel(const Data& l) |
555 | 0 | { |
556 | 0 | Data pri( l.prefix("LOG_") ? l.substr(4) : l); |
557 | |
|
558 | 0 | int i=0; |
559 | 0 | while (strlen(mDescriptions[i])) |
560 | 0 | { |
561 | 0 | if (isEqualNoCase(pri, Data(mDescriptions[i]))) |
562 | 0 | { |
563 | 0 | return Level(i-1); |
564 | 0 | } |
565 | 0 | i++; |
566 | 0 | } |
567 | | |
568 | 0 | cerr << "Choosing Debug level since string was not understood: " << l << endl; |
569 | 0 | return Log::Debug; |
570 | 0 | } |
571 | | |
572 | | Log::Type |
573 | | Log::toType(const Data& arg) |
574 | 0 | { |
575 | 0 | if (arg == "cout" || arg == "COUT") |
576 | 0 | { |
577 | 0 | return Log::Cout; |
578 | 0 | } |
579 | 0 | else if (arg == "cerr" || arg == "CERR") |
580 | 0 | { |
581 | 0 | return Log::Cerr; |
582 | 0 | } |
583 | 0 | else if (arg == "file" || arg == "FILE") |
584 | 0 | { |
585 | 0 | return Log::File; |
586 | 0 | } |
587 | 0 | else |
588 | 0 | { |
589 | 0 | return Log::Syslog; |
590 | 0 | } |
591 | 0 | } |
592 | | |
593 | | EncodeStream & |
594 | | Log::tags(Log::Level level, |
595 | | const Subsystem& subsystem, |
596 | | const char* pfile, |
597 | | int line, |
598 | | const char* methodName, |
599 | | EncodeStream& strm, |
600 | | MessageStructure messageStructure) |
601 | 0 | { |
602 | 0 | char buffer[256] = ""; |
603 | 0 | Data ts(Data::Borrow, buffer, sizeof(buffer)); |
604 | | #if defined( __APPLE__ ) |
605 | | uint64_t threadId; |
606 | | pthread_threadid_np(nullptr, &threadId); |
607 | | const char* file = pfile; |
608 | | #elif defined( WIN32 ) |
609 | | int threadId = (int)GetCurrentThreadId(); |
610 | | const char* file = pfile + strlen(pfile); |
611 | | while (file != pfile && |
612 | | *file != '\\') |
613 | | { |
614 | | --file; |
615 | | } |
616 | | if (file != pfile) |
617 | | { |
618 | | ++file; |
619 | | } |
620 | | #else // #if defined( WIN32 ) || defined( __APPLE__ ) |
621 | 0 | std::make_unsigned<pthread_t>::type threadId = pthread_self(); |
622 | 0 | const char* file = pfile; |
623 | 0 | #endif |
624 | |
|
625 | 0 | switch(messageStructure) |
626 | 0 | { |
627 | 0 | case JSON_CEE: |
628 | 0 | { |
629 | 0 | auto now = std::chrono::high_resolution_clock::now(); |
630 | 0 | std::time_t now_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
631 | 0 | auto now_ns = now.time_since_epoch().count() % 1000000000; |
632 | |
|
633 | 0 | if(resip::Log::getLoggerData().type() == Syslog) |
634 | 0 | { |
635 | 0 | strm << "@cee: "; |
636 | 0 | } |
637 | 0 | strm << "{"; |
638 | 0 | strm << "\"hostname\":\"" << mFqdn << "\","; |
639 | 0 | strm << "\"pri\":\"" << mCEEPri[level+1] << "\","; |
640 | 0 | strm << "\"syslog\":{"; |
641 | 0 | strm << "\"level\":" << mSyslogPriority[level+1]; |
642 | 0 | strm << "},"; // "syslog" |
643 | 0 | strm << "\"time\":\"" << std::put_time(gmtime(&now_t), "%FT%T.") |
644 | 0 | << std::setfill('0') << std::setw(9) << now_ns << "Z" << "\","; |
645 | 0 | strm << "\"pname\":\"" << mAppName << "\","; |
646 | 0 | if(!mInstanceName.empty()) |
647 | 0 | { |
648 | 0 | strm << "\"appname\":\"" << mInstanceName << "\","; |
649 | 0 | } |
650 | 0 | strm << "\"subsys\":\"" << subsystem << "\","; |
651 | 0 | strm << "\"proc\":{"; |
652 | | #ifdef WIN32 |
653 | | strm << "\"id\":\"" << GetCurrentProcessId() << "\","; |
654 | | #else |
655 | 0 | strm << "\"id\":\"" << getpid() << "\","; |
656 | 0 | #endif |
657 | 0 | strm << "\"tid\":" << threadId; |
658 | 0 | strm << "},"; // "proc" |
659 | 0 | strm << "\"file\":{"; |
660 | 0 | strm << "\"name\":\"" << file << "\","; |
661 | 0 | strm << "\"line\":" << line; |
662 | 0 | strm << "},"; // "file" |
663 | 0 | strm << "\"native\":{"; |
664 | 0 | strm << "\"function\":\"" << methodName << "\""; |
665 | 0 | strm << "},"; // "native" |
666 | 0 | strm << "\"msg\":\""; |
667 | 0 | } |
668 | 0 | break; |
669 | 0 | case Unstructured: |
670 | 0 | default: |
671 | 0 | if(resip::Log::getLoggerData().type() == Syslog) |
672 | 0 | { |
673 | 0 | strm // << mDescriptions[level+1] << Log::delim |
674 | | // << timestamp(ts) << Log::delim |
675 | | // << mHostname << Log::delim |
676 | | // << mAppName << Log::delim |
677 | 0 | << subsystem << Log::delim |
678 | 0 | << "0x" << std::hex << threadId << std::dec << Log::delim |
679 | 0 | << file << ":" << line; |
680 | 0 | } |
681 | 0 | else |
682 | 0 | { |
683 | 0 | strm << mDescriptions[level+1] << Log::delim |
684 | 0 | << timestamp(ts) << Log::delim |
685 | 0 | << mAppName; |
686 | 0 | if(!mInstanceName.empty()) |
687 | 0 | { |
688 | 0 | strm << '[' << mInstanceName << ']'; |
689 | 0 | } |
690 | 0 | strm << Log::delim |
691 | 0 | << subsystem << Log::delim |
692 | 0 | << "0x" << std::hex << threadId << std::dec << Log::delim |
693 | 0 | << file << ":" << line; |
694 | 0 | } |
695 | 0 | } |
696 | 0 | return strm; |
697 | 0 | } |
698 | | |
699 | | Data |
700 | | Log::timestamp() |
701 | 0 | { |
702 | 0 | char buffer[256] = ""; |
703 | 0 | Data result(Data::Borrow, buffer, sizeof(buffer)); |
704 | 0 | return timestamp(result); |
705 | 0 | } |
706 | | |
707 | | Data& |
708 | | Log::timestamp(Data& res) |
709 | 0 | { |
710 | 0 | char* datebuf = const_cast<char*>(res.data()); |
711 | 0 | const unsigned int datebufSize = 256; |
712 | 0 | res.clear(); |
713 | | |
714 | | #ifdef WIN32 |
715 | | int result = 1; |
716 | | SYSTEMTIME systemTime; |
717 | | struct { time_t tv_sec; int tv_usec; } tv = {0,0}; |
718 | | time(&tv.tv_sec); |
719 | | GetLocalTime(&systemTime); |
720 | | tv.tv_usec = systemTime.wMilliseconds * 1000; |
721 | | #else |
722 | 0 | struct tm localTimeResult; |
723 | 0 | struct timeval tv; |
724 | 0 | int result = gettimeofday (&tv, NULL); |
725 | 0 | #endif |
726 | |
|
727 | 0 | if (result == -1) |
728 | 0 | { |
729 | | /* If we can't get the time of day, don't print a timestamp. |
730 | | Under Unix, this will never happen: gettimeofday can fail only |
731 | | if the timezone is invalid which it can't be, since it is |
732 | | uninitialized]or if tv or tz are invalid pointers. */ |
733 | 0 | datebuf [0] = 0; |
734 | 0 | } |
735 | 0 | else |
736 | 0 | { |
737 | | /* The tv_sec field represents the number of seconds passed since |
738 | | the Epoch, which is exactly the argument gettimeofday needs. */ |
739 | 0 | const time_t timeInSeconds = (time_t) tv.tv_sec; |
740 | 0 | strftime (datebuf, |
741 | 0 | datebufSize, |
742 | 0 | "%Y%m%d-%H%M%S", /* guaranteed to fit in 256 chars, |
743 | | hence don't check return code */ |
744 | | #ifdef WIN32 |
745 | | localtime (&timeInSeconds)); // Thread safe call on Windows |
746 | | #else |
747 | 0 | localtime_r (&timeInSeconds, &localTimeResult)); // Thread safe version of localtime on linux |
748 | 0 | #endif |
749 | 0 | } |
750 | | |
751 | 0 | char msbuf[5]; |
752 | | /* Dividing (without remainder) by 1000 rounds the microseconds |
753 | | measure to the nearest millisecond. */ |
754 | 0 | result = snprintf(msbuf, 5, ".%3.3ld", long(tv.tv_usec / 1000)); |
755 | 0 | if(result < 0) |
756 | 0 | { |
757 | | // snprint can error (negative return code) and the compiler now generates a warning |
758 | | // if we don't act on the return code |
759 | 0 | memcpy(msbuf, "0000", 5); |
760 | 0 | } |
761 | |
|
762 | 0 | int datebufCharsRemaining = datebufSize - (int)strlen(datebuf); |
763 | | #if defined(WIN32) && defined(_M_ARM) |
764 | | // There is a bug under ARM with strncat - we use strcat instead - buffer is plenty large accomdate our timestamp, no |
765 | | // real need to be safe here anyway. |
766 | | strcat(datebuf, msbuf); |
767 | | #else |
768 | 0 | strncat (datebuf, msbuf, datebufCharsRemaining - 1); |
769 | 0 | #endif |
770 | 0 | datebuf[datebufSize - 1] = '\0'; /* Just in case strncat truncated msbuf, |
771 | | thereby leaving its last character at |
772 | | the end, instead of a null terminator */ |
773 | | |
774 | | // ugh, resize the Data |
775 | 0 | res.at(strlen(datebuf)-1); |
776 | 0 | return res; |
777 | 0 | } |
778 | | |
779 | | Log::Level |
780 | | Log::getServiceLevel(int service) |
781 | 0 | { |
782 | 0 | Lock lock(_mutex); |
783 | 0 | HashMap<int, Level>::iterator res = Log::mServiceToLevel.find(service); |
784 | 0 | if(res == Log::mServiceToLevel.end()) |
785 | 0 | { |
786 | | //!dcm! -- should perhaps throw an exception here, instead of setting a |
787 | | //default level of LOG_ERROR, but nobody uses this yet |
788 | 0 | Log::mServiceToLevel[service] = Err; |
789 | 0 | return Err; |
790 | 0 | } |
791 | 0 | return res->second; |
792 | 0 | } |
793 | | |
794 | | const Log::ThreadSetting* |
795 | | Log::getThreadSetting() |
796 | 0 | { |
797 | | #ifndef LOG_ENABLE_THREAD_SETTING |
798 | | return 0; |
799 | | #else |
800 | 0 | ThreadSetting* setting = static_cast<ThreadSetting*>(ThreadIf::tlsGetValue(*Log::mLevelKey)); |
801 | 0 | if (setting == 0) |
802 | 0 | { |
803 | 0 | return 0; |
804 | 0 | } |
805 | 0 | if (Log::touchCount > 0) |
806 | 0 | { |
807 | 0 | Lock lock(_mutex); |
808 | 0 | ThreadIf::Id thread = ThreadIf::selfId(); |
809 | 0 | HashMap<ThreadIf::Id, pair<ThreadSetting, bool> >::iterator res = Log::mThreadToLevel.find(thread); |
810 | 0 | resip_assert(res != Log::mThreadToLevel.end()); |
811 | 0 | if (res->second.second) |
812 | 0 | { |
813 | 0 | setting->mLevel = res->second.first.mLevel; |
814 | 0 | res->second.second = false; |
815 | 0 | touchCount--; |
816 | | // cerr << "**Log::getThreadSetting:touchCount: " << Log::touchCount << "**" << endl; |
817 | | |
818 | | //cerr << "touchcount decremented" << endl; |
819 | 0 | } |
820 | 0 | } |
821 | 0 | return setting; |
822 | 0 | #endif |
823 | 0 | } |
824 | | |
825 | | void |
826 | | Log::setThreadSetting(int serv) |
827 | 0 | { |
828 | 0 | Log::setThreadSetting(ThreadSetting(serv, getServiceLevel(serv))); |
829 | 0 | } |
830 | | |
831 | | void |
832 | | Log::setThreadSetting(int serv, Log::Level l) |
833 | 0 | { |
834 | 0 | Log::setThreadSetting(ThreadSetting(serv, l)); |
835 | 0 | } |
836 | | |
837 | | void |
838 | | Log::setThreadSetting(ThreadSetting info) |
839 | 0 | { |
840 | | #ifndef LOG_ENABLE_THREAD_SETTING |
841 | | resip_assert(0); |
842 | | #else |
843 | | //cerr << "Log::setThreadSetting: " << "service: " << info.service << " level " << toString(info.level) << " for " << pthread_self() << endl; |
844 | 0 | ThreadIf::Id thread = ThreadIf::selfId(); |
845 | 0 | ThreadIf::tlsSetValue(*mLevelKey, (void *) new ThreadSetting(info)); |
846 | 0 | Lock lock(_mutex); |
847 | |
|
848 | 0 | if (Log::mThreadToLevel.find(thread) != Log::mThreadToLevel.end()) |
849 | 0 | { |
850 | 0 | if (Log::mThreadToLevel[thread].second == true) |
851 | 0 | { |
852 | 0 | touchCount--; |
853 | 0 | } |
854 | 0 | } |
855 | 0 | Log::mThreadToLevel[thread].first = info; |
856 | 0 | Log::mThreadToLevel[thread].second = false; |
857 | 0 | Log::mServiceToThreads[info.mService].insert(thread); |
858 | 0 | #endif |
859 | 0 | } |
860 | | |
861 | | void |
862 | | Log::setServiceLevel(int service, Level l) |
863 | 0 | { |
864 | 0 | Lock lock(_mutex); |
865 | 0 | Log::mServiceToLevel[service] = l; |
866 | | #ifndef LOG_ENABLE_THREAD_SETTING |
867 | | resip_assert(0); |
868 | | #else |
869 | 0 | set<ThreadIf::Id>& threads = Log::mServiceToThreads[service]; |
870 | 0 | for (set<ThreadIf::Id>::iterator i = threads.begin(); i != threads.end(); i++) |
871 | 0 | { |
872 | 0 | Log::mThreadToLevel[*i].first.mLevel = l; |
873 | 0 | Log::mThreadToLevel[*i].second = true; |
874 | 0 | } |
875 | 0 | Log::touchCount += (short)threads.size(); |
876 | 0 | #endif |
877 | | // cerr << "**Log::setServiceLevel:touchCount: " << Log::touchCount << "**" << endl; |
878 | 0 | } |
879 | | |
880 | | Log::LocalLoggerId Log::localLoggerCreate(Log::Type type, |
881 | | Log::Level level, |
882 | | const char * logFileName, |
883 | | ExternalLogger* externalLogger, |
884 | | MessageStructure messageStructure) |
885 | 0 | { |
886 | 0 | return mLocalLoggerMap.create(type, level, logFileName, externalLogger, messageStructure); |
887 | 0 | } |
888 | | |
889 | | int Log::localLoggerReinitialize(Log::LocalLoggerId loggerId, |
890 | | Log::Type type, |
891 | | Log::Level level, |
892 | | const char * logFileName, |
893 | | ExternalLogger* externalLogger, |
894 | | MessageStructure messageStructure) |
895 | 0 | { |
896 | 0 | return mLocalLoggerMap.reinitialize(loggerId, type, level, logFileName, externalLogger, messageStructure); |
897 | 0 | } |
898 | | |
899 | | int Log::localLoggerRemove(Log::LocalLoggerId loggerId) |
900 | 0 | { |
901 | 0 | return mLocalLoggerMap.remove(loggerId); |
902 | 0 | } |
903 | | |
904 | | int Log::setThreadLocalLogger(Log::LocalLoggerId loggerId) |
905 | 0 | { |
906 | 0 | ThreadData* pData = static_cast<ThreadData*>(ThreadIf::tlsGetValue(*Log::mLocalLoggerKey)); |
907 | 0 | if (pData) |
908 | 0 | { |
909 | | // There was some local logger installed. Decrease its use count before we |
910 | | // continue. |
911 | 0 | mLocalLoggerMap.decreaseUseCount(pData->id()); |
912 | 0 | pData = NULL; |
913 | 0 | } |
914 | 0 | if (loggerId) |
915 | 0 | { |
916 | 0 | pData = mLocalLoggerMap.getData(loggerId); |
917 | 0 | } |
918 | 0 | ThreadIf::tlsSetValue(*mLocalLoggerKey, (void *) pData); |
919 | 0 | return (loggerId == 0) || (pData != NULL)?0:1; |
920 | 0 | } |
921 | | |
922 | | std::ostream& |
923 | | Log::Instance(unsigned int bytesToWrite) |
924 | 0 | { |
925 | 0 | return getLoggerData().Instance(bytesToWrite); |
926 | 0 | } |
927 | | |
928 | | void |
929 | | Log::reset() |
930 | 0 | { |
931 | 0 | getLoggerData().reset(); |
932 | 0 | } |
933 | | |
934 | | #ifndef WIN32 |
935 | | void |
936 | | Log::droppingPrivileges(uid_t uid, pid_t pid) |
937 | 0 | { |
938 | 0 | getLoggerData().droppingPrivileges(uid, pid); |
939 | 0 | } |
940 | | #endif |
941 | | |
942 | | bool |
943 | | Log::isLogging(Log::Level level, const resip::Subsystem& sub) |
944 | 1.34M | { |
945 | 1.34M | if (sub.getLevel() != Log::None) |
946 | 0 | { |
947 | 0 | return level <= sub.getLevel(); |
948 | 0 | } |
949 | 1.34M | else |
950 | 1.34M | { |
951 | 1.34M | return (level <= Log::getLoggerData().mLevel); |
952 | 1.34M | } |
953 | 1.34M | } |
954 | | |
955 | | void |
956 | | Log::OutputToWin32DebugWindow(const Data& result) |
957 | 0 | { |
958 | | #ifdef WIN32 |
959 | | const char *text = result.c_str(); |
960 | | #ifdef UNDER_CE |
961 | | LPWSTR lpwstrText = resip::ToWString(text); |
962 | | OutputDebugStringW(lpwstrText); |
963 | | FreeWString(lpwstrText); |
964 | | #else |
965 | | OutputDebugStringA(text); |
966 | | #endif |
967 | | #endif |
968 | 0 | } |
969 | | |
970 | | Log::LocalLoggerId Log::LocalLoggerMap::create(Log::Type type, |
971 | | Log::Level level, |
972 | | const char * logFileName, |
973 | | ExternalLogger* externalLogger, |
974 | | MessageStructure messageStructure) |
975 | 0 | { |
976 | 0 | Lock lock(mLoggerInstancesMapMutex); |
977 | 0 | Log::LocalLoggerId id = ++mLastLocalLoggerId; |
978 | 0 | Log::ThreadData *pNewData = new Log::ThreadData(id, type, level, logFileName, |
979 | 0 | externalLogger, messageStructure); |
980 | 0 | mLoggerInstancesMap[id].first = pNewData; |
981 | 0 | mLoggerInstancesMap[id].second = 0; |
982 | 0 | return id; |
983 | 0 | } |
984 | | |
985 | | int Log::LocalLoggerMap::reinitialize(Log::LocalLoggerId loggerId, |
986 | | Log::Type type, |
987 | | Log::Level level, |
988 | | const char * logFileName, |
989 | | ExternalLogger* externalLogger, |
990 | | MessageStructure messageStructure) |
991 | 0 | { |
992 | 0 | Lock lock(mLoggerInstancesMapMutex); |
993 | 0 | LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId); |
994 | 0 | if (it == mLoggerInstancesMap.end()) |
995 | 0 | { |
996 | | // No such logger ID |
997 | 0 | std::cerr << "Log::LocalLoggerMap::remove(): Unknown local logger id=" << loggerId << std::endl; |
998 | 0 | return 1; |
999 | 0 | } |
1000 | 0 | it->second.first->reset(); |
1001 | 0 | it->second.first->set(type, level, logFileName, externalLogger, messageStructure); |
1002 | 0 | return 0; |
1003 | 0 | } |
1004 | | |
1005 | | int Log::LocalLoggerMap::remove(Log::LocalLoggerId loggerId) |
1006 | 0 | { |
1007 | 0 | Lock lock(mLoggerInstancesMapMutex); |
1008 | 0 | LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId); |
1009 | 0 | if (it == mLoggerInstancesMap.end()) |
1010 | 0 | { |
1011 | | // No such logger ID |
1012 | 0 | std::cerr << "Log::LocalLoggerMap::remove(): Unknown local logger id=" << loggerId << std::endl; |
1013 | 0 | return 1; |
1014 | 0 | } |
1015 | 0 | if (it->second.second > 0) |
1016 | 0 | { |
1017 | | // Non-zero use-count. |
1018 | 0 | std::cerr << "Log::LocalLoggerMap::remove(): Use count is non-zero (" << it->second.second << ")!" << std::endl; |
1019 | 0 | return 2; |
1020 | 0 | } |
1021 | 0 | delete it->second.first; // delete ThreadData |
1022 | 0 | mLoggerInstancesMap.erase(it); |
1023 | 0 | return 0; |
1024 | 0 | } |
1025 | | |
1026 | | Log::ThreadData *Log::LocalLoggerMap::getData(Log::LocalLoggerId loggerId) |
1027 | 0 | { |
1028 | 0 | Lock lock(mLoggerInstancesMapMutex); |
1029 | 0 | LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId); |
1030 | 0 | if (it == mLoggerInstancesMap.end()) |
1031 | 0 | { |
1032 | | // No such logger ID |
1033 | 0 | return NULL; |
1034 | 0 | } |
1035 | 0 | it->second.second++; |
1036 | 0 | return it->second.first; |
1037 | 0 | } |
1038 | | |
1039 | | void Log::LocalLoggerMap::decreaseUseCount(Log::LocalLoggerId loggerId) |
1040 | 0 | { |
1041 | 0 | Lock lock(mLoggerInstancesMapMutex); |
1042 | 0 | LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId); |
1043 | 0 | if (it != mLoggerInstancesMap.end()) |
1044 | 0 | { |
1045 | 0 | it->second.second--; |
1046 | 0 | resip_assert(it->second.second >= 0); |
1047 | 0 | } |
1048 | 0 | } |
1049 | | |
1050 | | |
1051 | | Log::Guard::Guard(resip::Log::Level level, |
1052 | | const resip::Subsystem& subsystem, |
1053 | | const char* file, |
1054 | | int line, |
1055 | | const char* methodName) : |
1056 | | mLevel(level), |
1057 | | mSubsystem(subsystem), |
1058 | | mFile(file), |
1059 | | mLine(line), |
1060 | | mMethodName(methodName), |
1061 | | mData(Data::Borrow, mBuffer, sizeof(mBuffer)), |
1062 | | mStream(mData.clear()) |
1063 | 0 | { |
1064 | | |
1065 | 0 | if (resip::Log::getLoggerData().mType != resip::Log::OnlyExternalNoHeaders) |
1066 | 0 | { |
1067 | 0 | MessageStructure messageStructure = resip::Log::getLoggerData().mMessageStructure; |
1068 | 0 | if(messageStructure == Unstructured) |
1069 | 0 | { |
1070 | 0 | Log::tags(mLevel, mSubsystem, mFile, mLine, mMethodName, mStream, messageStructure); |
1071 | 0 | mStream << resip::Log::delim; |
1072 | 0 | mStream.flush(); |
1073 | 0 | } |
1074 | |
|
1075 | 0 | mHeaderLength = mData.size(); |
1076 | 0 | } |
1077 | 0 | else |
1078 | 0 | { |
1079 | 0 | mHeaderLength = 0; |
1080 | 0 | } |
1081 | 0 | } |
1082 | | |
1083 | | Log::Guard::~Guard() |
1084 | 0 | { |
1085 | 0 | MessageStructure messageStructure = resip::Log::getLoggerData().mMessageStructure; |
1086 | 0 | if(messageStructure == JSON_CEE) |
1087 | 0 | { |
1088 | 0 | mStream.flush(); |
1089 | 0 | Data msg; |
1090 | 0 | oDataStream o(msg); |
1091 | | // add the JSON message attributes |
1092 | 0 | Log::tags(mLevel, mSubsystem, mFile, mLine, mMethodName, o, messageStructure); |
1093 | | |
1094 | | // JSON encode the message body |
1095 | | // FIXME - this could be done on the fly in DataStream |
1096 | |
|
1097 | 0 | static const char *json_special = "\"\\/\b\f\n\r\t"; |
1098 | 0 | static const char *json_special_replace = "\"\\/bfnrt"; |
1099 | 0 | const char* _data = mData.data(); |
1100 | 0 | for(Data::size_type i = 0; i < mData.size(); i++) |
1101 | 0 | { |
1102 | 0 | const char& c = _data[i]; |
1103 | 0 | const char *special = strchr (json_special, c); |
1104 | 0 | if (special != NULL) |
1105 | 0 | { |
1106 | 0 | const char *replace = json_special_replace + (special - json_special); |
1107 | 0 | o << '\\' << *replace; |
1108 | 0 | } |
1109 | 0 | else if (c < 0x20) |
1110 | 0 | { |
1111 | | /* Everything below 0x20 must be escaped */ |
1112 | 0 | o << "\\u00" << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)c; |
1113 | 0 | } |
1114 | 0 | else |
1115 | 0 | { |
1116 | 0 | o << _data[i]; |
1117 | 0 | } |
1118 | 0 | } |
1119 | | // add the trailing JSON |
1120 | 0 | o << "\"}"; |
1121 | 0 | o.flush(); |
1122 | |
|
1123 | 0 | mData.takeBuf(msg); |
1124 | 0 | } |
1125 | |
|
1126 | 0 | mStream.flush(); |
1127 | |
|
1128 | 0 | if (resip::Log::getExternal()) |
1129 | 0 | { |
1130 | 0 | const resip::Data rest(resip::Data::Share, |
1131 | 0 | mData.data() + mHeaderLength, |
1132 | 0 | (int)mData.size() - mHeaderLength); |
1133 | 0 | if (!(*resip::Log::getExternal())(mLevel, |
1134 | 0 | mSubsystem, |
1135 | 0 | resip::Log::getAppName(), |
1136 | 0 | mFile, |
1137 | 0 | mLine, |
1138 | 0 | rest, |
1139 | 0 | mData, |
1140 | 0 | mInstanceName)) |
1141 | 0 | { |
1142 | 0 | return; |
1143 | 0 | } |
1144 | 0 | } |
1145 | | |
1146 | 0 | Type logType = resip::Log::getLoggerData().mType; |
1147 | |
|
1148 | 0 | if(logType == resip::Log::OnlyExternal || |
1149 | 0 | logType == resip::Log::OnlyExternalNoHeaders) |
1150 | 0 | { |
1151 | 0 | return; |
1152 | 0 | } |
1153 | | |
1154 | 0 | resip::Lock lock(resip::Log::_mutex); |
1155 | | // !dlb! implement VSDebugWindow as an external logger |
1156 | 0 | if (logType == resip::Log::VSDebugWindow) |
1157 | 0 | { |
1158 | 0 | mData += "\r\n"; |
1159 | 0 | OutputToWin32DebugWindow(mData); |
1160 | 0 | } |
1161 | 0 | else |
1162 | 0 | { |
1163 | | // endl is magic in syslog -- so put it here |
1164 | 0 | std::ostream& _instance = Instance((int)mData.size()+2); |
1165 | 0 | if (logType == resip::Log::Syslog) |
1166 | 0 | { |
1167 | 0 | _instance << mLevel; |
1168 | 0 | } |
1169 | 0 | _instance << mData << std::endl; |
1170 | 0 | } |
1171 | 0 | } |
1172 | | |
1173 | | std::ostream& |
1174 | | Log::ThreadData::Instance(unsigned int bytesToWrite) |
1175 | 0 | { |
1176 | | // std::cerr << "Log::ThreadData::Instance() id=" << mId << " type=" << mType << std::endl; |
1177 | 0 | switch (mType) |
1178 | 0 | { |
1179 | 0 | case Log::Syslog: |
1180 | 0 | if (mLogger == 0) |
1181 | 0 | { |
1182 | 0 | mLogger = new SysLogStream(mAppName, mSyslogFacility); |
1183 | 0 | } |
1184 | 0 | return *mLogger; |
1185 | | |
1186 | 0 | case Log::Cerr: |
1187 | 0 | return std::cerr; |
1188 | | |
1189 | 0 | case Log::Cout: |
1190 | 0 | return std::cout; |
1191 | | |
1192 | 0 | case Log::File: |
1193 | 0 | if (mLogger == 0 || |
1194 | 0 | (maxLineCount() && mLineCount >= maxLineCount()) || |
1195 | 0 | (maxByteCount() && ((unsigned int)mLogger->tellp() + bytesToWrite) >= maxByteCount())) |
1196 | 0 | { |
1197 | 0 | Data logFileName(mLogFileName != "" ? mLogFileName : "resiprocate.log"); |
1198 | 0 | if (mLogger) |
1199 | 0 | { |
1200 | 0 | if (keepAllLogFiles()) |
1201 | 0 | { |
1202 | 0 | char buffer[256]; |
1203 | 0 | Data ts(Data::Borrow, buffer, sizeof(buffer)); |
1204 | 0 | Data oldLogFileName(logFileName + "_" + timestamp(ts)); |
1205 | |
|
1206 | 0 | delete mLogger; |
1207 | | |
1208 | | // Keep all log files, rename the log file with timestamp |
1209 | 0 | rename(logFileName.c_str(), oldLogFileName.c_str()); |
1210 | 0 | } |
1211 | 0 | else |
1212 | 0 | { |
1213 | 0 | Data oldLogFileName(logFileName + ".old"); |
1214 | 0 | delete mLogger; |
1215 | | // Keep one backup file: Delete .old file, Rename log file to .old |
1216 | | // Could be expanded in the future to keep X backup log files |
1217 | 0 | remove(oldLogFileName.c_str()); |
1218 | 0 | rename(logFileName.c_str(), oldLogFileName.c_str()); |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 | mLogger = new std::ofstream(logFileName.c_str(), std::ios_base::out | std::ios_base::app); |
1222 | 0 | mLineCount = 0; |
1223 | 0 | } |
1224 | 0 | mLineCount++; |
1225 | 0 | return *mLogger; |
1226 | 0 | default: |
1227 | 0 | resip_assert(0); |
1228 | 0 | return std::cout; |
1229 | 0 | } |
1230 | 0 | } |
1231 | | |
1232 | | void |
1233 | | Log::ThreadData::set(Type type, Level level, |
1234 | | const char* logFileName, |
1235 | | ExternalLogger* pExternalLogger, |
1236 | | MessageStructure messageStructure, |
1237 | | const Data& instanceName) |
1238 | 2 | { |
1239 | 2 | mType = type; |
1240 | 2 | mLevel = level; |
1241 | | |
1242 | 2 | if (logFileName) |
1243 | 0 | { |
1244 | | #ifdef USE_FMT |
1245 | | fmt::memory_buffer _loggingFilename; |
1246 | | fmt::format_to(std::back_inserter(_loggingFilename), |
1247 | | logFileName, |
1248 | | #ifdef WIN32 |
1249 | | fmt::arg("pid", (int)GetCurrentProcess()), |
1250 | | #else |
1251 | | fmt::arg("pid", getpid()), |
1252 | | #endif |
1253 | | fmt::arg("timestamp", time(0))); |
1254 | | mLogFileName = Data(_loggingFilename.data(), _loggingFilename.size()); |
1255 | | #else |
1256 | 0 | mLogFileName = logFileName; |
1257 | 0 | mLogFileName.replace("{timestamp}", Data((uint64_t)time(0))); |
1258 | | #ifdef WIN32 |
1259 | | mLogFileName.replace("{pid}", Data((int)GetCurrentProcess())); |
1260 | | #else |
1261 | 0 | mLogFileName.replace("{pid}", Data(getpid())); |
1262 | 0 | #endif |
1263 | 0 | #endif |
1264 | 0 | } |
1265 | 2 | else |
1266 | 2 | { |
1267 | 2 | mLogFileName.clear(); |
1268 | 2 | } |
1269 | 2 | mExternalLogger = pExternalLogger; |
1270 | 2 | mMessageStructure = messageStructure; |
1271 | 2 | mInstanceName = instanceName; |
1272 | 2 | } |
1273 | | |
1274 | | void |
1275 | | Log::ThreadData::reset() |
1276 | 2 | { |
1277 | 2 | delete mLogger; |
1278 | 2 | mLogger = NULL; |
1279 | 2 | } |
1280 | | |
1281 | | #ifndef WIN32 |
1282 | | void |
1283 | | Log::ThreadData::droppingPrivileges(uid_t uid, pid_t pid) |
1284 | 0 | { |
1285 | 0 | if(mType == Log::File) |
1286 | 0 | { |
1287 | 0 | Data logFileName(mLogFileName != "" ? mLogFileName : "resiprocate.log"); |
1288 | 0 | if(chown(logFileName.c_str(), uid, pid) < 0) |
1289 | 0 | { |
1290 | | // Some error occurred |
1291 | 0 | std::cerr << "ERROR: chown failed on " << logFileName << std::endl; |
1292 | 0 | } |
1293 | 0 | } |
1294 | 0 | } |
1295 | | #endif |
1296 | | |
1297 | | /* ==================================================================== |
1298 | | * The Vovida Software License, Version 1.0 |
1299 | | * |
1300 | | * Copyright (c) 2000-2005 |
1301 | | * |
1302 | | * Redistribution and use in source and binary forms, with or without |
1303 | | * modification, are permitted provided that the following conditions |
1304 | | * are met: |
1305 | | * |
1306 | | * 1. Redistributions of source code must retain the above copyright |
1307 | | * notice, this list of conditions and the following disclaimer. |
1308 | | * |
1309 | | * 2. Redistributions in binary form must reproduce the above copyright |
1310 | | * notice, this list of conditions and the following disclaimer in |
1311 | | * the documentation and/or other materials provided with the |
1312 | | * distribution. |
1313 | | * |
1314 | | * 3. The names "VOCAL", "Vovida Open Communication Application Library", |
1315 | | * and "Vovida Open Communication Application Library (VOCAL)" must |
1316 | | * not be used to endorse or promote products derived from this |
1317 | | * software without prior written permission. For written |
1318 | | * permission, please contact vocal@vovida.org. |
1319 | | * |
1320 | | * 4. Products derived from this software may not be called "VOCAL", nor |
1321 | | * may "VOCAL" appear in their name, without prior written |
1322 | | * permission of Vovida Networks, Inc. |
1323 | | * |
1324 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
1325 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
1326 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
1327 | | * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
1328 | | * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
1329 | | * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
1330 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1331 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1332 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
1333 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1334 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
1335 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
1336 | | * DAMAGE. |
1337 | | * |
1338 | | * ==================================================================== |
1339 | | * |
1340 | | * This software consists of voluntary contributions made by Vovida |
1341 | | * Networks, Inc. and many individuals on behalf of Vovida Networks, |
1342 | | * Inc. For more information on Vovida Networks, Inc., please see |
1343 | | * <http://www.vovida.org/>. |
1344 | | * |
1345 | | */ |