761a59f7e8ca1366255918cbfe69edf559c0fa8a
[public/netxms.git] / src / libnetxms / log.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Utility Library
4 ** Copyright (C) 2003-2017 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published
8 ** by the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: log.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <nxstat.h>
26 #include "debug_tag_tree.h"
27
28 #if HAVE_SYSLOG_H
29 #include <syslog.h>
30 #endif
31
32 #define MAX_LOG_HISTORY_SIZE 128
33
34 /**
35 * Static data
36 */
37 #ifdef _WIN32
38 static HANDLE m_eventLogHandle = NULL;
39 static HMODULE m_msgModuleHandle = NULL;
40 #else
41 static unsigned int m_numMessages;
42 static const TCHAR **m_messages;
43 static char s_syslogName[64];
44 #endif
45 static TCHAR m_logFileName[MAX_PATH] = _T("");
46 static FILE *m_logFileHandle = NULL;
47 static MUTEX m_mutexLogAccess = INVALID_MUTEX_HANDLE;
48 static UINT32 s_flags = 0;
49 static DWORD s_debugMsg = 0;
50 static DWORD s_debugMsgTag = 0;
51 static DWORD s_genericMsg = 0;
52 static int s_rotationMode = NXLOG_ROTATION_BY_SIZE;
53 static UINT64 s_maxLogSize = 4096 * 1024; // 4 MB
54 static int s_logHistorySize = 4; // Keep up 4 previous log files
55 static TCHAR s_dailyLogSuffixTemplate[64] = _T("%Y%m%d");
56 static time_t m_currentDayStart = 0;
57 static NxLogConsoleWriter m_consoleWriter = (NxLogConsoleWriter)_tprintf;
58 static String s_logBuffer;
59 static THREAD s_writerThread = INVALID_THREAD_HANDLE;
60 static CONDITION s_writerStopCondition = INVALID_CONDITION_HANDLE;
61 static NxLogDebugWriter s_debugWriter = NULL;
62 static DebugTagTree* volatile tagTreeActive = new DebugTagTree();
63 static DebugTagTree* volatile tagTreeSecondary = new DebugTagTree();
64 static MUTEX m_mutexDebugTagTreeWrite = INVALID_MUTEX_HANDLE;
65
66 /**
67 * Swaps tag tree pointers and waits till reader count drops to 0
68 */
69 static inline void SwapAndWait()
70 {
71 tagTreeSecondary = InterlockedExchangeObjectPointer(&tagTreeActive, tagTreeSecondary);
72 ThreadSleepMs(10);
73
74 // Wait for tree reader count to drop to 0
75 while(tagTreeSecondary->getReaderCount() > 0)
76 ThreadSleepMs(10);
77 }
78
79 /**
80 * Set debug level
81 */
82 void LIBNETXMS_EXPORTABLE nxlog_set_debug_level(int level)
83 {
84 if ((level >= 0) && (level <= 9))
85 {
86 MutexLock(m_mutexDebugTagTreeWrite);
87 tagTreeSecondary->setRootDebugLevel(level); // Update the secondary tree
88 SwapAndWait();
89 tagTreeSecondary->setRootDebugLevel(level); // Update the previously active tree
90 MutexUnlock(m_mutexDebugTagTreeWrite);
91 }
92 }
93
94 /**
95 * Set debug level for tag
96 */
97 void LIBNETXMS_EXPORTABLE nxlog_set_debug_level_tag(const TCHAR *tag, int level)
98 {
99 if ((tag == NULL) || !_tcscmp(tag, _T("*")))
100 {
101 nxlog_set_debug_level(level);
102 }
103 else
104 {
105 MutexLock(m_mutexDebugTagTreeWrite);
106 if ((level >= 0) && (level <= 9))
107 {
108 tagTreeSecondary->add(tag, level);
109 SwapAndWait();
110 tagTreeSecondary->add(tag, level);
111 }
112 else if (level < 0)
113 {
114 tagTreeSecondary->remove(tag);
115 SwapAndWait();
116 tagTreeSecondary->remove(tag);
117 }
118 MutexUnlock(m_mutexDebugTagTreeWrite);
119 }
120 }
121
122 /**
123 * Get current debug level
124 */
125 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level()
126 {
127 return tagTreeActive->getRootDebugLevel();
128 }
129
130 /**
131 * Get current debug level for tag
132 */
133 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level_tag(const TCHAR *tag)
134 {
135 return tagTreeActive->getDebugLevel(tag);
136 }
137
138 /**
139 * Get current debug level for tag
140 */
141 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level_tag_object(const TCHAR *tag, UINT32 objectId)
142 {
143 TCHAR fullTag[256];
144 _sntprintf(fullTag, 256, _T("%s.%u"), tag, objectId);
145 return tagTreeActive->getDebugLevel(fullTag);
146 }
147
148 /**
149 * Get all configured debug tags
150 */
151 ObjectArray<DebugTagInfo> LIBNETXMS_EXPORTABLE *nxlog_get_all_debug_tags()
152 {
153 return tagTreeActive->getAllTags();
154 }
155
156 /**
157 * Set additional debug writer callback. It will be called for each line written with nxlog_debug.
158 */
159 extern "C" void LIBNETXMS_EXPORTABLE nxlog_set_debug_writer(NxLogDebugWriter writer)
160 {
161 s_debugWriter = writer;
162 }
163
164 /**
165 * Format current time for output
166 */
167 static TCHAR *FormatLogTimestamp(TCHAR *buffer)
168 {
169 INT64 now = GetCurrentTimeMs();
170 time_t t = now / 1000;
171 #if HAVE_LOCALTIME_R
172 struct tm ltmBuffer;
173 struct tm *loc = localtime_r(&t, &ltmBuffer);
174 #else
175 struct tm *loc = localtime(&t);
176 #endif
177 _tcsftime(buffer, 32, _T("%Y.%m.%d %H:%M:%S"), loc);
178 _sntprintf(&buffer[19], 8, _T(".%03d"), (int)(now % 1000));
179 return buffer;
180 }
181
182 /**
183 * Set timestamp of start of the current day
184 */
185 static void SetDayStart()
186 {
187 time_t now = time(NULL);
188 struct tm dayStart;
189 #if HAVE_LOCALTIME_R
190 localtime_r(&now, &dayStart);
191 #else
192 struct tm *ltm = localtime(&now);
193 memcpy(&dayStart, ltm, sizeof(struct tm));
194 #endif
195 dayStart.tm_hour = 0;
196 dayStart.tm_min = 0;
197 dayStart.tm_sec = 0;
198 m_currentDayStart = mktime(&dayStart);
199 }
200
201 /**
202 * Set log rotation policy
203 * Setting log size to 0 or mode to NXLOG_ROTATION_DISABLED disables log rotation
204 */
205 bool LIBNETXMS_EXPORTABLE nxlog_set_rotation_policy(int rotationMode, UINT64 maxLogSize, int historySize, const TCHAR *dailySuffix)
206 {
207 bool isValid = true;
208
209 if ((rotationMode >= 0) || (rotationMode <= 2))
210 {
211 s_rotationMode = rotationMode;
212 if (rotationMode == NXLOG_ROTATION_BY_SIZE)
213 {
214 if ((maxLogSize == 0) || (maxLogSize >= 1024))
215 {
216 s_maxLogSize = maxLogSize;
217 }
218 else
219 {
220 s_maxLogSize = 1024;
221 isValid = false;
222 }
223
224 if ((historySize >= 0) && (historySize <= MAX_LOG_HISTORY_SIZE))
225 {
226 s_logHistorySize = historySize;
227 }
228 else
229 {
230 if (historySize > MAX_LOG_HISTORY_SIZE)
231 s_logHistorySize = MAX_LOG_HISTORY_SIZE;
232 isValid = false;
233 }
234 }
235 else if (rotationMode == NXLOG_ROTATION_DAILY)
236 {
237 if ((dailySuffix != NULL) && (dailySuffix[0] != 0))
238 nx_strncpy(s_dailyLogSuffixTemplate, dailySuffix, sizeof(s_dailyLogSuffixTemplate) / sizeof(TCHAR));
239 SetDayStart();
240 }
241 }
242 else
243 {
244 isValid = false;
245 }
246
247 if (isValid)
248 nxlog_debug(0, _T("Log rotation policy set to %d (size=") UINT64_FMT _T(", count=%d)"), rotationMode, maxLogSize, historySize);
249
250 return isValid;
251 }
252
253 /**
254 * Set console writer
255 */
256 void LIBNETXMS_EXPORTABLE nxlog_set_console_writer(NxLogConsoleWriter writer)
257 {
258 m_consoleWriter = writer;
259 }
260
261 /**
262 * Rotate log
263 */
264 static bool RotateLog(bool needLock)
265 {
266 int i;
267 TCHAR oldName[MAX_PATH], newName[MAX_PATH];
268
269 if (s_flags & NXLOG_USE_SYSLOG)
270 return FALSE; // Cannot rotate system logs
271
272 if (needLock)
273 MutexLock(m_mutexLogAccess);
274
275 if ((m_logFileHandle != NULL) && (s_flags & NXLOG_IS_OPEN))
276 {
277 fclose(m_logFileHandle);
278 s_flags &= ~NXLOG_IS_OPEN;
279 }
280
281 if (s_rotationMode == NXLOG_ROTATION_BY_SIZE)
282 {
283 // Delete old files
284 for(i = MAX_LOG_HISTORY_SIZE; i >= s_logHistorySize; i--)
285 {
286 _sntprintf(oldName, MAX_PATH, _T("%s.%d"), m_logFileName, i);
287 _tunlink(oldName);
288 }
289
290 // Shift file names
291 for(; i >= 0; i--)
292 {
293 _sntprintf(oldName, MAX_PATH, _T("%s.%d"), m_logFileName, i);
294 _sntprintf(newName, MAX_PATH, _T("%s.%d"), m_logFileName, i + 1);
295 _trename(oldName, newName);
296 }
297
298 // Rename current log to name.0
299 _sntprintf(newName, MAX_PATH, _T("%s.0"), m_logFileName);
300 _trename(m_logFileName, newName);
301 }
302 else if (s_rotationMode == NXLOG_ROTATION_DAILY)
303 {
304 #if HAVE_LOCALTIME_R
305 struct tm ltmBuffer;
306 struct tm *loc = localtime_r(&m_currentDayStart, &ltmBuffer);
307 #else
308 struct tm *loc = localtime(&m_currentDayStart);
309 #endif
310 TCHAR buffer[64];
311 _tcsftime(buffer, 64, s_dailyLogSuffixTemplate, loc);
312
313 // Rename current log to name.suffix
314 _sntprintf(newName, MAX_PATH, _T("%s.%s"), m_logFileName, buffer);
315 _trename(m_logFileName, newName);
316
317 SetDayStart();
318 }
319
320 // Reopen log
321 #if HAVE_FOPEN64
322 m_logFileHandle = _tfopen64(m_logFileName, _T("w"));
323 #else
324 m_logFileHandle = _tfopen(m_logFileName, _T("w"));
325 #endif
326 if (m_logFileHandle != NULL)
327 {
328 s_flags |= NXLOG_IS_OPEN;
329 TCHAR buffer[32];
330 _ftprintf(m_logFileHandle, _T("%s Log file truncated.\n"), FormatLogTimestamp(buffer));
331 fflush(m_logFileHandle);
332 #ifndef _WIN32
333 int fd = fileno(m_logFileHandle);
334 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
335 #endif
336 }
337
338 if (needLock)
339 MutexUnlock(m_mutexLogAccess);
340
341 return (s_flags & NXLOG_IS_OPEN) ? true : false;
342 }
343
344 /**
345 * API for application to force log rotation
346 */
347 bool LIBNETXMS_EXPORTABLE nxlog_rotate()
348 {
349 return RotateLog(true);
350 }
351
352 /**
353 * Background writer thread
354 */
355 static THREAD_RESULT THREAD_CALL BackgroundWriterThread(void *arg)
356 {
357 bool stop = false;
358 while(!stop)
359 {
360 stop = ConditionWait(s_writerStopCondition, 1000);
361
362 // Check for new day start
363 time_t t = time(NULL);
364 if ((s_rotationMode == NXLOG_ROTATION_DAILY) && (t >= m_currentDayStart + 86400))
365 {
366 RotateLog(FALSE);
367 }
368
369 MutexLock(m_mutexLogAccess);
370 if (!s_logBuffer.isEmpty())
371 {
372 if (s_flags & NXLOG_PRINT_TO_STDOUT)
373 m_consoleWriter(_T("%s"), s_logBuffer.getBuffer());
374
375 size_t buflen = s_logBuffer.length();
376 char *data = s_logBuffer.getUTF8String();
377 s_logBuffer.clear();
378 MutexUnlock(m_mutexLogAccess);
379
380 if (s_flags & NXLOG_DEBUG_MODE)
381 {
382 char marker[64];
383 sprintf(marker, "##(" INT64_FMTA ")" INT64_FMTA " @" INT64_FMTA "\n",
384 (INT64)buflen, (INT64)strlen(data), GetCurrentTimeMs());
385 #ifdef _WIN32
386 fwrite(marker, 1, strlen(marker), m_logFileHandle);
387 #else
388 write(fileno(m_logFileHandle), marker, strlen(marker));
389 #endif
390 }
391
392 #ifdef _WIN32
393 fwrite(data, 1, strlen(data), m_logFileHandle);
394 #else
395 // write is used here because on linux fwrite is not working
396 // after calling fwprintf on a stream
397 size_t size = strlen(data);
398 size_t offset = 0;
399 do
400 {
401 int bw = write(fileno(m_logFileHandle), &data[offset], size);
402 if (bw < 0)
403 break;
404 size -= bw;
405 offset += bw;
406 } while(size > 0);
407 #endif
408 free(data);
409
410 // Check log size
411 if ((m_logFileHandle != NULL) && (s_rotationMode == NXLOG_ROTATION_BY_SIZE) && (s_maxLogSize != 0))
412 {
413 NX_STAT_STRUCT st;
414 NX_FSTAT(fileno(m_logFileHandle), &st);
415 if ((UINT64)st.st_size >= s_maxLogSize)
416 RotateLog(FALSE);
417 }
418 }
419 else
420 {
421 MutexUnlock(m_mutexLogAccess);
422 }
423 }
424 return THREAD_OK;
425 }
426
427 /**
428 * Initialize log
429 */
430 bool LIBNETXMS_EXPORTABLE nxlog_open(const TCHAR *logName, UINT32 flags,
431 const TCHAR *msgModule, unsigned int msgCount, const TCHAR **messages,
432 DWORD debugMsg, DWORD debugMsgTag, DWORD genericMsg)
433 {
434 s_flags = flags & 0x7FFFFFFF;
435 #ifdef _WIN32
436 m_msgModuleHandle = GetModuleHandle(msgModule);
437 #else
438 m_numMessages = msgCount;
439 m_messages = messages;
440 #endif
441 s_debugMsg = debugMsg;
442 s_debugMsgTag = debugMsgTag;
443 s_genericMsg = genericMsg;
444
445 if (s_flags & NXLOG_USE_SYSLOG)
446 {
447 #ifdef _WIN32
448 m_eventLogHandle = RegisterEventSource(NULL, logName);
449 if (m_eventLogHandle != NULL)
450 s_flags |= NXLOG_IS_OPEN;
451 #else
452 #ifdef UNICODE
453 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, logName, -1, s_syslogName, 64, NULL, NULL);
454 s_syslogName[63] = 0;
455 #else
456 nx_strncpy(s_syslogName, logName, 64);
457 #endif
458 openlog(s_syslogName, LOG_PID, LOG_DAEMON);
459 s_flags |= NXLOG_IS_OPEN;
460 #endif
461 }
462 else
463 {
464 TCHAR buffer[32];
465
466 nx_strncpy(m_logFileName, logName, MAX_PATH);
467 #if HAVE_FOPEN64
468 m_logFileHandle = _tfopen64(logName, _T("a"));
469 #else
470 m_logFileHandle = _tfopen(logName, _T("a"));
471 #endif
472 if (m_logFileHandle != NULL)
473 {
474 s_flags |= NXLOG_IS_OPEN;
475 _ftprintf(m_logFileHandle, _T("\n%s Log file opened (rotation policy %d, max size ") UINT64_FMT _T(")\n"),
476 FormatLogTimestamp(buffer), s_rotationMode, s_maxLogSize);
477 fflush(m_logFileHandle);
478
479 #ifndef _WIN32
480 int fd = fileno(m_logFileHandle);
481 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
482 #endif
483
484 if (s_flags & NXLOG_BACKGROUND_WRITER)
485 {
486 s_logBuffer.setAllocationStep(8192);
487 s_writerStopCondition = ConditionCreate(TRUE);
488 s_writerThread = ThreadCreateEx(BackgroundWriterThread, 0, NULL);
489 }
490 }
491
492 m_mutexLogAccess = MutexCreate();
493 m_mutexDebugTagTreeWrite = MutexCreate();
494 SetDayStart();
495 }
496 return (s_flags & NXLOG_IS_OPEN) ? true : false;
497 }
498
499 /**
500 * Close log
501 */
502 void LIBNETXMS_EXPORTABLE nxlog_close()
503 {
504 if (s_flags & NXLOG_IS_OPEN)
505 {
506 if (s_flags & NXLOG_USE_SYSLOG)
507 {
508 #ifdef _WIN32
509 DeregisterEventSource(m_eventLogHandle);
510 #else
511 closelog();
512 #endif
513 }
514 else
515 {
516 if (s_flags & NXLOG_BACKGROUND_WRITER)
517 {
518 ConditionSet(s_writerStopCondition);
519 ThreadJoin(s_writerThread);
520 ConditionDestroy(s_writerStopCondition);
521 }
522
523 if (m_logFileHandle != NULL)
524 fclose(m_logFileHandle);
525 if (m_mutexLogAccess != INVALID_MUTEX_HANDLE)
526 MutexDestroy(m_mutexLogAccess);
527 if (m_mutexDebugTagTreeWrite != INVALID_MUTEX_HANDLE)
528 MutexDestroy(m_mutexDebugTagTreeWrite);
529 }
530 s_flags &= ~NXLOG_IS_OPEN;
531 }
532 }
533
534 /**
535 * Write record to log file
536 */
537 static void WriteLogToFile(TCHAR *message, const WORD type)
538 {
539 const TCHAR *loglevel;
540 switch(type)
541 {
542 case NXLOG_ERROR:
543 loglevel = _T("*E* ");
544 break;
545 case NXLOG_WARNING:
546 loglevel = _T("*W* ");
547 break;
548 case NXLOG_INFO:
549 loglevel = _T("*I* ");
550 break;
551 case NXLOG_DEBUG:
552 loglevel = _T("*D* ");
553 break;
554 default:
555 loglevel = _T("*?* ");
556 break;
557 }
558
559 TCHAR buffer[64];
560 if (s_flags & NXLOG_BACKGROUND_WRITER)
561 {
562 MutexLock(m_mutexLogAccess);
563
564 FormatLogTimestamp(buffer);
565 s_logBuffer.append(buffer);
566 s_logBuffer.append(_T(" "));
567 s_logBuffer.append(loglevel);
568 s_logBuffer.append(message);
569
570 MutexUnlock(m_mutexLogAccess);
571 }
572 else
573 {
574 // Prevent simultaneous write to log file
575 MutexLock(m_mutexLogAccess);
576
577 // Check for new day start
578 time_t t = time(NULL);
579 if ((s_rotationMode == NXLOG_ROTATION_DAILY) && (t >= m_currentDayStart + 86400))
580 {
581 RotateLog(FALSE);
582 }
583
584 FormatLogTimestamp(buffer);
585 if (m_logFileHandle != NULL)
586 {
587 _ftprintf(m_logFileHandle, _T("%s %s%s"), buffer, loglevel, message);
588 fflush(m_logFileHandle);
589 }
590 if (s_flags & NXLOG_PRINT_TO_STDOUT)
591 m_consoleWriter(_T("%s %s%s"), buffer, loglevel, message);
592
593 // Check log size
594 if ((m_logFileHandle != NULL) && (s_rotationMode == NXLOG_ROTATION_BY_SIZE) && (s_maxLogSize != 0))
595 {
596 NX_STAT_STRUCT st;
597 NX_FSTAT(fileno(m_logFileHandle), &st);
598 if ((UINT64)st.st_size >= s_maxLogSize)
599 RotateLog(FALSE);
600 }
601
602 MutexUnlock(m_mutexLogAccess);
603 }
604 }
605
606 /**
607 * Format message (UNIX version)
608 */
609 #ifndef _WIN32
610
611 static TCHAR *FormatMessageUX(UINT32 dwMsgId, TCHAR **ppStrings)
612 {
613 const TCHAR *p;
614 TCHAR *pMsg;
615 int i, iSize, iLen;
616
617 if (dwMsgId >= m_numMessages)
618 {
619 // No message with this ID
620 pMsg = (TCHAR *)malloc(64 * sizeof(TCHAR));
621 _sntprintf(pMsg, 64, _T("MSG 0x%08X - Unable to find message text\n"), (unsigned int)dwMsgId);
622 }
623 else
624 {
625 iSize = (_tcslen(m_messages[dwMsgId]) + 2) * sizeof(TCHAR);
626 pMsg = (TCHAR *)malloc(iSize);
627
628 for(i = 0, p = m_messages[dwMsgId]; *p != 0; p++)
629 if (*p == _T('%'))
630 {
631 p++;
632 if ((*p >= _T('1')) && (*p <= _T('9')))
633 {
634 iLen = _tcslen(ppStrings[*p - _T('1')]);
635 iSize += iLen * sizeof(TCHAR);
636 pMsg = (TCHAR *)realloc(pMsg, iSize);
637 _tcscpy(&pMsg[i], ppStrings[*p - _T('1')]);
638 i += iLen;
639 }
640 else
641 {
642 if (*p == 0) // Handle single % character at the string end
643 break;
644 pMsg[i++] = *p;
645 }
646 }
647 else
648 {
649 pMsg[i++] = *p;
650 }
651 pMsg[i++] = _T('\n');
652 pMsg[i] = 0;
653 }
654
655 return pMsg;
656 }
657
658 #endif /* ! _WIN32 */
659
660 /**
661 * Write log record
662 * Parameters:
663 * msg - Message ID
664 * wType - Message type (see ReportEvent() for details)
665 * format - Parameter format string, each parameter represented by one character.
666 * The following format characters can be used:
667 * s - String (multibyte or UNICODE, depending on build)
668 * m - multibyte string
669 * u - UNICODE string
670 * d - Decimal integer
671 * x - Hex integer
672 * e - System error code (will appear in log as textual description)
673 * a - IP address in network byte order
674 * A - IPv6 address in network byte order
675 * H - IPv6 address in network byte order (will appear in [])
676 * I - pointer to InetAddress object
677 */
678 void LIBNETXMS_EXPORTABLE nxlog_write(DWORD msg, WORD wType, const char *format, ...)
679 {
680 va_list args;
681 TCHAR *strings[16], *pMsg, szBuffer[256];
682 int numStrings = 0;
683 UINT32 error;
684 #if defined(UNICODE) && !defined(_WIN32)
685 char *mbMsg;
686 #endif
687
688 if (!(s_flags & NXLOG_IS_OPEN) || (msg == 0))
689 return;
690
691 memset(strings, 0, sizeof(TCHAR *) * 16);
692
693 if (format != NULL)
694 {
695 va_start(args, format);
696
697 for(; (format[numStrings] != 0) && (numStrings < 16); numStrings++)
698 {
699 switch(format[numStrings])
700 {
701 case 's':
702 strings[numStrings] = _tcsdup(va_arg(args, TCHAR *));
703 break;
704 case 'm':
705 #ifdef UNICODE
706 strings[numStrings] = WideStringFromMBString(va_arg(args, char *));
707 #else
708 strings[numStrings] = strdup(va_arg(args, char *));
709 #endif
710 break;
711 case 'u':
712 #ifdef UNICODE
713 strings[numStrings] = wcsdup(va_arg(args, WCHAR *));
714 #else
715 strings[numStrings] = MBStringFromWideString(va_arg(args, WCHAR *));
716 #endif
717 break;
718 case 'd':
719 strings[numStrings] = (TCHAR *)malloc(16 * sizeof(TCHAR));
720 _sntprintf(strings[numStrings], 16, _T("%d"), (int)(va_arg(args, LONG)));
721 break;
722 case 'x':
723 strings[numStrings] = (TCHAR *)malloc(16 * sizeof(TCHAR));
724 _sntprintf(strings[numStrings], 16, _T("0x%08X"), (unsigned int)(va_arg(args, UINT32)));
725 break;
726 case 'a':
727 strings[numStrings] = (TCHAR *)malloc(20 * sizeof(TCHAR));
728 IpToStr(va_arg(args, UINT32), strings[numStrings]);
729 break;
730 case 'A':
731 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
732 Ip6ToStr(va_arg(args, BYTE *), strings[numStrings]);
733 break;
734 case 'H':
735 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
736 strings[numStrings][0] = _T('[');
737 Ip6ToStr(va_arg(args, BYTE *), strings[numStrings] + 1);
738 _tcscat(strings[numStrings], _T("]"));
739 break;
740 case 'I':
741 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
742 va_arg(args, InetAddress *)->toString(strings[numStrings]);
743 break;
744 case 'e':
745 error = va_arg(args, UINT32);
746 #ifdef _WIN32
747 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
748 FORMAT_MESSAGE_FROM_SYSTEM |
749 FORMAT_MESSAGE_IGNORE_INSERTS,
750 NULL, error,
751 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), // Default language
752 (LPTSTR)&pMsg, 0, NULL) > 0)
753 {
754 pMsg[_tcscspn(pMsg, _T("\r\n"))] = 0;
755 strings[numStrings] = (TCHAR *)malloc((_tcslen(pMsg) + 1) * sizeof(TCHAR));
756 _tcscpy(strings[numStrings], pMsg);
757 LocalFree(pMsg);
758 }
759 else
760 {
761 strings[numStrings] = (TCHAR *)malloc(64 * sizeof(TCHAR));
762 _sntprintf(strings[numStrings], 64, _T("MSG 0x%08X - Unable to find message text"), error);
763 }
764 #else /* _WIN32 */
765 #if HAVE_STRERROR_R
766 #if HAVE_POSIX_STRERROR_R
767 strings[numStrings] = (TCHAR *)malloc(256 * sizeof(TCHAR));
768 _tcserror_r((int)error, strings[numStrings], 256);
769 #else
770 strings[numStrings] = _tcsdup(_tcserror_r((int)error, szBuffer, 256));
771 #endif
772 #else
773 strings[numStrings] = _tcsdup(_tcserror((int)error));
774 #endif
775 #endif
776 break;
777 default:
778 strings[numStrings] = (TCHAR *)malloc(32 * sizeof(TCHAR));
779 _sntprintf(strings[numStrings], 32, _T("BAD FORMAT (0x%08X)"), (unsigned int)(va_arg(args, UINT32)));
780 break;
781 }
782 }
783 va_end(args);
784 }
785
786 #ifdef _WIN32
787 if (!(s_flags & NXLOG_USE_SYSLOG) || (s_flags & NXLOG_PRINT_TO_STDOUT))
788 {
789 LPVOID lpMsgBuf;
790
791 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
792 FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
793 m_msgModuleHandle, msg, 0, (LPTSTR)&lpMsgBuf, 0, (va_list *)strings) > 0)
794 {
795 TCHAR *pCR;
796
797 // Replace trailing CR/LF pair with LF
798 pCR = _tcschr((TCHAR *)lpMsgBuf, _T('\r'));
799 if (pCR != NULL)
800 {
801 *pCR = _T('\n');
802 pCR++;
803 *pCR = 0;
804 }
805 if (s_flags & NXLOG_USE_SYSLOG)
806 {
807 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), (TCHAR *)lpMsgBuf);
808 }
809 else
810 {
811 WriteLogToFile((TCHAR *)lpMsgBuf, wType);
812 }
813 LocalFree(lpMsgBuf);
814 }
815 else
816 {
817 TCHAR message[64];
818
819 _sntprintf(message, 64, _T("MSG 0x%08X - cannot format message"), msg);
820 if (s_flags & NXLOG_USE_SYSLOG)
821 {
822 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), message);
823 }
824 else
825 {
826 WriteLogToFile(message, wType);
827 }
828 }
829 }
830
831 if (s_flags & NXLOG_USE_SYSLOG)
832 {
833 ReportEvent(m_eventLogHandle, (wType == EVENTLOG_DEBUG_TYPE) ? EVENTLOG_INFORMATION_TYPE : wType, 0, msg, NULL, numStrings, 0, (const TCHAR **)strings, NULL);
834 }
835 #else /* _WIN32 */
836 pMsg = FormatMessageUX(msg, strings);
837 if (s_flags & NXLOG_USE_SYSLOG)
838 {
839 int level;
840
841 switch(wType)
842 {
843 case EVENTLOG_ERROR_TYPE:
844 level = LOG_ERR;
845 break;
846 case EVENTLOG_WARNING_TYPE:
847 level = LOG_WARNING;
848 break;
849 case EVENTLOG_INFORMATION_TYPE:
850 level = LOG_NOTICE;
851 break;
852 case EVENTLOG_DEBUG_TYPE:
853 level = LOG_DEBUG;
854 break;
855 default:
856 level = LOG_INFO;
857 break;
858 }
859 #ifdef UNICODE
860 mbMsg = MBStringFromWideString(pMsg);
861 syslog(level, "%s", mbMsg);
862 free(mbMsg);
863 #else
864 syslog(level, "%s", pMsg);
865 #endif
866
867 if (s_flags & NXLOG_PRINT_TO_STDOUT)
868 {
869 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), pMsg);
870 }
871 }
872 else
873 {
874 WriteLogToFile(pMsg, wType);
875 }
876 free(pMsg);
877 #endif /* _WIN32 */
878
879 while(--numStrings >= 0)
880 free(strings[numStrings]);
881 }
882
883 /**
884 * Write generic message to log (useful for warning and error messages generated within libraries)
885 */
886 void LIBNETXMS_EXPORTABLE nxlog_write_generic(WORD type, const TCHAR *format, ...)
887 {
888 va_list args;
889 va_start(args, format);
890 TCHAR msg[8192];
891 _vsntprintf(msg, 8192, format, args);
892 va_end(args);
893 nxlog_write(s_genericMsg, type, "s", msg);
894 }
895
896 /**
897 * Write debug message
898 */
899 void LIBNETXMS_EXPORTABLE nxlog_debug(int level, const TCHAR *format, ...)
900 {
901 if (level > nxlog_get_debug_level_tag(_T("*")))
902 return;
903
904 va_list args;
905 va_start(args, format);
906 TCHAR buffer[8192];
907 _vsntprintf(buffer, 8192, format, args);
908 va_end(args);
909 nxlog_write(s_debugMsg, NXLOG_DEBUG, "s", buffer);
910
911 if (s_debugWriter != NULL)
912 s_debugWriter(NULL, buffer);
913 }
914
915 /**
916 * Write debug message
917 */
918 void LIBNETXMS_EXPORTABLE nxlog_debug2(int level, const TCHAR *format, va_list args)
919 {
920 if (level > nxlog_get_debug_level_tag(_T("*")))
921 return;
922
923 TCHAR buffer[8192];
924 _vsntprintf(buffer, 8192, format, args);
925 nxlog_write(s_debugMsg, NXLOG_DEBUG, "s", buffer);
926
927 if (s_debugWriter != NULL)
928 s_debugWriter(NULL, buffer);
929 }
930
931 /**
932 * Write debug message with tag
933 */
934 static void nxlog_debug_tag_internal(const TCHAR *tag, int level, const TCHAR *format, va_list args)
935 {
936 TCHAR tagf[20];
937 int i;
938 for(i = 0; (i < 19) && tag[i] != 0; i++)
939 tagf[i] = tag[i];
940 for(; i < 19; i++)
941 tagf[i] = ' ';
942 tagf[i] = 0;
943
944 TCHAR buffer[8192];
945 _vsntprintf(buffer, 8192, format, args);
946 nxlog_write(s_debugMsgTag, NXLOG_DEBUG, "ss", tagf, buffer);
947
948 if (s_debugWriter != NULL)
949 s_debugWriter(tag, buffer);
950 }
951
952 /**
953 * Write debug message with tag
954 */
955 void LIBNETXMS_EXPORTABLE nxlog_debug_tag(const TCHAR *tag, int level, const TCHAR *format, ...)
956 {
957 if (level > nxlog_get_debug_level_tag(tag))
958 return;
959
960 va_list args;
961 va_start(args, format);
962 nxlog_debug_tag_internal(tag, level, format, args);
963 va_end(args);
964 }
965
966 /**
967 * Write debug message with tag
968 */
969 void LIBNETXMS_EXPORTABLE nxlog_debug_tag2(const TCHAR *tag, int level, const TCHAR *format, va_list args)
970 {
971 if (level > nxlog_get_debug_level_tag(tag))
972 return;
973
974 nxlog_debug_tag_internal(tag, level, format, args);
975 }
976
977 /**
978 * Write debug message with tag and object ID (added as last part of a tag)
979 */
980 void LIBNETXMS_EXPORTABLE nxlog_debug_tag_object(const TCHAR *tag, UINT32 objectId, int level, const TCHAR *format, ...)
981 {
982 TCHAR fullTag[256];
983 _sntprintf(fullTag, 256, _T("%s.%u"), tag, objectId);
984 if (level > nxlog_get_debug_level_tag(fullTag))
985 return;
986
987 va_list args;
988 va_start(args, format);
989 nxlog_debug_tag_internal(fullTag, level, format, args);
990 va_end(args);
991 }
992
993 /**
994 * Write debug message with tag and object ID (added as last part of a tag)
995 */
996 void LIBNETXMS_EXPORTABLE nxlog_debug_tag_object2(const TCHAR *tag, UINT32 objectId, int level, const TCHAR *format, va_list args)
997 {
998 TCHAR fullTag[256];
999 _sntprintf(fullTag, 256, _T("%s.%u"), tag, objectId);
1000 if (level > nxlog_get_debug_level_tag(fullTag))
1001 return;
1002
1003 nxlog_debug_tag_internal(fullTag, level, format, args);
1004 }