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