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